@livestore/common 0.4.0-dev.0 → 0.4.0-dev.10
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/dist/.tsbuildinfo +1 -1
- package/dist/ClientSessionLeaderThreadProxy.d.ts +7 -2
- package/dist/ClientSessionLeaderThreadProxy.d.ts.map +1 -1
- package/dist/ClientSessionLeaderThreadProxy.js.map +1 -1
- package/dist/adapter-types.d.ts +9 -3
- package/dist/adapter-types.d.ts.map +1 -1
- package/dist/adapter-types.js.map +1 -1
- package/dist/devtools/devtools-messages-client-session.d.ts +21 -21
- package/dist/devtools/devtools-messages-common.d.ts +7 -14
- package/dist/devtools/devtools-messages-common.d.ts.map +1 -1
- package/dist/devtools/devtools-messages-common.js +1 -6
- package/dist/devtools/devtools-messages-common.js.map +1 -1
- package/dist/devtools/devtools-messages-leader.d.ts +27 -25
- package/dist/devtools/devtools-messages-leader.d.ts.map +1 -1
- package/dist/errors.d.ts +47 -5
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +22 -3
- package/dist/errors.js.map +1 -1
- package/dist/leader-thread/LeaderSyncProcessor.d.ts +7 -3
- package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -1
- package/dist/leader-thread/LeaderSyncProcessor.js +122 -49
- package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -1
- package/dist/leader-thread/eventlog.d.ts +4 -10
- package/dist/leader-thread/eventlog.d.ts.map +1 -1
- package/dist/leader-thread/eventlog.js +4 -6
- package/dist/leader-thread/eventlog.js.map +1 -1
- package/dist/leader-thread/leader-worker-devtools.d.ts +1 -1
- package/dist/leader-thread/leader-worker-devtools.js +6 -2
- package/dist/leader-thread/leader-worker-devtools.js.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.d.ts +1 -2
- package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.js +68 -19
- package/dist/leader-thread/make-leader-thread-layer.js.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.test.d.ts +2 -0
- package/dist/leader-thread/make-leader-thread-layer.test.d.ts.map +1 -0
- package/dist/leader-thread/make-leader-thread-layer.test.js +32 -0
- package/dist/leader-thread/make-leader-thread-layer.test.js.map +1 -0
- package/dist/leader-thread/materialize-event.d.ts +2 -2
- package/dist/leader-thread/materialize-event.d.ts.map +1 -1
- package/dist/leader-thread/materialize-event.js +23 -9
- package/dist/leader-thread/materialize-event.js.map +1 -1
- package/dist/leader-thread/recreate-db.d.ts +2 -3
- package/dist/leader-thread/recreate-db.d.ts.map +1 -1
- package/dist/leader-thread/recreate-db.js +1 -1
- package/dist/leader-thread/recreate-db.js.map +1 -1
- package/dist/leader-thread/shutdown-channel.d.ts +2 -2
- package/dist/leader-thread/shutdown-channel.d.ts.map +1 -1
- package/dist/leader-thread/shutdown-channel.js +2 -2
- package/dist/leader-thread/shutdown-channel.js.map +1 -1
- package/dist/leader-thread/types.d.ts +7 -5
- package/dist/leader-thread/types.d.ts.map +1 -1
- package/dist/leader-thread/types.js.map +1 -1
- package/dist/materializer-helper.d.ts +1 -1
- package/dist/materializer-helper.d.ts.map +1 -1
- package/dist/materializer-helper.js +20 -4
- package/dist/materializer-helper.js.map +1 -1
- package/dist/rematerialize-from-eventlog.d.ts +1 -1
- package/dist/rematerialize-from-eventlog.d.ts.map +1 -1
- package/dist/rematerialize-from-eventlog.js +25 -16
- package/dist/rematerialize-from-eventlog.js.map +1 -1
- package/dist/schema/EventDef.d.ts +3 -0
- package/dist/schema/EventDef.d.ts.map +1 -1
- package/dist/schema/EventDef.js.map +1 -1
- package/dist/schema/LiveStoreEvent.d.ts +1 -1
- package/dist/schema/LiveStoreEvent.d.ts.map +1 -1
- package/dist/schema/LiveStoreEvent.js +1 -2
- package/dist/schema/LiveStoreEvent.js.map +1 -1
- package/dist/schema/mod.d.ts +2 -0
- package/dist/schema/mod.d.ts.map +1 -1
- package/dist/schema/mod.js +1 -0
- package/dist/schema/mod.js.map +1 -1
- package/dist/schema/schema.d.ts +15 -0
- package/dist/schema/schema.d.ts.map +1 -1
- package/dist/schema/schema.js +26 -1
- package/dist/schema/schema.js.map +1 -1
- package/dist/schema/state/sqlite/client-document-def.d.ts +35 -5
- package/dist/schema/state/sqlite/client-document-def.d.ts.map +1 -1
- package/dist/schema/state/sqlite/client-document-def.js +95 -4
- package/dist/schema/state/sqlite/client-document-def.js.map +1 -1
- package/dist/schema/state/sqlite/client-document-def.test.js +16 -0
- package/dist/schema/state/sqlite/client-document-def.test.js.map +1 -1
- package/dist/schema/state/sqlite/column-annotations.d.ts.map +1 -1
- package/dist/schema/state/sqlite/column-annotations.js +14 -6
- package/dist/schema/state/sqlite/column-annotations.js.map +1 -1
- package/dist/schema/state/sqlite/column-def.d.ts +19 -0
- package/dist/schema/state/sqlite/column-def.d.ts.map +1 -0
- package/dist/schema/state/sqlite/column-def.js +179 -0
- package/dist/schema/state/sqlite/column-def.js.map +1 -0
- package/dist/schema/state/sqlite/column-def.test.d.ts +2 -0
- package/dist/schema/state/sqlite/column-def.test.d.ts.map +1 -0
- package/dist/schema/state/sqlite/column-def.test.js +572 -0
- package/dist/schema/state/sqlite/column-def.test.js.map +1 -0
- package/dist/schema/state/sqlite/db-schema/ast/sqlite.d.ts +2 -1
- package/dist/schema/state/sqlite/db-schema/ast/sqlite.d.ts.map +1 -1
- package/dist/schema/state/sqlite/db-schema/ast/sqlite.js +23 -6
- package/dist/schema/state/sqlite/db-schema/ast/sqlite.js.map +1 -1
- package/dist/schema/state/sqlite/db-schema/dsl/mod.d.ts.map +1 -1
- package/dist/schema/state/sqlite/db-schema/dsl/mod.js +2 -1
- package/dist/schema/state/sqlite/db-schema/dsl/mod.js.map +1 -1
- package/dist/schema/state/sqlite/mod.d.ts +1 -1
- package/dist/schema/state/sqlite/mod.d.ts.map +1 -1
- package/dist/schema/state/sqlite/mod.js +1 -1
- package/dist/schema/state/sqlite/mod.js.map +1 -1
- package/dist/schema/state/sqlite/query-builder/api.d.ts +5 -2
- package/dist/schema/state/sqlite/query-builder/api.d.ts.map +1 -1
- package/dist/schema/state/sqlite/query-builder/impl.d.ts.map +1 -1
- package/dist/schema/state/sqlite/query-builder/impl.js +6 -2
- package/dist/schema/state/sqlite/query-builder/impl.js.map +1 -1
- package/dist/schema/state/sqlite/query-builder/impl.test.js +137 -2
- package/dist/schema/state/sqlite/query-builder/impl.test.js.map +1 -1
- package/dist/schema/state/sqlite/system-tables.d.ts +42 -6
- package/dist/schema/state/sqlite/system-tables.d.ts.map +1 -1
- package/dist/schema/state/sqlite/system-tables.js +2 -0
- package/dist/schema/state/sqlite/system-tables.js.map +1 -1
- package/dist/schema/state/sqlite/table-def.d.ts +6 -8
- package/dist/schema/state/sqlite/table-def.d.ts.map +1 -1
- package/dist/schema/state/sqlite/table-def.js +4 -211
- package/dist/schema/state/sqlite/table-def.js.map +1 -1
- package/dist/schema/state/sqlite/table-def.test.js +59 -453
- package/dist/schema/state/sqlite/table-def.test.js.map +1 -1
- package/dist/schema/unknown-events.d.ts +47 -0
- package/dist/schema/unknown-events.d.ts.map +1 -0
- package/dist/schema/unknown-events.js +69 -0
- package/dist/schema/unknown-events.js.map +1 -0
- package/dist/sql-queries/sql-query-builder.d.ts.map +1 -1
- package/dist/sql-queries/sql-query-builder.js +2 -1
- package/dist/sql-queries/sql-query-builder.js.map +1 -1
- package/dist/sync/ClientSessionSyncProcessor.d.ts +9 -11
- package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -1
- package/dist/sync/ClientSessionSyncProcessor.js +35 -33
- package/dist/sync/ClientSessionSyncProcessor.js.map +1 -1
- package/dist/sync/errors.d.ts +61 -0
- package/dist/sync/errors.d.ts.map +1 -0
- package/dist/sync/errors.js +36 -0
- package/dist/sync/errors.js.map +1 -0
- package/dist/sync/index.d.ts +3 -0
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +3 -0
- package/dist/sync/index.js.map +1 -1
- package/dist/sync/mock-sync-backend.d.ts +23 -0
- package/dist/sync/mock-sync-backend.d.ts.map +1 -0
- package/dist/sync/mock-sync-backend.js +114 -0
- package/dist/sync/mock-sync-backend.js.map +1 -0
- package/dist/sync/next/compact-events.d.ts.map +1 -1
- package/dist/sync/next/compact-events.js +4 -5
- package/dist/sync/next/compact-events.js.map +1 -1
- package/dist/sync/next/facts.d.ts.map +1 -1
- package/dist/sync/next/facts.js +1 -2
- package/dist/sync/next/facts.js.map +1 -1
- package/dist/sync/next/history-dag-common.d.ts +50 -11
- package/dist/sync/next/history-dag-common.d.ts.map +1 -1
- package/dist/sync/next/history-dag-common.js +193 -4
- package/dist/sync/next/history-dag-common.js.map +1 -1
- package/dist/sync/next/history-dag.d.ts.map +1 -1
- package/dist/sync/next/history-dag.js +3 -1
- package/dist/sync/next/history-dag.js.map +1 -1
- package/dist/sync/sync-backend-kv.d.ts +7 -0
- package/dist/sync/sync-backend-kv.d.ts.map +1 -0
- package/dist/sync/sync-backend-kv.js +18 -0
- package/dist/sync/sync-backend-kv.js.map +1 -0
- package/dist/sync/sync-backend.d.ts +105 -0
- package/dist/sync/sync-backend.d.ts.map +1 -0
- package/dist/sync/sync-backend.js +61 -0
- package/dist/sync/sync-backend.js.map +1 -0
- package/dist/sync/sync.d.ts +6 -84
- package/dist/sync/sync.d.ts.map +1 -1
- package/dist/sync/sync.js +2 -27
- package/dist/sync/sync.js.map +1 -1
- package/dist/sync/transport-chunking.d.ts +36 -0
- package/dist/sync/transport-chunking.d.ts.map +1 -0
- package/dist/sync/transport-chunking.js +56 -0
- package/dist/sync/transport-chunking.js.map +1 -0
- package/dist/sync/validate-push-payload.d.ts +1 -1
- package/dist/sync/validate-push-payload.d.ts.map +1 -1
- package/dist/sync/validate-push-payload.js +6 -6
- package/dist/sync/validate-push-payload.js.map +1 -1
- package/dist/testing/event-factory.d.ts +68 -0
- package/dist/testing/event-factory.d.ts.map +1 -0
- package/dist/testing/event-factory.js +80 -0
- package/dist/testing/event-factory.js.map +1 -0
- package/dist/testing/mod.d.ts +2 -0
- package/dist/testing/mod.d.ts.map +1 -0
- package/dist/testing/mod.js +2 -0
- package/dist/testing/mod.js.map +1 -0
- package/dist/version.d.ts +2 -2
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +2 -2
- package/dist/version.js.map +1 -1
- package/package.json +7 -8
- package/src/ClientSessionLeaderThreadProxy.ts +7 -2
- package/src/adapter-types.ts +13 -3
- package/src/devtools/devtools-messages-common.ts +1 -8
- package/src/errors.ts +33 -4
- package/src/leader-thread/LeaderSyncProcessor.ts +179 -57
- package/src/leader-thread/eventlog.ts +10 -6
- package/src/leader-thread/leader-worker-devtools.ts +6 -2
- package/src/leader-thread/make-leader-thread-layer.test.ts +44 -0
- package/src/leader-thread/make-leader-thread-layer.ts +137 -26
- package/src/leader-thread/materialize-event.ts +34 -9
- package/src/leader-thread/recreate-db.ts +11 -3
- package/src/leader-thread/shutdown-channel.ts +16 -2
- package/src/leader-thread/types.ts +7 -5
- package/src/materializer-helper.ts +22 -5
- package/src/rematerialize-from-eventlog.ts +33 -23
- package/src/schema/EventDef.ts +3 -0
- package/src/schema/LiveStoreEvent.ts +1 -2
- package/src/schema/mod.ts +2 -0
- package/src/schema/schema.ts +37 -1
- package/src/schema/state/sqlite/client-document-def.test.ts +17 -0
- package/src/schema/state/sqlite/client-document-def.ts +117 -5
- package/src/schema/state/sqlite/column-annotations.ts +16 -6
- package/src/schema/state/sqlite/column-def.test.ts +722 -0
- package/src/schema/state/sqlite/column-def.ts +215 -0
- package/src/schema/state/sqlite/db-schema/ast/sqlite.ts +26 -6
- package/src/schema/state/sqlite/db-schema/dsl/mod.ts +2 -1
- package/src/schema/state/sqlite/mod.ts +1 -0
- package/src/schema/state/sqlite/query-builder/api.ts +7 -2
- package/src/schema/state/sqlite/query-builder/impl.test.ts +187 -6
- package/src/schema/state/sqlite/query-builder/impl.ts +8 -2
- package/src/schema/state/sqlite/system-tables.ts +2 -0
- package/src/schema/state/sqlite/table-def.test.ts +74 -569
- package/src/schema/state/sqlite/table-def.ts +13 -262
- package/src/schema/unknown-events.ts +131 -0
- package/src/sql-queries/sql-query-builder.ts +2 -1
- package/src/sync/ClientSessionSyncProcessor.ts +55 -49
- package/src/sync/errors.ts +38 -0
- package/src/sync/index.ts +3 -0
- package/src/sync/mock-sync-backend.ts +184 -0
- package/src/sync/next/compact-events.ts +4 -5
- package/src/sync/next/facts.ts +1 -3
- package/src/sync/next/history-dag-common.ts +272 -21
- package/src/sync/next/history-dag.ts +3 -1
- package/src/sync/sync-backend-kv.ts +22 -0
- package/src/sync/sync-backend.ts +185 -0
- package/src/sync/sync.ts +6 -89
- package/src/sync/transport-chunking.ts +90 -0
- package/src/sync/validate-push-payload.ts +6 -7
- package/src/testing/event-factory.ts +133 -0
- package/src/testing/mod.ts +1 -0
- package/src/version.ts +2 -2
- package/dist/schema-management/migrations.test.d.ts +0 -2
- package/dist/schema-management/migrations.test.d.ts.map +0 -1
- package/dist/schema-management/migrations.test.js +0 -52
- package/dist/schema-management/migrations.test.js.map +0 -1
- package/dist/sync/next/graphology.d.ts +0 -8
- package/dist/sync/next/graphology.d.ts.map +0 -1
- package/dist/sync/next/graphology.js +0 -30
- package/dist/sync/next/graphology.js.map +0 -1
- package/dist/sync/next/graphology_.d.ts +0 -3
- package/dist/sync/next/graphology_.d.ts.map +0 -1
- package/dist/sync/next/graphology_.js +0 -3
- package/dist/sync/next/graphology_.js.map +0 -1
- package/src/sync/next/ambient.d.ts +0 -3
- package/src/sync/next/graphology.ts +0 -41
- package/src/sync/next/graphology_.ts +0 -2
package/dist/errors.d.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import { Effect, Schema, Stream } from '@livestore/utils/effect';
|
1
|
+
import { Effect, Layer, Schema, Stream } from '@livestore/utils/effect';
|
2
2
|
declare const UnexpectedError_base: Schema.TaggedErrorClass<UnexpectedError, "LiveStore.UnexpectedError", {
|
3
3
|
readonly _tag: Schema.tag<"LiveStore.UnexpectedError">;
|
4
4
|
} & {
|
@@ -8,14 +8,18 @@ declare const UnexpectedError_base: Schema.TaggedErrorClass<UnexpectedError, "Li
|
|
8
8
|
}>;
|
9
9
|
export declare class UnexpectedError extends UnexpectedError_base {
|
10
10
|
static mapToUnexpectedError: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, UnexpectedError, R>;
|
11
|
+
static mapToUnexpectedErrorLayer: <A, E, R>(layer: Layer.Layer<A, E, R>) => Layer.Layer<A, UnexpectedError, R>;
|
11
12
|
static mapToUnexpectedErrorStream: <A, E, R>(stream: Stream.Stream<A, E, R>) => Stream.Stream<A, UnexpectedError, R>;
|
12
13
|
}
|
13
|
-
declare const
|
14
|
-
readonly _tag: Schema.tag<"LiveStore.
|
14
|
+
declare const MaterializerHashMismatchError_base: Schema.TaggedErrorClass<MaterializerHashMismatchError, "LiveStore.MaterializerHashMismatchError", {
|
15
|
+
readonly _tag: Schema.tag<"LiveStore.MaterializerHashMismatchError">;
|
15
16
|
} & {
|
16
|
-
|
17
|
+
eventName: typeof Schema.String;
|
18
|
+
note: Schema.optionalWith<typeof Schema.String, {
|
19
|
+
default: () => string;
|
20
|
+
}>;
|
17
21
|
}>;
|
18
|
-
export declare class
|
22
|
+
export declare class MaterializerHashMismatchError extends MaterializerHashMismatchError_base {
|
19
23
|
}
|
20
24
|
declare const IntentionalShutdownCause_base: Schema.TaggedErrorClass<IntentionalShutdownCause, "LiveStore.IntentionalShutdownCause", {
|
21
25
|
readonly _tag: Schema.tag<"LiveStore.IntentionalShutdownCause">;
|
@@ -46,5 +50,43 @@ declare const SqliteError_base: Schema.TaggedErrorClass<SqliteError, "LiveStore.
|
|
46
50
|
}>;
|
47
51
|
export declare class SqliteError extends SqliteError_base {
|
48
52
|
}
|
53
|
+
declare const UnknownEventError_base: Schema.TaggedErrorClass<UnknownEventError, "LiveStore.UnknownEventError", {
|
54
|
+
readonly _tag: Schema.tag<"LiveStore.UnknownEventError">;
|
55
|
+
} & {
|
56
|
+
event: Schema.SchemaClass<{
|
57
|
+
readonly clientId: string;
|
58
|
+
readonly sessionId: string;
|
59
|
+
readonly name: string;
|
60
|
+
readonly seqNum: {
|
61
|
+
readonly global: number & import("effect/Brand").Brand<"GlobalEventSequenceNumber">;
|
62
|
+
readonly client: number & import("effect/Brand").Brand<"ClientEventSequenceNumber">;
|
63
|
+
readonly rebaseGeneration: number;
|
64
|
+
};
|
65
|
+
readonly args: any;
|
66
|
+
}, {
|
67
|
+
readonly clientId: string;
|
68
|
+
readonly sessionId: string;
|
69
|
+
readonly name: string;
|
70
|
+
readonly seqNum: {
|
71
|
+
readonly global: number;
|
72
|
+
readonly client: number;
|
73
|
+
readonly rebaseGeneration: number;
|
74
|
+
};
|
75
|
+
readonly args: any;
|
76
|
+
}, never>;
|
77
|
+
reason: Schema.Literal<["event-definition-missing", "materializer-missing"]>;
|
78
|
+
operation: typeof Schema.String;
|
79
|
+
note: Schema.optional<typeof Schema.String>;
|
80
|
+
}>;
|
81
|
+
export declare class UnknownEventError extends UnknownEventError_base {
|
82
|
+
}
|
83
|
+
declare const MaterializeError_base: Schema.TaggedErrorClass<MaterializeError, "LiveStore.MaterializeError", {
|
84
|
+
readonly _tag: Schema.tag<"LiveStore.MaterializeError">;
|
85
|
+
} & {
|
86
|
+
cause: Schema.Union<[typeof MaterializerHashMismatchError, typeof SqliteError, typeof UnknownEventError]>;
|
87
|
+
note: Schema.optional<typeof Schema.String>;
|
88
|
+
}>;
|
89
|
+
export declare class MaterializeError extends MaterializeError_base {
|
90
|
+
}
|
49
91
|
export {};
|
50
92
|
//# sourceMappingURL=errors.d.ts.map
|
package/dist/errors.d.ts.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAA;;;;;;;;AAI9E,qBAAa,eAAgB,SAAQ,oBAInC;IACA,MAAM,CAAC,oBAAoB,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,0CAInE;IAEH,MAAM,CAAC,yBAAyB,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,wCAOrE;IAEH,MAAM,CAAC,0BAA0B,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,0CAGzE;CACJ;;;;;;;;;AAED,qBAAa,6BAA8B,SAAQ,kCAQlD;CAAG;;;;;;AAEJ,qBAAa,wBAAyB,SAAQ,6BAK7C;CAAG;;;;;;AAEJ,qBAAa,gBAAiB,SAAQ,qBAEpC;CAAG;;;;;;;;IASH,6BAA6B;;IAI7B,iCAAiC;;;;AAXnC,qBAAa,WAAY,SAAQ,gBAc/B;CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEL,qBAAa,iBAAkB,SAAQ,sBAKrC;CAAG;;;;;;;AAEL,qBAAa,gBAAiB,SAAQ,qBAGpC;CAAG"}
|
package/dist/errors.js
CHANGED
@@ -1,14 +1,21 @@
|
|
1
|
-
import { Effect, Schema, Stream } from '@livestore/utils/effect';
|
1
|
+
import { Cause, Effect, Layer, Schema, Stream } from '@livestore/utils/effect';
|
2
|
+
import * as LiveStoreEvent from "./schema/LiveStoreEvent.js";
|
2
3
|
export class UnexpectedError extends Schema.TaggedError()('LiveStore.UnexpectedError', {
|
3
4
|
cause: Schema.Defect,
|
4
5
|
note: Schema.optional(Schema.String),
|
5
6
|
payload: Schema.optional(Schema.Any),
|
6
7
|
}) {
|
7
8
|
static mapToUnexpectedError = (effect) => effect.pipe(Effect.mapError((cause) => (Schema.is(UnexpectedError)(cause) ? cause : new UnexpectedError({ cause }))), Effect.catchAllDefect((cause) => new UnexpectedError({ cause })));
|
9
|
+
static mapToUnexpectedErrorLayer = (layer) => layer.pipe(Layer.catchAllCause((cause) => Cause.isFailType(cause) && Schema.is(UnexpectedError)(cause.error)
|
10
|
+
? Layer.fail(cause.error)
|
11
|
+
: Layer.fail(new UnexpectedError({ cause: cause }))));
|
8
12
|
static mapToUnexpectedErrorStream = (stream) => stream.pipe(Stream.mapError((cause) => (Schema.is(UnexpectedError)(cause) ? cause : new UnexpectedError({ cause }))));
|
9
13
|
}
|
10
|
-
export class
|
11
|
-
|
14
|
+
export class MaterializerHashMismatchError extends Schema.TaggedError()('LiveStore.MaterializerHashMismatchError', {
|
15
|
+
eventName: Schema.String,
|
16
|
+
note: Schema.optionalWith(Schema.String, {
|
17
|
+
default: () => 'Please make sure your event materializer is a pure function without side effects.',
|
18
|
+
}),
|
12
19
|
}) {
|
13
20
|
}
|
14
21
|
export class IntentionalShutdownCause extends Schema.TaggedError()('LiveStore.IntentionalShutdownCause', {
|
@@ -33,4 +40,16 @@ export class SqliteError extends Schema.TaggedError()('LiveStore.SqliteError', {
|
|
33
40
|
note: Schema.optional(Schema.String),
|
34
41
|
}) {
|
35
42
|
}
|
43
|
+
export class UnknownEventError extends Schema.TaggedError()('LiveStore.UnknownEventError', {
|
44
|
+
event: LiveStoreEvent.AnyEncoded.pipe(Schema.pick('name', 'args', 'seqNum', 'clientId', 'sessionId')),
|
45
|
+
reason: Schema.Literal('event-definition-missing', 'materializer-missing'),
|
46
|
+
operation: Schema.String,
|
47
|
+
note: Schema.optional(Schema.String),
|
48
|
+
}) {
|
49
|
+
}
|
50
|
+
export class MaterializeError extends Schema.TaggedError()('LiveStore.MaterializeError', {
|
51
|
+
cause: Schema.Union(MaterializerHashMismatchError, SqliteError, UnknownEventError),
|
52
|
+
note: Schema.optional(Schema.String),
|
53
|
+
}) {
|
54
|
+
}
|
36
55
|
//# sourceMappingURL=errors.js.map
|
package/dist/errors.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAA;
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
|
@@ -1,7 +1,6 @@
|
|
1
1
|
import type { Scope } from '@livestore/utils/effect';
|
2
2
|
import { Effect } from '@livestore/utils/effect';
|
3
|
-
import type
|
4
|
-
import { UnexpectedError } from '../adapter-types.ts';
|
3
|
+
import { type SqliteDb, UnexpectedError } from '../adapter-types.ts';
|
5
4
|
import type { LiveStoreSchema } from '../schema/mod.ts';
|
6
5
|
import * as SyncState from '../sync/syncstate.ts';
|
7
6
|
import type { InitialBlockingSyncContext, LeaderSyncProcessor } from './types.ts';
|
@@ -35,7 +34,7 @@ import type { InitialBlockingSyncContext, LeaderSyncProcessor } from './types.ts
|
|
35
34
|
*
|
36
35
|
* See ClientSessionSyncProcessor for how the leader and session sync processors are similar/different.
|
37
36
|
*/
|
38
|
-
export declare const makeLeaderSyncProcessor: ({ schema, dbState, initialBlockingSyncContext, initialSyncState, onError, params, testing, }: {
|
37
|
+
export declare const makeLeaderSyncProcessor: ({ schema, dbState, initialBlockingSyncContext, initialSyncState, onError, livePull, params, testing, }: {
|
39
38
|
schema: LiveStoreSchema;
|
40
39
|
dbState: SqliteDb;
|
41
40
|
initialBlockingSyncContext: InitialBlockingSyncContext;
|
@@ -52,6 +51,11 @@ export declare const makeLeaderSyncProcessor: ({ schema, dbState, initialBlockin
|
|
52
51
|
*/
|
53
52
|
backendPushBatchSize?: number;
|
54
53
|
};
|
54
|
+
/**
|
55
|
+
* Whether the sync backend should reactively pull new events from the sync backend
|
56
|
+
* When `false`, the sync processor will only do an initial pull
|
57
|
+
*/
|
58
|
+
livePull: boolean;
|
55
59
|
testing: {
|
56
60
|
delays?: {
|
57
61
|
localPushProcessing?: Effect.Effect<void>;
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"LeaderSyncProcessor.d.ts","sourceRoot":"","sources":["../../src/leader-thread/LeaderSyncProcessor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAuB,KAAK,EAAU,MAAM,yBAAyB,CAAA;AACjF,OAAO,
|
1
|
+
{"version":3,"file":"LeaderSyncProcessor.d.ts","sourceRoot":"","sources":["../../src/leader-thread/LeaderSyncProcessor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAuB,KAAK,EAAU,MAAM,yBAAyB,CAAA;AACjF,OAAO,EAKL,MAAM,EAaP,MAAM,yBAAyB,CAAA;AAEhC,OAAO,EAGL,KAAK,QAAQ,EACb,eAAe,EAChB,MAAM,qBAAqB,CAAA;AAE5B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AASvD,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAA;AAIjD,OAAO,KAAK,EAAE,0BAA0B,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAQjF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,uBAAuB,GAAI,wGASrC;IACD,MAAM,EAAE,eAAe,CAAA;IACvB,OAAO,EAAE,QAAQ,CAAA;IACjB,0BAA0B,EAAE,0BAA0B,CAAA;IACtD,sFAAsF;IACtF,gBAAgB,EAAE,SAAS,CAAC,SAAS,CAAA;IACrC,OAAO,EAAE,UAAU,GAAG,QAAQ,CAAA;IAC9B,MAAM,EAAE;QACN;;WAEG;QACH,kBAAkB,CAAC,EAAE,MAAM,CAAA;QAC3B;;WAEG;QACH,oBAAoB,CAAC,EAAE,MAAM,CAAA;KAC9B,CAAA;IACD;;;OAGG;IACH,QAAQ,EAAE,OAAO,CAAA;IACjB,OAAO,EAAE;QACP,MAAM,CAAC,EAAE;YACP,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;SAC1C,CAAA;KACF,CAAA;CACF,KAAG,MAAM,CAAC,MAAM,CAAC,mBAAmB,EAAE,eAAe,EAAE,KAAK,CAAC,KAAK,CAuR/D,CAAA"}
|
@@ -1,9 +1,9 @@
|
|
1
1
|
import { casesHandled, isNotUndefined, LS_DEV, shouldNeverHappen, TRACE_VERBOSE } from '@livestore/utils';
|
2
|
-
import { BucketQueue, Deferred, Effect, Exit, FiberHandle, Option, OtelTracer, pipe, Queue, ReadonlyArray, Stream, Subscribable, SubscriptionRef, } from '@livestore/utils/effect';
|
3
|
-
import {
|
2
|
+
import { BucketQueue, Cause, Deferred, Duration, Effect, Exit, FiberHandle, Layer, Option, OtelTracer, pipe, Queue, ReadonlyArray, Schedule, Stream, Subscribable, SubscriptionRef, } from '@livestore/utils/effect';
|
3
|
+
import { UnexpectedError, } from "../adapter-types.js";
|
4
4
|
import { makeMaterializerHash } from "../materializer-helper.js";
|
5
|
-
import { EventSequenceNumber,
|
6
|
-
import { LeaderAheadError } from "../sync/sync.js";
|
5
|
+
import { EventSequenceNumber, LiveStoreEvent, resolveEventDef, SystemTables } from "../schema/mod.js";
|
6
|
+
import { LeaderAheadError, } from "../sync/sync.js";
|
7
7
|
import * as SyncState from "../sync/syncstate.js";
|
8
8
|
import { sql } from "../util.js";
|
9
9
|
import * as Eventlog from "./eventlog.js";
|
@@ -39,15 +39,12 @@ import { LeaderThreadCtx } from "./types.js";
|
|
39
39
|
*
|
40
40
|
* See ClientSessionSyncProcessor for how the leader and session sync processors are similar/different.
|
41
41
|
*/
|
42
|
-
export const makeLeaderSyncProcessor = ({ schema, dbState, initialBlockingSyncContext, initialSyncState, onError, params, testing, }) => Effect.gen(function* () {
|
42
|
+
export const makeLeaderSyncProcessor = ({ schema, dbState, initialBlockingSyncContext, initialSyncState, onError, livePull, params, testing, }) => Effect.gen(function* () {
|
43
43
|
const syncBackendPushQueue = yield* BucketQueue.make();
|
44
44
|
const localPushBatchSize = params.localPushBatchSize ?? 1;
|
45
45
|
const backendPushBatchSize = params.backendPushBatchSize ?? 2;
|
46
46
|
const syncStateSref = yield* SubscriptionRef.make(undefined);
|
47
|
-
const isClientEvent = (eventEncoded) =>
|
48
|
-
const { eventDef } = getEventDef(schema, eventEncoded.name);
|
49
|
-
return eventDef.options.clientOnly;
|
50
|
-
};
|
47
|
+
const isClientEvent = (eventEncoded) => schema.eventsDefsMap.get(eventEncoded.name)?.options.clientOnly ?? false;
|
51
48
|
const connectedClientSessionPullQueues = yield* makePullQueueSet;
|
52
49
|
// This context depends on data from `boot`, we should find a better implementation to avoid this ref indirection.
|
53
50
|
const ctxRef = {
|
@@ -98,13 +95,30 @@ export const makeLeaderSyncProcessor = ({ schema, dbState, initialBlockingSyncCo
|
|
98
95
|
const syncState = yield* syncStateSref;
|
99
96
|
if (syncState === undefined)
|
100
97
|
return shouldNeverHappen('Not initialized');
|
101
|
-
const
|
98
|
+
const resolution = yield* resolveEventDef(schema, {
|
99
|
+
operation: '@livestore/common:LeaderSyncProcessor:pushPartial',
|
100
|
+
event: {
|
101
|
+
name,
|
102
|
+
args,
|
103
|
+
clientId,
|
104
|
+
sessionId,
|
105
|
+
seqNum: syncState.localHead,
|
106
|
+
},
|
107
|
+
}).pipe(UnexpectedError.mapToUnexpectedError);
|
108
|
+
if (resolution._tag === 'unknown') {
|
109
|
+
// Ignore partial pushes for unrecognised events – they are still
|
110
|
+
// persisted server-side once a schema update ships.
|
111
|
+
return;
|
112
|
+
}
|
102
113
|
const eventEncoded = new LiveStoreEvent.EncodedWithMeta({
|
103
114
|
name,
|
104
115
|
args,
|
105
116
|
clientId,
|
106
117
|
sessionId,
|
107
|
-
...EventSequenceNumber.nextPair({
|
118
|
+
...EventSequenceNumber.nextPair({
|
119
|
+
seqNum: syncState.localHead,
|
120
|
+
isClient: resolution.eventDef.options.clientOnly,
|
121
|
+
}),
|
108
122
|
});
|
109
123
|
yield* push([eventEncoded]);
|
110
124
|
}).pipe(Effect.catchTag('LeaderAheadError', Effect.orDie));
|
@@ -127,18 +141,23 @@ export const makeLeaderSyncProcessor = ({ schema, dbState, initialBlockingSyncCo
|
|
127
141
|
const globalPendingEvents = initialSyncState.pending
|
128
142
|
// Don't sync clientOnly events
|
129
143
|
.filter((eventEncoded) => {
|
130
|
-
const
|
131
|
-
return eventDef.options.clientOnly === false;
|
144
|
+
const eventDef = schema.eventsDefsMap.get(eventEncoded.name);
|
145
|
+
return eventDef === undefined ? true : eventDef.options.clientOnly === false;
|
132
146
|
});
|
133
147
|
if (globalPendingEvents.length > 0) {
|
134
148
|
yield* BucketQueue.offerAll(syncBackendPushQueue, globalPendingEvents);
|
135
149
|
}
|
136
150
|
}
|
137
|
-
const
|
138
|
-
if (onError === '
|
139
|
-
|
140
|
-
|
151
|
+
const maybeShutdownOnError = (cause) => Effect.gen(function* () {
|
152
|
+
if (onError === 'ignore') {
|
153
|
+
if (LS_DEV) {
|
154
|
+
yield* Effect.logDebug(`Ignoring sync error (${cause._tag === 'Fail' ? cause.error._tag : cause._tag})`, Cause.pretty(cause));
|
155
|
+
}
|
156
|
+
return;
|
141
157
|
}
|
158
|
+
const errorToSend = Cause.isFailType(cause) ? cause.error : UnexpectedError.make({ cause });
|
159
|
+
yield* shutdownChannel.send(errorToSend).pipe(Effect.orDie);
|
160
|
+
return yield* Effect.die(cause);
|
142
161
|
});
|
143
162
|
yield* backgroundApplyLocalPushes({
|
144
163
|
localPushesLatch,
|
@@ -154,17 +173,16 @@ export const makeLeaderSyncProcessor = ({ schema, dbState, initialBlockingSyncCo
|
|
154
173
|
testing: {
|
155
174
|
delay: testing?.delays?.localPushProcessing,
|
156
175
|
},
|
157
|
-
}).pipe(Effect.
|
176
|
+
}).pipe(Effect.catchAllCause(maybeShutdownOnError), Effect.forkScoped);
|
158
177
|
const backendPushingFiberHandle = yield* FiberHandle.make();
|
159
178
|
const backendPushingEffect = backgroundBackendPushing({
|
160
179
|
syncBackendPushQueue,
|
161
180
|
otelSpan,
|
162
181
|
devtoolsLatch: ctxRef.current?.devtoolsLatch,
|
163
182
|
backendPushBatchSize,
|
164
|
-
}).pipe(Effect.
|
183
|
+
}).pipe(Effect.catchAllCause(maybeShutdownOnError));
|
165
184
|
yield* FiberHandle.run(backendPushingFiberHandle, backendPushingEffect);
|
166
185
|
yield* backgroundBackendPulling({
|
167
|
-
initialBackendHead: initialSyncState.upstreamHead.global,
|
168
186
|
isClientEvent,
|
169
187
|
restartBackendPushing: (filteredRebasedPending) => Effect.gen(function* () {
|
170
188
|
// Stop current pushing fiber
|
@@ -178,13 +196,20 @@ export const makeLeaderSyncProcessor = ({ schema, dbState, initialBlockingSyncCo
|
|
178
196
|
syncStateSref,
|
179
197
|
localPushesLatch,
|
180
198
|
pullLatch,
|
199
|
+
livePull,
|
181
200
|
dbState,
|
182
201
|
otelSpan,
|
183
202
|
initialBlockingSyncContext,
|
184
203
|
devtoolsLatch: ctxRef.current?.devtoolsLatch,
|
185
204
|
connectedClientSessionPullQueues,
|
186
205
|
advancePushHead,
|
187
|
-
}).pipe(Effect.
|
206
|
+
}).pipe(Effect.retry({
|
207
|
+
// We want to retry pulling if we've lost connection to the sync backend
|
208
|
+
while: (cause) => cause._tag === 'IsOfflineError',
|
209
|
+
}), Effect.catchAllCause(maybeShutdownOnError),
|
210
|
+
// Needed to avoid `Fiber terminated with an unhandled error` logs which seem to happen because of the `Effect.retry` above.
|
211
|
+
// This might be a bug in Effect. Only seems to happen in the browser.
|
212
|
+
Effect.provide(Layer.setUnhandledErrorLogLevel(Option.none())), Effect.forkScoped);
|
188
213
|
return { initialLeaderHead: initialSyncState.localHead };
|
189
214
|
}).pipe(Effect.withSpanScoped('@livestore/common:LeaderSyncProcessor:boot'));
|
190
215
|
const pull = ({ cursor }) => Effect.gen(function* () {
|
@@ -243,7 +268,10 @@ const backgroundApplyLocalPushes = ({ localPushesLatch, localPushesQueue, pullLa
|
|
243
268
|
const currentRebaseGeneration = syncState.localHead.rebaseGeneration;
|
244
269
|
// Since the rebase generation might have changed since enqueuing, we need to filter out items with older generation
|
245
270
|
// It's important that we filter after we got localPushesLatch, otherwise we might filter with the old generation
|
246
|
-
const [newEvents, deferreds] = pipe(batchItems, ReadonlyArray.filter(([eventEncoded]) =>
|
271
|
+
const [newEvents, deferreds] = pipe(batchItems, ReadonlyArray.filter(([eventEncoded]) =>
|
272
|
+
// Keep events that match the current generation or newer. Older generations will
|
273
|
+
// be rejected below when their sequence numbers no longer advance the local head.
|
274
|
+
eventEncoded.seqNum.rebaseGeneration >= currentRebaseGeneration), ReadonlyArray.unzip);
|
247
275
|
if (newEvents.length === 0) {
|
248
276
|
// console.log('dropping old-gen batch', currentLocalPushGenerationRef.current)
|
249
277
|
// Allow the backend pulling to start
|
@@ -262,7 +290,7 @@ const backgroundApplyLocalPushes = ({ localPushesLatch, localPushesQueue, pullLa
|
|
262
290
|
batchSize: newEvents.length,
|
263
291
|
newEvents: TRACE_VERBOSE ? JSON.stringify(newEvents) : undefined,
|
264
292
|
});
|
265
|
-
return yield* new
|
293
|
+
return yield* new UnexpectedError({ cause: mergeResult.message });
|
266
294
|
}
|
267
295
|
case 'rebase': {
|
268
296
|
return shouldNeverHappen('The leader thread should never have to rebase due to a local push');
|
@@ -314,8 +342,8 @@ const backgroundApplyLocalPushes = ({ localPushesLatch, localPushesQueue, pullLa
|
|
314
342
|
});
|
315
343
|
// Don't sync clientOnly events
|
316
344
|
const filteredBatch = mergeResult.newEvents.filter((eventEncoded) => {
|
317
|
-
const
|
318
|
-
return eventDef.options.clientOnly === false;
|
345
|
+
const eventDef = schema.eventsDefsMap.get(eventEncoded.name);
|
346
|
+
return eventDef === undefined ? true : eventDef.options.clientOnly === false;
|
319
347
|
});
|
320
348
|
yield* BucketQueue.offerAll(syncBackendPushQueue, filteredBatch);
|
321
349
|
yield* materializeEventsBatch({ batchItems: mergeResult.newEvents, deferreds });
|
@@ -348,12 +376,12 @@ const materializeEventsBatch = ({ batchItems, deferreds }) => Effect.gen(functio
|
|
348
376
|
dbEventlog.execute('COMMIT', undefined); // Commit the transaction
|
349
377
|
}).pipe(Effect.uninterruptible, Effect.scoped, Effect.withSpan('@livestore/common:LeaderSyncProcessor:materializeEventItems', {
|
350
378
|
attributes: { batchSize: batchItems.length },
|
351
|
-
}), Effect.tapCauseLogPretty
|
352
|
-
const backgroundBackendPulling = ({
|
379
|
+
}), Effect.tapCauseLogPretty);
|
380
|
+
const backgroundBackendPulling = ({ isClientEvent, restartBackendPushing, otelSpan, dbState, syncStateSref, localPushesLatch, livePull, pullLatch, devtoolsLatch, initialBlockingSyncContext, connectedClientSessionPullQueues, advancePushHead, }) => Effect.gen(function* () {
|
353
381
|
const { syncBackend, dbState: db, dbEventlog, schema } = yield* LeaderThreadCtx;
|
354
382
|
if (syncBackend === undefined)
|
355
383
|
return;
|
356
|
-
const onNewPullChunk = (newEvents,
|
384
|
+
const onNewPullChunk = (newEvents, pageInfo) => Effect.gen(function* () {
|
357
385
|
if (newEvents.length === 0)
|
358
386
|
return;
|
359
387
|
if (devtoolsLatch !== undefined) {
|
@@ -381,7 +409,7 @@ const backgroundBackendPulling = ({ initialBackendHead, isClientEvent, restartBa
|
|
381
409
|
newEventsCount: newEvents.length,
|
382
410
|
newEvents: TRACE_VERBOSE ? JSON.stringify(newEvents) : undefined,
|
383
411
|
});
|
384
|
-
return yield* new
|
412
|
+
return yield* new UnexpectedError({ cause: mergeResult.message });
|
385
413
|
}
|
386
414
|
const newBackendHead = newEvents.at(-1).seqNum;
|
387
415
|
Eventlog.updateBackendHead(dbEventlog, newBackendHead);
|
@@ -393,8 +421,8 @@ const backgroundBackendPulling = ({ initialBackendHead, isClientEvent, restartBa
|
|
393
421
|
mergeResult: TRACE_VERBOSE ? JSON.stringify(mergeResult) : undefined,
|
394
422
|
});
|
395
423
|
const globalRebasedPendingEvents = mergeResult.newSyncState.pending.filter((event) => {
|
396
|
-
const
|
397
|
-
return eventDef.options.clientOnly === false;
|
424
|
+
const eventDef = schema.eventsDefsMap.get(event.name);
|
425
|
+
return eventDef === undefined ? true : eventDef.options.clientOnly === false;
|
398
426
|
});
|
399
427
|
yield* restartBackendPushing(globalRebasedPendingEvents);
|
400
428
|
if (mergeResult.rollbackEvents.length > 0) {
|
@@ -414,6 +442,12 @@ const backgroundBackendPulling = ({ initialBackendHead, isClientEvent, restartBa
|
|
414
442
|
newEventsCount: newEvents.length,
|
415
443
|
mergeResult: TRACE_VERBOSE ? JSON.stringify(mergeResult) : undefined,
|
416
444
|
});
|
445
|
+
// Ensure push fiber is active after advance by restarting with current pending (non-client) events
|
446
|
+
const globalPendingEvents = mergeResult.newSyncState.pending.filter((event) => {
|
447
|
+
const eventDef = schema.eventsDefsMap.get(event.name);
|
448
|
+
return eventDef === undefined ? true : eventDef.options.clientOnly === false;
|
449
|
+
});
|
450
|
+
yield* restartBackendPushing(globalPendingEvents);
|
417
451
|
yield* connectedClientSessionPullQueues.offer({
|
418
452
|
payload: SyncState.payloadFromMergeResult(mergeResult),
|
419
453
|
leaderHead: mergeResult.newSyncState.localHead,
|
@@ -422,7 +456,7 @@ const backgroundBackendPulling = ({ initialBackendHead, isClientEvent, restartBa
|
|
422
456
|
// `mergeResult.confirmedEvents` don't contain the correct sync metadata, so we need to use
|
423
457
|
// `newEvents` instead which we filter via `mergeResult.confirmedEvents`
|
424
458
|
const confirmedNewEvents = newEvents.filter((event) => mergeResult.confirmedEvents.some((confirmedEvent) => EventSequenceNumber.isEqual(event.seqNum, confirmedEvent.seqNum)));
|
425
|
-
yield* Eventlog.updateSyncMetadata(confirmedNewEvents);
|
459
|
+
yield* Eventlog.updateSyncMetadata(confirmedNewEvents).pipe(UnexpectedError.mapToUnexpectedError);
|
426
460
|
}
|
427
461
|
}
|
428
462
|
// Removes the changeset rows which are no longer needed as we'll never have to rollback beyond this point
|
@@ -431,15 +465,18 @@ const backgroundBackendPulling = ({ initialBackendHead, isClientEvent, restartBa
|
|
431
465
|
yield* materializeEventsBatch({ batchItems: mergeResult.newEvents, deferreds: undefined });
|
432
466
|
yield* SubscriptionRef.set(syncStateSref, mergeResult.newSyncState);
|
433
467
|
// Allow local pushes to be processed again
|
434
|
-
if (
|
468
|
+
if (pageInfo._tag === 'NoMore') {
|
435
469
|
yield* localPushesLatch.open;
|
436
470
|
}
|
437
471
|
});
|
438
|
-
const
|
472
|
+
const syncState = yield* syncStateSref;
|
473
|
+
if (syncState === undefined)
|
474
|
+
return shouldNeverHappen('Not initialized');
|
475
|
+
const cursorInfo = yield* Eventlog.getSyncBackendCursorInfo({ remoteHead: syncState.upstreamHead.global });
|
439
476
|
const hashMaterializerResult = makeMaterializerHash({ schema, dbState });
|
440
|
-
yield* syncBackend.pull(cursorInfo).pipe(
|
477
|
+
yield* syncBackend.pull(cursorInfo, { live: livePull }).pipe(
|
441
478
|
// TODO only take from queue while connected
|
442
|
-
Stream.tap(({ batch,
|
479
|
+
Stream.tap(({ batch, pageInfo }) => Effect.gen(function* () {
|
443
480
|
// yield* Effect.spanEvent('batch', {
|
444
481
|
// attributes: {
|
445
482
|
// batchSize: batch.length,
|
@@ -456,9 +493,11 @@ const backgroundBackendPulling = ({ initialBackendHead, isClientEvent, restartBa
|
|
456
493
|
// This is a bug and needs to be fixed https://github.com/livestorejs/livestore/issues/503#issuecomment-3114533165
|
457
494
|
materializerHashLeader: hashMaterializerResult(LiveStoreEvent.encodedFromGlobal(_.eventEncoded)),
|
458
495
|
materializerHashSession: Option.none(),
|
459
|
-
})),
|
460
|
-
yield* initialBlockingSyncContext.update({ processed: batch.length,
|
496
|
+
})), pageInfo);
|
497
|
+
yield* initialBlockingSyncContext.update({ processed: batch.length, pageInfo });
|
461
498
|
})), Stream.runDrain, Effect.interruptible);
|
499
|
+
// Should only ever happen when livePull is false
|
500
|
+
yield* Effect.logDebug('backend-pulling finished', { livePull });
|
462
501
|
}).pipe(Effect.withSpan('@livestore/common:LeaderSyncProcessor:backend-pulling'));
|
463
502
|
const backgroundBackendPushing = ({ syncBackendPushQueue, otelSpan, devtoolsLatch, backendPushBatchSize, }) => Effect.gen(function* () {
|
464
503
|
const { syncBackend } = yield* LeaderThreadCtx;
|
@@ -475,16 +514,39 @@ const backgroundBackendPushing = ({ syncBackendPushQueue, otelSpan, devtoolsLatc
|
|
475
514
|
batchSize: queueItems.length,
|
476
515
|
batch: TRACE_VERBOSE ? JSON.stringify(queueItems) : undefined,
|
477
516
|
});
|
478
|
-
//
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
517
|
+
// Push with declarative retry/backoff using Effect schedules
|
518
|
+
// - Exponential backoff starting at 1s and doubling (1s, 2s, 4s, 8s, 16s, 30s ...)
|
519
|
+
// - Delay clamped at 30s (continues retrying at 30s)
|
520
|
+
// - Resets automatically after successful push
|
521
|
+
// TODO(metrics): expose counters/gauges for retry attempts and queue health via devtools/metrics
|
522
|
+
// Only retry for transient UnexpectedError cases
|
523
|
+
const isRetryable = (err) => err._tag === 'InvalidPushError' && err.cause._tag === 'LiveStore.UnexpectedError';
|
524
|
+
// Input: InvalidPushError | IsOfflineError, Output: Duration
|
525
|
+
const retrySchedule = Schedule.exponential(Duration.seconds(1)).pipe(Schedule.andThenEither(Schedule.spaced(Duration.seconds(30))), // clamp at 30 second intervals
|
526
|
+
Schedule.compose(Schedule.elapsed), Schedule.whileInput(isRetryable));
|
527
|
+
yield* Effect.gen(function* () {
|
528
|
+
const iteration = yield* Schedule.CurrentIterationMetadata;
|
529
|
+
const pushResult = yield* syncBackend.push(queueItems.map((_) => _.toGlobal())).pipe(Effect.either);
|
530
|
+
const retries = iteration.recurrence;
|
531
|
+
if (retries > 0 && pushResult._tag === 'Right') {
|
532
|
+
otelSpan?.addEvent('backend-push-retry-success', { retries, batchSize: queueItems.length });
|
483
533
|
}
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
534
|
+
if (pushResult._tag === 'Left') {
|
535
|
+
otelSpan?.addEvent('backend-push-error', {
|
536
|
+
error: pushResult.left.toString(),
|
537
|
+
retries,
|
538
|
+
batchSize: queueItems.length,
|
539
|
+
});
|
540
|
+
const error = pushResult.left;
|
541
|
+
if (error._tag === 'IsOfflineError' ||
|
542
|
+
(error._tag === 'InvalidPushError' && error.cause._tag === 'ServerAheadError')) {
|
543
|
+
// It's a core part of the sync protocol that the sync backend will emit a new pull chunk alongside the ServerAheadError
|
544
|
+
yield* Effect.logDebug('handled backend-push-error (waiting for interupt caused by pull)', { error });
|
545
|
+
return yield* Effect.never;
|
546
|
+
}
|
547
|
+
return yield* error;
|
548
|
+
}
|
549
|
+
}).pipe(Effect.retry(retrySchedule));
|
488
550
|
}
|
489
551
|
}).pipe(Effect.interruptible, Effect.withSpan('@livestore/common:LeaderSyncProcessor:backend-pushing'));
|
490
552
|
const trimChangesetRows = (db, newHead) => {
|
@@ -575,14 +637,25 @@ const makePullQueueSet = Effect.gen(function* () {
|
|
575
637
|
offer,
|
576
638
|
};
|
577
639
|
});
|
640
|
+
/**
|
641
|
+
* Validate a client-provided batch before it is admitted to the leader queue.
|
642
|
+
* Ensures the numbers form a strictly increasing chain and that the first
|
643
|
+
* event sits ahead of the current push head.
|
644
|
+
*/
|
578
645
|
const validatePushBatch = (batch, pushHead) => Effect.gen(function* () {
|
579
646
|
if (batch.length === 0) {
|
580
647
|
return;
|
581
648
|
}
|
582
|
-
//
|
649
|
+
// Example: session A already enqueued e1…e6 while session B (same client, different
|
650
|
+
// session) still believes the head is e1 and submits [e2, e7, e8]. The numbers look
|
651
|
+
// monotonic from B’s perspective, but we must reject and force B to rebase locally
|
652
|
+
// so the leader never regresses.
|
583
653
|
for (let i = 1; i < batch.length; i++) {
|
584
654
|
if (EventSequenceNumber.isGreaterThanOrEqual(batch[i - 1].seqNum, batch[i].seqNum)) {
|
585
|
-
|
655
|
+
return yield* LeaderAheadError.make({
|
656
|
+
minimumExpectedNum: batch[i - 1].seqNum,
|
657
|
+
providedNum: batch[i].seqNum,
|
658
|
+
});
|
586
659
|
}
|
587
660
|
}
|
588
661
|
// Make sure smallest sequence number is > pushHead
|