envio 3.0.2 → 3.1.0-rc.0
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/README.md +0 -1
- package/evm.schema.json +15 -8
- package/fuel.schema.json +19 -12
- package/index.d.ts +0 -2
- package/package.json +6 -7
- package/rescript.json +1 -1
- package/src/Batch.res +4 -214
- package/src/Batch.res.mjs +6 -165
- package/src/ChainFetcher.res +4 -5
- package/src/ChainFetcher.res.mjs +6 -7
- package/src/ChainManager.res +10 -9
- package/src/ChainManager.res.mjs +6 -10
- package/src/Config.res +9 -25
- package/src/Config.res.mjs +17 -27
- package/src/Core.res +7 -0
- package/src/Ctx.res +1 -0
- package/src/Env.res +0 -1
- package/src/Env.res.mjs +0 -3
- package/src/EventConfigBuilder.res +13 -123
- package/src/EventConfigBuilder.res.mjs +6 -73
- package/src/EventProcessing.res +5 -29
- package/src/EventProcessing.res.mjs +11 -20
- package/src/EventUtils.res +0 -27
- package/src/EventUtils.res.mjs +0 -24
- package/src/FetchState.res +1 -11
- package/src/FetchState.res.mjs +2 -16
- package/src/GlobalState.res +23 -37
- package/src/GlobalState.res.mjs +10 -38
- package/src/HandlerLoader.res +6 -5
- package/src/HandlerLoader.res.mjs +27 -9
- package/src/HandlerRegister.res +1 -12
- package/src/HandlerRegister.res.mjs +1 -6
- package/src/HandlerRegister.resi +1 -1
- package/src/Hasura.res +96 -32
- package/src/Hasura.res.mjs +93 -38
- package/src/InMemoryStore.res +181 -45
- package/src/InMemoryStore.res.mjs +143 -40
- package/src/InMemoryTable.res +147 -247
- package/src/InMemoryTable.res.mjs +131 -230
- package/src/Internal.res +10 -34
- package/src/Internal.res.mjs +9 -3
- package/src/LoadLayer.res +5 -5
- package/src/LoadLayer.res.mjs +5 -5
- package/src/Main.res +4 -6
- package/src/Main.res.mjs +26 -15
- package/src/Persistence.res +7 -132
- package/src/Persistence.res.mjs +1 -102
- package/src/PgStorage.res +57 -40
- package/src/PgStorage.res.mjs +60 -34
- package/src/ReorgDetection.res +35 -58
- package/src/ReorgDetection.res.mjs +21 -29
- package/src/SimulateItems.res.mjs +21 -3
- package/src/Sink.res +2 -2
- package/src/Sink.res.mjs +1 -1
- package/src/TableIndices.res +9 -2
- package/src/TableIndices.res.mjs +7 -1
- package/src/TestIndexer.res +53 -60
- package/src/TestIndexer.res.mjs +77 -63
- package/src/TestIndexerProxyStorage.res +4 -14
- package/src/TestIndexerProxyStorage.res.mjs +1 -5
- package/src/UserContext.res +2 -4
- package/src/UserContext.res.mjs +4 -5
- package/src/Utils.res +0 -2
- package/src/Utils.res.mjs +0 -3
- package/src/bindings/ClickHouse.res +45 -38
- package/src/bindings/ClickHouse.res.mjs +16 -17
- package/src/bindings/Vitest.res +3 -0
- package/src/db/InternalTable.res +59 -18
- package/src/db/InternalTable.res.mjs +82 -51
- package/src/db/Table.res +9 -2
- package/src/db/Table.res.mjs +10 -7
- package/src/sources/EvmChain.res +32 -9
- package/src/sources/EvmChain.res.mjs +31 -4
- package/src/sources/HyperFuelSource.res +14 -57
- package/src/sources/HyperFuelSource.res.mjs +18 -38
- package/src/sources/HyperSync.res +36 -101
- package/src/sources/HyperSync.res.mjs +42 -96
- package/src/sources/HyperSync.resi +4 -22
- package/src/sources/HyperSyncClient.res +67 -245
- package/src/sources/HyperSyncClient.res.mjs +47 -46
- package/src/sources/HyperSyncSource.res +76 -147
- package/src/sources/HyperSyncSource.res.mjs +61 -114
- package/src/sources/RpcSource.res +43 -22
- package/src/sources/RpcSource.res.mjs +50 -35
- package/src/sources/SimulateSource.res +1 -7
- package/src/sources/SimulateSource.res.mjs +1 -7
- package/src/sources/Source.res +8 -1
- package/src/sources/SourceManager.res +9 -0
- package/src/sources/SourceManager.res.mjs +10 -0
- package/src/sources/SourceManager.resi +2 -0
- package/svm.schema.json +11 -4
package/src/TestIndexer.res.mjs
CHANGED
|
@@ -95,71 +95,66 @@ function handleWriteBatch(state, updatedEntities, checkpointIds, checkpointChain
|
|
|
95
95
|
entityDict = dict$1;
|
|
96
96
|
}
|
|
97
97
|
let entityConfig = state.entityConfigs[entityName];
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
entityChanges = changes;
|
|
108
|
-
} else {
|
|
109
|
-
let changes$1 = {};
|
|
110
|
-
changesByCheckpoint[checkpointKey] = changes$1;
|
|
111
|
-
entityChanges = changes$1;
|
|
112
|
-
}
|
|
113
|
-
let change$1 = entityChanges[entityName];
|
|
114
|
-
let entityChange;
|
|
115
|
-
if (change$1 !== undefined) {
|
|
116
|
-
entityChange = change$1;
|
|
117
|
-
} else {
|
|
118
|
-
let change_sets = [];
|
|
119
|
-
let change_deleted = [];
|
|
120
|
-
let change$2 = {
|
|
121
|
-
sets: change_sets,
|
|
122
|
-
deleted: change_deleted
|
|
123
|
-
};
|
|
124
|
-
entityChanges[entityName] = change$2;
|
|
125
|
-
entityChange = change$2;
|
|
126
|
-
}
|
|
127
|
-
entityChange.sets.push(parsedEntity);
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
let entityId = change.entityId;
|
|
131
|
-
Stdlib_Dict.$$delete(entityDict, entityId);
|
|
132
|
-
let checkpointKey$1 = change.checkpointId.toString();
|
|
133
|
-
let changes$2 = changesByCheckpoint[checkpointKey$1];
|
|
134
|
-
let entityChanges$1;
|
|
135
|
-
if (changes$2 !== undefined) {
|
|
136
|
-
entityChanges$1 = changes$2;
|
|
98
|
+
let processChange = change => {
|
|
99
|
+
if (change.type === "SET") {
|
|
100
|
+
let parsedEntity = S$RescriptSchema.parseOrThrow(change.entity, entityConfig.schema);
|
|
101
|
+
entityDict[change.entityId] = parsedEntity;
|
|
102
|
+
let checkpointKey = change.checkpointId.toString();
|
|
103
|
+
let changes = changesByCheckpoint[checkpointKey];
|
|
104
|
+
let entityChanges;
|
|
105
|
+
if (changes !== undefined) {
|
|
106
|
+
entityChanges = changes;
|
|
137
107
|
} else {
|
|
138
|
-
let changes$
|
|
139
|
-
changesByCheckpoint[checkpointKey
|
|
140
|
-
entityChanges
|
|
108
|
+
let changes$1 = {};
|
|
109
|
+
changesByCheckpoint[checkpointKey] = changes$1;
|
|
110
|
+
entityChanges = changes$1;
|
|
141
111
|
}
|
|
142
|
-
let change$
|
|
143
|
-
let entityChange
|
|
144
|
-
if (change$
|
|
145
|
-
entityChange
|
|
112
|
+
let change$1 = entityChanges[entityName];
|
|
113
|
+
let entityChange;
|
|
114
|
+
if (change$1 !== undefined) {
|
|
115
|
+
entityChange = change$1;
|
|
146
116
|
} else {
|
|
147
|
-
let change_sets
|
|
148
|
-
let change_deleted
|
|
149
|
-
let change$
|
|
150
|
-
sets: change_sets
|
|
151
|
-
deleted: change_deleted
|
|
117
|
+
let change_sets = [];
|
|
118
|
+
let change_deleted = [];
|
|
119
|
+
let change$2 = {
|
|
120
|
+
sets: change_sets,
|
|
121
|
+
deleted: change_deleted
|
|
152
122
|
};
|
|
153
|
-
entityChanges
|
|
154
|
-
entityChange
|
|
123
|
+
entityChanges[entityName] = change$2;
|
|
124
|
+
entityChange = change$2;
|
|
155
125
|
}
|
|
156
|
-
entityChange
|
|
157
|
-
|
|
158
|
-
update.history.forEach(processChange);
|
|
159
|
-
if (update.history.length === 0) {
|
|
160
|
-
return processChange(update.latestChange);
|
|
126
|
+
entityChange.sets.push(parsedEntity);
|
|
127
|
+
return;
|
|
161
128
|
}
|
|
162
|
-
|
|
129
|
+
let entityId = change.entityId;
|
|
130
|
+
Stdlib_Dict.$$delete(entityDict, entityId);
|
|
131
|
+
let checkpointKey$1 = change.checkpointId.toString();
|
|
132
|
+
let changes$2 = changesByCheckpoint[checkpointKey$1];
|
|
133
|
+
let entityChanges$1;
|
|
134
|
+
if (changes$2 !== undefined) {
|
|
135
|
+
entityChanges$1 = changes$2;
|
|
136
|
+
} else {
|
|
137
|
+
let changes$3 = {};
|
|
138
|
+
changesByCheckpoint[checkpointKey$1] = changes$3;
|
|
139
|
+
entityChanges$1 = changes$3;
|
|
140
|
+
}
|
|
141
|
+
let change$3 = entityChanges$1[entityName];
|
|
142
|
+
let entityChange$1;
|
|
143
|
+
if (change$3 !== undefined) {
|
|
144
|
+
entityChange$1 = change$3;
|
|
145
|
+
} else {
|
|
146
|
+
let change_sets$1 = [];
|
|
147
|
+
let change_deleted$1 = [];
|
|
148
|
+
let change$4 = {
|
|
149
|
+
sets: change_sets$1,
|
|
150
|
+
deleted: change_deleted$1
|
|
151
|
+
};
|
|
152
|
+
entityChanges$1[entityName] = change$4;
|
|
153
|
+
entityChange$1 = change$4;
|
|
154
|
+
}
|
|
155
|
+
entityChange$1.deleted.push(entityId);
|
|
156
|
+
};
|
|
157
|
+
param.changes.forEach(processChange);
|
|
163
158
|
});
|
|
164
159
|
for (let i = 0, i_finish = checkpointIds.length; i < i_finish; ++i) {
|
|
165
160
|
let checkpointId = checkpointIds[i];
|
|
@@ -649,12 +644,31 @@ function initTestWorker() {
|
|
|
649
644
|
}
|
|
650
645
|
let patchConfig = (config, _registrations) => {
|
|
651
646
|
let config$1 = SimulateItems.patchConfig(config, processConfig);
|
|
652
|
-
if (
|
|
647
|
+
if (exitAfterFirstEventBlock) {
|
|
648
|
+
return {
|
|
649
|
+
name: config$1.name,
|
|
650
|
+
description: config$1.description,
|
|
651
|
+
handlers: config$1.handlers,
|
|
652
|
+
contractHandlers: config$1.contractHandlers,
|
|
653
|
+
shouldRollbackOnReorg: config$1.shouldRollbackOnReorg,
|
|
654
|
+
shouldSaveFullHistory: config$1.shouldSaveFullHistory,
|
|
655
|
+
storage: config$1.storage,
|
|
656
|
+
chainMap: config$1.chainMap,
|
|
657
|
+
defaultChain: config$1.defaultChain,
|
|
658
|
+
ecosystem: config$1.ecosystem,
|
|
659
|
+
enableRawEvents: config$1.enableRawEvents,
|
|
660
|
+
maxAddrInPartition: config$1.maxAddrInPartition,
|
|
661
|
+
batchSize: 1,
|
|
662
|
+
lowercaseAddresses: config$1.lowercaseAddresses,
|
|
663
|
+
isDev: config$1.isDev,
|
|
664
|
+
userEntitiesByName: config$1.userEntitiesByName,
|
|
665
|
+
userEntities: config$1.userEntities,
|
|
666
|
+
allEntities: config$1.allEntities,
|
|
667
|
+
allEnums: config$1.allEnums
|
|
668
|
+
};
|
|
669
|
+
} else {
|
|
653
670
|
return config$1;
|
|
654
671
|
}
|
|
655
|
-
let newrecord = {...config$1};
|
|
656
|
-
newrecord.batchSize = 1;
|
|
657
|
-
return newrecord;
|
|
658
672
|
};
|
|
659
673
|
Main.start(persistence, undefined, true, exitAfterFirstEventBlock, patchConfig);
|
|
660
674
|
}
|
|
@@ -7,15 +7,9 @@ type serializableChange =
|
|
|
7
7
|
| @as("SET") Set({entityId: string, entity: JSON.t, checkpointId: bigint})
|
|
8
8
|
| @as("DELETE") Delete({entityId: string, checkpointId: bigint})
|
|
9
9
|
|
|
10
|
-
type serializableEntityUpdate = {
|
|
11
|
-
latestChange: serializableChange,
|
|
12
|
-
history: array<serializableChange>,
|
|
13
|
-
containsRollbackDiffChange: bool,
|
|
14
|
-
}
|
|
15
|
-
|
|
16
10
|
type serializableUpdatedEntity = {
|
|
17
11
|
entityName: string,
|
|
18
|
-
|
|
12
|
+
changes: array<serializableChange>,
|
|
19
13
|
}
|
|
20
14
|
|
|
21
15
|
// Worker -> Main thread payloads
|
|
@@ -138,7 +132,7 @@ let makeStorage = (proxy: t): Persistence.storage => {
|
|
|
138
132
|
writeBatch: async (
|
|
139
133
|
~batch,
|
|
140
134
|
~rawEvents as _,
|
|
141
|
-
~
|
|
135
|
+
~rollback as _,
|
|
142
136
|
~isInReorgThreshold as _,
|
|
143
137
|
~config as _,
|
|
144
138
|
~allEntities as _,
|
|
@@ -147,7 +141,7 @@ let makeStorage = (proxy: t): Persistence.storage => {
|
|
|
147
141
|
) => {
|
|
148
142
|
// Encode entities to JSON for serialization across worker boundary
|
|
149
143
|
let serializableEntities = updatedEntities->Array.map((
|
|
150
|
-
{entityConfig,
|
|
144
|
+
{entityConfig, changes}: Persistence.updatedEntity,
|
|
151
145
|
) => {
|
|
152
146
|
let encodeChange = (change: Change.t<Internal.entity>): serializableChange => {
|
|
153
147
|
switch change {
|
|
@@ -162,11 +156,7 @@ let makeStorage = (proxy: t): Persistence.storage => {
|
|
|
162
156
|
}
|
|
163
157
|
{
|
|
164
158
|
entityName: entityConfig.name,
|
|
165
|
-
|
|
166
|
-
latestChange: encodeChange(update.latestChange),
|
|
167
|
-
history: update.history->Array.map(encodeChange),
|
|
168
|
-
containsRollbackDiffChange: update.containsRollbackDiffChange,
|
|
169
|
-
}),
|
|
159
|
+
changes: changes->Array.map(encodeChange),
|
|
170
160
|
}
|
|
171
161
|
})
|
|
172
162
|
let _ = await proxy->sendRequest(
|
|
@@ -98,11 +98,7 @@ function makeStorage(proxy) {
|
|
|
98
98
|
};
|
|
99
99
|
return {
|
|
100
100
|
entityName: entityConfig.name,
|
|
101
|
-
|
|
102
|
-
latestChange: encodeChange(update.latestChange),
|
|
103
|
-
history: update.history.map(encodeChange),
|
|
104
|
-
containsRollbackDiffChange: update.containsRollbackDiffChange
|
|
105
|
-
}))
|
|
101
|
+
changes: param.changes.map(encodeChange)
|
|
106
102
|
};
|
|
107
103
|
});
|
|
108
104
|
await sendRequest(proxy, {
|
package/src/UserContext.res
CHANGED
|
@@ -7,7 +7,6 @@ type contextParams = {
|
|
|
7
7
|
loadManager: LoadManager.t,
|
|
8
8
|
persistence: Persistence.t,
|
|
9
9
|
isPreload: bool,
|
|
10
|
-
shouldSaveHistory: bool,
|
|
11
10
|
chains: Internal.chains,
|
|
12
11
|
config: Config.t,
|
|
13
12
|
mutable isResolved: bool,
|
|
@@ -223,12 +222,12 @@ let entityTraps: Utils.Proxy.traps<entityContextParams> = {
|
|
|
223
222
|
params.inMemoryStore
|
|
224
223
|
->InMemoryStore.getInMemTable(~entityConfig=params.entityConfig)
|
|
225
224
|
->InMemoryTable.Entity.set(
|
|
225
|
+
~committedCheckpointId=params.inMemoryStore.committedCheckpointId,
|
|
226
226
|
Set({
|
|
227
227
|
entityId: entity.id,
|
|
228
228
|
checkpointId: params.checkpointId,
|
|
229
229
|
entity,
|
|
230
230
|
}),
|
|
231
|
-
~shouldSaveHistory=params.shouldSaveHistory,
|
|
232
231
|
)
|
|
233
232
|
}
|
|
234
233
|
|
|
@@ -329,11 +328,11 @@ let entityTraps: Utils.Proxy.traps<entityContextParams> = {
|
|
|
329
328
|
params.inMemoryStore
|
|
330
329
|
->InMemoryStore.getInMemTable(~entityConfig=params.entityConfig)
|
|
331
330
|
->InMemoryTable.Entity.set(
|
|
331
|
+
~committedCheckpointId=params.inMemoryStore.committedCheckpointId,
|
|
332
332
|
Delete({
|
|
333
333
|
entityId,
|
|
334
334
|
checkpointId: params.checkpointId,
|
|
335
335
|
}),
|
|
336
|
-
~shouldSaveHistory=params.shouldSaveHistory,
|
|
337
336
|
)
|
|
338
337
|
}
|
|
339
338
|
}->(Utils.magic: (string => unit) => unknown)
|
|
@@ -379,7 +378,6 @@ let handlerTraps: Utils.Proxy.traps<contextParams> = {
|
|
|
379
378
|
inMemoryStore: params.inMemoryStore,
|
|
380
379
|
loadManager: params.loadManager,
|
|
381
380
|
persistence: params.persistence,
|
|
382
|
-
shouldSaveHistory: params.shouldSaveHistory,
|
|
383
381
|
checkpointId: params.checkpointId,
|
|
384
382
|
chains: params.chains,
|
|
385
383
|
isResolved: params.isResolved,
|
package/src/UserContext.res.mjs
CHANGED
|
@@ -125,22 +125,22 @@ function throwClickHouseReadOnly(entityConfig, op) {
|
|
|
125
125
|
|
|
126
126
|
let entityTraps_get = (params, prop) => {
|
|
127
127
|
let isClickHouseOnly = !params.entityConfig.storage.postgres;
|
|
128
|
-
let set = params.isPreload ? noopSet : entity => InMemoryTable.Entity.set(InMemoryStore.getInMemTable(params.inMemoryStore, params.entityConfig), {
|
|
128
|
+
let set = params.isPreload ? noopSet : entity => InMemoryTable.Entity.set(InMemoryStore.getInMemTable(params.inMemoryStore, params.entityConfig), params.inMemoryStore.committedCheckpointId, {
|
|
129
129
|
type: "SET",
|
|
130
130
|
entityId: entity.id,
|
|
131
131
|
entity: entity,
|
|
132
132
|
checkpointId: params.checkpointId
|
|
133
|
-
}
|
|
133
|
+
});
|
|
134
134
|
switch (prop) {
|
|
135
135
|
case "deleteUnsafe" :
|
|
136
136
|
if (params.isPreload) {
|
|
137
137
|
return noopDeleteUnsafe;
|
|
138
138
|
} else {
|
|
139
|
-
return entityId => InMemoryTable.Entity.set(InMemoryStore.getInMemTable(params.inMemoryStore, params.entityConfig), {
|
|
139
|
+
return entityId => InMemoryTable.Entity.set(InMemoryStore.getInMemTable(params.inMemoryStore, params.entityConfig), params.inMemoryStore.committedCheckpointId, {
|
|
140
140
|
type: "DELETE",
|
|
141
141
|
entityId: entityId,
|
|
142
142
|
checkpointId: params.checkpointId
|
|
143
|
-
}
|
|
143
|
+
});
|
|
144
144
|
}
|
|
145
145
|
case "get" :
|
|
146
146
|
if (isClickHouseOnly) {
|
|
@@ -218,7 +218,6 @@ let handlerTraps_get = (params, prop) => {
|
|
|
218
218
|
loadManager: params.loadManager,
|
|
219
219
|
persistence: params.persistence,
|
|
220
220
|
isPreload: params.isPreload,
|
|
221
|
-
shouldSaveHistory: params.shouldSaveHistory,
|
|
222
221
|
chains: params.chains,
|
|
223
222
|
config: params.config,
|
|
224
223
|
isResolved: params.isResolved,
|
package/src/Utils.res
CHANGED
|
@@ -114,8 +114,6 @@ module Dict = {
|
|
|
114
114
|
@get_index
|
|
115
115
|
external dangerouslyGetByIntNonOption: (dict<'a>, int) => option<'a> = ""
|
|
116
116
|
|
|
117
|
-
let has: (dict<'a>, string) => bool = %raw(`(dict, key) => key in dict`)
|
|
118
|
-
|
|
119
117
|
let push = (dict, key, value) => {
|
|
120
118
|
switch dict->dangerouslyGetNonOption(key) {
|
|
121
119
|
| Some(arr) => arr->Array.push(value)
|
package/src/Utils.res.mjs
CHANGED
|
@@ -72,8 +72,6 @@ function getOrInsertEmptyDict(dict, key) {
|
|
|
72
72
|
return d$1;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
let has = ((dict, key) => key in dict);
|
|
76
|
-
|
|
77
75
|
function push(dict, key, value) {
|
|
78
76
|
let arr = dict[key];
|
|
79
77
|
if (arr !== undefined) {
|
|
@@ -171,7 +169,6 @@ let incrementByInt = ((dict, key) => {
|
|
|
171
169
|
let Dict = {
|
|
172
170
|
$$delete: Stdlib_Dict.$$delete,
|
|
173
171
|
getOrInsertEmptyDict: getOrInsertEmptyDict,
|
|
174
|
-
has: has,
|
|
175
172
|
push: push,
|
|
176
173
|
pushMany: pushMany,
|
|
177
174
|
merge: merge,
|
|
@@ -35,8 +35,10 @@ type queryResult<'a>
|
|
|
35
35
|
@send
|
|
36
36
|
external query: (client, queryParams) => promise<queryResult<'a>> = "query"
|
|
37
37
|
|
|
38
|
+
// The default `JSON` query format resolves to a `ResponseJSON` wrapper whose
|
|
39
|
+
// rows live under `data`, not at the top level.
|
|
38
40
|
@send
|
|
39
|
-
external json: queryResult<'a> => promise<'a> = "json"
|
|
41
|
+
external json: queryResult<'a> => promise<{"data": array<'a>}> = "json"
|
|
40
42
|
|
|
41
43
|
let getClickHouseFieldType = (
|
|
42
44
|
~fieldType: Table.fieldType,
|
|
@@ -76,13 +78,13 @@ let getClickHouseFieldType = (
|
|
|
76
78
|
| Json => "String"
|
|
77
79
|
| Date => "DateTime64(3, 'UTC')"
|
|
78
80
|
| Enum({config}) => {
|
|
79
|
-
let variantsLength = config.variants->
|
|
81
|
+
let variantsLength = config.variants->Array.length
|
|
80
82
|
// Theoretically we can store 256 variants in Enum8,
|
|
81
83
|
// but it'd require to explicitly start with a negative index (probably)
|
|
82
84
|
let enumType = variantsLength <= 127 ? "Enum8" : "Enum16"
|
|
83
85
|
let enumValues =
|
|
84
86
|
config.variants
|
|
85
|
-
->
|
|
87
|
+
->Array.map(variant => {
|
|
86
88
|
let variantStr = variant->(Utils.magic: 'a => string)
|
|
87
89
|
`'${variantStr}'`
|
|
88
90
|
})
|
|
@@ -105,7 +107,7 @@ let getClickHouseFieldType = (
|
|
|
105
107
|
let makeClickHouseEntitySchema = (table: Table.table): S.t<Internal.entity> => {
|
|
106
108
|
S.schema(s => {
|
|
107
109
|
let dict = Dict.make()
|
|
108
|
-
table.fields->
|
|
110
|
+
table.fields->Array.forEach(field => {
|
|
109
111
|
switch field {
|
|
110
112
|
| Field(f) => {
|
|
111
113
|
let fieldName = f->Table.getDbFieldName
|
|
@@ -203,11 +205,11 @@ let setCheckpointsOrThrow = async (client, ~batch: Batch.t, ~database: string) =
|
|
|
203
205
|
for idx in 0 to checkpointsCount - 1 {
|
|
204
206
|
checkpointRows
|
|
205
207
|
->Array.push((
|
|
206
|
-
batch.checkpointIds->
|
|
207
|
-
batch.checkpointChainIds->
|
|
208
|
-
batch.checkpointBlockNumbers->
|
|
209
|
-
batch.checkpointBlockHashes->
|
|
210
|
-
batch.checkpointEventsProcessed->
|
|
208
|
+
batch.checkpointIds->Array.getUnsafe(idx)->BigInt.toString,
|
|
209
|
+
batch.checkpointChainIds->Array.getUnsafe(idx),
|
|
210
|
+
batch.checkpointBlockNumbers->Array.getUnsafe(idx),
|
|
211
|
+
batch.checkpointBlockHashes->Array.getUnsafe(idx),
|
|
212
|
+
batch.checkpointEventsProcessed->Array.getUnsafe(idx),
|
|
211
213
|
))
|
|
212
214
|
->ignore
|
|
213
215
|
}
|
|
@@ -233,17 +235,17 @@ let setCheckpointsOrThrow = async (client, ~batch: Batch.t, ~database: string) =
|
|
|
233
235
|
|
|
234
236
|
type setUpdatesCache = {
|
|
235
237
|
tableName: string,
|
|
236
|
-
convertOrThrow: Change.t<Internal.entity
|
|
238
|
+
convertOrThrow: array<Change.t<Internal.entity>> => array<JSON.t>,
|
|
237
239
|
}
|
|
238
240
|
|
|
239
241
|
let setUpdatesOrThrow = async (
|
|
240
242
|
client,
|
|
241
243
|
~cache: Utils.WeakMap.t<Internal.entityConfig, setUpdatesCache>,
|
|
242
|
-
~
|
|
244
|
+
~changes: array<Change.t<Internal.entity>>,
|
|
243
245
|
~entityConfig: Internal.entityConfig,
|
|
244
246
|
~database: string,
|
|
245
247
|
) => {
|
|
246
|
-
if
|
|
248
|
+
if changes->Array.length === 0 {
|
|
247
249
|
()
|
|
248
250
|
} else {
|
|
249
251
|
let {convertOrThrow, tableName} = switch cache->Utils.WeakMap.get(entityConfig) {
|
|
@@ -255,23 +257,29 @@ let setUpdatesOrThrow = async (
|
|
|
255
257
|
~entityIndex=entityConfig.index,
|
|
256
258
|
)}\``,
|
|
257
259
|
convertOrThrow: S.compile(
|
|
258
|
-
S.
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
260
|
+
S.array(
|
|
261
|
+
S.union([
|
|
262
|
+
EntityHistory.makeSetUpdateSchema(makeClickHouseEntitySchema(entityConfig.table)),
|
|
263
|
+
S.object(s => {
|
|
264
|
+
s.tag(EntityHistory.changeFieldName, EntityHistory.RowAction.DELETE)
|
|
265
|
+
Change.Delete({
|
|
266
|
+
entityId: s.field(Table.idFieldName, S.string),
|
|
267
|
+
checkpointId: s.field(
|
|
268
|
+
EntityHistory.checkpointIdFieldName,
|
|
269
|
+
EntityHistory.unsafeCheckpointIdSchema,
|
|
270
|
+
),
|
|
271
|
+
})
|
|
272
|
+
}),
|
|
273
|
+
]),
|
|
274
|
+
),
|
|
271
275
|
~input=Value,
|
|
272
276
|
~output=Json,
|
|
273
277
|
~typeValidation=false,
|
|
274
278
|
~mode=Sync,
|
|
279
|
+
)->(
|
|
280
|
+
Utils.magic: (array<Change.t<Internal.entity>> => JSON.t) => array<
|
|
281
|
+
Change.t<Internal.entity>,
|
|
282
|
+
> => array<JSON.t>
|
|
275
283
|
),
|
|
276
284
|
}
|
|
277
285
|
|
|
@@ -280,10 +288,9 @@ let setUpdatesOrThrow = async (
|
|
|
280
288
|
}
|
|
281
289
|
|
|
282
290
|
try {
|
|
283
|
-
//
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
})
|
|
291
|
+
// The entity history table is the source of truth for ClickHouse, so every
|
|
292
|
+
// intermediate change must be persisted, not only the current value.
|
|
293
|
+
let values = changes->convertOrThrow
|
|
287
294
|
|
|
288
295
|
await insertWithRetry(client, ~table=tableName, ~values, ~format="JSONEachRow")
|
|
289
296
|
} catch {
|
|
@@ -305,7 +312,7 @@ let makeCreateHistoryTableQuery = (
|
|
|
305
312
|
~replicated: bool=false,
|
|
306
313
|
) => {
|
|
307
314
|
let tableEngine = replicated ? "ReplicatedMergeTree" : "MergeTree()"
|
|
308
|
-
let fieldDefinitions = entityConfig.table.fields->
|
|
315
|
+
let fieldDefinitions = entityConfig.table.fields->Array.filterMap(field => {
|
|
309
316
|
switch field {
|
|
310
317
|
| Field(field) =>
|
|
311
318
|
Some({
|
|
@@ -389,7 +396,7 @@ let makeCreateViewQuery = (~entityConfig: Internal.entityConfig, ~database: stri
|
|
|
389
396
|
|
|
390
397
|
let entityFields =
|
|
391
398
|
entityConfig.table.fields
|
|
392
|
-
->
|
|
399
|
+
->Array.filterMap(field => {
|
|
393
400
|
switch field {
|
|
394
401
|
| Field(field) => {
|
|
395
402
|
let fieldName = field->Table.getDbFieldName
|
|
@@ -433,12 +440,12 @@ let initialize = async (
|
|
|
433
440
|
|
|
434
441
|
switch databaseEngine {
|
|
435
442
|
| Some(engineSpec) => {
|
|
436
|
-
let expectedEngineName = engineSpec->String.split("(")->
|
|
443
|
+
let expectedEngineName = engineSpec->String.split("(")->Array.getUnsafe(0)->String.trim
|
|
437
444
|
let existingResult = await client->query({
|
|
438
445
|
query: `SELECT engine FROM system.databases WHERE name = '${database}'`,
|
|
439
446
|
})
|
|
440
|
-
let rows
|
|
441
|
-
switch rows->
|
|
447
|
+
let rows = (await existingResult->json)["data"]
|
|
448
|
+
switch rows->Array.get(0) {
|
|
442
449
|
| Some(row) if row["engine"] !== expectedEngineName =>
|
|
443
450
|
JsError.throwWithMessage(
|
|
444
451
|
`ClickHouse database "${database}" exists with engine "${row["engine"]}" but ENVIO_CLICKHOUSE_DATABASE_ENGINE specifies "${expectedEngineName}". Drop the database manually to change its engine.`,
|
|
@@ -456,14 +463,14 @@ let initialize = async (
|
|
|
456
463
|
await client->exec({query: `USE ${database}`})
|
|
457
464
|
|
|
458
465
|
await Promise.all(
|
|
459
|
-
entities->
|
|
466
|
+
entities->Array.map(entityConfig =>
|
|
460
467
|
client->exec({query: makeCreateHistoryTableQuery(~entityConfig, ~database, ~replicated)})
|
|
461
468
|
),
|
|
462
469
|
)->Utils.Promise.ignoreValue
|
|
463
470
|
await client->exec({query: makeCreateCheckpointsTableQuery(~database, ~replicated)})
|
|
464
471
|
|
|
465
472
|
await Promise.all(
|
|
466
|
-
entities->
|
|
473
|
+
entities->Array.map(entityConfig =>
|
|
467
474
|
client->exec({query: makeCreateViewQuery(~entityConfig, ~database)})
|
|
468
475
|
),
|
|
469
476
|
)->Utils.Promise.ignoreValue
|
|
@@ -496,11 +503,11 @@ let resume = async (client, ~database: string, ~checkpointId: Internal.checkpoin
|
|
|
496
503
|
let tablesResult = await client->query({
|
|
497
504
|
query: `SHOW TABLES FROM ${database} LIKE '${EntityHistory.historyTablePrefix}%'`,
|
|
498
505
|
})
|
|
499
|
-
let tables
|
|
506
|
+
let tables = (await tablesResult->json)["data"]
|
|
500
507
|
|
|
501
508
|
// Delete rows with checkpoint IDs higher than the target for each history table
|
|
502
509
|
await Promise.all(
|
|
503
|
-
tables->
|
|
510
|
+
tables->Array.map(table => {
|
|
504
511
|
let tableName = table["name"]
|
|
505
512
|
client->exec({
|
|
506
513
|
query: `ALTER TABLE ${database}.\`${tableName}\` DELETE WHERE \`${EntityHistory.checkpointIdFieldName}\` > ${checkpointId->BigInt.toString}`,
|
|
@@ -4,8 +4,8 @@ import * as Env from "../Env.res.mjs";
|
|
|
4
4
|
import * as Table from "../db/Table.res.mjs";
|
|
5
5
|
import * as Utils from "../Utils.res.mjs";
|
|
6
6
|
import * as Logging from "../Logging.res.mjs";
|
|
7
|
-
import * as Belt_Array from "@rescript/runtime/lib/es6/Belt_Array.js";
|
|
8
7
|
import * as Persistence from "../Persistence.res.mjs";
|
|
8
|
+
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
9
9
|
import * as EntityHistory from "../db/EntityHistory.res.mjs";
|
|
10
10
|
import * as InternalTable from "../db/InternalTable.res.mjs";
|
|
11
11
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
@@ -65,7 +65,7 @@ function getClickHouseFieldType(fieldType, isNullable, isArray) {
|
|
|
65
65
|
let config$1 = fieldType.config;
|
|
66
66
|
let variantsLength = config$1.variants.length;
|
|
67
67
|
let enumType = variantsLength <= 127 ? "Enum8" : "Enum16";
|
|
68
|
-
let enumValues =
|
|
68
|
+
let enumValues = config$1.variants.map(variant => `'` + variant + `'`).join(", ");
|
|
69
69
|
baseType = enumType + `(` + enumValues + `)`;
|
|
70
70
|
break;
|
|
71
71
|
case "Entity" :
|
|
@@ -84,7 +84,7 @@ function getClickHouseFieldType(fieldType, isNullable, isArray) {
|
|
|
84
84
|
function makeClickHouseEntitySchema(table) {
|
|
85
85
|
return S$RescriptSchema.schema(s => {
|
|
86
86
|
let dict = {};
|
|
87
|
-
|
|
87
|
+
table.fields.forEach(field => {
|
|
88
88
|
if (field.TAG !== "Field") {
|
|
89
89
|
return;
|
|
90
90
|
}
|
|
@@ -192,8 +192,8 @@ async function setCheckpointsOrThrow(client, batch, database) {
|
|
|
192
192
|
}
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
-
async function setUpdatesOrThrow(client, cache,
|
|
196
|
-
if (
|
|
195
|
+
async function setUpdatesOrThrow(client, cache, changes, entityConfig, database) {
|
|
196
|
+
if (changes.length === 0) {
|
|
197
197
|
return;
|
|
198
198
|
}
|
|
199
199
|
let cached = cache.get(entityConfig);
|
|
@@ -202,7 +202,7 @@ async function setUpdatesOrThrow(client, cache, updates, entityConfig, database)
|
|
|
202
202
|
match = cached;
|
|
203
203
|
} else {
|
|
204
204
|
let cached_tableName = database + `.\`` + EntityHistory.historyTableName(entityConfig.name, entityConfig.index) + `\``;
|
|
205
|
-
let cached_convertOrThrow = S$RescriptSchema.compile(S$RescriptSchema.union([
|
|
205
|
+
let cached_convertOrThrow = S$RescriptSchema.compile(S$RescriptSchema.array(S$RescriptSchema.union([
|
|
206
206
|
EntityHistory.makeSetUpdateSchema(makeClickHouseEntitySchema(entityConfig.table)),
|
|
207
207
|
S$RescriptSchema.object(s => {
|
|
208
208
|
s.tag(EntityHistory.changeFieldName, "DELETE");
|
|
@@ -212,7 +212,7 @@ async function setUpdatesOrThrow(client, cache, updates, entityConfig, database)
|
|
|
212
212
|
checkpointId: s.f(EntityHistory.checkpointIdFieldName, EntityHistory.unsafeCheckpointIdSchema)
|
|
213
213
|
};
|
|
214
214
|
})
|
|
215
|
-
]), "Output", "Json", "Sync", false);
|
|
215
|
+
])), "Output", "Json", "Sync", false);
|
|
216
216
|
let cached$1 = {
|
|
217
217
|
tableName: cached_tableName,
|
|
218
218
|
convertOrThrow: cached_convertOrThrow
|
|
@@ -220,10 +220,9 @@ async function setUpdatesOrThrow(client, cache, updates, entityConfig, database)
|
|
|
220
220
|
cache.set(entityConfig, cached$1);
|
|
221
221
|
match = cached$1;
|
|
222
222
|
}
|
|
223
|
-
let convertOrThrow = match.convertOrThrow;
|
|
224
223
|
let tableName = match.tableName;
|
|
225
224
|
try {
|
|
226
|
-
let values =
|
|
225
|
+
let values = match.convertOrThrow(changes);
|
|
227
226
|
return await insertWithRetry(client, tableName, values, "JSONEachRow", undefined);
|
|
228
227
|
} catch (raw_exn) {
|
|
229
228
|
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
@@ -239,7 +238,7 @@ async function setUpdatesOrThrow(client, cache, updates, entityConfig, database)
|
|
|
239
238
|
function makeCreateHistoryTableQuery(entityConfig, database, replicatedOpt) {
|
|
240
239
|
let replicated = replicatedOpt !== undefined ? replicatedOpt : false;
|
|
241
240
|
let tableEngine = replicated ? "ReplicatedMergeTree" : "MergeTree()";
|
|
242
|
-
let fieldDefinitions =
|
|
241
|
+
let fieldDefinitions = Stdlib_Array.filterMap(entityConfig.table.fields, field => {
|
|
243
242
|
if (field.TAG !== "Field") {
|
|
244
243
|
return;
|
|
245
244
|
}
|
|
@@ -277,7 +276,7 @@ ORDER BY (` + "id" + `)`;
|
|
|
277
276
|
function makeCreateViewQuery(entityConfig, database) {
|
|
278
277
|
let historyTableName = EntityHistory.historyTableName(entityConfig.name, entityConfig.index);
|
|
279
278
|
let checkpointsTableName = InternalTable.Checkpoints.table.tableName;
|
|
280
|
-
let entityFields =
|
|
279
|
+
let entityFields = Stdlib_Array.filterMap(entityConfig.table.fields, field => {
|
|
281
280
|
if (field.TAG !== "Field") {
|
|
282
281
|
return;
|
|
283
282
|
}
|
|
@@ -307,8 +306,8 @@ async function initialize(client, database, entities, param) {
|
|
|
307
306
|
let existingResult = await client.query({
|
|
308
307
|
query: `SELECT engine FROM system.databases WHERE name = '` + database + `'`
|
|
309
308
|
});
|
|
310
|
-
let rows = await existingResult.json();
|
|
311
|
-
let row =
|
|
309
|
+
let rows = (await existingResult.json()).data;
|
|
310
|
+
let row = rows[0];
|
|
312
311
|
if (row !== undefined) {
|
|
313
312
|
let row$1 = Primitive_option.valFromOption(row);
|
|
314
313
|
if (row$1.engine !== expectedEngineName) {
|
|
@@ -325,13 +324,13 @@ async function initialize(client, database, entities, param) {
|
|
|
325
324
|
await client.exec({
|
|
326
325
|
query: `USE ` + database
|
|
327
326
|
});
|
|
328
|
-
await Promise.all(
|
|
327
|
+
await Promise.all(entities.map(entityConfig => client.exec({
|
|
329
328
|
query: makeCreateHistoryTableQuery(entityConfig, database, replicated)
|
|
330
329
|
})));
|
|
331
330
|
await client.exec({
|
|
332
331
|
query: makeCreateCheckpointsTableQuery(database, replicated)
|
|
333
332
|
});
|
|
334
|
-
await Promise.all(
|
|
333
|
+
await Promise.all(entities.map(entityConfig => client.exec({
|
|
335
334
|
query: makeCreateViewQuery(entityConfig, database)
|
|
336
335
|
})));
|
|
337
336
|
return Logging.trace("ClickHouse storage initialization completed successfully");
|
|
@@ -356,8 +355,8 @@ async function resume(client, database, checkpointId) {
|
|
|
356
355
|
let tablesResult = await client.query({
|
|
357
356
|
query: `SHOW TABLES FROM ` + database + ` LIKE '` + EntityHistory.historyTablePrefix + `%'`
|
|
358
357
|
});
|
|
359
|
-
let tables = await tablesResult.json();
|
|
360
|
-
await Promise.all(
|
|
358
|
+
let tables = (await tablesResult.json()).data;
|
|
359
|
+
await Promise.all(tables.map(table => {
|
|
361
360
|
let tableName = table.name;
|
|
362
361
|
return client.exec({
|
|
363
362
|
query: `ALTER TABLE ` + database + `.\`` + tableName + `\` DELETE WHERE \`` + EntityHistory.checkpointIdFieldName + `\` > ` + checkpointId.toString()
|
package/src/bindings/Vitest.res
CHANGED
|
@@ -117,6 +117,9 @@ module Async = {
|
|
|
117
117
|
@module("vitest") @scope("it")
|
|
118
118
|
external it_skipIf: bool => (string, testContext => promise<unit>) => unit = "skipIf"
|
|
119
119
|
|
|
120
|
+
@module("vitest") @scope("it")
|
|
121
|
+
external it_fails: (string, testContext => promise<unit>) => unit = "fails"
|
|
122
|
+
|
|
120
123
|
let isClaudeCloud: bool = %raw(`process.env.CLAUDE_CODE_CONTAINER_ID != null`)
|
|
121
124
|
let itSkipInClaudeCloud = (name, fn) => it_skipIf(isClaudeCloud)(name, fn)
|
|
122
125
|
|