@livestore/common 0.3.0-dev.27 → 0.3.0-dev.29
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/__tests__/fixture.d.ts +83 -221
- package/dist/__tests__/fixture.d.ts.map +1 -1
- package/dist/__tests__/fixture.js +33 -11
- package/dist/__tests__/fixture.js.map +1 -1
- package/dist/adapter-types.d.ts +22 -15
- package/dist/adapter-types.d.ts.map +1 -1
- package/dist/adapter-types.js +15 -2
- package/dist/adapter-types.js.map +1 -1
- package/dist/bounded-collections.d.ts +1 -1
- package/dist/bounded-collections.d.ts.map +1 -1
- package/dist/debug-info.d.ts.map +1 -1
- package/dist/debug-info.js +1 -0
- package/dist/debug-info.js.map +1 -1
- package/dist/devtools/devtools-messages-client-session.d.ts +21 -21
- package/dist/devtools/devtools-messages-common.d.ts +6 -6
- package/dist/devtools/devtools-messages-leader.d.ts +45 -45
- package/dist/devtools/devtools-messages-leader.d.ts.map +1 -1
- package/dist/devtools/devtools-messages-leader.js +11 -11
- package/dist/devtools/devtools-messages-leader.js.map +1 -1
- package/dist/index.d.ts +2 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -5
- package/dist/index.js.map +1 -1
- package/dist/leader-thread/LeaderSyncProcessor.d.ts +25 -12
- package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -1
- package/dist/leader-thread/LeaderSyncProcessor.js +125 -89
- package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -1
- package/dist/leader-thread/{apply-mutation.d.ts → apply-event.d.ts} +7 -7
- package/dist/leader-thread/apply-event.d.ts.map +1 -0
- package/dist/leader-thread/apply-event.js +103 -0
- package/dist/leader-thread/apply-event.js.map +1 -0
- package/dist/leader-thread/eventlog.d.ts +27 -0
- package/dist/leader-thread/eventlog.d.ts.map +1 -0
- package/dist/leader-thread/eventlog.js +123 -0
- package/dist/leader-thread/eventlog.js.map +1 -0
- package/dist/leader-thread/leader-worker-devtools.js +18 -18
- package/dist/leader-thread/leader-worker-devtools.js.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.d.ts +16 -4
- package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.js +23 -16
- package/dist/leader-thread/make-leader-thread-layer.js.map +1 -1
- package/dist/leader-thread/mod.d.ts +1 -1
- package/dist/leader-thread/mod.d.ts.map +1 -1
- package/dist/leader-thread/mod.js +1 -1
- package/dist/leader-thread/mod.js.map +1 -1
- package/dist/leader-thread/recreate-db.d.ts.map +1 -1
- package/dist/leader-thread/recreate-db.js +6 -8
- package/dist/leader-thread/recreate-db.js.map +1 -1
- package/dist/leader-thread/types.d.ts +11 -11
- package/dist/leader-thread/types.d.ts.map +1 -1
- package/dist/materializer-helper.d.ts +23 -0
- package/dist/materializer-helper.d.ts.map +1 -0
- package/dist/materializer-helper.js +70 -0
- package/dist/materializer-helper.js.map +1 -0
- package/dist/query-builder/api.d.ts +58 -53
- package/dist/query-builder/api.d.ts.map +1 -1
- package/dist/query-builder/api.js +3 -5
- package/dist/query-builder/api.js.map +1 -1
- package/dist/query-builder/astToSql.d.ts.map +1 -1
- package/dist/query-builder/astToSql.js +59 -37
- package/dist/query-builder/astToSql.js.map +1 -1
- package/dist/query-builder/impl.d.ts +2 -3
- package/dist/query-builder/impl.d.ts.map +1 -1
- package/dist/query-builder/impl.js +48 -46
- package/dist/query-builder/impl.js.map +1 -1
- package/dist/query-builder/impl.test.d.ts +86 -1
- package/dist/query-builder/impl.test.d.ts.map +1 -1
- package/dist/query-builder/impl.test.js +244 -36
- package/dist/query-builder/impl.test.js.map +1 -1
- package/dist/rehydrate-from-eventlog.d.ts +14 -0
- package/dist/rehydrate-from-eventlog.d.ts.map +1 -0
- package/dist/{rehydrate-from-mutationlog.js → rehydrate-from-eventlog.js} +25 -26
- package/dist/rehydrate-from-eventlog.js.map +1 -0
- package/dist/schema/EventDef.d.ts +136 -0
- package/dist/schema/EventDef.d.ts.map +1 -0
- package/dist/schema/EventDef.js +58 -0
- package/dist/schema/EventDef.js.map +1 -0
- package/dist/schema/EventId.d.ts +2 -2
- package/dist/schema/EventId.d.ts.map +1 -1
- package/dist/schema/EventId.js +8 -2
- package/dist/schema/EventId.js.map +1 -1
- package/dist/schema/{MutationEvent.d.ts → LiveStoreEvent.d.ts} +56 -56
- package/dist/schema/LiveStoreEvent.d.ts.map +1 -0
- package/dist/schema/{MutationEvent.js → LiveStoreEvent.js} +25 -25
- package/dist/schema/LiveStoreEvent.js.map +1 -0
- package/dist/schema/client-document-def.d.ts +223 -0
- package/dist/schema/client-document-def.d.ts.map +1 -0
- package/dist/schema/client-document-def.js +170 -0
- package/dist/schema/client-document-def.js.map +1 -0
- package/dist/schema/client-document-def.test.d.ts +2 -0
- package/dist/schema/client-document-def.test.d.ts.map +1 -0
- package/dist/schema/client-document-def.test.js +201 -0
- package/dist/schema/client-document-def.test.js.map +1 -0
- package/dist/schema/db-schema/dsl/mod.d.ts.map +1 -1
- package/dist/schema/events.d.ts +2 -0
- package/dist/schema/events.d.ts.map +1 -0
- package/dist/schema/events.js +2 -0
- package/dist/schema/events.js.map +1 -0
- package/dist/schema/mod.d.ts +4 -3
- package/dist/schema/mod.d.ts.map +1 -1
- package/dist/schema/mod.js +4 -3
- package/dist/schema/mod.js.map +1 -1
- package/dist/schema/schema.d.ts +27 -23
- package/dist/schema/schema.d.ts.map +1 -1
- package/dist/schema/schema.js +45 -43
- package/dist/schema/schema.js.map +1 -1
- package/dist/schema/sqlite-state.d.ts +12 -0
- package/dist/schema/sqlite-state.d.ts.map +1 -0
- package/dist/schema/sqlite-state.js +36 -0
- package/dist/schema/sqlite-state.js.map +1 -0
- package/dist/schema/system-tables.d.ts +67 -98
- package/dist/schema/system-tables.d.ts.map +1 -1
- package/dist/schema/system-tables.js +62 -48
- package/dist/schema/system-tables.js.map +1 -1
- package/dist/schema/table-def.d.ts +26 -96
- package/dist/schema/table-def.d.ts.map +1 -1
- package/dist/schema/table-def.js +16 -64
- package/dist/schema/table-def.js.map +1 -1
- package/dist/schema/view.d.ts +3 -0
- package/dist/schema/view.d.ts.map +1 -0
- package/dist/schema/view.js +3 -0
- package/dist/schema/view.js.map +1 -0
- package/dist/schema-management/common.d.ts +4 -4
- package/dist/schema-management/common.d.ts.map +1 -1
- package/dist/schema-management/migrations.d.ts.map +1 -1
- package/dist/schema-management/migrations.js +6 -6
- package/dist/schema-management/migrations.js.map +1 -1
- package/dist/schema-management/validate-mutation-defs.d.ts +3 -3
- package/dist/schema-management/validate-mutation-defs.d.ts.map +1 -1
- package/dist/schema-management/validate-mutation-defs.js +17 -17
- package/dist/schema-management/validate-mutation-defs.js.map +1 -1
- package/dist/sync/ClientSessionSyncProcessor.d.ts +7 -7
- package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -1
- package/dist/sync/ClientSessionSyncProcessor.js +31 -30
- package/dist/sync/ClientSessionSyncProcessor.js.map +1 -1
- package/dist/sync/next/facts.d.ts +19 -19
- package/dist/sync/next/facts.d.ts.map +1 -1
- package/dist/sync/next/facts.js +2 -2
- package/dist/sync/next/facts.js.map +1 -1
- package/dist/sync/next/history-dag-common.d.ts +3 -3
- package/dist/sync/next/history-dag-common.d.ts.map +1 -1
- package/dist/sync/next/history-dag-common.js +1 -1
- package/dist/sync/next/history-dag-common.js.map +1 -1
- package/dist/sync/next/history-dag.js +1 -1
- package/dist/sync/next/history-dag.js.map +1 -1
- package/dist/sync/next/rebase-events.d.ts +7 -7
- package/dist/sync/next/rebase-events.d.ts.map +1 -1
- package/dist/sync/next/rebase-events.js +5 -5
- package/dist/sync/next/rebase-events.js.map +1 -1
- package/dist/sync/next/test/compact-events.calculator.test.js +38 -33
- package/dist/sync/next/test/compact-events.calculator.test.js.map +1 -1
- package/dist/sync/next/test/compact-events.test.js +71 -71
- package/dist/sync/next/test/compact-events.test.js.map +1 -1
- package/dist/sync/next/test/{mutation-fixtures.d.ts → event-fixtures.d.ts} +29 -29
- package/dist/sync/next/test/event-fixtures.d.ts.map +1 -0
- package/dist/sync/next/test/{mutation-fixtures.js → event-fixtures.js} +60 -25
- package/dist/sync/next/test/event-fixtures.js.map +1 -0
- package/dist/sync/next/test/mod.d.ts +1 -1
- package/dist/sync/next/test/mod.d.ts.map +1 -1
- package/dist/sync/next/test/mod.js +1 -1
- package/dist/sync/next/test/mod.js.map +1 -1
- package/dist/sync/sync.d.ts +3 -3
- package/dist/sync/sync.d.ts.map +1 -1
- package/dist/sync/syncstate.d.ts +32 -32
- package/dist/sync/syncstate.d.ts.map +1 -1
- package/dist/sync/syncstate.js +31 -25
- package/dist/sync/syncstate.js.map +1 -1
- package/dist/sync/syncstate.test.js +165 -175
- package/dist/sync/syncstate.test.js.map +1 -1
- package/dist/sync/validate-push-payload.d.ts +2 -2
- package/dist/sync/validate-push-payload.d.ts.map +1 -1
- package/dist/sync/validate-push-payload.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
- package/src/__tests__/fixture.ts +36 -15
- package/src/adapter-types.ts +23 -16
- package/src/debug-info.ts +1 -0
- package/src/devtools/devtools-messages-leader.ts +13 -13
- package/src/index.ts +2 -5
- package/src/leader-thread/LeaderSyncProcessor.ts +183 -122
- package/src/leader-thread/{apply-mutation.ts → apply-event.ts} +50 -74
- package/src/leader-thread/eventlog.ts +199 -0
- package/src/leader-thread/leader-worker-devtools.ts +18 -18
- package/src/leader-thread/make-leader-thread-layer.ts +51 -29
- package/src/leader-thread/mod.ts +1 -1
- package/src/leader-thread/recreate-db.ts +6 -9
- package/src/leader-thread/types.ts +12 -12
- package/src/materializer-helper.ts +110 -0
- package/src/query-builder/api.ts +79 -105
- package/src/query-builder/astToSql.ts +68 -39
- package/src/query-builder/impl.test.ts +264 -42
- package/src/query-builder/impl.ts +72 -56
- package/src/{rehydrate-from-mutationlog.ts → rehydrate-from-eventlog.ts} +33 -40
- package/src/schema/EventDef.ts +216 -0
- package/src/schema/EventId.ts +11 -3
- package/src/schema/{MutationEvent.ts → LiveStoreEvent.ts} +68 -69
- package/src/schema/client-document-def.test.ts +239 -0
- package/src/schema/client-document-def.ts +444 -0
- package/src/schema/db-schema/dsl/mod.ts +0 -1
- package/src/schema/events.ts +1 -0
- package/src/schema/mod.ts +4 -3
- package/src/schema/schema.ts +79 -69
- package/src/schema/sqlite-state.ts +62 -0
- package/src/schema/system-tables.ts +42 -53
- package/src/schema/table-def.ts +53 -209
- package/src/schema/view.ts +2 -0
- package/src/schema-management/common.ts +4 -4
- package/src/schema-management/migrations.ts +8 -9
- package/src/schema-management/validate-mutation-defs.ts +22 -24
- package/src/sync/ClientSessionSyncProcessor.ts +37 -36
- package/src/sync/next/facts.ts +31 -32
- package/src/sync/next/history-dag-common.ts +4 -4
- package/src/sync/next/history-dag.ts +1 -1
- package/src/sync/next/rebase-events.ts +13 -13
- package/src/sync/next/test/compact-events.calculator.test.ts +45 -45
- package/src/sync/next/test/compact-events.test.ts +73 -73
- package/src/sync/next/test/event-fixtures.ts +219 -0
- package/src/sync/next/test/mod.ts +1 -1
- package/src/sync/sync.ts +3 -3
- package/src/sync/syncstate.test.ts +168 -179
- package/src/sync/syncstate.ts +48 -38
- package/src/sync/validate-push-payload.ts +2 -2
- package/src/version.ts +1 -1
- package/tmp/pack.tgz +0 -0
- package/tsconfig.json +1 -0
- package/dist/derived-mutations.d.ts +0 -109
- package/dist/derived-mutations.d.ts.map +0 -1
- package/dist/derived-mutations.js +0 -54
- package/dist/derived-mutations.js.map +0 -1
- package/dist/derived-mutations.test.d.ts +0 -2
- package/dist/derived-mutations.test.d.ts.map +0 -1
- package/dist/derived-mutations.test.js +0 -93
- package/dist/derived-mutations.test.js.map +0 -1
- package/dist/init-singleton-tables.d.ts +0 -4
- package/dist/init-singleton-tables.d.ts.map +0 -1
- package/dist/init-singleton-tables.js +0 -16
- package/dist/init-singleton-tables.js.map +0 -1
- package/dist/leader-thread/apply-mutation.d.ts.map +0 -1
- package/dist/leader-thread/apply-mutation.js +0 -122
- package/dist/leader-thread/apply-mutation.js.map +0 -1
- package/dist/leader-thread/mutationlog.d.ts +0 -27
- package/dist/leader-thread/mutationlog.d.ts.map +0 -1
- package/dist/leader-thread/mutationlog.js +0 -124
- package/dist/leader-thread/mutationlog.js.map +0 -1
- package/dist/leader-thread/pull-queue-set.d.ts +0 -7
- package/dist/leader-thread/pull-queue-set.d.ts.map +0 -1
- package/dist/leader-thread/pull-queue-set.js +0 -38
- package/dist/leader-thread/pull-queue-set.js.map +0 -1
- package/dist/mutation.d.ts +0 -20
- package/dist/mutation.d.ts.map +0 -1
- package/dist/mutation.js +0 -68
- package/dist/mutation.js.map +0 -1
- package/dist/query-info.d.ts +0 -41
- package/dist/query-info.d.ts.map +0 -1
- package/dist/query-info.js +0 -7
- package/dist/query-info.js.map +0 -1
- package/dist/rehydrate-from-mutationlog.d.ts +0 -15
- package/dist/rehydrate-from-mutationlog.d.ts.map +0 -1
- package/dist/rehydrate-from-mutationlog.js.map +0 -1
- package/dist/schema/MutationEvent.d.ts.map +0 -1
- package/dist/schema/MutationEvent.js.map +0 -1
- package/dist/schema/mutations.d.ts +0 -115
- package/dist/schema/mutations.d.ts.map +0 -1
- package/dist/schema/mutations.js +0 -42
- package/dist/schema/mutations.js.map +0 -1
- package/dist/sync/next/test/mutation-fixtures.d.ts.map +0 -1
- package/dist/sync/next/test/mutation-fixtures.js.map +0 -1
- package/src/derived-mutations.test.ts +0 -101
- package/src/derived-mutations.ts +0 -170
- package/src/init-singleton-tables.ts +0 -24
- package/src/leader-thread/mutationlog.ts +0 -202
- package/src/mutation.ts +0 -108
- package/src/query-info.ts +0 -83
- package/src/schema/mutations.ts +0 -193
- package/src/sync/next/test/mutation-fixtures.ts +0 -228
@@ -1,68 +1,66 @@
|
|
1
|
-
import { LS_DEV,
|
1
|
+
import { LS_DEV, shouldNeverHappen } from '@livestore/utils'
|
2
2
|
import { Effect, ReadonlyArray, Schema } from '@livestore/utils/effect'
|
3
3
|
|
4
4
|
import type { SqliteDb } from '../adapter-types.js'
|
5
|
-
import {
|
6
|
-
import type { LiveStoreSchema,
|
5
|
+
import { getExecArgsFromEvent } from '../materializer-helper.js'
|
6
|
+
import type { LiveStoreSchema, SessionChangesetMetaRow } from '../schema/mod.js'
|
7
7
|
import {
|
8
8
|
EventId,
|
9
|
-
|
10
|
-
|
9
|
+
EVENTLOG_META_TABLE,
|
10
|
+
getEventDef,
|
11
11
|
SESSION_CHANGESET_META_TABLE,
|
12
12
|
sessionChangesetMetaTable,
|
13
13
|
} from '../schema/mod.js'
|
14
14
|
import { insertRow } from '../sql-queries/index.js'
|
15
15
|
import { sql } from '../util.js'
|
16
16
|
import { execSql, execSqlPrepared } from './connection.js'
|
17
|
-
import * as
|
18
|
-
import type {
|
17
|
+
import * as Eventlog from './eventlog.js'
|
18
|
+
import type { ApplyEvent } from './types.js'
|
19
19
|
|
20
|
-
export const
|
20
|
+
export const makeApplyEvent = ({
|
21
21
|
schema,
|
22
22
|
dbReadModel: db,
|
23
|
-
|
23
|
+
dbEventlog,
|
24
24
|
}: {
|
25
25
|
schema: LiveStoreSchema
|
26
26
|
dbReadModel: SqliteDb
|
27
|
-
|
28
|
-
}): Effect.Effect<
|
27
|
+
dbEventlog: SqliteDb
|
28
|
+
}): Effect.Effect<ApplyEvent, never> =>
|
29
29
|
Effect.gen(function* () {
|
30
|
-
const
|
31
|
-
|
32
|
-
const mutationDefSchemaHashMap = new Map(
|
30
|
+
const eventDefSchemaHashMap = new Map(
|
33
31
|
// TODO Running `Schema.hash` can be a bottleneck for larger schemas. There is an opportunity to run this
|
34
32
|
// at build time and lookup the pre-computed hash at runtime.
|
35
33
|
// Also see https://github.com/Effect-TS/effect/issues/2719
|
36
|
-
[...schema.
|
34
|
+
[...schema.eventsDefsMap.entries()].map(([k, v]) => [k, Schema.hash(v.schema)] as const),
|
37
35
|
)
|
38
36
|
|
39
|
-
return (
|
37
|
+
return (eventEncoded, options) =>
|
40
38
|
Effect.gen(function* () {
|
41
|
-
const
|
39
|
+
const skipEventlog = options?.skipEventlog ?? false
|
42
40
|
|
43
|
-
const
|
44
|
-
const
|
41
|
+
const eventName = eventEncoded.name
|
42
|
+
const eventDef = getEventDef(schema, eventName)
|
45
43
|
|
46
|
-
const execArgsArr =
|
47
|
-
|
48
|
-
|
44
|
+
const execArgsArr = getExecArgsFromEvent({
|
45
|
+
eventDef,
|
46
|
+
event: { decoded: undefined, encoded: eventEncoded },
|
49
47
|
})
|
50
48
|
|
51
|
-
// NOTE we might want to bring this back if we want to debug no-op
|
49
|
+
// NOTE we might want to bring this back if we want to debug no-op events
|
52
50
|
// const makeExecuteOptions = (statementSql: string, bindValues: any) => ({
|
53
51
|
// onRowsChanged: (rowsChanged: number) => {
|
54
52
|
// if (rowsChanged === 0) {
|
55
|
-
// console.warn(`
|
53
|
+
// console.warn(`Event "${eventDef.name}" did not affect any rows:`, statementSql, bindValues)
|
56
54
|
// }
|
57
55
|
// },
|
58
56
|
// })
|
59
57
|
|
60
|
-
// console.group('[@livestore/common:leader-thread:
|
58
|
+
// console.group('[@livestore/common:leader-thread:applyEvent]', { eventName })
|
61
59
|
|
62
60
|
const session = db.session()
|
63
61
|
|
64
62
|
for (const { statementSql, bindValues } of execArgsArr) {
|
65
|
-
// console.debug(
|
63
|
+
// console.debug(eventName, statementSql, bindValues)
|
66
64
|
// TODO use cached prepared statements instead of exec
|
67
65
|
yield* execSqlPrepared(db, statementSql, bindValues)
|
68
66
|
}
|
@@ -77,9 +75,9 @@ export const makeApplyMutation = ({
|
|
77
75
|
tableName: SESSION_CHANGESET_META_TABLE,
|
78
76
|
columns: sessionChangesetMetaTable.sqliteDef.columns,
|
79
77
|
values: {
|
80
|
-
idGlobal:
|
81
|
-
idClient:
|
82
|
-
// NOTE the changeset will be empty (i.e. null) for no-op
|
78
|
+
idGlobal: eventEncoded.id.global,
|
79
|
+
idClient: eventEncoded.id.client,
|
80
|
+
// NOTE the changeset will be empty (i.e. null) for no-op events
|
83
81
|
changeset: changeset ?? null,
|
84
82
|
debug: LS_DEV ? execArgsArr : null,
|
85
83
|
},
|
@@ -88,22 +86,21 @@ export const makeApplyMutation = ({
|
|
88
86
|
|
89
87
|
// console.groupEnd()
|
90
88
|
|
91
|
-
// write to
|
92
|
-
|
93
|
-
|
94
|
-
const
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
mutationEventEncoded.sessionId,
|
89
|
+
// write to eventlog
|
90
|
+
if (skipEventlog === false) {
|
91
|
+
const eventName = eventEncoded.name
|
92
|
+
const eventDefSchemaHash =
|
93
|
+
eventDefSchemaHashMap.get(eventName) ?? shouldNeverHappen(`Unknown event definition: ${eventName}`)
|
94
|
+
|
95
|
+
yield* Eventlog.insertIntoEventlog(
|
96
|
+
eventEncoded,
|
97
|
+
dbEventlog,
|
98
|
+
eventDefSchemaHash,
|
99
|
+
eventEncoded.clientId,
|
100
|
+
eventEncoded.sessionId,
|
104
101
|
)
|
105
102
|
} else {
|
106
|
-
// console.debug('[@livestore/common:leader-thread] skipping
|
103
|
+
// console.debug('[@livestore/common:leader-thread] skipping eventlog write', mutation, statementSql, bindValues)
|
107
104
|
}
|
108
105
|
|
109
106
|
return {
|
@@ -116,24 +113,24 @@ export const makeApplyMutation = ({
|
|
116
113
|
: { _tag: 'no-op' as const },
|
117
114
|
}
|
118
115
|
}).pipe(
|
119
|
-
Effect.withSpan(`@livestore/common:leader-thread:
|
116
|
+
Effect.withSpan(`@livestore/common:leader-thread:applyEvent`, {
|
120
117
|
attributes: {
|
121
|
-
|
122
|
-
mutationId:
|
123
|
-
'span.label': `${EventId.toString(
|
118
|
+
eventName: eventEncoded.name,
|
119
|
+
mutationId: eventEncoded.id,
|
120
|
+
'span.label': `${EventId.toString(eventEncoded.id)} ${eventEncoded.name}`,
|
124
121
|
},
|
125
122
|
}),
|
126
|
-
// Effect.logDuration('@livestore/common:leader-thread:
|
123
|
+
// Effect.logDuration('@livestore/common:leader-thread:applyEvent'),
|
127
124
|
)
|
128
125
|
})
|
129
126
|
|
130
127
|
export const rollback = ({
|
131
128
|
db,
|
132
|
-
|
129
|
+
dbEventlog,
|
133
130
|
eventIdsToRollback,
|
134
131
|
}: {
|
135
132
|
db: SqliteDb
|
136
|
-
|
133
|
+
dbEventlog: SqliteDb
|
137
134
|
eventIdsToRollback: EventId.EventId[]
|
138
135
|
}) =>
|
139
136
|
Effect.gen(function* () {
|
@@ -163,10 +160,10 @@ export const rollback = ({
|
|
163
160
|
)
|
164
161
|
}
|
165
162
|
|
166
|
-
// Delete the
|
163
|
+
// Delete the eventlog rows
|
167
164
|
for (const eventIdPairChunk of eventIdPairChunks) {
|
168
|
-
|
169
|
-
sql`DELETE FROM ${
|
165
|
+
dbEventlog.execute(
|
166
|
+
sql`DELETE FROM ${EVENTLOG_META_TABLE} WHERE (idGlobal, idClient) IN (${eventIdPairChunk.join(', ')})`,
|
170
167
|
)
|
171
168
|
}
|
172
169
|
}).pipe(
|
@@ -174,24 +171,3 @@ export const rollback = ({
|
|
174
171
|
attributes: { count: eventIdsToRollback.length },
|
175
172
|
}),
|
176
173
|
)
|
177
|
-
|
178
|
-
// TODO let's consider removing this "should exclude" mechanism in favour of log compaction etc
|
179
|
-
const makeShouldExcludeMutationFromLog = memoizeByRef((schema: LiveStoreSchema) => {
|
180
|
-
const migrationOptions = schema.migrationOptions
|
181
|
-
const mutationLogExclude =
|
182
|
-
migrationOptions.strategy === 'from-mutation-log'
|
183
|
-
? (migrationOptions.excludeMutations ?? new Set(['livestore.RawSql']))
|
184
|
-
: new Set(['livestore.RawSql'])
|
185
|
-
|
186
|
-
return (mutationName: string, mutationEventEncoded: MutationEvent.AnyEncoded): boolean => {
|
187
|
-
if (mutationLogExclude.has(mutationName)) return true
|
188
|
-
|
189
|
-
const mutationDef = getMutationDef(schema, mutationName)
|
190
|
-
const execArgsArr = getExecArgsFromMutation({
|
191
|
-
mutationDef,
|
192
|
-
mutationEvent: { decoded: undefined, encoded: mutationEventEncoded },
|
193
|
-
})
|
194
|
-
|
195
|
-
return execArgsArr.some((_) => _.statementSql.includes('__livestore'))
|
196
|
-
}
|
197
|
-
})
|
@@ -0,0 +1,199 @@
|
|
1
|
+
import { LS_DEV, shouldNeverHappen } from '@livestore/utils'
|
2
|
+
import { Effect, Option, Schema } from '@livestore/utils/effect'
|
3
|
+
|
4
|
+
import type { SqliteDb } from '../adapter-types.js'
|
5
|
+
import * as EventId from '../schema/EventId.js'
|
6
|
+
import * as LiveStoreEvent from '../schema/LiveStoreEvent.js'
|
7
|
+
import {
|
8
|
+
EVENTLOG_META_TABLE,
|
9
|
+
eventlogMetaTable,
|
10
|
+
sessionChangesetMetaTable,
|
11
|
+
SYNC_STATUS_TABLE,
|
12
|
+
syncStatusTable,
|
13
|
+
} from '../schema/system-tables.js'
|
14
|
+
import { migrateTable } from '../schema-management/migrations.js'
|
15
|
+
import { insertRow, updateRows } from '../sql-queries/sql-queries.js'
|
16
|
+
import type { PreparedBindValues } from '../util.js'
|
17
|
+
import { prepareBindValues, sql } from '../util.js'
|
18
|
+
import { execSql } from './connection.js'
|
19
|
+
import type { InitialSyncInfo } from './types.js'
|
20
|
+
import { LeaderThreadCtx } from './types.js'
|
21
|
+
|
22
|
+
export const initEventlogDb = (dbEventlog: SqliteDb) =>
|
23
|
+
Effect.gen(function* () {
|
24
|
+
yield* migrateTable({
|
25
|
+
db: dbEventlog,
|
26
|
+
behaviour: 'create-if-not-exists',
|
27
|
+
tableAst: eventlogMetaTable.sqliteDef.ast,
|
28
|
+
skipMetaTable: true,
|
29
|
+
})
|
30
|
+
|
31
|
+
yield* migrateTable({
|
32
|
+
db: dbEventlog,
|
33
|
+
behaviour: 'create-if-not-exists',
|
34
|
+
tableAst: syncStatusTable.sqliteDef.ast,
|
35
|
+
skipMetaTable: true,
|
36
|
+
})
|
37
|
+
|
38
|
+
// Create sync status row if it doesn't exist
|
39
|
+
yield* execSql(
|
40
|
+
dbEventlog,
|
41
|
+
sql`INSERT INTO ${SYNC_STATUS_TABLE} (head)
|
42
|
+
SELECT ${EventId.ROOT.global}
|
43
|
+
WHERE NOT EXISTS (SELECT 1 FROM ${SYNC_STATUS_TABLE})`,
|
44
|
+
{},
|
45
|
+
)
|
46
|
+
})
|
47
|
+
|
48
|
+
/** Exclusive of the "since event" */
|
49
|
+
export const getEventsSince = (
|
50
|
+
since: EventId.EventId,
|
51
|
+
): Effect.Effect<ReadonlyArray<LiveStoreEvent.EncodedWithMeta>, never, LeaderThreadCtx> =>
|
52
|
+
Effect.gen(function* () {
|
53
|
+
const { dbEventlog, dbReadModel } = yield* LeaderThreadCtx
|
54
|
+
|
55
|
+
const query = eventlogMetaTable.where('idGlobal', '>=', since.global).asSql()
|
56
|
+
const pendingEventsRaw = dbEventlog.select(query.query, prepareBindValues(query.bindValues, query.query))
|
57
|
+
const pendingEvents = Schema.decodeUnknownSync(eventlogMetaTable.rowSchema.pipe(Schema.Array))(pendingEventsRaw)
|
58
|
+
|
59
|
+
const sessionChangesetRows = sessionChangesetMetaTable.where('idGlobal', '>=', since.global).asSql()
|
60
|
+
const sessionChangesetRowsRaw = dbReadModel.select(
|
61
|
+
sessionChangesetRows.query,
|
62
|
+
prepareBindValues(sessionChangesetRows.bindValues, sessionChangesetRows.query),
|
63
|
+
)
|
64
|
+
const sessionChangesetRowsDecoded = Schema.decodeUnknownSync(
|
65
|
+
sessionChangesetMetaTable.rowSchema.pipe(Schema.Array),
|
66
|
+
)(sessionChangesetRowsRaw)
|
67
|
+
|
68
|
+
return pendingEvents
|
69
|
+
.map((eventlogEvent) => {
|
70
|
+
const sessionChangeset = sessionChangesetRowsDecoded.find(
|
71
|
+
(readModelEvent) =>
|
72
|
+
readModelEvent.idGlobal === eventlogEvent.idGlobal && readModelEvent.idClient === eventlogEvent.idClient,
|
73
|
+
)
|
74
|
+
return LiveStoreEvent.EncodedWithMeta.make({
|
75
|
+
name: eventlogEvent.name,
|
76
|
+
args: eventlogEvent.argsJson,
|
77
|
+
id: { global: eventlogEvent.idGlobal, client: eventlogEvent.idClient },
|
78
|
+
parentId: { global: eventlogEvent.parentIdGlobal, client: eventlogEvent.parentIdClient },
|
79
|
+
clientId: eventlogEvent.clientId,
|
80
|
+
sessionId: eventlogEvent.sessionId,
|
81
|
+
meta: {
|
82
|
+
sessionChangeset:
|
83
|
+
sessionChangeset && sessionChangeset.changeset !== null
|
84
|
+
? {
|
85
|
+
_tag: 'sessionChangeset' as const,
|
86
|
+
data: sessionChangeset.changeset,
|
87
|
+
debug: sessionChangeset.debug,
|
88
|
+
}
|
89
|
+
: { _tag: 'unset' as const },
|
90
|
+
syncMetadata: eventlogEvent.syncMetadataJson,
|
91
|
+
},
|
92
|
+
})
|
93
|
+
})
|
94
|
+
.filter((_) => EventId.compare(_.id, since) > 0)
|
95
|
+
.sort((a, b) => EventId.compare(a.id, b.id))
|
96
|
+
})
|
97
|
+
|
98
|
+
export const getClientHeadFromDb = (dbEventlog: SqliteDb): EventId.EventId => {
|
99
|
+
const res = dbEventlog.select<{ idGlobal: EventId.GlobalEventId; idClient: EventId.ClientEventId }>(
|
100
|
+
sql`select idGlobal, idClient from ${EVENTLOG_META_TABLE} order by idGlobal DESC, idClient DESC limit 1`,
|
101
|
+
)[0]
|
102
|
+
|
103
|
+
return res ? { global: res.idGlobal, client: res.idClient } : EventId.ROOT
|
104
|
+
}
|
105
|
+
|
106
|
+
export const getBackendHeadFromDb = (dbEventlog: SqliteDb): EventId.GlobalEventId =>
|
107
|
+
dbEventlog.select<{ head: EventId.GlobalEventId }>(sql`select head from ${SYNC_STATUS_TABLE}`)[0]?.head ??
|
108
|
+
EventId.ROOT.global
|
109
|
+
|
110
|
+
// TODO use prepared statements
|
111
|
+
export const updateBackendHead = (dbEventlog: SqliteDb, head: EventId.EventId) =>
|
112
|
+
dbEventlog.execute(sql`UPDATE ${SYNC_STATUS_TABLE} SET head = ${head.global}`)
|
113
|
+
|
114
|
+
export const insertIntoEventlog = (
|
115
|
+
eventEncoded: LiveStoreEvent.EncodedWithMeta,
|
116
|
+
dbEventlog: SqliteDb,
|
117
|
+
eventDefSchemaHash: number,
|
118
|
+
clientId: string,
|
119
|
+
sessionId: string,
|
120
|
+
) =>
|
121
|
+
Effect.gen(function* () {
|
122
|
+
// Check history consistency during LS_DEV
|
123
|
+
if (LS_DEV && eventEncoded.parentId.global !== EventId.ROOT.global) {
|
124
|
+
const parentEventExists =
|
125
|
+
dbEventlog.select<{ count: number }>(
|
126
|
+
`SELECT COUNT(*) as count FROM ${EVENTLOG_META_TABLE} WHERE idGlobal = ? AND idClient = ?`,
|
127
|
+
[eventEncoded.parentId.global, eventEncoded.parentId.client] as any as PreparedBindValues,
|
128
|
+
)[0]!.count === 1
|
129
|
+
|
130
|
+
if (parentEventExists === false) {
|
131
|
+
shouldNeverHappen(
|
132
|
+
`Parent mutation ${eventEncoded.parentId.global},${eventEncoded.parentId.client} does not exist`,
|
133
|
+
)
|
134
|
+
}
|
135
|
+
}
|
136
|
+
|
137
|
+
// TODO use prepared statements
|
138
|
+
yield* execSql(
|
139
|
+
dbEventlog,
|
140
|
+
...insertRow({
|
141
|
+
tableName: EVENTLOG_META_TABLE,
|
142
|
+
columns: eventlogMetaTable.sqliteDef.columns,
|
143
|
+
values: {
|
144
|
+
idGlobal: eventEncoded.id.global,
|
145
|
+
idClient: eventEncoded.id.client,
|
146
|
+
parentIdGlobal: eventEncoded.parentId.global,
|
147
|
+
parentIdClient: eventEncoded.parentId.client,
|
148
|
+
name: eventEncoded.name,
|
149
|
+
argsJson: eventEncoded.args ?? {},
|
150
|
+
clientId,
|
151
|
+
sessionId,
|
152
|
+
schemaHash: eventDefSchemaHash,
|
153
|
+
syncMetadataJson: eventEncoded.meta.syncMetadata,
|
154
|
+
},
|
155
|
+
}),
|
156
|
+
)
|
157
|
+
})
|
158
|
+
|
159
|
+
export const updateSyncMetadata = (items: ReadonlyArray<LiveStoreEvent.EncodedWithMeta>) =>
|
160
|
+
Effect.gen(function* () {
|
161
|
+
const { dbEventlog } = yield* LeaderThreadCtx
|
162
|
+
|
163
|
+
// TODO try to do this in a single query
|
164
|
+
for (let i = 0; i < items.length; i++) {
|
165
|
+
const event = items[i]!
|
166
|
+
|
167
|
+
yield* execSql(
|
168
|
+
dbEventlog,
|
169
|
+
...updateRows({
|
170
|
+
tableName: EVENTLOG_META_TABLE,
|
171
|
+
columns: eventlogMetaTable.sqliteDef.columns,
|
172
|
+
where: { idGlobal: event.id.global, idClient: event.id.client },
|
173
|
+
updateValues: { syncMetadataJson: event.meta.syncMetadata },
|
174
|
+
}),
|
175
|
+
)
|
176
|
+
}
|
177
|
+
})
|
178
|
+
|
179
|
+
export const getSyncBackendCursorInfo = (remoteHead: EventId.GlobalEventId) =>
|
180
|
+
Effect.gen(function* () {
|
181
|
+
const { dbEventlog } = yield* LeaderThreadCtx
|
182
|
+
|
183
|
+
if (remoteHead === EventId.ROOT.global) return Option.none()
|
184
|
+
|
185
|
+
const EventlogQuerySchema = Schema.Struct({
|
186
|
+
syncMetadataJson: Schema.parseJson(Schema.Option(Schema.JsonValue)),
|
187
|
+
}).pipe(Schema.pluck('syncMetadataJson'), Schema.Array, Schema.head)
|
188
|
+
|
189
|
+
const syncMetadataOption = yield* Effect.sync(() =>
|
190
|
+
dbEventlog.select<{ syncMetadataJson: string }>(
|
191
|
+
sql`SELECT syncMetadataJson FROM ${EVENTLOG_META_TABLE} WHERE idGlobal = ${remoteHead} ORDER BY idClient ASC LIMIT 1`,
|
192
|
+
),
|
193
|
+
).pipe(Effect.andThen(Schema.decode(EventlogQuerySchema)), Effect.map(Option.flatten), Effect.orDie)
|
194
|
+
|
195
|
+
return Option.some({
|
196
|
+
cursor: { global: remoteHead, client: EventId.clientDefault },
|
197
|
+
metadata: syncMetadataOption,
|
198
|
+
}) satisfies InitialSyncInfo
|
199
|
+
}).pipe(Effect.withSpan('@livestore/common:eventlog:getSyncBackendCursorInfo', { attributes: { remoteHead } }))
|
@@ -2,7 +2,7 @@ import { Effect, FiberMap, Option, Stream, SubscriptionRef } from '@livestore/ut
|
|
2
2
|
import { nanoid } from '@livestore/utils/nanoid'
|
3
3
|
|
4
4
|
import { Devtools, IntentionalShutdownCause, liveStoreVersion, UnexpectedError } from '../index.js'
|
5
|
-
import {
|
5
|
+
import { EVENTLOG_META_TABLE, SCHEMA_EVENT_DEFS_META_TABLE, SCHEMA_META_TABLE } from '../schema/mod.js'
|
6
6
|
import type { DevtoolsOptions, PersistenceInfoPair } from './types.js'
|
7
7
|
import { LeaderThreadCtx } from './types.js'
|
8
8
|
|
@@ -63,7 +63,7 @@ const listenToDevtools = ({
|
|
63
63
|
syncBackend,
|
64
64
|
makeSqliteDb,
|
65
65
|
dbReadModel,
|
66
|
-
|
66
|
+
dbEventlog,
|
67
67
|
shutdownStateSubRef,
|
68
68
|
shutdownChannel,
|
69
69
|
syncProcessor,
|
@@ -143,20 +143,20 @@ const listenToDevtools = ({
|
|
143
143
|
}
|
144
144
|
|
145
145
|
try {
|
146
|
-
if (tableNames.has(
|
147
|
-
// Is
|
146
|
+
if (tableNames.has(EVENTLOG_META_TABLE)) {
|
147
|
+
// Is eventlog
|
148
148
|
yield* SubscriptionRef.set(shutdownStateSubRef, 'shutting-down')
|
149
149
|
|
150
|
-
|
150
|
+
dbEventlog.import(data)
|
151
151
|
|
152
152
|
dbReadModel.destroy()
|
153
|
-
} else if (tableNames.has(SCHEMA_META_TABLE) && tableNames.has(
|
153
|
+
} else if (tableNames.has(SCHEMA_META_TABLE) && tableNames.has(SCHEMA_EVENT_DEFS_META_TABLE)) {
|
154
154
|
// Is read model
|
155
155
|
yield* SubscriptionRef.set(shutdownStateSubRef, 'shutting-down')
|
156
156
|
|
157
157
|
dbReadModel.import(data)
|
158
158
|
|
159
|
-
|
159
|
+
dbEventlog.destroy()
|
160
160
|
} else {
|
161
161
|
yield* sendMessage(
|
162
162
|
Devtools.Leader.LoadDatabaseFile.Error.make({
|
@@ -190,7 +190,7 @@ const listenToDevtools = ({
|
|
190
190
|
dbReadModel.destroy()
|
191
191
|
|
192
192
|
if (mode === 'all-data') {
|
193
|
-
|
193
|
+
dbEventlog.destroy()
|
194
194
|
}
|
195
195
|
|
196
196
|
yield* sendMessage(Devtools.Leader.ResetAllData.Success.make({ ...reqPayload }))
|
@@ -207,33 +207,33 @@ const listenToDevtools = ({
|
|
207
207
|
|
208
208
|
const dbSizeQuery = `SELECT page_count * page_size as size FROM pragma_page_count(), pragma_page_size();`
|
209
209
|
const dbFileSize = dbReadModel.select<{ size: number }>(dbSizeQuery, undefined)[0]!.size
|
210
|
-
const
|
210
|
+
const eventlogFileSize = dbEventlog.select<{ size: number }>(dbSizeQuery, undefined)[0]!.size
|
211
211
|
|
212
212
|
yield* sendMessage(
|
213
213
|
Devtools.Leader.DatabaseFileInfoRes.make({
|
214
214
|
readModel: { fileSize: dbFileSize, persistenceInfo: persistenceInfo.readModel },
|
215
|
-
|
215
|
+
eventlog: { fileSize: eventlogFileSize, persistenceInfo: persistenceInfo.eventlog },
|
216
216
|
...reqPayload,
|
217
217
|
}),
|
218
218
|
)
|
219
219
|
|
220
220
|
return
|
221
221
|
}
|
222
|
-
case 'LSD.Leader.
|
223
|
-
const
|
222
|
+
case 'LSD.Leader.EventlogReq': {
|
223
|
+
const eventlog = dbEventlog.export()
|
224
224
|
|
225
|
-
yield* sendMessage(Devtools.Leader.
|
225
|
+
yield* sendMessage(Devtools.Leader.EventlogRes.make({ eventlog, ...reqPayload }))
|
226
226
|
|
227
227
|
return
|
228
228
|
}
|
229
|
-
case 'LSD.Leader.
|
229
|
+
case 'LSD.Leader.CommitEventReq': {
|
230
230
|
yield* syncProcessor.pushPartial({
|
231
|
-
|
231
|
+
event: decodedEvent.eventEncoded,
|
232
232
|
clientId: `devtools-${clientId}`,
|
233
233
|
sessionId: `devtools-${clientId}`,
|
234
234
|
})
|
235
235
|
|
236
|
-
yield* sendMessage(Devtools.Leader.
|
236
|
+
yield* sendMessage(Devtools.Leader.CommitEventRes.make({ ...reqPayload }))
|
237
237
|
|
238
238
|
return
|
239
239
|
}
|
@@ -245,10 +245,10 @@ const listenToDevtools = ({
|
|
245
245
|
yield* syncBackend.pull(Option.none()).pipe(
|
246
246
|
Stream.map((_) => _.batch),
|
247
247
|
Stream.flattenIterables,
|
248
|
-
Stream.tap(({
|
248
|
+
Stream.tap(({ eventEncoded, metadata }) =>
|
249
249
|
sendMessage(
|
250
250
|
Devtools.Leader.SyncHistoryRes.make({
|
251
|
-
|
251
|
+
eventEncoded,
|
252
252
|
metadata,
|
253
253
|
subscriptionId,
|
254
254
|
...reqPayload,
|