envio 3.5.0-alpha.1 → 3.5.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +10 -6
- package/package.json +6 -6
- package/src/BatchProcessing.res +13 -0
- package/src/BatchProcessing.res.mjs +4 -0
- package/src/ChainFetching.res +5 -5
- package/src/ChainFetching.res.mjs +3 -4
- package/src/ChainState.res +38 -21
- package/src/ChainState.res.mjs +17 -24
- package/src/ChainState.resi +1 -5
- package/src/Change.res +3 -3
- package/src/Config.res +0 -4
- package/src/Config.res.mjs +0 -10
- package/src/Core.res +3 -0
- package/src/EntityId.res +15 -0
- package/src/EntityId.res.mjs +9 -0
- package/src/EventConfigBuilder.res +1 -58
- package/src/EventConfigBuilder.res.mjs +1 -33
- package/src/FetchState.res +345 -459
- package/src/FetchState.res.mjs +269 -430
- package/src/HandlerRegister.res +45 -0
- package/src/HandlerRegister.res.mjs +43 -0
- package/src/InMemoryStore.res +11 -4
- package/src/InMemoryTable.res +12 -10
- package/src/InMemoryTable.res.mjs +6 -5
- package/src/IndexerState.res +59 -2
- package/src/IndexerState.res.mjs +48 -3
- package/src/IndexerState.resi +2 -0
- package/src/Internal.res +17 -8
- package/src/LogSelection.res +2 -1
- package/src/Metrics.res +37 -6
- package/src/Metrics.res.mjs +5 -1
- package/src/Persistence.res +1 -1
- package/src/PgStorage.res +55 -22
- package/src/PgStorage.res.mjs +25 -17
- package/src/SimulateItems.res +2 -2
- package/src/TestIndexer.res +6 -5
- package/src/TestIndexer.res.mjs +3 -2
- package/src/UserContext.res +3 -3
- package/src/Writing.res +12 -2
- package/src/Writing.res.mjs +13 -2
- package/src/bindings/ClickHouse.res +5 -3
- package/src/bindings/ClickHouse.res.mjs +2 -5
- package/src/bindings/Vitest.res +22 -1
- package/src/bindings/Vitest.res.mjs +15 -0
- package/src/db/EntityHistory.res +16 -7
- package/src/db/EntityHistory.res.mjs +7 -6
- package/src/db/Table.res +40 -9
- package/src/db/Table.res.mjs +44 -6
- package/src/sources/AddressSet.res +48 -0
- package/src/sources/AddressSet.res.mjs +11 -0
- package/src/sources/AddressStore.res +152 -0
- package/src/sources/AddressStore.res.mjs +97 -0
- package/src/sources/EvmChain.res +3 -0
- package/src/sources/EvmChain.res.mjs +4 -2
- package/src/sources/EvmHyperSyncSource.res +6 -3
- package/src/sources/EvmHyperSyncSource.res.mjs +3 -3
- package/src/sources/EvmRpcClient.res +6 -3
- package/src/sources/EvmRpcClient.res.mjs +3 -3
- package/src/sources/FuelHyperSync.res +4 -3
- package/src/sources/FuelHyperSync.res.mjs +3 -3
- package/src/sources/FuelHyperSync.resi +2 -1
- package/src/sources/FuelHyperSyncClient.res +12 -6
- package/src/sources/FuelHyperSyncClient.res.mjs +2 -2
- package/src/sources/FuelHyperSyncSource.res +7 -4
- package/src/sources/FuelHyperSyncSource.res.mjs +3 -3
- package/src/sources/HyperSync.res +6 -4
- package/src/sources/HyperSync.res.mjs +2 -3
- package/src/sources/HyperSync.resi +1 -1
- package/src/sources/HyperSyncClient.res +18 -9
- package/src/sources/HyperSyncClient.res.mjs +4 -4
- package/src/sources/RpcSource.res +15 -10
- package/src/sources/RpcSource.res.mjs +3 -4
- package/src/sources/SimulateSource.res +26 -11
- package/src/sources/SimulateSource.res.mjs +13 -5
- package/src/sources/Source.res +4 -2
- package/src/sources/SourceManager.res +2 -3
- package/src/sources/SourceManager.res.mjs +2 -3
- package/src/sources/Svm.res +1 -2
- package/src/sources/Svm.res.mjs +1 -1
- package/src/sources/SvmHyperSyncClient.res +12 -3
- package/src/sources/SvmHyperSyncClient.res.mjs +2 -2
- package/src/sources/SvmHyperSyncSource.res +10 -5
- package/src/sources/SvmHyperSyncSource.res.mjs +5 -4
- package/src/IndexingAddresses.res +0 -151
- package/src/IndexingAddresses.res.mjs +0 -130
- package/src/IndexingAddresses.resi +0 -39
package/src/Metrics.res
CHANGED
|
@@ -98,12 +98,19 @@ type sourceHeightMetrics = {
|
|
|
98
98
|
|
|
99
99
|
type t = {
|
|
100
100
|
startTime: Date.t,
|
|
101
|
+
// Wall clock when this snapshot was built, so a scrape can be dated.
|
|
102
|
+
metricTime: Date.t,
|
|
103
|
+
// Measured monotonically, not as metricTime - startTime: a wall-clock
|
|
104
|
+
// correction would otherwise skew the counter shares derived from it.
|
|
105
|
+
elapsedSeconds: float,
|
|
101
106
|
targetBufferSize: int,
|
|
102
107
|
isInReorgThreshold: bool,
|
|
103
108
|
rollbackEnabled: bool,
|
|
104
109
|
maxBatchSize: int,
|
|
105
110
|
preloadSeconds: float,
|
|
106
111
|
processingSeconds: float,
|
|
112
|
+
processingStalledOnFetchSeconds: float,
|
|
113
|
+
processingStalledOnStorageWriteSeconds: float,
|
|
107
114
|
rollbackSeconds: float,
|
|
108
115
|
rollbackCount: int,
|
|
109
116
|
rollbackEventsCount: float,
|
|
@@ -241,6 +248,24 @@ let renderMetrics = (b: builder, metrics: t) => {
|
|
|
241
248
|
byLabels->Dict.toArray
|
|
242
249
|
}
|
|
243
250
|
|
|
251
|
+
b->single(
|
|
252
|
+
~name="envio_process_start_time_seconds",
|
|
253
|
+
~help="Start time of the process since unix epoch in seconds.",
|
|
254
|
+
~kind="gauge",
|
|
255
|
+
~value=metrics.startTime->Date.getTime /. 1000.,
|
|
256
|
+
)
|
|
257
|
+
b->single(
|
|
258
|
+
~name="envio_process_metric_time_seconds",
|
|
259
|
+
~help="The time these metrics were collected. Use it to tell how fresh a snapshot is, or to measure rates between two snapshots.",
|
|
260
|
+
~kind="gauge",
|
|
261
|
+
~value=metrics.metricTime->Date.getTime /. 1000.,
|
|
262
|
+
)
|
|
263
|
+
b->single(
|
|
264
|
+
~name="envio_process_elapsed_seconds",
|
|
265
|
+
~help="How long the indexer has been running. Divide a cumulative seconds metric by this to get the share of the run it took, eg envio_processing_seconds for time spent in event handlers.",
|
|
266
|
+
~kind="gauge",
|
|
267
|
+
~value=metrics.elapsedSeconds,
|
|
268
|
+
)
|
|
244
269
|
b->single(
|
|
245
270
|
~name="envio_preload_seconds",
|
|
246
271
|
~help="Cumulative time spent on preloading entities during batch processing.",
|
|
@@ -253,6 +278,18 @@ let renderMetrics = (b: builder, metrics: t) => {
|
|
|
253
278
|
~kind="counter",
|
|
254
279
|
~value=metrics.processingSeconds,
|
|
255
280
|
)
|
|
281
|
+
b->single(
|
|
282
|
+
~name="envio_processing_stalled_on_fetch_seconds",
|
|
283
|
+
~help="Time the indexer had nothing to process while waiting for events to be fetched. A high rate means fetching is the bottleneck: check the data-source latency and whether it can be queried with more concurrency. Waiting at the chain head for new blocks is not counted.",
|
|
284
|
+
~kind="counter",
|
|
285
|
+
~value=metrics.processingStalledOnFetchSeconds,
|
|
286
|
+
)
|
|
287
|
+
b->single(
|
|
288
|
+
~name="envio_processing_stalled_on_storage_write_seconds",
|
|
289
|
+
~help="Time the indexer paused processing because too many changes were still waiting to be written. A high rate means storage writes are the bottleneck: check envio_storage_write_seconds and the database performance.",
|
|
290
|
+
~kind="counter",
|
|
291
|
+
~value=metrics.processingStalledOnStorageWriteSeconds,
|
|
292
|
+
)
|
|
256
293
|
b->series(
|
|
257
294
|
~name="envio_progress_ready",
|
|
258
295
|
~help="Whether the chain is fully synced to the head.",
|
|
@@ -346,12 +383,6 @@ let renderMetrics = (b: builder, metrics: t) => {
|
|
|
346
383
|
~entries=chains,
|
|
347
384
|
~value=m => m.sourceBlockNumber->Int.toFloat,
|
|
348
385
|
)
|
|
349
|
-
b->single(
|
|
350
|
-
~name="envio_process_start_time_seconds",
|
|
351
|
-
~help="Start time of the process since unix epoch in seconds.",
|
|
352
|
-
~kind="gauge",
|
|
353
|
-
~value=metrics.startTime->Date.getTime /. 1000.,
|
|
354
|
-
)
|
|
355
386
|
b->series(
|
|
356
387
|
~name="envio_indexing_concurrency",
|
|
357
388
|
~help="The number of executing concurrent queries to the chain data-source.",
|
package/src/Metrics.res.mjs
CHANGED
|
@@ -108,8 +108,13 @@ function renderMetrics(b, metrics) {
|
|
|
108
108
|
}
|
|
109
109
|
});
|
|
110
110
|
let sources = Object.entries(byLabels$1);
|
|
111
|
+
single(b, "envio_process_start_time_seconds", "Start time of the process since unix epoch in seconds.", "gauge", metrics.startTime.getTime() / 1000);
|
|
112
|
+
single(b, "envio_process_metric_time_seconds", "The time these metrics were collected. Use it to tell how fresh a snapshot is, or to measure rates between two snapshots.", "gauge", metrics.metricTime.getTime() / 1000);
|
|
113
|
+
single(b, "envio_process_elapsed_seconds", "How long the indexer has been running. Divide a cumulative seconds metric by this to get the share of the run it took, eg envio_processing_seconds for time spent in event handlers.", "gauge", metrics.elapsedSeconds);
|
|
111
114
|
single(b, "envio_preload_seconds", "Cumulative time spent on preloading entities during batch processing.", "counter", metrics.preloadSeconds);
|
|
112
115
|
single(b, "envio_processing_seconds", "Cumulative time spent executing event handlers during batch processing.", "counter", metrics.processingSeconds);
|
|
116
|
+
single(b, "envio_processing_stalled_on_fetch_seconds", "Time the indexer had nothing to process while waiting for events to be fetched. A high rate means fetching is the bottleneck: check the data-source latency and whether it can be queried with more concurrency. Waiting at the chain head for new blocks is not counted.", "counter", metrics.processingStalledOnFetchSeconds);
|
|
117
|
+
single(b, "envio_processing_stalled_on_storage_write_seconds", "Time the indexer paused processing because too many changes were still waiting to be written. A high rate means storage writes are the bottleneck: check envio_storage_write_seconds and the database performance.", "counter", metrics.processingStalledOnStorageWriteSeconds);
|
|
113
118
|
series(b, "envio_progress_ready", "Whether the chain is fully synced to the head.", "gauge", chains, m => {
|
|
114
119
|
if (m.isReady) {
|
|
115
120
|
return 1;
|
|
@@ -129,7 +134,6 @@ function renderMetrics(b, metrics) {
|
|
|
129
134
|
series(b, "envio_fetching_block_range_events_total", "Cumulative number of events fetched across all block range operations.", "counter", chains, m => m.blockRangeFetchedEvents);
|
|
130
135
|
series(b, "envio_fetching_block_range_size", "Cumulative number of blocks covered across all block range fetch operations.", "counter", chains, m => m.blockRangeFetchedBlocks);
|
|
131
136
|
series(b, "envio_indexing_known_height", "The latest known block number reported by the active indexing source. This value may lag behind the actual chain height, as it is updated only when needed.", "gauge", chains, m => m.sourceBlockNumber);
|
|
132
|
-
single(b, "envio_process_start_time_seconds", "Start time of the process since unix epoch in seconds.", "gauge", metrics.startTime.getTime() / 1000);
|
|
133
137
|
series(b, "envio_indexing_concurrency", "The number of executing concurrent queries to the chain data-source.", "gauge", chains, m => m.concurrency);
|
|
134
138
|
series(b, "envio_indexing_partitions", "The number of partitions used to split fetching logic by addresses and block ranges.", "gauge", chains, m => m.partitionsCount);
|
|
135
139
|
series(b, "envio_indexing_idle_seconds", "The time the indexer source syncing has been idle. A high value may indicate the source sync is a bottleneck.", "counter", chains, m => m.idleSeconds);
|
package/src/Persistence.res
CHANGED
|
@@ -121,7 +121,7 @@ type storage = {
|
|
|
121
121
|
getRollbackData: (
|
|
122
122
|
~entityConfig: Internal.entityConfig,
|
|
123
123
|
~rollbackTargetCheckpointId: Internal.checkpointId,
|
|
124
|
-
) => promise<(array<
|
|
124
|
+
) => promise<(array<EntityId.t>, array<unknown>)>,
|
|
125
125
|
// Write batch to storage
|
|
126
126
|
writeBatch: (
|
|
127
127
|
~batch: Batch.t,
|
package/src/PgStorage.res
CHANGED
|
@@ -158,7 +158,10 @@ let getEntityHistory = (~entityConfig: Internal.entityConfig): EntityHistory.pgE
|
|
|
158
158
|
~fields=dataFields->Array.concat([checkpointIdField, actionField]),
|
|
159
159
|
)
|
|
160
160
|
|
|
161
|
-
let setChangeSchema = EntityHistory.makeSetUpdateSchema(
|
|
161
|
+
let setChangeSchema = EntityHistory.makeSetUpdateSchema(
|
|
162
|
+
~idSchema=entityConfig.table->Table.getIdSchema,
|
|
163
|
+
entityConfig.schema,
|
|
164
|
+
)
|
|
162
165
|
|
|
163
166
|
{
|
|
164
167
|
EntityHistory.table,
|
|
@@ -355,8 +358,8 @@ let makeDeleteByIdQuery = (~pgSchema, ~tableName) => {
|
|
|
355
358
|
`DELETE FROM "${pgSchema}"."${tableName}" WHERE id = $1;`
|
|
356
359
|
}
|
|
357
360
|
|
|
358
|
-
let makeDeleteByIdsQuery = (~pgSchema, ~tableName) => {
|
|
359
|
-
`DELETE FROM "${pgSchema}"."${tableName}" WHERE id = ANY($1
|
|
361
|
+
let makeDeleteByIdsQuery = (~pgSchema, ~tableName, ~idPgType) => {
|
|
362
|
+
`DELETE FROM "${pgSchema}"."${tableName}" WHERE id = ANY($1::${idPgType}[]);`
|
|
360
363
|
}
|
|
361
364
|
|
|
362
365
|
let makeLoadAllQuery = (~pgSchema, ~tableName) => {
|
|
@@ -744,18 +747,26 @@ let getConnectedPsqlExec = {
|
|
|
744
747
|
}
|
|
745
748
|
}
|
|
746
749
|
|
|
747
|
-
let deleteByIdsOrThrow = async (sql, ~pgSchema, ~ids
|
|
750
|
+
let deleteByIdsOrThrow = async (sql, ~pgSchema, ~ids: array<EntityId.t>, ~table: Table.table) => {
|
|
751
|
+
// A JSON array of the serialized ids. For a single id the query binds it as
|
|
752
|
+
// `$1` directly (the array is the positional-params array); for many it binds
|
|
753
|
+
// the whole array to `$1` behind an `ANY(...)`.
|
|
754
|
+
let idsJson = table->Table.encodeIdsToJson(ids)
|
|
748
755
|
switch await (
|
|
749
756
|
switch ids {
|
|
750
757
|
| [_] =>
|
|
751
758
|
sql->Postgres.preparedUnsafe(
|
|
752
759
|
makeDeleteByIdQuery(~pgSchema, ~tableName=table.tableName),
|
|
753
|
-
|
|
760
|
+
idsJson->Obj.magic,
|
|
754
761
|
)
|
|
755
762
|
| _ =>
|
|
756
763
|
sql->Postgres.preparedUnsafe(
|
|
757
|
-
makeDeleteByIdsQuery(
|
|
758
|
-
|
|
764
|
+
makeDeleteByIdsQuery(
|
|
765
|
+
~pgSchema,
|
|
766
|
+
~tableName=table.tableName,
|
|
767
|
+
~idPgType=table->Table.getIdPgFieldType(~pgSchema),
|
|
768
|
+
),
|
|
769
|
+
[idsJson]->Obj.magic,
|
|
759
770
|
)
|
|
760
771
|
}
|
|
761
772
|
) {
|
|
@@ -811,9 +822,11 @@ let makeInsertDeleteUpdatesQuery = (~entityConfig: Internal.entityConfig, ~pgSch
|
|
|
811
822
|
~isNullable=false,
|
|
812
823
|
)
|
|
813
824
|
|
|
825
|
+
let idPgType = entityConfig.table->Table.getIdPgFieldType(~pgSchema)
|
|
826
|
+
|
|
814
827
|
`INSERT INTO "${pgSchema}"."${historyTableName}" (${allHistoryFieldNamesStr})
|
|
815
828
|
SELECT ${selectPartsStr}
|
|
816
|
-
FROM UNNEST($1
|
|
829
|
+
FROM UNNEST($1::${idPgType}[], $2::${checkpointIdPgType}[]) AS u(${Table.idFieldName}, ${EntityHistory.checkpointIdFieldName})`
|
|
817
830
|
}
|
|
818
831
|
|
|
819
832
|
let executeSet = (
|
|
@@ -912,17 +925,21 @@ let rec writeBatch = async (
|
|
|
912
925
|
// Single pass over the change log: track each id's latest change (the last
|
|
913
926
|
// one seen) and, when saving history, fan every non-diff change out to the
|
|
914
927
|
// history-table batches.
|
|
928
|
+
// Keyed/deduped in memory by the id's string key (toKey), while the
|
|
929
|
+
// batches sent to SQL keep the real id values so they serialize with the
|
|
930
|
+
// id column's type.
|
|
915
931
|
let latestChangeById = Dict.make()
|
|
916
932
|
let orderedIds = []
|
|
917
933
|
changes->Array.forEach(change => {
|
|
918
934
|
let entityId = change->Change.getEntityId
|
|
919
|
-
|
|
935
|
+
let entityKey = entityId->EntityId.toKey
|
|
936
|
+
if latestChangeById->Utils.Dict.dangerouslyGetNonOption(entityKey)->Option.isNone {
|
|
920
937
|
orderedIds->Array.push(entityId)
|
|
921
938
|
}
|
|
922
|
-
latestChangeById->Dict.set(
|
|
939
|
+
latestChangeById->Dict.set(entityKey, change)
|
|
923
940
|
if shouldSaveHistory {
|
|
924
941
|
if Some(change->Change.getCheckpointId) === diffCheckpointId {
|
|
925
|
-
idsWithDiff->Utils.Set.add(
|
|
942
|
+
idsWithDiff->Utils.Set.add(entityKey)->ignore
|
|
926
943
|
} else {
|
|
927
944
|
switch change {
|
|
928
945
|
| Delete({entityId, checkpointId}) =>
|
|
@@ -936,13 +953,14 @@ let rec writeBatch = async (
|
|
|
936
953
|
|
|
937
954
|
let backfillHistoryIds = Utils.Set.make()
|
|
938
955
|
orderedIds->Array.forEach(entityId => {
|
|
939
|
-
|
|
956
|
+
let entityKey = entityId->EntityId.toKey
|
|
957
|
+
switch latestChangeById->Dict.getUnsafe(entityKey) {
|
|
940
958
|
| Set({entity}) => entitiesToSet->Array.push(entity)
|
|
941
959
|
| Delete({entityId}) => idsToDelete->Array.push(entityId)
|
|
942
960
|
}
|
|
943
961
|
|
|
944
962
|
// An id needs a history backfill iff none of its changes is the diff.
|
|
945
|
-
if shouldSaveHistory && !(idsWithDiff->Utils.Set.has(
|
|
963
|
+
if shouldSaveHistory && !(idsWithDiff->Utils.Set.has(entityKey)) {
|
|
946
964
|
backfillHistoryIds->Utils.Set.add(entityId)->ignore
|
|
947
965
|
}
|
|
948
966
|
})
|
|
@@ -962,7 +980,7 @@ let rec writeBatch = async (
|
|
|
962
980
|
await EntityHistory.backfillHistory(
|
|
963
981
|
sql,
|
|
964
982
|
~pgSchema,
|
|
965
|
-
~
|
|
983
|
+
~table=entityConfig.table,
|
|
966
984
|
~entityIndex=entityConfig.index,
|
|
967
985
|
~ids=backfillHistoryIds->Utils.Set.toArray,
|
|
968
986
|
)
|
|
@@ -974,7 +992,7 @@ let rec writeBatch = async (
|
|
|
974
992
|
->Postgres.preparedUnsafe(
|
|
975
993
|
makeInsertDeleteUpdatesQuery(~entityConfig, ~pgSchema),
|
|
976
994
|
(
|
|
977
|
-
batchDeleteEntityIds,
|
|
995
|
+
entityConfig.table->Table.encodeIdsToJson(batchDeleteEntityIds),
|
|
978
996
|
batchDeleteCheckpointIds->Utils.BigInt.arrayToStringArray,
|
|
979
997
|
)->Obj.magic,
|
|
980
998
|
)
|
|
@@ -1230,10 +1248,25 @@ let makeGetRollbackRemovedIdsQuery = (~entityConfig: Internal.entityConfig, ~pgS
|
|
|
1230
1248
|
)`
|
|
1231
1249
|
}
|
|
1232
1250
|
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1251
|
+
// Memoized per table so the id is parsed with that entity's id schema (a
|
|
1252
|
+
// numeric id comes back from Postgres as a number, not a string) and the
|
|
1253
|
+
// schema's operations compile once rather than per rollback row.
|
|
1254
|
+
let rollbackRowStateSchema: Table.table => S.t<(
|
|
1255
|
+
EntityId.t,
|
|
1256
|
+
EntityHistory.RowAction.t,
|
|
1257
|
+
)> = Utils.WeakMap.memoize(table =>
|
|
1258
|
+
S.object(s => (
|
|
1259
|
+
s.field(Table.idFieldName, table->Table.getIdSchema),
|
|
1260
|
+
s.field(EntityHistory.changeFieldName, EntityHistory.RowAction.schema),
|
|
1261
|
+
))
|
|
1262
|
+
)
|
|
1263
|
+
|
|
1264
|
+
// Same reason as above for the id-only rows: both rollback queries must yield
|
|
1265
|
+
// ids in the entity's own representation, or the two halves of the diff would
|
|
1266
|
+
// disagree (Postgres hands back a NUMERIC id as a string, not a bigint).
|
|
1267
|
+
let rollbackRemovedIdsSchema: Table.table => S.t<array<EntityId.t>> = Utils.WeakMap.memoize(table =>
|
|
1268
|
+
S.array(S.object(s => s.field(Table.idFieldName, table->Table.getIdSchema)))
|
|
1269
|
+
)
|
|
1237
1270
|
|
|
1238
1271
|
let make = (
|
|
1239
1272
|
~sql: Postgres.sql,
|
|
@@ -1757,7 +1790,7 @@ SELECT id, chain_id, -1, -1, contract_name FROM unnest($1::text[],$2::int[],$3::
|
|
|
1757
1790
|
makeGetRollbackRemovedIdsQuery(~entityConfig, ~pgSchema),
|
|
1758
1791
|
[rollbackTargetCheckpointId->BigInt.toString]->(Utils.magic: array<string> => unknown),
|
|
1759
1792
|
)
|
|
1760
|
-
->(Utils.magic: promise<unknown> => promise<array<
|
|
1793
|
+
->(Utils.magic: promise<unknown> => promise<array<unknown>>),
|
|
1761
1794
|
// Get the latest pre-target row, including its SET or DELETE action.
|
|
1762
1795
|
sql
|
|
1763
1796
|
->Postgres.preparedUnsafe(
|
|
@@ -1767,10 +1800,10 @@ SELECT id, chain_id, -1, -1, contract_name FROM unnest($1::text[],$2::int[],$3::
|
|
|
1767
1800
|
->(Utils.magic: promise<unknown> => promise<array<unknown>>),
|
|
1768
1801
|
))
|
|
1769
1802
|
|
|
1770
|
-
let removedIds = removedIdRows->
|
|
1803
|
+
let removedIds = removedIdRows->S.parseOrThrow(rollbackRemovedIdsSchema(entityConfig.table))
|
|
1771
1804
|
let restoredEntitiesResult = []
|
|
1772
1805
|
rollbackRows->Array.forEach(row => {
|
|
1773
|
-
let (entityId, action) = row->S.parseOrThrow(rollbackRowStateSchema)
|
|
1806
|
+
let (entityId, action) = row->S.parseOrThrow(rollbackRowStateSchema(entityConfig.table))
|
|
1774
1807
|
switch action {
|
|
1775
1808
|
| SET => restoredEntitiesResult->Array.push(row)->ignore
|
|
1776
1809
|
| DELETE => removedIds->Array.push(entityId)->ignore
|
package/src/PgStorage.res.mjs
CHANGED
|
@@ -10,6 +10,7 @@ import * as Config from "./Config.res.mjs";
|
|
|
10
10
|
import * as Hasura from "./Hasura.res.mjs";
|
|
11
11
|
import * as Schema from "./db/Schema.res.mjs";
|
|
12
12
|
import * as Logging from "./Logging.res.mjs";
|
|
13
|
+
import * as EntityId from "./EntityId.res.mjs";
|
|
13
14
|
import * as Internal from "./Internal.res.mjs";
|
|
14
15
|
import Postgres from "postgres";
|
|
15
16
|
import * as ChainState from "./ChainState.res.mjs";
|
|
@@ -157,7 +158,7 @@ function getEntityHistory(entityConfig) {
|
|
|
157
158
|
checkpointIdField,
|
|
158
159
|
actionField
|
|
159
160
|
]), undefined);
|
|
160
|
-
let setChangeSchema = EntityHistory.makeSetUpdateSchema(entityConfig.schema);
|
|
161
|
+
let setChangeSchema = EntityHistory.makeSetUpdateSchema(Table.getIdSchema(entityConfig.table), entityConfig.schema);
|
|
161
162
|
let cache_setChangeSchemaRows = S$RescriptSchema.array(setChangeSchema);
|
|
162
163
|
let cache$1 = {
|
|
163
164
|
table: table,
|
|
@@ -292,8 +293,8 @@ function makeDeleteByIdQuery(pgSchema, tableName) {
|
|
|
292
293
|
return `DELETE FROM "` + pgSchema + `"."` + tableName + `" WHERE id = $1;`;
|
|
293
294
|
}
|
|
294
295
|
|
|
295
|
-
function makeDeleteByIdsQuery(pgSchema, tableName) {
|
|
296
|
-
return `DELETE FROM "` + pgSchema + `"."` + tableName + `" WHERE id = ANY($1
|
|
296
|
+
function makeDeleteByIdsQuery(pgSchema, tableName, idPgType) {
|
|
297
|
+
return `DELETE FROM "` + pgSchema + `"."` + tableName + `" WHERE id = ANY($1::` + idPgType + `[]);`;
|
|
297
298
|
}
|
|
298
299
|
|
|
299
300
|
function makeLoadAllQuery(pgSchema, tableName) {
|
|
@@ -547,9 +548,10 @@ async function getConnectedPsqlExec(pgUser, pgHost, pgDatabase, pgPort, containe
|
|
|
547
548
|
}
|
|
548
549
|
|
|
549
550
|
async function deleteByIdsOrThrow(sql, pgSchema, ids, table) {
|
|
551
|
+
let idsJson = Table.encodeIdsToJson(table, ids);
|
|
550
552
|
try {
|
|
551
553
|
await (
|
|
552
|
-
ids.length !== 1 ? sql.unsafe(makeDeleteByIdsQuery(pgSchema, table.tableName), [
|
|
554
|
+
ids.length !== 1 ? sql.unsafe(makeDeleteByIdsQuery(pgSchema, table.tableName, Table.getIdPgFieldType(table, pgSchema)), [idsJson], {prepare: true}) : sql.unsafe(makeDeleteByIdQuery(pgSchema, table.tableName), idsJson, {prepare: true})
|
|
553
555
|
);
|
|
554
556
|
return;
|
|
555
557
|
} catch (raw_exn) {
|
|
@@ -586,9 +588,10 @@ function makeInsertDeleteUpdatesQuery(entityConfig, pgSchema) {
|
|
|
586
588
|
});
|
|
587
589
|
let selectPartsStr = selectParts.join(", ");
|
|
588
590
|
let checkpointIdPgType = Table.getPgFieldType(EntityHistory.checkpointIdFieldType, pgSchema, false, false, false);
|
|
591
|
+
let idPgType = Table.getIdPgFieldType(entityConfig.table, pgSchema);
|
|
589
592
|
return `INSERT INTO "` + pgSchema + `"."` + historyTableName + `" (` + allHistoryFieldNamesStr + `)
|
|
590
593
|
SELECT ` + selectPartsStr + `
|
|
591
|
-
FROM UNNEST($1
|
|
594
|
+
FROM UNNEST($1::` + idPgType + `[], $2::` + checkpointIdPgType + `[]) AS u(` + Table.idFieldName + `, ` + EntityHistory.checkpointIdFieldName + `)`;
|
|
592
595
|
}
|
|
593
596
|
|
|
594
597
|
function executeSet(sql, items, dbFunction) {
|
|
@@ -648,15 +651,16 @@ async function writeBatch(sql, batch, pgSchema, rollback, isInReorgThreshold, co
|
|
|
648
651
|
let orderedIds = [];
|
|
649
652
|
param.changes.forEach(change => {
|
|
650
653
|
let entityId = change.entityId;
|
|
651
|
-
|
|
654
|
+
let entityKey = EntityId.toKey(entityId);
|
|
655
|
+
if (Stdlib_Option.isNone(latestChangeById[entityKey])) {
|
|
652
656
|
orderedIds.push(entityId);
|
|
653
657
|
}
|
|
654
|
-
latestChangeById[
|
|
658
|
+
latestChangeById[entityKey] = change;
|
|
655
659
|
if (!shouldSaveHistory) {
|
|
656
660
|
return;
|
|
657
661
|
}
|
|
658
662
|
if (change.checkpointId === diffCheckpointId) {
|
|
659
|
-
idsWithDiff.add(
|
|
663
|
+
idsWithDiff.add(entityKey);
|
|
660
664
|
return;
|
|
661
665
|
}
|
|
662
666
|
if (change.type === "SET") {
|
|
@@ -668,13 +672,14 @@ async function writeBatch(sql, batch, pgSchema, rollback, isInReorgThreshold, co
|
|
|
668
672
|
});
|
|
669
673
|
let backfillHistoryIds = new Set();
|
|
670
674
|
orderedIds.forEach(entityId => {
|
|
671
|
-
let
|
|
675
|
+
let entityKey = EntityId.toKey(entityId);
|
|
676
|
+
let match = latestChangeById[entityKey];
|
|
672
677
|
if (match.type === "SET") {
|
|
673
678
|
entitiesToSet.push(match.entity);
|
|
674
679
|
} else {
|
|
675
680
|
idsToDelete.push(match.entityId);
|
|
676
681
|
}
|
|
677
|
-
if (shouldSaveHistory && !idsWithDiff.has(
|
|
682
|
+
if (shouldSaveHistory && !idsWithDiff.has(entityKey)) {
|
|
678
683
|
backfillHistoryIds.add(entityId);
|
|
679
684
|
return;
|
|
680
685
|
}
|
|
@@ -685,11 +690,11 @@ async function writeBatch(sql, batch, pgSchema, rollback, isInReorgThreshold, co
|
|
|
685
690
|
let promises = [];
|
|
686
691
|
if (shouldSaveHistory) {
|
|
687
692
|
if (backfillHistoryIds.size !== 0) {
|
|
688
|
-
await EntityHistory.backfillHistory(sql, pgSchema, entityConfig.
|
|
693
|
+
await EntityHistory.backfillHistory(sql, pgSchema, entityConfig.table, entityConfig.index, Array.from(backfillHistoryIds));
|
|
689
694
|
}
|
|
690
695
|
if (Utils.$$Array.notEmpty(batchDeleteCheckpointIds)) {
|
|
691
696
|
promises.push(sql.unsafe(makeInsertDeleteUpdatesQuery(entityConfig, pgSchema), [
|
|
692
|
-
batchDeleteEntityIds,
|
|
697
|
+
Table.encodeIdsToJson(entityConfig.table, batchDeleteEntityIds),
|
|
693
698
|
Utils.$$BigInt.arrayToStringArray(batchDeleteCheckpointIds)
|
|
694
699
|
], {prepare: true}));
|
|
695
700
|
}
|
|
@@ -822,10 +827,12 @@ function makeGetRollbackRemovedIdsQuery(entityConfig, pgSchema) {
|
|
|
822
827
|
)`;
|
|
823
828
|
}
|
|
824
829
|
|
|
825
|
-
let rollbackRowStateSchema = S$RescriptSchema.object(s => [
|
|
826
|
-
s.f(Table.idFieldName,
|
|
830
|
+
let rollbackRowStateSchema = Utils.$$WeakMap.memoize(table => S$RescriptSchema.object(s => [
|
|
831
|
+
s.f(Table.idFieldName, Table.getIdSchema(table)),
|
|
827
832
|
s.f(EntityHistory.changeFieldName, EntityHistory.RowAction.schema)
|
|
828
|
-
]);
|
|
833
|
+
]));
|
|
834
|
+
|
|
835
|
+
let rollbackRemovedIdsSchema = Utils.$$WeakMap.memoize(table => S$RescriptSchema.array(S$RescriptSchema.object(s => s.f(Table.idFieldName, Table.getIdSchema(table)))));
|
|
829
836
|
|
|
830
837
|
function make(sql, pgHost, pgSchema, pgPort, pgUser, pgDatabase, pgPassword, isHasuraEnabled, sink, onInitialize, onNewTables) {
|
|
831
838
|
let containerName = "envio-postgres";
|
|
@@ -1147,10 +1154,10 @@ SELECT id, chain_id, -1, -1, contract_name FROM unnest($1::text[],$2::int[],$3::
|
|
|
1147
1154
|
sql.unsafe(makeGetRollbackRemovedIdsQuery(entityConfig, pgSchema), [rollbackTargetCheckpointId.toString()], {prepare: true}),
|
|
1148
1155
|
sql.unsafe(makeGetRollbackPreTargetRowsQuery(entityConfig, pgSchema), [rollbackTargetCheckpointId.toString()], {prepare: true})
|
|
1149
1156
|
]);
|
|
1150
|
-
let removedIds = match[0]
|
|
1157
|
+
let removedIds = S$RescriptSchema.parseOrThrow(match[0], rollbackRemovedIdsSchema(entityConfig.table));
|
|
1151
1158
|
let restoredEntitiesResult = [];
|
|
1152
1159
|
match[1].forEach(row => {
|
|
1153
|
-
let match = S$RescriptSchema.parseOrThrow(row, rollbackRowStateSchema);
|
|
1160
|
+
let match = S$RescriptSchema.parseOrThrow(row, rollbackRowStateSchema(entityConfig.table));
|
|
1154
1161
|
if (match[1] === "SET") {
|
|
1155
1162
|
restoredEntitiesResult.push(row);
|
|
1156
1163
|
return;
|
|
@@ -1297,6 +1304,7 @@ export {
|
|
|
1297
1304
|
makeGetRollbackPreTargetRowsQuery,
|
|
1298
1305
|
makeGetRollbackRemovedIdsQuery,
|
|
1299
1306
|
rollbackRowStateSchema,
|
|
1307
|
+
rollbackRemovedIdsSchema,
|
|
1300
1308
|
make,
|
|
1301
1309
|
makeStorageFromEnv,
|
|
1302
1310
|
makePersistenceFromConfig,
|
package/src/SimulateItems.res
CHANGED
|
@@ -343,8 +343,8 @@ let parse = (
|
|
|
343
343
|
// run here the way real routing does (one item per registration).
|
|
344
344
|
// Registrations are built the same way `HandlerRegister.finishRegistration`
|
|
345
345
|
// does at startup (not stubs), so the address filter and `where` behave
|
|
346
|
-
// identically to real indexing — the dead-input tracker relies on
|
|
347
|
-
//
|
|
346
|
+
// identically to real indexing — the dead-input tracker relies on the
|
|
347
|
+
// source's address gate actually dropping unrouted items. Drop registrations
|
|
348
348
|
// whose `where` excludes this chain (they wouldn't be fetched live), and
|
|
349
349
|
// ignore the bare handler-less fallback so a missing handler surfaces below
|
|
350
350
|
// instead of silently running nothing.
|
package/src/TestIndexer.res
CHANGED
|
@@ -22,7 +22,7 @@ type t<'processConfig> = {process: 'processConfig => promise<processResult>}
|
|
|
22
22
|
|
|
23
23
|
type entityChange = {
|
|
24
24
|
sets: array<unknown>,
|
|
25
|
-
deleted: array<
|
|
25
|
+
deleted: array<EntityId.t>,
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
type testIndexerState = {
|
|
@@ -136,11 +136,12 @@ let handleWriteBatch = (
|
|
|
136
136
|
switch change {
|
|
137
137
|
| Set({entityId, entity, checkpointId}) =>
|
|
138
138
|
// The store keeps decoded entities so load comparisons (bigint /
|
|
139
|
-
// BigDecimal) work on real values.
|
|
140
|
-
|
|
139
|
+
// BigDecimal) work on real values. Ids are keyed by their string form
|
|
140
|
+
// since they may be string/int/bigint.
|
|
141
|
+
entityDict->Dict.set(entityId->EntityId.toKey, entity)
|
|
141
142
|
entityChangeFor(checkpointId).sets->Array.push(entity->Utils.magic)->ignore
|
|
142
143
|
| Delete({entityId, checkpointId}) =>
|
|
143
|
-
Dict.delete(entityDict->Obj.magic, entityId)
|
|
144
|
+
Dict.delete(entityDict->Obj.magic, entityId->EntityId.toKey)
|
|
144
145
|
entityChangeFor(checkpointId).deleted->Array.push(entityId)->ignore
|
|
145
146
|
}
|
|
146
147
|
}
|
|
@@ -197,7 +198,7 @@ let handleWriteBatch = (
|
|
|
197
198
|
entityObj->Dict.set("sets", sets->(Utils.magic: array<unknown> => unknown))
|
|
198
199
|
}
|
|
199
200
|
if deleted->Array.length > 0 {
|
|
200
|
-
entityObj->Dict.set("deleted", deleted->(Utils.magic: array<
|
|
201
|
+
entityObj->Dict.set("deleted", deleted->(Utils.magic: array<EntityId.t> => unknown))
|
|
201
202
|
}
|
|
202
203
|
// Match the capitalized entity accessor the generated change types expose.
|
|
203
204
|
change->Dict.set(
|
package/src/TestIndexer.res.mjs
CHANGED
|
@@ -5,6 +5,7 @@ import * as Utils from "./Utils.res.mjs";
|
|
|
5
5
|
import * as Config from "./Config.res.mjs";
|
|
6
6
|
import * as Logging from "./Logging.res.mjs";
|
|
7
7
|
import * as ChainMap from "./ChainMap.res.mjs";
|
|
8
|
+
import * as EntityId from "./EntityId.res.mjs";
|
|
8
9
|
import * as Stdlib_Int from "@rescript/runtime/lib/es6/Stdlib_Int.js";
|
|
9
10
|
import * as IndexerLoop from "./IndexerLoop.res.mjs";
|
|
10
11
|
import * as Persistence from "./Persistence.res.mjs";
|
|
@@ -102,12 +103,12 @@ function handleWriteBatch(state, updatedEntities, checkpointIds, checkpointChain
|
|
|
102
103
|
let processChange = change => {
|
|
103
104
|
if (change.type === "SET") {
|
|
104
105
|
let entity = change.entity;
|
|
105
|
-
entityDict[change.entityId] = entity;
|
|
106
|
+
entityDict[EntityId.toKey(change.entityId)] = entity;
|
|
106
107
|
entityChangeFor(change.checkpointId).sets.push(entity);
|
|
107
108
|
return;
|
|
108
109
|
}
|
|
109
110
|
let entityId = change.entityId;
|
|
110
|
-
Stdlib_Dict.$$delete(entityDict, entityId);
|
|
111
|
+
Stdlib_Dict.$$delete(entityDict, EntityId.toKey(entityId));
|
|
111
112
|
entityChangeFor(change.checkpointId).deleted.push(entityId);
|
|
112
113
|
};
|
|
113
114
|
param.changes.forEach(processChange);
|
package/src/UserContext.res
CHANGED
|
@@ -144,7 +144,7 @@ let getWhereHandler = (params: entityContextParams, filter: dict<dict<unknown>>)
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
let noopSet = (_entity: Internal.entity) => ()
|
|
147
|
-
let noopDeleteUnsafe = (_entityId:
|
|
147
|
+
let noopDeleteUnsafe = (_entityId: EntityId.t) => ()
|
|
148
148
|
|
|
149
149
|
// Reads against ClickHouse-only entities have no Postgres table to hit;
|
|
150
150
|
// surface a friendly error instead of letting the SQL layer fail with
|
|
@@ -168,7 +168,7 @@ let entityTraps: Utils.Proxy.traps<entityContextParams> = {
|
|
|
168
168
|
->InMemoryTable.Entity.set(
|
|
169
169
|
~committedCheckpointId=params.indexerState->IndexerState.committedCheckpointId,
|
|
170
170
|
Set({
|
|
171
|
-
entityId: entity.id,
|
|
171
|
+
entityId: entity.id->EntityId.unsafeOfString,
|
|
172
172
|
checkpointId: params.checkpointId,
|
|
173
173
|
entity,
|
|
174
174
|
}),
|
|
@@ -282,7 +282,7 @@ let entityTraps: Utils.Proxy.traps<entityContextParams> = {
|
|
|
282
282
|
}),
|
|
283
283
|
)
|
|
284
284
|
}
|
|
285
|
-
}->(Utils.magic: (
|
|
285
|
+
}->(Utils.magic: (EntityId.t => unit) => unknown)
|
|
286
286
|
| _ =>
|
|
287
287
|
JsError.throwWithMessage(`Invalid context.${params.entityConfig.name}.${prop} operation.`)
|
|
288
288
|
}
|
package/src/Writing.res
CHANGED
|
@@ -237,7 +237,7 @@ let dropCommitted = (state: IndexerState.t, ~keepLoadedFromDb) => {
|
|
|
237
237
|
|
|
238
238
|
// Blocks until the store holds fewer than keepLatestChangesLimit changes,
|
|
239
239
|
// freeing committed changes first and awaiting commits as a last resort.
|
|
240
|
-
let rec
|
|
240
|
+
let rec awaitCapacityLoop = async (state: IndexerState.t) => {
|
|
241
241
|
// After a failed write nothing will free capacity, so bail instead of waiting
|
|
242
242
|
// on a commit that won't come (the error already went to onError).
|
|
243
243
|
if !(state->IndexerState.hasFailedWrite) && state->getChangesCount >= keepLatestChangesLimit {
|
|
@@ -259,11 +259,21 @@ let rec awaitCapacity = async (state: IndexerState.t) => {
|
|
|
259
259
|
) {
|
|
260
260
|
state->schedule
|
|
261
261
|
await state->waitForCommit
|
|
262
|
-
await state->
|
|
262
|
+
await state->awaitCapacityLoop
|
|
263
263
|
}
|
|
264
264
|
}
|
|
265
265
|
}
|
|
266
266
|
|
|
267
|
+
// Only the over-limit path is a real stall. Timing every call would charge each
|
|
268
|
+
// batch the microtask hop of awaiting an already-resolved promise, which reads
|
|
269
|
+
// as storage backpressure that isn't there.
|
|
270
|
+
let awaitCapacity = async (state: IndexerState.t) =>
|
|
271
|
+
if state->getChangesCount >= keepLatestChangesLimit {
|
|
272
|
+
let timeRef = Performance.now()
|
|
273
|
+
await state->awaitCapacityLoop
|
|
274
|
+
state->IndexerState.recordStalledOnStorageWrite(~seconds=timeRef->Performance.secondsSince)
|
|
275
|
+
}
|
|
276
|
+
|
|
267
277
|
// Awaits until everything processed is persisted. On a failed write we stop
|
|
268
278
|
// draining (onError already surfaced it) rather than throw.
|
|
269
279
|
let rec flush = async (state: IndexerState.t) => {
|
package/src/Writing.res.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import * as Env from "./Env.res.mjs";
|
|
|
4
4
|
import * as Utils from "./Utils.res.mjs";
|
|
5
5
|
import * as Throttler from "./Throttler.res.mjs";
|
|
6
6
|
import * as EffectState from "./EffectState.res.mjs";
|
|
7
|
+
import * as Performance from "./bindings/Performance.res.mjs";
|
|
7
8
|
import * as IndexerState from "./IndexerState.res.mjs";
|
|
8
9
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
9
10
|
import * as InMemoryStore from "./InMemoryStore.res.mjs";
|
|
@@ -182,7 +183,7 @@ function dropCommitted(state, keepLoadedFromDb) {
|
|
|
182
183
|
EffectState.forEach(IndexerState.effectState(state), inMemTable => InMemoryStore.dropCommittedEffects(inMemTable, committedCheckpointId, keepLoadedFromDb));
|
|
183
184
|
}
|
|
184
185
|
|
|
185
|
-
async function
|
|
186
|
+
async function awaitCapacityLoop(state) {
|
|
186
187
|
if (!IndexerState.hasFailedWrite(state) && getChangesCount(state) >= Env.inMemoryObjectsTarget) {
|
|
187
188
|
dropCommitted(state, true);
|
|
188
189
|
if (getChangesCount(state) >= Env.inMemoryObjectsTarget) {
|
|
@@ -191,13 +192,22 @@ async function awaitCapacity(state) {
|
|
|
191
192
|
if (getChangesCount(state) >= Env.inMemoryObjectsTarget && Utils.$$Array.notEmpty(IndexerState.processedBatches(state))) {
|
|
192
193
|
schedule(state);
|
|
193
194
|
await new Promise((resolve, param) => IndexerState.addCommitWaiter(state, resolve));
|
|
194
|
-
return await
|
|
195
|
+
return await awaitCapacityLoop(state);
|
|
195
196
|
} else {
|
|
196
197
|
return;
|
|
197
198
|
}
|
|
198
199
|
}
|
|
199
200
|
}
|
|
200
201
|
|
|
202
|
+
async function awaitCapacity(state) {
|
|
203
|
+
if (getChangesCount(state) < Env.inMemoryObjectsTarget) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
let timeRef = Performance.now();
|
|
207
|
+
await awaitCapacityLoop(state);
|
|
208
|
+
return IndexerState.recordStalledOnStorageWrite(state, Performance.secondsSince(timeRef));
|
|
209
|
+
}
|
|
210
|
+
|
|
201
211
|
async function flush(state) {
|
|
202
212
|
if (IndexerState.hasFailedWrite(state)) {
|
|
203
213
|
return;
|
|
@@ -224,6 +234,7 @@ export {
|
|
|
224
234
|
setChainMeta,
|
|
225
235
|
commitBatch,
|
|
226
236
|
dropCommitted,
|
|
237
|
+
awaitCapacityLoop,
|
|
227
238
|
awaitCapacity,
|
|
228
239
|
flush,
|
|
229
240
|
}
|
|
@@ -91,7 +91,6 @@ let getClickHouseFieldType = (
|
|
|
91
91
|
->Array.joinUnsafe(", ")
|
|
92
92
|
`${enumType}(${enumValues})`
|
|
93
93
|
}
|
|
94
|
-
| Entity(_) => "String"
|
|
95
94
|
}
|
|
96
95
|
|
|
97
96
|
let baseType = if isArray {
|
|
@@ -261,11 +260,14 @@ let setUpdatesOrThrow = async (
|
|
|
261
260
|
convertOrThrow: S.compile(
|
|
262
261
|
S.array(
|
|
263
262
|
S.union([
|
|
264
|
-
EntityHistory.makeSetUpdateSchema(
|
|
263
|
+
EntityHistory.makeSetUpdateSchema(
|
|
264
|
+
~idSchema=entityConfig.table->Table.getIdSchema,
|
|
265
|
+
makeClickHouseEntitySchema(entityConfig.table),
|
|
266
|
+
),
|
|
265
267
|
S.object(s => {
|
|
266
268
|
s.tag(EntityHistory.changeFieldName, EntityHistory.RowAction.DELETE)
|
|
267
269
|
Change.Delete({
|
|
268
|
-
entityId: s.field(Table.idFieldName,
|
|
270
|
+
entityId: s.field(Table.idFieldName, entityConfig.table->Table.getIdSchema),
|
|
269
271
|
checkpointId: s.field(
|
|
270
272
|
EntityHistory.checkpointIdFieldName,
|
|
271
273
|
EntityHistory.unsafeCheckpointIdSchema,
|
|
@@ -68,9 +68,6 @@ function getClickHouseFieldType(fieldType, isNullable, isArray) {
|
|
|
68
68
|
let enumValues = config$1.variants.map(variant => `'` + variant + `'`).join(", ");
|
|
69
69
|
baseType = enumType + `(` + enumValues + `)`;
|
|
70
70
|
break;
|
|
71
|
-
case "Entity" :
|
|
72
|
-
baseType = "String";
|
|
73
|
-
break;
|
|
74
71
|
}
|
|
75
72
|
}
|
|
76
73
|
let baseType$1 = isArray ? `Array(` + baseType + `)` : baseType;
|
|
@@ -203,12 +200,12 @@ async function setUpdatesOrThrow(client, cache, changes, entityConfig, database)
|
|
|
203
200
|
} else {
|
|
204
201
|
let cached_tableName = database + `.\`` + EntityHistory.historyTableName(entityConfig.name, entityConfig.index) + `\``;
|
|
205
202
|
let cached_convertOrThrow = S$RescriptSchema.compile(S$RescriptSchema.array(S$RescriptSchema.union([
|
|
206
|
-
EntityHistory.makeSetUpdateSchema(makeClickHouseEntitySchema(entityConfig.table)),
|
|
203
|
+
EntityHistory.makeSetUpdateSchema(Table.getIdSchema(entityConfig.table), makeClickHouseEntitySchema(entityConfig.table)),
|
|
207
204
|
S$RescriptSchema.object(s => {
|
|
208
205
|
s.tag(EntityHistory.changeFieldName, "DELETE");
|
|
209
206
|
return {
|
|
210
207
|
type: "DELETE",
|
|
211
|
-
entityId: s.f(Table.idFieldName,
|
|
208
|
+
entityId: s.f(Table.idFieldName, Table.getIdSchema(entityConfig.table)),
|
|
212
209
|
checkpointId: s.f(EntityHistory.checkpointIdFieldName, EntityHistory.unsafeCheckpointIdSchema)
|
|
213
210
|
};
|
|
214
211
|
})
|