@livestore/common 0.0.0-snapshot-068f5fa8d02cd26bb64eb4e052900feac9ae59ff → 0.0.0-snapshot-6c61bcbb96b4666b347d3a9845d9f909cf96ae6d
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/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/leader-thread/LeaderSyncProcessor.d.ts +4 -4
- package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -1
- package/dist/leader-thread/LeaderSyncProcessor.js +21 -17
- package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -1
- package/dist/leader-thread/eventlog.js +2 -2
- package/dist/leader-thread/eventlog.js.map +1 -1
- package/dist/leader-thread/leader-worker-devtools.js +12 -11
- package/dist/leader-thread/leader-worker-devtools.js.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.d.ts +2 -2
- package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.js +11 -11
- package/dist/leader-thread/make-leader-thread-layer.js.map +1 -1
- package/dist/leader-thread/{apply-event.d.ts → materialize-event.d.ts} +7 -7
- package/dist/leader-thread/materialize-event.d.ts.map +1 -0
- package/dist/leader-thread/{apply-event.js → materialize-event.js} +14 -14
- package/dist/leader-thread/materialize-event.js.map +1 -0
- package/dist/leader-thread/recreate-db.d.ts.map +1 -1
- package/dist/leader-thread/recreate-db.js +7 -7
- package/dist/leader-thread/recreate-db.js.map +1 -1
- package/dist/leader-thread/types.d.ts +4 -4
- package/dist/leader-thread/types.d.ts.map +1 -1
- package/dist/{rehydrate-from-eventlog.d.ts → rematerialize-from-eventlog.d.ts} +4 -4
- package/dist/rematerialize-from-eventlog.d.ts.map +1 -0
- package/dist/{rehydrate-from-eventlog.js → rematerialize-from-eventlog.js} +12 -13
- package/dist/rematerialize-from-eventlog.js.map +1 -0
- package/dist/schema/mod.d.ts +1 -1
- package/dist/schema/mod.d.ts.map +1 -1
- package/dist/schema/mod.js +1 -1
- package/dist/schema/mod.js.map +1 -1
- package/dist/sync/ClientSessionSyncProcessor.d.ts +3 -3
- package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -1
- package/dist/sync/ClientSessionSyncProcessor.js +6 -6
- package/dist/sync/ClientSessionSyncProcessor.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +1 -1
- package/src/leader-thread/LeaderSyncProcessor.ts +28 -30
- package/src/leader-thread/eventlog.ts +2 -2
- package/src/leader-thread/leader-worker-devtools.ts +14 -11
- package/src/leader-thread/make-leader-thread-layer.ts +14 -14
- package/src/leader-thread/{apply-event.ts → materialize-event.ts} +22 -28
- package/src/leader-thread/recreate-db.ts +7 -7
- package/src/leader-thread/types.ts +4 -4
- package/src/{rehydrate-from-eventlog.ts → rematerialize-from-eventlog.ts} +41 -38
- package/src/schema/mod.ts +1 -1
- package/src/sync/ClientSessionSyncProcessor.ts +7 -7
- package/dist/leader-thread/apply-event.d.ts.map +0 -1
- package/dist/leader-thread/apply-event.js.map +0 -1
- package/dist/rehydrate-from-eventlog.d.ts.map +0 -1
- package/dist/rehydrate-from-eventlog.js.map +0 -1
|
@@ -18,18 +18,12 @@ import type * as otel from '@opentelemetry/api'
|
|
|
18
18
|
import type { SqliteDb } from '../adapter-types.js'
|
|
19
19
|
import { UnexpectedError } from '../adapter-types.js'
|
|
20
20
|
import type { LiveStoreSchema } from '../schema/mod.js'
|
|
21
|
-
import {
|
|
22
|
-
EventId,
|
|
23
|
-
getEventDef,
|
|
24
|
-
LEADER_MERGE_COUNTER_TABLE,
|
|
25
|
-
LiveStoreEvent,
|
|
26
|
-
SESSION_CHANGESET_META_TABLE,
|
|
27
|
-
} from '../schema/mod.js'
|
|
21
|
+
import { EventId, getEventDef, LiveStoreEvent, SystemTables } from '../schema/mod.js'
|
|
28
22
|
import { LeaderAheadError } from '../sync/sync.js'
|
|
29
23
|
import * as SyncState from '../sync/syncstate.js'
|
|
30
24
|
import { sql } from '../util.js'
|
|
31
|
-
import { rollback } from './apply-event.js'
|
|
32
25
|
import * as Eventlog from './eventlog.js'
|
|
26
|
+
import { rollback } from './materialize-event.js'
|
|
33
27
|
import type { InitialBlockingSyncContext, LeaderSyncProcessor } from './types.js'
|
|
34
28
|
import { LeaderThreadCtx } from './types.js'
|
|
35
29
|
|
|
@@ -73,8 +67,8 @@ export const makeLeaderSyncProcessor = ({
|
|
|
73
67
|
schema,
|
|
74
68
|
dbEventlogMissing,
|
|
75
69
|
dbEventlog,
|
|
76
|
-
|
|
77
|
-
|
|
70
|
+
dbState,
|
|
71
|
+
dbStateMissing,
|
|
78
72
|
initialBlockingSyncContext,
|
|
79
73
|
onError,
|
|
80
74
|
params,
|
|
@@ -84,9 +78,9 @@ export const makeLeaderSyncProcessor = ({
|
|
|
84
78
|
/** Only used to know whether we can safely query dbEventlog during setup execution */
|
|
85
79
|
dbEventlogMissing: boolean
|
|
86
80
|
dbEventlog: SqliteDb
|
|
87
|
-
|
|
88
|
-
/** Only used to know whether we can safely query
|
|
89
|
-
|
|
81
|
+
dbState: SqliteDb
|
|
82
|
+
/** Only used to know whether we can safely query dbState during setup execution */
|
|
83
|
+
dbStateMissing: boolean
|
|
90
84
|
initialBlockingSyncContext: InitialBlockingSyncContext
|
|
91
85
|
onError: 'shutdown' | 'ignore'
|
|
92
86
|
params: {
|
|
@@ -128,7 +122,7 @@ export const makeLeaderSyncProcessor = ({
|
|
|
128
122
|
const currentLocalPushGenerationRef = { current: 0 }
|
|
129
123
|
|
|
130
124
|
type MergeCounter = number
|
|
131
|
-
const mergeCounterRef = { current:
|
|
125
|
+
const mergeCounterRef = { current: dbStateMissing ? 0 : yield* getMergeCounterFromDb(dbState) }
|
|
132
126
|
const mergePayloads = new Map<MergeCounter, typeof SyncState.PayloadUpstream.Type>()
|
|
133
127
|
|
|
134
128
|
// This context depends on data from `boot`, we should find a better implementation to avoid this ref indirection.
|
|
@@ -552,14 +546,14 @@ const backgroundApplyLocalPushes = ({
|
|
|
552
546
|
|
|
553
547
|
yield* BucketQueue.offerAll(syncBackendPushQueue, filteredBatch)
|
|
554
548
|
|
|
555
|
-
yield*
|
|
549
|
+
yield* materializeEventsBatch({ batchItems: mergeResult.newEvents, deferreds })
|
|
556
550
|
|
|
557
551
|
// Allow the backend pulling to start
|
|
558
552
|
yield* pullLatch.open
|
|
559
553
|
}
|
|
560
554
|
})
|
|
561
555
|
|
|
562
|
-
type
|
|
556
|
+
type MaterializeEventsBatch = (_: {
|
|
563
557
|
batchItems: ReadonlyArray<LiveStoreEvent.EncodedWithMeta>
|
|
564
558
|
/**
|
|
565
559
|
* The deferreds are used by the caller to know when the mutation has been processed.
|
|
@@ -569,9 +563,9 @@ type ApplyEventsBatch = (_: {
|
|
|
569
563
|
}) => Effect.Effect<void, UnexpectedError, LeaderThreadCtx>
|
|
570
564
|
|
|
571
565
|
// TODO how to handle errors gracefully
|
|
572
|
-
const
|
|
566
|
+
const materializeEventsBatch: MaterializeEventsBatch = ({ batchItems, deferreds }) =>
|
|
573
567
|
Effect.gen(function* () {
|
|
574
|
-
const {
|
|
568
|
+
const { dbState: db, dbEventlog, materializeEvent } = yield* LeaderThreadCtx
|
|
575
569
|
|
|
576
570
|
// NOTE We always start a transaction to ensure consistency between db and eventlog (even for single-item batches)
|
|
577
571
|
db.execute('BEGIN TRANSACTION', undefined) // Start the transaction
|
|
@@ -588,7 +582,7 @@ const applyEventsBatch: ApplyEventsBatch = ({ batchItems, deferreds }) =>
|
|
|
588
582
|
)
|
|
589
583
|
|
|
590
584
|
for (let i = 0; i < batchItems.length; i++) {
|
|
591
|
-
const { sessionChangeset } = yield*
|
|
585
|
+
const { sessionChangeset } = yield* materializeEvent(batchItems[i]!)
|
|
592
586
|
batchItems[i]!.meta.sessionChangeset = sessionChangeset
|
|
593
587
|
|
|
594
588
|
if (deferreds?.[i] !== undefined) {
|
|
@@ -601,7 +595,7 @@ const applyEventsBatch: ApplyEventsBatch = ({ batchItems, deferreds }) =>
|
|
|
601
595
|
}).pipe(
|
|
602
596
|
Effect.uninterruptible,
|
|
603
597
|
Effect.scoped,
|
|
604
|
-
Effect.withSpan('@livestore/common:LeaderSyncProcessor:
|
|
598
|
+
Effect.withSpan('@livestore/common:LeaderSyncProcessor:materializeEventItems', {
|
|
605
599
|
attributes: { batchSize: batchItems.length },
|
|
606
600
|
}),
|
|
607
601
|
Effect.tapCauseLogPretty,
|
|
@@ -640,7 +634,7 @@ const backgroundBackendPulling = ({
|
|
|
640
634
|
advancePushHead: (eventId: EventId.EventId) => void
|
|
641
635
|
}) =>
|
|
642
636
|
Effect.gen(function* () {
|
|
643
|
-
const { syncBackend,
|
|
637
|
+
const { syncBackend, dbState: db, dbEventlog, schema } = yield* LeaderThreadCtx
|
|
644
638
|
|
|
645
639
|
if (syncBackend === undefined) return
|
|
646
640
|
|
|
@@ -700,7 +694,11 @@ const backgroundBackendPulling = ({
|
|
|
700
694
|
yield* restartBackendPushing(globalRebasedPendingEvents)
|
|
701
695
|
|
|
702
696
|
if (mergeResult.rollbackEvents.length > 0) {
|
|
703
|
-
yield* rollback({
|
|
697
|
+
yield* rollback({
|
|
698
|
+
dbState: db,
|
|
699
|
+
dbEventlog,
|
|
700
|
+
eventIdsToRollback: mergeResult.rollbackEvents.map((_) => _.id),
|
|
701
|
+
})
|
|
704
702
|
}
|
|
705
703
|
|
|
706
704
|
yield* connectedClientSessionPullQueues.offer({
|
|
@@ -744,7 +742,7 @@ const backgroundBackendPulling = ({
|
|
|
744
742
|
|
|
745
743
|
advancePushHead(mergeResult.newSyncState.localHead)
|
|
746
744
|
|
|
747
|
-
yield*
|
|
745
|
+
yield* materializeEventsBatch({ batchItems: mergeResult.newEvents, deferreds: undefined })
|
|
748
746
|
|
|
749
747
|
yield* SubscriptionRef.set(syncStateSref, mergeResult.newSyncState)
|
|
750
748
|
|
|
@@ -833,7 +831,7 @@ const backgroundBackendPushing = ({
|
|
|
833
831
|
const trimChangesetRows = (db: SqliteDb, newHead: EventId.EventId) => {
|
|
834
832
|
// Since we're using the session changeset rows to query for the current head,
|
|
835
833
|
// we're keeping at least one row for the current head, and thus are using `<` instead of `<=`
|
|
836
|
-
db.execute(sql`DELETE FROM ${SESSION_CHANGESET_META_TABLE} WHERE idGlobal < ${newHead.global}`)
|
|
834
|
+
db.execute(sql`DELETE FROM ${SystemTables.SESSION_CHANGESET_META_TABLE} WHERE idGlobal < ${newHead.global}`)
|
|
837
835
|
}
|
|
838
836
|
|
|
839
837
|
interface PullQueueSet {
|
|
@@ -894,18 +892,18 @@ const makePullQueueSet = Effect.gen(function* () {
|
|
|
894
892
|
|
|
895
893
|
const incrementMergeCounter = (mergeCounterRef: { current: number }) =>
|
|
896
894
|
Effect.gen(function* () {
|
|
897
|
-
const {
|
|
895
|
+
const { dbState } = yield* LeaderThreadCtx
|
|
898
896
|
mergeCounterRef.current++
|
|
899
|
-
|
|
900
|
-
sql`INSERT OR REPLACE INTO ${LEADER_MERGE_COUNTER_TABLE} (id, mergeCounter) VALUES (0, ${mergeCounterRef.current})`,
|
|
897
|
+
dbState.execute(
|
|
898
|
+
sql`INSERT OR REPLACE INTO ${SystemTables.LEADER_MERGE_COUNTER_TABLE} (id, mergeCounter) VALUES (0, ${mergeCounterRef.current})`,
|
|
901
899
|
)
|
|
902
900
|
return mergeCounterRef.current
|
|
903
901
|
})
|
|
904
902
|
|
|
905
|
-
const getMergeCounterFromDb = (
|
|
903
|
+
const getMergeCounterFromDb = (dbState: SqliteDb) =>
|
|
906
904
|
Effect.gen(function* () {
|
|
907
|
-
const result =
|
|
908
|
-
sql`SELECT mergeCounter FROM ${LEADER_MERGE_COUNTER_TABLE} WHERE id = 0`,
|
|
905
|
+
const result = dbState.select<{ mergeCounter: number }>(
|
|
906
|
+
sql`SELECT mergeCounter FROM ${SystemTables.LEADER_MERGE_COUNTER_TABLE} WHERE id = 0`,
|
|
909
907
|
)
|
|
910
908
|
return result[0]?.mergeCounter ?? 0
|
|
911
909
|
})
|
|
@@ -50,14 +50,14 @@ export const getEventsSince = (
|
|
|
50
50
|
since: EventId.EventId,
|
|
51
51
|
): Effect.Effect<ReadonlyArray<LiveStoreEvent.EncodedWithMeta>, never, LeaderThreadCtx> =>
|
|
52
52
|
Effect.gen(function* () {
|
|
53
|
-
const { dbEventlog,
|
|
53
|
+
const { dbEventlog, dbState } = yield* LeaderThreadCtx
|
|
54
54
|
|
|
55
55
|
const query = eventlogMetaTable.where('idGlobal', '>=', since.global).asSql()
|
|
56
56
|
const pendingEventsRaw = dbEventlog.select(query.query, prepareBindValues(query.bindValues, query.query))
|
|
57
57
|
const pendingEvents = Schema.decodeUnknownSync(eventlogMetaTable.rowSchema.pipe(Schema.Array))(pendingEventsRaw)
|
|
58
58
|
|
|
59
59
|
const sessionChangesetRows = sessionChangesetMetaTable.where('idGlobal', '>=', since.global).asSql()
|
|
60
|
-
const sessionChangesetRowsRaw =
|
|
60
|
+
const sessionChangesetRowsRaw = dbState.select(
|
|
61
61
|
sessionChangesetRows.query,
|
|
62
62
|
prepareBindValues(sessionChangesetRows.bindValues, sessionChangesetRows.query),
|
|
63
63
|
)
|
|
@@ -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 { SystemTables } from '../schema/mod.js'
|
|
6
6
|
import type { DevtoolsOptions, PersistenceInfoPair } from './types.js'
|
|
7
7
|
import { LeaderThreadCtx } from './types.js'
|
|
8
8
|
|
|
@@ -62,7 +62,7 @@ const listenToDevtools = ({
|
|
|
62
62
|
const {
|
|
63
63
|
syncBackend,
|
|
64
64
|
makeSqliteDb,
|
|
65
|
-
|
|
65
|
+
dbState,
|
|
66
66
|
dbEventlog,
|
|
67
67
|
shutdownStateSubRef,
|
|
68
68
|
shutdownChannel,
|
|
@@ -109,7 +109,7 @@ const listenToDevtools = ({
|
|
|
109
109
|
return
|
|
110
110
|
}
|
|
111
111
|
case 'LSD.Leader.SnapshotReq': {
|
|
112
|
-
const snapshot =
|
|
112
|
+
const snapshot = dbState.export()
|
|
113
113
|
|
|
114
114
|
yield* sendMessage(Devtools.Leader.SnapshotRes.make({ snapshot, ...reqPayload }))
|
|
115
115
|
|
|
@@ -143,18 +143,21 @@ const listenToDevtools = ({
|
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
try {
|
|
146
|
-
if (tableNames.has(EVENTLOG_META_TABLE)) {
|
|
147
|
-
// Is eventlog
|
|
146
|
+
if (tableNames.has(SystemTables.EVENTLOG_META_TABLE)) {
|
|
147
|
+
// Is eventlog db
|
|
148
148
|
yield* SubscriptionRef.set(shutdownStateSubRef, 'shutting-down')
|
|
149
149
|
|
|
150
150
|
dbEventlog.import(data)
|
|
151
151
|
|
|
152
|
-
|
|
153
|
-
} else if (
|
|
154
|
-
|
|
152
|
+
dbState.destroy()
|
|
153
|
+
} else if (
|
|
154
|
+
tableNames.has(SystemTables.SCHEMA_META_TABLE) &&
|
|
155
|
+
tableNames.has(SystemTables.SCHEMA_EVENT_DEFS_META_TABLE)
|
|
156
|
+
) {
|
|
157
|
+
// Is state db
|
|
155
158
|
yield* SubscriptionRef.set(shutdownStateSubRef, 'shutting-down')
|
|
156
159
|
|
|
157
|
-
|
|
160
|
+
dbState.import(data)
|
|
158
161
|
|
|
159
162
|
dbEventlog.destroy()
|
|
160
163
|
} else {
|
|
@@ -187,7 +190,7 @@ const listenToDevtools = ({
|
|
|
187
190
|
|
|
188
191
|
yield* SubscriptionRef.set(shutdownStateSubRef, 'shutting-down')
|
|
189
192
|
|
|
190
|
-
|
|
193
|
+
dbState.destroy()
|
|
191
194
|
|
|
192
195
|
if (mode === 'all-data') {
|
|
193
196
|
dbEventlog.destroy()
|
|
@@ -206,7 +209,7 @@ const listenToDevtools = ({
|
|
|
206
209
|
}
|
|
207
210
|
|
|
208
211
|
const dbSizeQuery = `SELECT page_count * page_size as size FROM pragma_page_count(), pragma_page_size();`
|
|
209
|
-
const dbFileSize =
|
|
212
|
+
const dbFileSize = dbState.select<{ size: number }>(dbSizeQuery, undefined)[0]!.size
|
|
210
213
|
const eventlogFileSize = dbEventlog.select<{ size: number }>(dbSizeQuery, undefined)[0]!.size
|
|
211
214
|
|
|
212
215
|
yield* sendMessage(
|
|
@@ -8,10 +8,10 @@ import type { LiveStoreSchema } from '../schema/mod.js'
|
|
|
8
8
|
import { LiveStoreEvent } from '../schema/mod.js'
|
|
9
9
|
import type { InvalidPullError, IsOfflineError, SyncOptions } from '../sync/sync.js'
|
|
10
10
|
import { sql } from '../util.js'
|
|
11
|
-
import { makeApplyEvent } from './apply-event.js'
|
|
12
11
|
import * as Eventlog from './eventlog.js'
|
|
13
12
|
import { bootDevtools } from './leader-worker-devtools.js'
|
|
14
13
|
import { makeLeaderSyncProcessor } from './LeaderSyncProcessor.js'
|
|
14
|
+
import { makeMaterializeEvent } from './materialize-event.js'
|
|
15
15
|
import { recreateDb } from './recreate-db.js'
|
|
16
16
|
import type { ShutdownChannel } from './shutdown-channel.js'
|
|
17
17
|
import type {
|
|
@@ -30,7 +30,7 @@ export interface MakeLeaderThreadLayerParams {
|
|
|
30
30
|
schema: LiveStoreSchema
|
|
31
31
|
makeSqliteDb: MakeSqliteDb
|
|
32
32
|
syncOptions: SyncOptions | undefined
|
|
33
|
-
|
|
33
|
+
dbState: LeaderSqliteDb
|
|
34
34
|
dbEventlog: LeaderSqliteDb
|
|
35
35
|
devtoolsOptions: DevtoolsOptions
|
|
36
36
|
shutdownChannel: ShutdownChannel
|
|
@@ -54,7 +54,7 @@ export const makeLeaderThreadLayer = ({
|
|
|
54
54
|
syncPayload,
|
|
55
55
|
makeSqliteDb,
|
|
56
56
|
syncOptions,
|
|
57
|
-
|
|
57
|
+
dbState,
|
|
58
58
|
dbEventlog,
|
|
59
59
|
devtoolsOptions,
|
|
60
60
|
shutdownChannel,
|
|
@@ -69,8 +69,8 @@ export const makeLeaderThreadLayer = ({
|
|
|
69
69
|
const dbEventlogMissing =
|
|
70
70
|
dbEventlog.select<{ count: number }>(sql`select count(*) as count from sqlite_master`)[0]!.count === 0
|
|
71
71
|
|
|
72
|
-
const
|
|
73
|
-
|
|
72
|
+
const dbStateMissing =
|
|
73
|
+
dbState.select<{ count: number }>(sql`select count(*) as count from sqlite_master`)[0]!.count === 0
|
|
74
74
|
|
|
75
75
|
const syncBackend =
|
|
76
76
|
syncOptions?.backend === undefined
|
|
@@ -91,8 +91,8 @@ export const makeLeaderThreadLayer = ({
|
|
|
91
91
|
schema,
|
|
92
92
|
dbEventlogMissing,
|
|
93
93
|
dbEventlog,
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
dbState,
|
|
95
|
+
dbStateMissing,
|
|
96
96
|
initialBlockingSyncContext,
|
|
97
97
|
onError: syncOptions?.onSyncError ?? 'ignore',
|
|
98
98
|
params: {
|
|
@@ -116,14 +116,14 @@ export const makeLeaderThreadLayer = ({
|
|
|
116
116
|
}
|
|
117
117
|
: { enabled: false as const }
|
|
118
118
|
|
|
119
|
-
const
|
|
119
|
+
const materializeEvent = yield* makeMaterializeEvent({ schema, dbState, dbEventlog })
|
|
120
120
|
|
|
121
121
|
const ctx = {
|
|
122
122
|
schema,
|
|
123
123
|
bootStatusQueue,
|
|
124
124
|
storeId,
|
|
125
125
|
clientId,
|
|
126
|
-
|
|
126
|
+
dbState,
|
|
127
127
|
dbEventlog,
|
|
128
128
|
makeSqliteDb,
|
|
129
129
|
eventSchema: LiveStoreEvent.makeEventDefSchema(schema),
|
|
@@ -131,7 +131,7 @@ export const makeLeaderThreadLayer = ({
|
|
|
131
131
|
shutdownChannel,
|
|
132
132
|
syncBackend,
|
|
133
133
|
syncProcessor,
|
|
134
|
-
|
|
134
|
+
materializeEvent,
|
|
135
135
|
extraIncomingMessagesQueue,
|
|
136
136
|
devtools: devtoolsContext,
|
|
137
137
|
// State will be set during `bootLeaderThread`
|
|
@@ -144,7 +144,7 @@ export const makeLeaderThreadLayer = ({
|
|
|
144
144
|
const layer = Layer.succeed(LeaderThreadCtx, ctx)
|
|
145
145
|
|
|
146
146
|
ctx.initialState = yield* bootLeaderThread({
|
|
147
|
-
|
|
147
|
+
dbStateMissing,
|
|
148
148
|
initialBlockingSyncContext,
|
|
149
149
|
devtoolsOptions,
|
|
150
150
|
}).pipe(Effect.provide(layer))
|
|
@@ -210,11 +210,11 @@ const makeInitialBlockingSyncContext = ({
|
|
|
210
210
|
* It also starts various background processes (e.g. syncing)
|
|
211
211
|
*/
|
|
212
212
|
const bootLeaderThread = ({
|
|
213
|
-
|
|
213
|
+
dbStateMissing,
|
|
214
214
|
initialBlockingSyncContext,
|
|
215
215
|
devtoolsOptions,
|
|
216
216
|
}: {
|
|
217
|
-
|
|
217
|
+
dbStateMissing: boolean
|
|
218
218
|
initialBlockingSyncContext: InitialBlockingSyncContext
|
|
219
219
|
devtoolsOptions: DevtoolsOptions
|
|
220
220
|
}): Effect.Effect<
|
|
@@ -228,7 +228,7 @@ const bootLeaderThread = ({
|
|
|
228
228
|
yield* Eventlog.initEventlogDb(dbEventlog)
|
|
229
229
|
|
|
230
230
|
let migrationsReport: MigrationsReport
|
|
231
|
-
if (
|
|
231
|
+
if (dbStateMissing) {
|
|
232
232
|
const recreateResult = yield* recreateDb
|
|
233
233
|
migrationsReport = recreateResult.migrationsReport
|
|
234
234
|
} else {
|
|
@@ -3,29 +3,23 @@ import { Effect, ReadonlyArray, Schema } from '@livestore/utils/effect'
|
|
|
3
3
|
|
|
4
4
|
import type { SqliteDb } from '../adapter-types.js'
|
|
5
5
|
import { getExecArgsFromEvent } from '../materializer-helper.js'
|
|
6
|
-
import type { LiveStoreSchema
|
|
7
|
-
import {
|
|
8
|
-
EventId,
|
|
9
|
-
EVENTLOG_META_TABLE,
|
|
10
|
-
getEventDef,
|
|
11
|
-
SESSION_CHANGESET_META_TABLE,
|
|
12
|
-
sessionChangesetMetaTable,
|
|
13
|
-
} from '../schema/mod.js'
|
|
6
|
+
import type { LiveStoreSchema } from '../schema/mod.js'
|
|
7
|
+
import { EventId, getEventDef, SystemTables } from '../schema/mod.js'
|
|
14
8
|
import { insertRow } from '../sql-queries/index.js'
|
|
15
9
|
import { sql } from '../util.js'
|
|
16
10
|
import { execSql, execSqlPrepared } from './connection.js'
|
|
17
11
|
import * as Eventlog from './eventlog.js'
|
|
18
|
-
import type {
|
|
12
|
+
import type { MaterializeEvent } from './types.js'
|
|
19
13
|
|
|
20
|
-
export const
|
|
14
|
+
export const makeMaterializeEvent = ({
|
|
21
15
|
schema,
|
|
22
|
-
|
|
16
|
+
dbState: db,
|
|
23
17
|
dbEventlog,
|
|
24
18
|
}: {
|
|
25
19
|
schema: LiveStoreSchema
|
|
26
|
-
|
|
20
|
+
dbState: SqliteDb
|
|
27
21
|
dbEventlog: SqliteDb
|
|
28
|
-
}): Effect.Effect<
|
|
22
|
+
}): Effect.Effect<MaterializeEvent, never> =>
|
|
29
23
|
Effect.gen(function* () {
|
|
30
24
|
const eventDefSchemaHashMap = new Map(
|
|
31
25
|
// TODO Running `Schema.hash` can be a bottleneck for larger schemas. There is an opportunity to run this
|
|
@@ -55,7 +49,7 @@ export const makeApplyEvent = ({
|
|
|
55
49
|
// },
|
|
56
50
|
// })
|
|
57
51
|
|
|
58
|
-
// console.group('[@livestore/common:leader-thread:
|
|
52
|
+
// console.group('[@livestore/common:leader-thread:materializeEvent]', { eventName })
|
|
59
53
|
|
|
60
54
|
const session = db.session()
|
|
61
55
|
|
|
@@ -72,8 +66,8 @@ export const makeApplyEvent = ({
|
|
|
72
66
|
yield* execSql(
|
|
73
67
|
db,
|
|
74
68
|
...insertRow({
|
|
75
|
-
tableName: SESSION_CHANGESET_META_TABLE,
|
|
76
|
-
columns: sessionChangesetMetaTable.sqliteDef.columns,
|
|
69
|
+
tableName: SystemTables.SESSION_CHANGESET_META_TABLE,
|
|
70
|
+
columns: SystemTables.sessionChangesetMetaTable.sqliteDef.columns,
|
|
77
71
|
values: {
|
|
78
72
|
idGlobal: eventEncoded.id.global,
|
|
79
73
|
idClient: eventEncoded.id.client,
|
|
@@ -113,30 +107,30 @@ export const makeApplyEvent = ({
|
|
|
113
107
|
: { _tag: 'no-op' as const },
|
|
114
108
|
}
|
|
115
109
|
}).pipe(
|
|
116
|
-
Effect.withSpan(`@livestore/common:leader-thread:
|
|
110
|
+
Effect.withSpan(`@livestore/common:leader-thread:materializeEvent`, {
|
|
117
111
|
attributes: {
|
|
118
112
|
eventName: eventEncoded.name,
|
|
119
|
-
|
|
113
|
+
eventId: eventEncoded.id,
|
|
120
114
|
'span.label': `${EventId.toString(eventEncoded.id)} ${eventEncoded.name}`,
|
|
121
115
|
},
|
|
122
116
|
}),
|
|
123
|
-
// Effect.logDuration('@livestore/common:leader-thread:
|
|
117
|
+
// Effect.logDuration('@livestore/common:leader-thread:materializeEvent'),
|
|
124
118
|
)
|
|
125
119
|
})
|
|
126
120
|
|
|
127
121
|
export const rollback = ({
|
|
128
|
-
|
|
122
|
+
dbState,
|
|
129
123
|
dbEventlog,
|
|
130
124
|
eventIdsToRollback,
|
|
131
125
|
}: {
|
|
132
|
-
|
|
126
|
+
dbState: SqliteDb
|
|
133
127
|
dbEventlog: SqliteDb
|
|
134
128
|
eventIdsToRollback: EventId.EventId[]
|
|
135
129
|
}) =>
|
|
136
130
|
Effect.gen(function* () {
|
|
137
|
-
const rollbackEvents =
|
|
138
|
-
.select<SessionChangesetMetaRow>(
|
|
139
|
-
sql`SELECT * FROM ${SESSION_CHANGESET_META_TABLE} WHERE (idGlobal, idClient) IN (${eventIdsToRollback.map((id) => `(${id.global}, ${id.client})`).join(', ')})`,
|
|
131
|
+
const rollbackEvents = dbState
|
|
132
|
+
.select<SystemTables.SessionChangesetMetaRow>(
|
|
133
|
+
sql`SELECT * FROM ${SystemTables.SESSION_CHANGESET_META_TABLE} WHERE (idGlobal, idClient) IN (${eventIdsToRollback.map((id) => `(${id.global}, ${id.client})`).join(', ')})`,
|
|
140
134
|
)
|
|
141
135
|
.map((_) => ({ id: { global: _.idGlobal, client: _.idClient }, changeset: _.changeset, debug: _.debug }))
|
|
142
136
|
.toSorted((a, b) => EventId.compare(a.id, b.id))
|
|
@@ -145,7 +139,7 @@ export const rollback = ({
|
|
|
145
139
|
for (let i = rollbackEvents.length - 1; i >= 0; i--) {
|
|
146
140
|
const { changeset } = rollbackEvents[i]!
|
|
147
141
|
if (changeset !== null) {
|
|
148
|
-
|
|
142
|
+
dbState.makeChangeset(changeset).invert().apply()
|
|
149
143
|
}
|
|
150
144
|
}
|
|
151
145
|
|
|
@@ -155,15 +149,15 @@ export const rollback = ({
|
|
|
155
149
|
|
|
156
150
|
// Delete the changeset rows
|
|
157
151
|
for (const eventIdPairChunk of eventIdPairChunks) {
|
|
158
|
-
|
|
159
|
-
sql`DELETE FROM ${SESSION_CHANGESET_META_TABLE} WHERE (idGlobal, idClient) IN (${eventIdPairChunk.join(', ')})`,
|
|
152
|
+
dbState.execute(
|
|
153
|
+
sql`DELETE FROM ${SystemTables.SESSION_CHANGESET_META_TABLE} WHERE (idGlobal, idClient) IN (${eventIdPairChunk.join(', ')})`,
|
|
160
154
|
)
|
|
161
155
|
}
|
|
162
156
|
|
|
163
157
|
// Delete the eventlog rows
|
|
164
158
|
for (const eventIdPairChunk of eventIdPairChunks) {
|
|
165
159
|
dbEventlog.execute(
|
|
166
|
-
sql`DELETE FROM ${EVENTLOG_META_TABLE} WHERE (idGlobal, idClient) IN (${eventIdPairChunk.join(', ')})`,
|
|
160
|
+
sql`DELETE FROM ${SystemTables.EVENTLOG_META_TABLE} WHERE (idGlobal, idClient) IN (${eventIdPairChunk.join(', ')})`,
|
|
167
161
|
)
|
|
168
162
|
}
|
|
169
163
|
}).pipe(
|
|
@@ -3,7 +3,7 @@ import type { HttpClient } from '@livestore/utils/effect'
|
|
|
3
3
|
import { Effect, Queue } from '@livestore/utils/effect'
|
|
4
4
|
|
|
5
5
|
import type { InvalidPullError, IsOfflineError, MigrationHooks, MigrationsReport, SqliteError } from '../index.js'
|
|
6
|
-
import { migrateDb,
|
|
6
|
+
import { migrateDb, rematerializeFromEventlog, UnexpectedError } from '../index.js'
|
|
7
7
|
import { configureConnection } from './connection.js'
|
|
8
8
|
import { LeaderThreadCtx } from './types.js'
|
|
9
9
|
|
|
@@ -12,14 +12,14 @@ export const recreateDb: Effect.Effect<
|
|
|
12
12
|
UnexpectedError | SqliteError | IsOfflineError | InvalidPullError,
|
|
13
13
|
LeaderThreadCtx | HttpClient.HttpClient
|
|
14
14
|
> = Effect.gen(function* () {
|
|
15
|
-
const {
|
|
15
|
+
const { dbState, dbEventlog, schema, bootStatusQueue, materializeEvent } = yield* LeaderThreadCtx
|
|
16
16
|
|
|
17
17
|
const migrationOptions = schema.migrationOptions
|
|
18
18
|
let migrationsReport: MigrationsReport
|
|
19
19
|
|
|
20
20
|
yield* Effect.addFinalizer(
|
|
21
21
|
Effect.fn('recreateDb:finalizer')(function* (ex) {
|
|
22
|
-
if (ex._tag === 'Failure')
|
|
22
|
+
if (ex._tag === 'Failure') dbState.destroy()
|
|
23
23
|
}),
|
|
24
24
|
)
|
|
25
25
|
|
|
@@ -27,7 +27,7 @@ export const recreateDb: Effect.Effect<
|
|
|
27
27
|
// and later we'll overwrite the persisted database with the new data
|
|
28
28
|
// TODO bring back this optimization
|
|
29
29
|
// const tmpDb = yield* makeSqliteDb({ _tag: 'in-memory' })
|
|
30
|
-
const tmpDb =
|
|
30
|
+
const tmpDb = dbState
|
|
31
31
|
yield* configureConnection(tmpDb, { foreignKeys: true })
|
|
32
32
|
|
|
33
33
|
const initDb = (hooks: Partial<MigrationHooks> | undefined) =>
|
|
@@ -53,11 +53,11 @@ export const recreateDb: Effect.Effect<
|
|
|
53
53
|
|
|
54
54
|
migrationsReport = initResult.migrationsReport
|
|
55
55
|
|
|
56
|
-
yield*
|
|
56
|
+
yield* rematerializeFromEventlog({
|
|
57
57
|
// db: initResult.tmpDb,
|
|
58
58
|
dbEventlog,
|
|
59
59
|
schema,
|
|
60
|
-
|
|
60
|
+
materializeEvent,
|
|
61
61
|
onProgress: ({ done, total }) =>
|
|
62
62
|
Queue.offer(bootStatusQueue, { stage: 'rehydrating', progress: { done, total } }),
|
|
63
63
|
})
|
|
@@ -79,7 +79,7 @@ export const recreateDb: Effect.Effect<
|
|
|
79
79
|
break
|
|
80
80
|
}
|
|
81
81
|
case 'manual': {
|
|
82
|
-
const oldDbData =
|
|
82
|
+
const oldDbData = dbState.export()
|
|
83
83
|
|
|
84
84
|
migrationsReport = { migrations: [] }
|
|
85
85
|
|
|
@@ -89,7 +89,7 @@ export class LeaderThreadCtx extends Context.Tag('LeaderThreadCtx')<
|
|
|
89
89
|
storeId: string
|
|
90
90
|
clientId: string
|
|
91
91
|
makeSqliteDb: MakeSqliteDb
|
|
92
|
-
|
|
92
|
+
dbState: LeaderSqliteDb
|
|
93
93
|
dbEventlog: LeaderSqliteDb
|
|
94
94
|
bootStatusQueue: Queue.Queue<BootStatus>
|
|
95
95
|
// TODO we should find a more elegant way to handle cases which need this ref for their implementation
|
|
@@ -99,7 +99,7 @@ export class LeaderThreadCtx extends Context.Tag('LeaderThreadCtx')<
|
|
|
99
99
|
devtools: DevtoolsContext
|
|
100
100
|
syncBackend: SyncBackend | undefined
|
|
101
101
|
syncProcessor: LeaderSyncProcessor
|
|
102
|
-
|
|
102
|
+
materializeEvent: MaterializeEvent
|
|
103
103
|
initialState: {
|
|
104
104
|
leaderHead: EventId.EventId
|
|
105
105
|
migrationsReport: MigrationsReport
|
|
@@ -113,10 +113,10 @@ export class LeaderThreadCtx extends Context.Tag('LeaderThreadCtx')<
|
|
|
113
113
|
}
|
|
114
114
|
>() {}
|
|
115
115
|
|
|
116
|
-
export type
|
|
116
|
+
export type MaterializeEvent = (
|
|
117
117
|
eventEncoded: LiveStoreEvent.EncodedWithMeta,
|
|
118
118
|
options?: {
|
|
119
|
-
/** Needed for
|
|
119
|
+
/** Needed for rematerializeFromEventlog */
|
|
120
120
|
skipEventlog?: boolean
|
|
121
121
|
},
|
|
122
122
|
) => Effect.Effect<
|