@@ -28,14 +28,7 @@ import {
2828 isStreamingSyncCheckpointPartiallyComplete ,
2929 isStreamingSyncData
3030} from './streaming-sync-types.js' ;
31- import {
32- extractBsonObjects ,
33- extractJsonLines ,
34- injectable ,
35- InjectableIterator ,
36- map ,
37- SimpleAsyncIterator
38- } from '../../../utils/stream_transform.js' ;
31+ import { injectable , InjectableIterator , map , SimpleAsyncIterator } from '../../../utils/stream_transform.js' ;
3932import type { BSON } from 'bson' ;
4033
4134export enum LockType {
@@ -216,6 +209,7 @@ export interface StreamingSyncImplementation
216209 waitForStatus ( status : SyncStatusOptions ) : Promise < void > ;
217210 waitUntilStatusMatches ( predicate : ( status : SyncStatus ) => boolean ) : Promise < void > ;
218211 updateSubscriptions ( subscriptions : SubscribedStream [ ] ) : void ;
212+ markConnectionMayHaveChanged ( ) : void ;
219213}
220214
221215export const DEFAULT_CRUD_UPLOAD_THROTTLE_MS = 1000 ;
@@ -263,6 +257,7 @@ export abstract class AbstractStreamingSyncImplementation
263257 protected streamingSyncPromise ?: Promise < void > ;
264258 protected logger : ILogger ;
265259 private activeStreams : SubscribedStream [ ] ;
260+ private connectionMayHaveChanged = false ;
266261
267262 private isUploadingCrud : boolean = false ;
268263 private notifyCompletedUploads ?: ( ) => void ;
@@ -579,6 +574,10 @@ The next upload iteration will be delayed.`);
579574 this . logger . warn ( ex ) ;
580575 shouldDelayRetry = false ;
581576 // A disconnect was requested, we should not delay since there is no explicit retry
577+ } else if ( this . connectionMayHaveChanged && ( ex as Error ) . message ?. indexOf ( 'No iteration is active' ) >= 0 ) {
578+ this . connectionMayHaveChanged = false ;
579+ this . logger . info ( 'Sync error after changed connection, retrying immediately' ) ;
580+ shouldDelayRetry = false ;
582581 } else {
583582 this . logger . error ( ex ) ;
584583 }
@@ -614,6 +613,17 @@ The next upload iteration will be delayed.`);
614613 this . updateSyncStatus ( { connected : false , connecting : false } ) ;
615614 }
616615
616+ markConnectionMayHaveChanged ( ) {
617+ // By setting this field, we'll immediately retry if the next sync connection causes an error triggered by us not
618+ // having an active sync iteration on the connection in use.
619+ this . connectionMayHaveChanged = true ;
620+
621+ // This triggers a `powersync_control` invocation if a sync iteration is currently active. This is a cheap call to
622+ // make when no subscriptions have actually changed, we're mainly interested in this immediately throwing if no
623+ // iteration is active. That allows us to reconnect ASAP, instead of having to wait for the next sync line.
624+ this . handleActiveStreamsChange ?.( ) ;
625+ }
626+
617627 private async collectLocalBucketState ( ) : Promise < [ BucketRequest [ ] , Map < string , BucketDescription | null > ] > {
618628 const bucketEntries = await this . options . adapter . getBucketStates ( ) ;
619629 const req : BucketRequest [ ] = bucketEntries . map ( ( entry ) => ( {
@@ -1041,6 +1051,10 @@ The next upload iteration will be delayed.`);
10411051 rawResponse
10421052 ) ;
10431053
1054+ if ( op != PowerSyncControlCommand . STOP ) {
1055+ // Evidently we have a working connection here, otherwise powersync_control would have failed.
1056+ syncImplementation . connectionMayHaveChanged = false ;
1057+ }
10441058 await handleInstructions ( JSON . parse ( rawResponse ) ) ;
10451059 }
10461060
0 commit comments