envio 3.6.1 → 3.7.0-subgraph
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 +197 -9
- package/package.json +6 -6
- package/src/Batch.res +64 -25
- package/src/Batch.res.mjs +69 -46
- package/src/ChainFetching.res +13 -17
- package/src/ChainFetching.res.mjs +9 -14
- package/src/ChainState.res +160 -119
- package/src/ChainState.res.mjs +75 -46
- package/src/ChainState.resi +15 -12
- package/src/Config.res +18 -2
- package/src/Config.res.mjs +9 -6
- package/src/Core.res +20 -0
- package/src/Core.res.mjs +11 -0
- package/src/Ecosystem.res +0 -1
- package/src/Envio.res +16 -3
- package/src/EventConfigBuilder.res +155 -40
- package/src/EventConfigBuilder.res.mjs +76 -24
- package/src/EventProcessing.res +10 -14
- package/src/EventProcessing.res.mjs +12 -10
- package/src/EventUtils.res +3 -47
- package/src/FetchState.res +124 -104
- package/src/FetchState.res.mjs +116 -100
- package/src/HandlerLoader.res +20 -8
- package/src/HandlerLoader.res.mjs +11 -2
- package/src/HandlerRegister.res +108 -2
- package/src/HandlerRegister.res.mjs +59 -10
- package/src/HandlerRegister.resi +4 -0
- package/src/InMemoryStore.res +1 -6
- package/src/InMemoryStore.res.mjs +1 -3
- package/src/Internal.res +84 -24
- package/src/Internal.res.mjs +33 -2
- package/src/LazyLoader.res +12 -11
- package/src/LazyLoader.res.mjs +13 -7
- package/src/Main.res +22 -9
- package/src/Main.res.mjs +18 -5
- package/src/MemoryStorage.res +756 -0
- package/src/MemoryStorage.res.mjs +623 -0
- package/src/Persistence.res +4 -2
- package/src/PgStorage.res +24 -8
- package/src/PgStorage.res.mjs +15 -12
- package/src/ReorgDetection.res +0 -222
- package/src/ReorgDetection.res.mjs +0 -163
- package/src/Rollback.res +14 -15
- package/src/Rollback.res.mjs +4 -5
- package/src/SimulateItems.res +2 -2
- package/src/UserContext.res +358 -51
- package/src/UserContext.res.mjs +271 -35
- package/src/Utils.res +3 -0
- package/src/bindings/ClickHouse.res +16 -11
- package/src/bindings/ClickHouse.res.mjs +10 -10
- package/src/bindings/Vitest.res +1 -1
- package/src/sources/BlockStore.res +115 -5
- package/src/sources/BlockStore.res.mjs +25 -0
- package/src/sources/Evm.res +0 -1
- package/src/sources/Evm.res.mjs +0 -1
- package/src/sources/EvmHyperSyncSource.res +19 -84
- package/src/sources/EvmHyperSyncSource.res.mjs +24 -60
- package/src/sources/Fuel.res +24 -4
- package/src/sources/Fuel.res.mjs +23 -3
- package/src/sources/FuelHyperSync.res +8 -3
- package/src/sources/FuelHyperSync.res.mjs +5 -4
- package/src/sources/FuelHyperSync.resi +3 -1
- package/src/sources/FuelHyperSyncClient.res +7 -10
- package/src/sources/FuelHyperSyncSource.res +18 -45
- package/src/sources/FuelHyperSyncSource.res.mjs +18 -35
- package/src/sources/HyperSync.res +49 -229
- package/src/sources/HyperSync.res.mjs +74 -191
- package/src/sources/HyperSync.resi +11 -35
- package/src/sources/HyperSyncClient.res +9 -79
- package/src/sources/HyperSyncClient.res.mjs +2 -6
- package/src/sources/RequestStat.res +5 -0
- package/src/sources/RequestStat.res.mjs +2 -0
- package/src/sources/RpcSource.res +149 -43
- package/src/sources/RpcSource.res.mjs +104 -41
- package/src/sources/SimulateSource.res +8 -5
- package/src/sources/SimulateSource.res.mjs +6 -6
- package/src/sources/Source.res +89 -16
- package/src/sources/Source.res.mjs +50 -1
- package/src/sources/SourceManager.res +255 -50
- package/src/sources/SourceManager.res.mjs +149 -55
- package/src/sources/SourceManager.resi +2 -6
- package/src/sources/Svm.res +0 -7
- package/src/sources/Svm.res.mjs +0 -8
- package/src/sources/SvmHyperSyncClient.res +21 -16
- package/src/sources/SvmHyperSyncClient.res.mjs +3 -4
- package/src/sources/SvmHyperSyncSource.res +25 -117
- package/src/sources/SvmHyperSyncSource.res.mjs +8 -121
- package/src/subgraph/blocks.ts +176 -0
- package/src/subgraph/calls.ts +217 -0
- package/src/subgraph/conformance.ts +99 -0
- package/src/subgraph/division.ts +110 -0
- package/src/subgraph/errors.ts +90 -0
- package/src/subgraph/graph-ts-types/VERSION +2 -0
- package/src/subgraph/graph-ts-types/chain/arweave.d.ts +70 -0
- package/src/subgraph/graph-ts-types/chain/cosmos.d.ts +327 -0
- package/src/subgraph/graph-ts-types/chain/ethereum.d.ts +233 -0
- package/src/subgraph/graph-ts-types/chain/near.d.ts +253 -0
- package/src/subgraph/graph-ts-types/chain/starknet.d.ts +32 -0
- package/src/subgraph/graph-ts-types/common/collections.d.ts +136 -0
- package/src/subgraph/graph-ts-types/common/conversion.d.ts +11 -0
- package/src/subgraph/graph-ts-types/common/datasource.d.ts +30 -0
- package/src/subgraph/graph-ts-types/common/eager-offset.d.ts +0 -0
- package/src/subgraph/graph-ts-types/common/json.d.ts +17 -0
- package/src/subgraph/graph-ts-types/common/numbers.d.ts +120 -0
- package/src/subgraph/graph-ts-types/common/value.d.ts +120 -0
- package/src/subgraph/graph-ts-types/common/yaml.d.ts +90 -0
- package/src/subgraph/graph-ts-types/global/global.d.ts +194 -0
- package/src/subgraph/graph-ts-types/helper-functions.d.ts +22 -0
- package/src/subgraph/graph-ts-types/index.d.ts +102 -0
- package/src/subgraph/graph-ts.ts +1871 -0
- package/src/subgraph/hosts.ts +148 -0
- package/src/subgraph/runtime.ts +845 -0
- package/src/subgraph/scope.ts +63 -0
- package/svm.schema.json +3 -2
package/src/UserContext.res.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as Utils from "./Utils.res.mjs";
|
|
4
|
+
import * as ChainId from "./ChainId.res.mjs";
|
|
4
5
|
import * as Logging from "./Logging.res.mjs";
|
|
5
6
|
import * as Internal from "./Internal.res.mjs";
|
|
6
7
|
import * as Ecosystem from "./Ecosystem.res.mjs";
|
|
@@ -12,8 +13,70 @@ import * as InMemoryStore from "./InMemoryStore.res.mjs";
|
|
|
12
13
|
import * as InMemoryTable from "./InMemoryTable.res.mjs";
|
|
13
14
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
14
15
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
16
|
+
import * as Stdlib_Promise from "@rescript/runtime/lib/es6/Stdlib_Promise.js";
|
|
15
17
|
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
16
18
|
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
|
|
19
|
+
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
20
|
+
|
|
21
|
+
let Suspend = /* @__PURE__ */Primitive_exceptions.create("UserContext.Suspend");
|
|
22
|
+
|
|
23
|
+
function isSuspend(exn) {
|
|
24
|
+
return exn.RE_EXN_ID === Suspend;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function makeSyncState() {
|
|
28
|
+
return {
|
|
29
|
+
status: "Active",
|
|
30
|
+
pending: undefined,
|
|
31
|
+
memo: undefined
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function getPending(sync) {
|
|
36
|
+
let pending = sync.pending;
|
|
37
|
+
if (pending !== undefined) {
|
|
38
|
+
return pending;
|
|
39
|
+
}
|
|
40
|
+
let pending$1 = [];
|
|
41
|
+
sync.pending = pending$1;
|
|
42
|
+
return pending$1;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function getMemo(sync) {
|
|
46
|
+
let memo = sync.memo;
|
|
47
|
+
if (memo !== undefined) {
|
|
48
|
+
return memo;
|
|
49
|
+
}
|
|
50
|
+
let memo$1 = {};
|
|
51
|
+
sync.memo = memo$1;
|
|
52
|
+
return memo$1;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function checkStatusOrThrow(params, access) {
|
|
56
|
+
let exn = params.sync.status;
|
|
57
|
+
if (typeof exn !== "object") {
|
|
58
|
+
if (exn === "Active") {
|
|
59
|
+
return;
|
|
60
|
+
} else {
|
|
61
|
+
return ErrorHandling.mkLogAndRaise(Ecosystem.getItemLogger(params.item, params.config.ecosystem), undefined, new Error(`Impossible to access ` + access + ` after the handler is resolved. Make sure you didn't miss an await in the handler.`));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
throw exn._0;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function scheduleAndSuspend(params, promise) {
|
|
68
|
+
getPending(params.sync).push(promise);
|
|
69
|
+
params.sync.status = {
|
|
70
|
+
TAG: "Aborted",
|
|
71
|
+
_0: {
|
|
72
|
+
RE_EXN_ID: Suspend
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
throw {
|
|
76
|
+
RE_EXN_ID: Suspend,
|
|
77
|
+
Error: new Error()
|
|
78
|
+
};
|
|
79
|
+
}
|
|
17
80
|
|
|
18
81
|
let paramsByThis = new WeakMap();
|
|
19
82
|
|
|
@@ -47,28 +110,69 @@ var EffectContext = function(params, chainId, effectName, defaultShouldCache, ca
|
|
|
47
110
|
EffectContext.prototype = effectContextPrototype;
|
|
48
111
|
;
|
|
49
112
|
|
|
50
|
-
function
|
|
51
|
-
let handlerChainId = Internal.getItemChainId(params.item);
|
|
113
|
+
function prepareEffectCall(params, effect, input, caller) {
|
|
52
114
|
let isCrossChain = effect => Stdlib_Option.getOr(effect.crossChain, params.config.defaultCrossChain);
|
|
53
|
-
let
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
115
|
+
let scope = isCrossChain(effect) ? "crossChain" : Internal.getItemChainId(params.item);
|
|
116
|
+
if (caller !== undefined && isCrossChain(caller) && !isCrossChain(effect)) {
|
|
117
|
+
Stdlib_JsError.throwWithMessage(`The cross-chain effect "` + caller.name + `" cannot call the chain-scoped effect "` + effect.name + `", because a cross-chain effect isn't tied to a single chain. Make "` + effect.name + `" cross-chain (\`crossChain: true\`), or make "` + caller.name + `" chain-scoped (\`crossChain: false\`).`);
|
|
118
|
+
}
|
|
119
|
+
let tmp;
|
|
120
|
+
tmp = scope === "crossChain" ? undefined : Primitive_option.some(scope);
|
|
121
|
+
let effectContext = new EffectContext(params, tmp, effect.name, effect.defaultShouldCache, (nested, nestedInput) => callEffectAsync(params, nested, nestedInput, effect));
|
|
122
|
+
let effectArgs_cacheKey = Utils.Hash.makeOrThrow(S$RescriptSchema.reverseConvertOrThrow(input, effect.input));
|
|
123
|
+
let effectArgs_checkpointId = params.checkpointId;
|
|
124
|
+
let effectArgs = {
|
|
125
|
+
input: input,
|
|
126
|
+
context: effectContext,
|
|
127
|
+
cacheKey: effectArgs_cacheKey,
|
|
128
|
+
checkpointId: effectArgs_checkpointId
|
|
129
|
+
};
|
|
130
|
+
return [
|
|
131
|
+
scope,
|
|
132
|
+
effectArgs
|
|
133
|
+
];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function callEffectAsync(params, effect, input, caller) {
|
|
137
|
+
let match = prepareEffectCall(params, effect, input, caller);
|
|
138
|
+
return LoadLayer.loadEffect(params.loadManager, params.persistence, effect, match[1], match[0], params.indexerState, params.isPreload, params.item, params.config.ecosystem);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function initEffect(params) {
|
|
142
|
+
return (effect, input) => {
|
|
143
|
+
checkStatusOrThrow(params, "context.effect");
|
|
144
|
+
return callEffectAsync(params, effect, input, undefined);
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function effectMemoKey(effect, scope, cacheKey) {
|
|
149
|
+
if (scope === "crossChain") {
|
|
150
|
+
return effect.name + `.` + cacheKey;
|
|
151
|
+
} else {
|
|
152
|
+
return effect.name + `.` + ChainId.toString(scope) + `.` + cacheKey;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function initEffectSync(params) {
|
|
157
|
+
return (effect, input) => {
|
|
158
|
+
checkStatusOrThrow(params, "context.effectSync");
|
|
159
|
+
let match = prepareEffectCall(params, effect, input, undefined);
|
|
160
|
+
let effectArgs = match[1];
|
|
161
|
+
let scope = match[0];
|
|
162
|
+
let memo = getMemo(params.sync);
|
|
163
|
+
let memoKey = effectMemoKey(effect, scope, effectArgs.cacheKey);
|
|
164
|
+
let output = memo[memoKey];
|
|
165
|
+
if (output !== undefined) {
|
|
166
|
+
return Primitive_option.valFromOption(output);
|
|
57
167
|
}
|
|
58
|
-
let
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
let
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
cacheKey: effectArgs_cacheKey,
|
|
67
|
-
checkpointId: effectArgs_checkpointId
|
|
68
|
-
};
|
|
69
|
-
return LoadLayer.loadEffect(params.loadManager, params.persistence, effect, effectArgs, scope, params.indexerState, params.isPreload, params.item, params.config.ecosystem);
|
|
70
|
-
});
|
|
71
|
-
return makeCaller(undefined);
|
|
168
|
+
let inMemTable = InMemoryStore.getEffectInMemTable(params.indexerState, effect, scope);
|
|
169
|
+
if (!InMemoryStore.hasEffectOutput(inMemTable, effectArgs.cacheKey)) {
|
|
170
|
+
return scheduleAndSuspend(params, LoadLayer.loadEffect(params.loadManager, params.persistence, effect, effectArgs, scope, params.indexerState, params.isPreload, params.item, params.config.ecosystem));
|
|
171
|
+
}
|
|
172
|
+
let output$1 = InMemoryStore.getEffectOutputUnsafe(inMemTable, effectArgs.cacheKey);
|
|
173
|
+
memo[memoKey] = output$1;
|
|
174
|
+
return output$1;
|
|
175
|
+
};
|
|
72
176
|
}
|
|
73
177
|
|
|
74
178
|
function entityScope(params) {
|
|
@@ -97,24 +201,83 @@ function throwClickHouseReadOnly(entityConfig, op) {
|
|
|
97
201
|
return Stdlib_JsError.throwWithMessage(`context.` + entityConfig.name + `.` + op + `() is unavailable: ClickHouse storage is currently write-only. Follow Envio releases to be notified when ClickHouse supports both reads and writes from handlers.`);
|
|
98
202
|
}
|
|
99
203
|
|
|
204
|
+
function getSyncHandler(params, entityId) {
|
|
205
|
+
let inMemTable = InMemoryStore.getInMemTable(params.indexerState, params.entityConfig, entityScope(params));
|
|
206
|
+
if (entityId in inMemTable.latestEntityChangeById) {
|
|
207
|
+
return InMemoryTable.Entity.getUnsafe(inMemTable)(entityId);
|
|
208
|
+
} else {
|
|
209
|
+
return scheduleAndSuspend(params, LoadLayer.loadById(params.loadManager, params.persistence, params.entityConfig, entityScope(params), params.indexerState, params.isPreload, params.item, params.config.ecosystem, entityId));
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function getWhereSyncHandler(params, filter) {
|
|
214
|
+
let entityConfig = params.entityConfig;
|
|
215
|
+
let inMemTable = InMemoryStore.getInMemTable(params.indexerState, entityConfig, entityScope(params));
|
|
216
|
+
let hasIndex = InMemoryTable.Entity.hasIndex(inMemTable);
|
|
217
|
+
let getOnIndex = InMemoryTable.Entity.getUnsafeOnIndex(inMemTable);
|
|
218
|
+
let filters = EntityFilter.parseGetWhereOrThrow(filter, entityConfig.name, entityConfig.table);
|
|
219
|
+
let missing = [];
|
|
220
|
+
let entities = [];
|
|
221
|
+
filters.forEach(filter => {
|
|
222
|
+
let filterKey = EntityFilter.toString(filter);
|
|
223
|
+
if (hasIndex(filterKey)) {
|
|
224
|
+
entities.push(...getOnIndex(filterKey));
|
|
225
|
+
} else {
|
|
226
|
+
missing.push(LoadLayer.loadByFilter(params.loadManager, params.persistence, entityConfig, entityScope(params), params.indexerState, params.isPreload, params.item, params.config.ecosystem, filter));
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
if (Utils.$$Array.notEmpty(missing)) {
|
|
230
|
+
let pending = getPending(params.sync);
|
|
231
|
+
missing.forEach(promise => {
|
|
232
|
+
pending.push(promise);
|
|
233
|
+
});
|
|
234
|
+
params.sync.status = {
|
|
235
|
+
TAG: "Aborted",
|
|
236
|
+
_0: {
|
|
237
|
+
RE_EXN_ID: Suspend
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
throw {
|
|
241
|
+
RE_EXN_ID: Suspend,
|
|
242
|
+
Error: new Error()
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
return entities;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function getInBlockSyncHandler(params, entityId) {
|
|
249
|
+
let inMemTable = InMemoryStore.getInMemTable(params.indexerState, params.entityConfig, entityScope(params));
|
|
250
|
+
let change = inMemTable.latestEntityChangeById[entityId];
|
|
251
|
+
if (change !== undefined && change.checkpointId === params.checkpointId) {
|
|
252
|
+
return InMemoryTable.Entity.mapChangeToEntity(change);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
100
256
|
let entityTraps_get = (params, prop) => {
|
|
257
|
+
checkStatusOrThrow(params, `context.` + params.entityConfig.name + `.` + prop);
|
|
101
258
|
let isClickHouseOnly = !params.entityConfig.storage.postgres;
|
|
102
|
-
let set = params.isPreload ? noopSet : entity =>
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
259
|
+
let set = params.isPreload ? noopSet : entity => {
|
|
260
|
+
checkStatusOrThrow(params, `context.` + params.entityConfig.name + `.set`);
|
|
261
|
+
InMemoryTable.Entity.set(InMemoryStore.getInMemTable(params.indexerState, params.entityConfig, entityScope(params)), IndexerState.committedCheckpointId(params.indexerState), {
|
|
262
|
+
type: "SET",
|
|
263
|
+
entityId: entity.id,
|
|
264
|
+
entity: entity,
|
|
265
|
+
checkpointId: params.checkpointId
|
|
266
|
+
});
|
|
267
|
+
};
|
|
108
268
|
switch (prop) {
|
|
109
269
|
case "deleteUnsafe" :
|
|
110
270
|
if (params.isPreload) {
|
|
111
271
|
return noopDeleteUnsafe;
|
|
112
272
|
} else {
|
|
113
|
-
return entityId =>
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
273
|
+
return entityId => {
|
|
274
|
+
checkStatusOrThrow(params, `context.` + params.entityConfig.name + `.deleteUnsafe`);
|
|
275
|
+
InMemoryTable.Entity.set(InMemoryStore.getInMemTable(params.indexerState, params.entityConfig, entityScope(params)), IndexerState.committedCheckpointId(params.indexerState), {
|
|
276
|
+
type: "DELETE",
|
|
277
|
+
entityId: entityId,
|
|
278
|
+
checkpointId: params.checkpointId
|
|
279
|
+
});
|
|
280
|
+
};
|
|
118
281
|
}
|
|
119
282
|
case "get" :
|
|
120
283
|
if (isClickHouseOnly) {
|
|
@@ -122,6 +285,8 @@ let entityTraps_get = (params, prop) => {
|
|
|
122
285
|
} else {
|
|
123
286
|
return entityId => LoadLayer.loadById(params.loadManager, params.persistence, params.entityConfig, entityScope(params), params.indexerState, params.isPreload, params.item, params.config.ecosystem, entityId);
|
|
124
287
|
}
|
|
288
|
+
case "getInBlockSync" :
|
|
289
|
+
return entityId => getInBlockSyncHandler(params, entityId);
|
|
125
290
|
case "getOrCreate" :
|
|
126
291
|
if (isClickHouseOnly) {
|
|
127
292
|
return _entity => throwClickHouseReadOnly(params.entityConfig, "getOrCreate");
|
|
@@ -147,12 +312,16 @@ let entityTraps_get = (params, prop) => {
|
|
|
147
312
|
}
|
|
148
313
|
});
|
|
149
314
|
}
|
|
315
|
+
case "getSync" :
|
|
316
|
+
return entityId => getSyncHandler(params, entityId);
|
|
150
317
|
case "getWhere" :
|
|
151
318
|
if (isClickHouseOnly) {
|
|
152
319
|
return _filter => throwClickHouseReadOnly(params.entityConfig, "getWhere");
|
|
153
320
|
} else {
|
|
154
321
|
return filter => getWhereHandler(params, filter);
|
|
155
322
|
}
|
|
323
|
+
case "getWhereSync" :
|
|
324
|
+
return filter => getWhereSyncHandler(params, filter);
|
|
156
325
|
case "set" :
|
|
157
326
|
return set;
|
|
158
327
|
default:
|
|
@@ -164,16 +333,63 @@ let entityTraps = {
|
|
|
164
333
|
get: entityTraps_get
|
|
165
334
|
};
|
|
166
335
|
|
|
167
|
-
|
|
168
|
-
if (
|
|
169
|
-
|
|
336
|
+
async function runSyncRound(params, fn, round) {
|
|
337
|
+
if (round > 10000) {
|
|
338
|
+
Stdlib_JsError.throwWithMessage(`The handler suspended on a synchronous read too many times: gave up after ` + (10000).toString() + ` rounds. This usually means the code isn't deterministic across reruns.`);
|
|
339
|
+
}
|
|
340
|
+
params.sync.status = "Active";
|
|
341
|
+
params.sync.pending = undefined;
|
|
342
|
+
let suspended;
|
|
343
|
+
try {
|
|
344
|
+
fn();
|
|
345
|
+
suspended = false;
|
|
346
|
+
} catch (raw_exn) {
|
|
347
|
+
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
348
|
+
if (exn.RE_EXN_ID === Suspend) {
|
|
349
|
+
suspended = true;
|
|
350
|
+
} else {
|
|
351
|
+
let pending = params.sync.pending;
|
|
352
|
+
if (pending !== undefined) {
|
|
353
|
+
params.sync.pending = undefined;
|
|
354
|
+
pending.forEach(promise => {
|
|
355
|
+
Utils.$$Promise.silentCatch(promise);
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
throw exn;
|
|
359
|
+
}
|
|
170
360
|
}
|
|
361
|
+
let pending$1 = params.sync.pending;
|
|
362
|
+
if (pending$1 === undefined) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
params.sync.pending = undefined;
|
|
366
|
+
if (suspended) {
|
|
367
|
+
let errors = [];
|
|
368
|
+
await Promise.all(pending$1.map(promise => Stdlib_Promise.$$catch(promise, exn => {
|
|
369
|
+
errors.push(exn);
|
|
370
|
+
return Promise.resolve();
|
|
371
|
+
})));
|
|
372
|
+
let exn$1 = errors[0];
|
|
373
|
+
if (exn$1 !== undefined) {
|
|
374
|
+
throw exn$1;
|
|
375
|
+
}
|
|
376
|
+
return await runSyncRound(params, fn, round + 1 | 0);
|
|
377
|
+
}
|
|
378
|
+
pending$1.forEach(promise => {
|
|
379
|
+
Utils.$$Promise.silentCatch(promise);
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
let handlerTraps_get = (params, prop) => {
|
|
384
|
+
checkStatusOrThrow(params, `context.` + prop);
|
|
171
385
|
switch (prop) {
|
|
172
386
|
case "chain" :
|
|
173
387
|
let chainId = Internal.getItemChainId(params.item);
|
|
174
388
|
return params.chains[chainId];
|
|
175
389
|
case "effect" :
|
|
176
390
|
return initEffect(params);
|
|
391
|
+
case "effectSync" :
|
|
392
|
+
return initEffectSync(params);
|
|
177
393
|
case "isPreload" :
|
|
178
394
|
return params.isPreload;
|
|
179
395
|
case "log" :
|
|
@@ -182,6 +398,8 @@ let handlerTraps_get = (params, prop) => {
|
|
|
182
398
|
} else {
|
|
183
399
|
return Ecosystem.getItemUserLogger(params.item, params.config.ecosystem);
|
|
184
400
|
}
|
|
401
|
+
case "runSync" :
|
|
402
|
+
return fn => runSyncRound(params, fn, 1);
|
|
185
403
|
default:
|
|
186
404
|
let entityConfig = params.config.userEntitiesByName[prop];
|
|
187
405
|
if (entityConfig !== undefined) {
|
|
@@ -194,7 +412,7 @@ let handlerTraps_get = (params, prop) => {
|
|
|
194
412
|
isPreload: params.isPreload,
|
|
195
413
|
chains: params.chains,
|
|
196
414
|
config: params.config,
|
|
197
|
-
|
|
415
|
+
sync: params.sync,
|
|
198
416
|
entityConfig: entityConfig
|
|
199
417
|
}, entityTraps);
|
|
200
418
|
} else {
|
|
@@ -211,16 +429,34 @@ function getHandlerContext(params) {
|
|
|
211
429
|
return new Proxy(params, handlerTraps);
|
|
212
430
|
}
|
|
213
431
|
|
|
432
|
+
let maxSyncRounds = 10000;
|
|
433
|
+
|
|
214
434
|
export {
|
|
435
|
+
Suspend,
|
|
436
|
+
isSuspend,
|
|
437
|
+
makeSyncState,
|
|
438
|
+
getPending,
|
|
439
|
+
getMemo,
|
|
440
|
+
checkStatusOrThrow,
|
|
441
|
+
scheduleAndSuspend,
|
|
215
442
|
paramsByThis,
|
|
216
443
|
effectContextPrototype,
|
|
444
|
+
prepareEffectCall,
|
|
445
|
+
callEffectAsync,
|
|
217
446
|
initEffect,
|
|
447
|
+
effectMemoKey,
|
|
448
|
+
initEffectSync,
|
|
218
449
|
entityScope,
|
|
219
450
|
getWhereHandler,
|
|
220
451
|
noopSet,
|
|
221
452
|
noopDeleteUnsafe,
|
|
222
453
|
throwClickHouseReadOnly,
|
|
454
|
+
getSyncHandler,
|
|
455
|
+
getWhereSyncHandler,
|
|
456
|
+
getInBlockSyncHandler,
|
|
223
457
|
entityTraps,
|
|
458
|
+
maxSyncRounds,
|
|
459
|
+
runSyncRound,
|
|
224
460
|
handlerTraps,
|
|
225
461
|
getHandlerContext,
|
|
226
462
|
}
|
package/src/Utils.res
CHANGED
|
@@ -576,6 +576,9 @@ module Set = {
|
|
|
576
576
|
@send
|
|
577
577
|
external intersection: (t<'value>, t<'value>) => t<'value> = "intersection"
|
|
578
578
|
|
|
579
|
+
@send
|
|
580
|
+
external union: (t<'value>, t<'value>) => t<'value> = "union"
|
|
581
|
+
|
|
579
582
|
let immutableAdd: (t<'a>, 'a) => t<'a> = %raw(`(set, value) => {
|
|
580
583
|
return new Set([...set, value])
|
|
581
584
|
}`)
|
|
@@ -14,8 +14,13 @@ type execParams = {query: string}
|
|
|
14
14
|
@module("@clickhouse/client")
|
|
15
15
|
external createClient: clientConfig => client = "createClient"
|
|
16
16
|
|
|
17
|
+
// `command`, not `exec`: exec hands its response stream to the caller and holds
|
|
18
|
+
// the socket until it is consumed, which nothing here does. The pool is 10
|
|
19
|
+
// sockets wide, so a schema whose DDL needs more than that — initialize issues
|
|
20
|
+
// 2N+3 statements for N entities — stalled every statement past the tenth until
|
|
21
|
+
// the 30s request timeout freed one. command destroys the stream for us.
|
|
17
22
|
@send
|
|
18
|
-
external
|
|
23
|
+
external command: (client, execParams) => promise<unit> = "command"
|
|
19
24
|
|
|
20
25
|
@send
|
|
21
26
|
external close: client => promise<unit> = "close"
|
|
@@ -628,21 +633,21 @@ let initialize = async (
|
|
|
628
633
|
// CLUSTER removes the database from every node — the engine's own log
|
|
629
634
|
// can't replicate the drop of the database it lives in — and SYNC waits
|
|
630
635
|
// for the drop to finish before the CREATE below.
|
|
631
|
-
await client->
|
|
636
|
+
await client->command({
|
|
632
637
|
query: `DROP DATABASE IF EXISTS ${database} ON CLUSTER '{cluster}' SYNC`,
|
|
633
638
|
})
|
|
634
639
|
} else {
|
|
635
|
-
await client->
|
|
640
|
+
await client->command({
|
|
636
641
|
query: `TRUNCATE DATABASE IF EXISTS ${database}${onClusterClause(~onCluster=ddlOnCluster)}`,
|
|
637
642
|
})
|
|
638
643
|
}
|
|
639
|
-
await client->
|
|
644
|
+
await client->command({
|
|
640
645
|
query: `CREATE DATABASE IF NOT EXISTS ${database}${databaseOnClusterClause}${databaseEngineClause}`,
|
|
641
646
|
})
|
|
642
647
|
|
|
643
648
|
await Promise.all(
|
|
644
649
|
entities->Array.map(entityConfig =>
|
|
645
|
-
client->
|
|
650
|
+
client->command({
|
|
646
651
|
query: makeCreateHistoryTableQuery(
|
|
647
652
|
~entityConfig,
|
|
648
653
|
~database,
|
|
@@ -653,7 +658,7 @@ let initialize = async (
|
|
|
653
658
|
})
|
|
654
659
|
),
|
|
655
660
|
)->Utils.Promise.ignoreValue
|
|
656
|
-
await client->
|
|
661
|
+
await client->command({
|
|
657
662
|
query: makeCreateCheckpointsTableQuery(
|
|
658
663
|
~database,
|
|
659
664
|
~replicated,
|
|
@@ -670,14 +675,14 @@ let initialize = async (
|
|
|
670
675
|
// caught up before creating the views. ON CLUSTER must precede the
|
|
671
676
|
// database name in this command's grammar.
|
|
672
677
|
if hasReplicatedDatabaseEngine {
|
|
673
|
-
await client->
|
|
678
|
+
await client->command({
|
|
674
679
|
query: `SYSTEM SYNC DATABASE REPLICA ON CLUSTER '{cluster}' ${database}`,
|
|
675
680
|
})
|
|
676
681
|
}
|
|
677
682
|
|
|
678
683
|
await Promise.all(
|
|
679
684
|
entities->Array.map(entityConfig =>
|
|
680
|
-
client->
|
|
685
|
+
client->command({
|
|
681
686
|
query: makeCreateViewQuery(~entityConfig, ~database, ~onCluster=ddlOnCluster),
|
|
682
687
|
})
|
|
683
688
|
),
|
|
@@ -697,7 +702,7 @@ let resume = async (client, ~database: string, ~checkpointId: Internal.checkpoin
|
|
|
697
702
|
try {
|
|
698
703
|
// Try to use the database - will throw if it doesn't exist
|
|
699
704
|
try {
|
|
700
|
-
await client->
|
|
705
|
+
await client->command({query: `USE ${database}`})
|
|
701
706
|
} catch {
|
|
702
707
|
| exn =>
|
|
703
708
|
Logging.errorWithExn(
|
|
@@ -717,14 +722,14 @@ let resume = async (client, ~database: string, ~checkpointId: Internal.checkpoin
|
|
|
717
722
|
await Promise.all(
|
|
718
723
|
tables->Array.map(table => {
|
|
719
724
|
let tableName = table["name"]
|
|
720
|
-
client->
|
|
725
|
+
client->command({
|
|
721
726
|
query: `ALTER TABLE ${database}.\`${tableName}\` DELETE WHERE \`${EntityHistory.checkpointIdFieldName}\` > ${checkpointId->BigInt.toString}`,
|
|
722
727
|
})
|
|
723
728
|
}),
|
|
724
729
|
)->Utils.Promise.ignoreValue
|
|
725
730
|
|
|
726
731
|
// Delete stale checkpoints
|
|
727
|
-
await client->
|
|
732
|
+
await client->command({
|
|
728
733
|
query: `DELETE FROM ${database}.\`${InternalTable.Checkpoints.table.tableName}\` WHERE \`${Table.idFieldName}\` > ${checkpointId->BigInt.toString}`,
|
|
729
734
|
})
|
|
730
735
|
} catch {
|
|
@@ -428,31 +428,31 @@ async function initialize(client, database, entities, param, chainIdModeOpt) {
|
|
|
428
428
|
}
|
|
429
429
|
}
|
|
430
430
|
if (hasReplicatedDatabaseEngine) {
|
|
431
|
-
await client.
|
|
431
|
+
await client.command({
|
|
432
432
|
query: `DROP DATABASE IF EXISTS ` + database + ` ON CLUSTER '{cluster}' SYNC`
|
|
433
433
|
});
|
|
434
434
|
} else {
|
|
435
|
-
await client.
|
|
435
|
+
await client.command({
|
|
436
436
|
query: `TRUNCATE DATABASE IF EXISTS ` + database + (
|
|
437
437
|
ddlOnCluster ? ` ON CLUSTER '{cluster}'` : ""
|
|
438
438
|
)
|
|
439
439
|
});
|
|
440
440
|
}
|
|
441
|
-
await client.
|
|
441
|
+
await client.command({
|
|
442
442
|
query: `CREATE DATABASE IF NOT EXISTS ` + database + databaseOnClusterClause + databaseEngineClause
|
|
443
443
|
});
|
|
444
|
-
await Promise.all(entities.map(entityConfig => client.
|
|
444
|
+
await Promise.all(entities.map(entityConfig => client.command({
|
|
445
445
|
query: makeCreateHistoryTableQuery(entityConfig, database, replicated, ddlOnCluster, chainIdMode)
|
|
446
446
|
})));
|
|
447
|
-
await client.
|
|
447
|
+
await client.command({
|
|
448
448
|
query: makeCreateCheckpointsTableQuery(database, replicated, ddlOnCluster, chainIdMode)
|
|
449
449
|
});
|
|
450
450
|
if (hasReplicatedDatabaseEngine) {
|
|
451
|
-
await client.
|
|
451
|
+
await client.command({
|
|
452
452
|
query: `SYSTEM SYNC DATABASE REPLICA ON CLUSTER '{cluster}' ` + database
|
|
453
453
|
});
|
|
454
454
|
}
|
|
455
|
-
await Promise.all(entities.map(entityConfig => client.
|
|
455
|
+
await Promise.all(entities.map(entityConfig => client.command({
|
|
456
456
|
query: makeCreateViewQuery(entityConfig, database, ddlOnCluster)
|
|
457
457
|
})));
|
|
458
458
|
return Logging.trace("ClickHouse storage initialization completed successfully");
|
|
@@ -466,7 +466,7 @@ async function initialize(client, database, entities, param, chainIdModeOpt) {
|
|
|
466
466
|
async function resume(client, database, checkpointId) {
|
|
467
467
|
try {
|
|
468
468
|
try {
|
|
469
|
-
await client.
|
|
469
|
+
await client.command({
|
|
470
470
|
query: `USE ` + database
|
|
471
471
|
});
|
|
472
472
|
} catch (raw_exn) {
|
|
@@ -480,11 +480,11 @@ async function resume(client, database, checkpointId) {
|
|
|
480
480
|
let tables = (await tablesResult.json()).data;
|
|
481
481
|
await Promise.all(tables.map(table => {
|
|
482
482
|
let tableName = table.name;
|
|
483
|
-
return client.
|
|
483
|
+
return client.command({
|
|
484
484
|
query: `ALTER TABLE ` + database + `.\`` + tableName + `\` DELETE WHERE \`` + EntityHistory.checkpointIdFieldName + `\` > ` + checkpointId.toString()
|
|
485
485
|
});
|
|
486
486
|
}));
|
|
487
|
-
return await client.
|
|
487
|
+
return await client.command({
|
|
488
488
|
query: `DELETE FROM ` + database + `.\`` + InternalTable.Checkpoints.table.tableName + `\` WHERE \`` + Table.idFieldName + `\` > ` + checkpointId.toString()
|
|
489
489
|
});
|
|
490
490
|
} catch (raw_exn$1) {
|
package/src/bindings/Vitest.res
CHANGED
|
@@ -98,7 +98,7 @@ external afterEach: (unit => unit) => unit = "afterEach"
|
|
|
98
98
|
// Async Module
|
|
99
99
|
// ============================================================================
|
|
100
100
|
|
|
101
|
-
type options = {retry?: int}
|
|
101
|
+
type options = {retry?: int, timeout?: int}
|
|
102
102
|
|
|
103
103
|
module Async = {
|
|
104
104
|
@module("vitest")
|