@@ -4,11 +4,10 @@ import { Option, type Runtime, type Scope } from '@livestore/utils/effect'
4
4
import { BucketQueue , Effect , FiberHandle , Queue , Schema , Stream , Subscribable } from '@livestore/utils/effect'
5
5
import type * as otel from '@opentelemetry/api'
6
6
7
- import type { ClientSession , UnexpectedError } from '../adapter-types.js'
7
+ import { type ClientSession , SyncError , type UnexpectedError } from '../adapter-types.js'
8
8
import * as EventSequenceNumber from '../schema/EventSequenceNumber.js'
9
9
import * as LiveStoreEvent from '../schema/LiveStoreEvent.js'
10
- import { getEventDef , type LiveStoreSchema , SystemTables } from '../schema/mod.js'
11
- import { sql } from '../util.js'
10
+ import { getEventDef , type LiveStoreSchema } from '../schema/mod.js'
12
11
import * as SyncState from './syncstate.js'
13
12
14
13
/**
@@ -21,6 +20,10 @@ import * as SyncState from './syncstate.js'
21
20
* - We might need to make the rebase behaviour configurable e.g. to let users manually trigger a rebase
22
21
*
23
22
* Longer term we should evalutate whether we can unify the ClientSessionSyncProcessor with the LeaderSyncProcessor.
23
+ *
24
+ * The session and leader sync processor are different in the following ways:
25
+ * - The leader sync processor pulls regular LiveStore events, while the session sync processor pulls SyncState.PayloadUpstream items
26
+ * - The session sync processor has no downstream nodes.
24
27
*/
25
28
export const makeClientSessionSyncProcessor = ( {
26
29
schema,
@@ -37,7 +40,7 @@ export const makeClientSessionSyncProcessor = ({
37
40
clientSession : ClientSession
38
41
runtime : Runtime . Runtime < Scope . Scope >
39
42
materializeEvent : (
40
- eventDecoded : LiveStoreEvent . PartialAnyDecoded ,
43
+ eventDecoded : LiveStoreEvent . AnyDecoded ,
41
44
options : { withChangeset : boolean ; materializerHashLeader : Option . Option < number > } ,
42
45
) => Effect . Effect < {
43
46
writeTables : Set < string >
@@ -82,7 +85,10 @@ export const makeClientSessionSyncProcessor = ({
82
85
let baseEventSequenceNumber = syncStateRef . current . localHead
83
86
const encodedEventDefs = batch . map ( ( { name, args } ) => {
84
87
const eventDef = getEventDef ( schema , name )
85
- const nextNumPair = EventSequenceNumber . nextPair ( baseEventSequenceNumber , eventDef . eventDef . options . clientOnly )
88
+ const nextNumPair = EventSequenceNumber . nextPair ( {
89
+ seqNum : baseEventSequenceNumber ,
90
+ isClient : eventDef . eventDef . options . clientOnly ,
91
+ } )
86
92
baseEventSequenceNumber = nextNumPair . seqNum
87
93
return new LiveStoreEvent . EncodedWithMeta (
88
94
Schema . encodeUnknownSync ( eventSchema ) ( {
@@ -106,13 +112,13 @@ export const makeClientSessionSyncProcessor = ({
106
112
)
107
113
108
114
if ( mergeResult . _tag === 'unexpected-error' ) {
109
- return yield * Effect . die ( new Error ( `Unexpected error in client-session-sync-processor: ${ mergeResult . cause } ` ) )
115
+ return yield * new SyncError ( { cause : mergeResult . message } )
110
116
}
111
117
112
118
if ( TRACE_VERBOSE ) yield * Effect . annotateCurrentSpan ( { mergeResult : JSON . stringify ( mergeResult ) } )
113
119
114
120
if ( mergeResult . _tag !== 'advance' ) {
115
- return yield * Effect . die ( new Error ( `Expected advance, got ${ mergeResult . _tag } ` ) )
121
+ return yield * new SyncError ( { cause : `Expected advance, got ${ mergeResult . _tag } ` } )
116
122
}
117
123
118
124
syncStateRef . current = mergeResult . newSyncState
@@ -182,18 +188,11 @@ export const makeClientSessionSyncProcessor = ({
182
188
183
189
yield * FiberHandle . run ( leaderPushingFiberHandle , backgroundLeaderPushing )
184
190
185
- const getMergeCounter = ( ) =>
186
- clientSession . sqliteDb . select < { mergeCounter : number } > (
187
- sql `SELECT mergeCounter FROM ${ SystemTables . LEADER_MERGE_COUNTER_TABLE } WHERE id = 0` ,
188
- ) [ 0 ] ?. mergeCounter ?? 0
189
-
190
191
// NOTE We need to lazily call `.pull` as we want the cursor to be updated
191
192
yield * Stream . suspend ( ( ) =>
192
- clientSession . leaderThread . events . pull ( {
193
- cursor : { mergeCounter : getMergeCounter ( ) , eventNum : syncStateRef . current . localHead } ,
194
- } ) ,
193
+ clientSession . leaderThread . events . pull ( { cursor : syncStateRef . current . upstreamHead } ) ,
195
194
) . pipe (
196
- Stream . tap ( ( { payload, mergeCounter : leaderMergeCounter } ) =>
195
+ Stream . tap ( ( { payload } ) =>
197
196
Effect . gen ( function * ( ) {
198
197
// yield* Effect.logDebug('ClientSessionSyncProcessor:pull', payload)
199
198
@@ -209,13 +208,13 @@ export const makeClientSessionSyncProcessor = ({
209
208
} )
210
209
211
210
if ( mergeResult . _tag === 'unexpected-error' ) {
212
- return yield * Effect . fail ( mergeResult . cause )
211
+ return yield * new SyncError ( { cause : mergeResult . message } )
213
212
} else if ( mergeResult . _tag === 'reject' ) {
214
213
return shouldNeverHappen ( 'Unexpected reject in client-session-sync-processor' , mergeResult )
215
214
}
216
215
217
216
syncStateRef . current = mergeResult . newSyncState
218
- syncStateUpdateQueue . offer ( mergeResult . newSyncState ) . pipe ( Effect . runSync )
217
+ yield * syncStateUpdateQueue . offer ( mergeResult . newSyncState )
219
218
220
219
if ( mergeResult . _tag === 'rebase' ) {
221
220
span . addEvent ( 'merge:pull:rebase' , {
@@ -224,7 +223,7 @@ export const makeClientSessionSyncProcessor = ({
224
223
newEventsCount : mergeResult . newEvents . length ,
225
224
rollbackCount : mergeResult . rollbackEvents . length ,
226
225
res : TRACE_VERBOSE ? JSON . stringify ( mergeResult ) : undefined ,
227
- leaderMergeCounter ,
226
+ rebaseGeneration : mergeResult . newSyncState . localHead . rebaseGeneration ,
228
227
} )
229
228
230
229
debugInfo . rebaseCount ++
@@ -241,7 +240,6 @@ export const makeClientSessionSyncProcessor = ({
241
240
'merge:pull:rebase: rollback' ,
242
241
mergeResult . rollbackEvents . length ,
243
242
...mergeResult . rollbackEvents . slice ( 0 , 10 ) . map ( ( _ ) => _ . toJSON ( ) ) ,
244
- { leaderMergeCounter } ,
245
243
) . pipe ( Effect . provide ( runtime ) , Effect . runSync )
246
244
}
247
245
@@ -261,7 +259,6 @@ export const makeClientSessionSyncProcessor = ({
261
259
payload : TRACE_VERBOSE ? JSON . stringify ( payload ) : undefined ,
262
260
newEventsCount : mergeResult . newEvents . length ,
263
261
res : TRACE_VERBOSE ? JSON . stringify ( mergeResult ) : undefined ,
264
- leaderMergeCounter,
265
262
} )
266
263
267
264
debugInfo . advanceCount ++
@@ -338,7 +335,7 @@ export interface ClientSessionSyncProcessor {
338
335
{
339
336
writeTables : Set < string >
340
337
} ,
341
- never
338
+ SyncError
342
339
>
343
340
boot : Effect . Effect < void , UnexpectedError , Scope . Scope >
344
341
/**
0 commit comments