@@ -9,13 +9,14 @@ import {
9
9
UpdateNotification ,
10
10
isBatchedUpdateNotification
11
11
} from '../db/DBAdapter.js' ;
12
+ import { FULL_SYNC_PRIORITY } from '../db/crud/SyncProgress.js' ;
12
13
import { SyncPriorityStatus , SyncStatus } from '../db/crud/SyncStatus.js' ;
13
14
import { UploadQueueStats } from '../db/crud/UploadQueueStatus.js' ;
14
15
import { Schema } from '../db/schema/Schema.js' ;
15
16
import { BaseObserver } from '../utils/BaseObserver.js' ;
16
17
import { ControlledExecutor } from '../utils/ControlledExecutor.js' ;
17
- import { mutexRunExclusive } from '../utils/mutex.js' ;
18
18
import { throttleTrailing } from '../utils/async.js' ;
19
+ import { mutexRunExclusive } from '../utils/mutex.js' ;
19
20
import { SQLOpenFactory , SQLOpenOptions , isDBAdapter , isSQLOpenFactory , isSQLOpenOptions } from './SQLOpenFactory.js' ;
20
21
import { PowerSyncBackendConnector } from './connection/PowerSyncBackendConnector.js' ;
21
22
import { runOnSchemaChange } from './runOnSchemaChange.js' ;
@@ -32,7 +33,6 @@ import {
32
33
type PowerSyncConnectionOptions ,
33
34
type RequiredAdditionalConnectionOptions
34
35
} from './sync/stream/AbstractStreamingSyncImplementation.js' ;
35
- import { FULL_SYNC_PRIORITY } from '../db/crud/SyncProgress.js' ;
36
36
37
37
export interface DisconnectAndClearOptions {
38
38
/** When set to false, data in local-only tables is preserved. */
@@ -98,6 +98,10 @@ export interface WatchOnChangeHandler {
98
98
onError ?: ( error : Error ) => void ;
99
99
}
100
100
101
+ export interface CrudTransactionBatchOptions {
102
+ transactionLimit : number ;
103
+ }
104
+
101
105
export interface PowerSyncDBListener extends StreamingSyncImplementationListener {
102
106
initialized : ( ) => void ;
103
107
schemaChanged : ( schema : Schema ) => void ;
@@ -122,6 +126,11 @@ export const DEFAULT_POWERSYNC_CLOSE_OPTIONS: PowerSyncCloseOptions = {
122
126
disconnect : true
123
127
} ;
124
128
129
+ export const DEFAULT_CRUD_TRANSACTION_BATCH_LIMIT = 10 ;
130
+ export const DEFAULT_CRUD_TRANSACTION_BATCH_OPTIONS : CrudTransactionBatchOptions = {
131
+ transactionLimit : DEFAULT_CRUD_TRANSACTION_BATCH_LIMIT
132
+ } ;
133
+
125
134
export const DEFAULT_WATCH_THROTTLE_MS = 30 ;
126
135
127
136
export const DEFAULT_POWERSYNC_DB_OPTIONS = {
@@ -580,6 +589,69 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
580
589
) ;
581
590
}
582
591
592
+ /**
593
+ * Get a batch of CRUD data, grouped by transaction, to upload.
594
+ *
595
+ * Returns null if there is no data to upload.
596
+ *
597
+ * This allows for processing and completing multiple transactions at once.
598
+ *
599
+ * Use this from the {@link PowerSyncBackendConnector.uploadData} callback.
600
+ *
601
+ * Once the data have been successfully uploaded, call {@link CrudBatch.complete} before
602
+ * requesting the next batch.
603
+ *
604
+ * Use {@link limit} to specify the maximum number of transactions to return in the batch.
605
+ *
606
+ * @param limit Maximum number of transactions to include in the batch
607
+ * @returns A batch of CRUD operations to upload, or null if there are none
608
+ */
609
+ async getNextCrudTransactionBatch (
610
+ options : CrudTransactionBatchOptions = DEFAULT_CRUD_TRANSACTION_BATCH_OPTIONS
611
+ ) : Promise < CrudBatch | null > {
612
+ const { transactionLimit } = options ;
613
+
614
+ // Transaction IDs are always incrementing
615
+ // We can fetch the first transaction id and use that to limit the query
616
+ // to the next batch of transactions
617
+ const first = await this . getOptional < CrudEntryJSON > ( `
618
+ SELECT id, tx_id, data FROM ${ PSInternalTable . CRUD } ORDER BY id ASC LIMIT 1` ) ;
619
+
620
+ if ( ! first ) {
621
+ return null ;
622
+ }
623
+
624
+ const items = await this . getAll < CrudEntryJSON > (
625
+ `SELECT
626
+ id, tx_id, data
627
+ FROM ${ PSInternalTable . CRUD }
628
+ WHERE
629
+ tx_id < ?
630
+ ORDER BY
631
+ id ASC
632
+ ` ,
633
+ [ ( first . tx_id ?? 0 ) + transactionLimit ]
634
+ ) ;
635
+
636
+ if ( items . length == 0 ) {
637
+ return null ;
638
+ }
639
+
640
+ // check if there are more items for haveMore
641
+ const nextItem = await this . getOptional < CrudEntryJSON > (
642
+ `
643
+ SELECT id FROM ${ PSInternalTable . CRUD } WHERE id > ? LIMIT 1` ,
644
+ [ items [ items . length - 1 ] . id ]
645
+ ) ;
646
+
647
+ const crudEntries : CrudEntry [ ] = items . map ( ( row ) => CrudEntry . fromRow ( row ) ) ?? [ ] ;
648
+ const last = crudEntries [ items . length - 1 ] ;
649
+
650
+ return new CrudBatch ( crudEntries , ! ! nextItem , async ( writeCheckpoint ?: string ) =>
651
+ this . handleCrudCheckpoint ( last . clientId , writeCheckpoint )
652
+ ) ;
653
+ }
654
+
583
655
/**
584
656
* Get the next recorded transaction to upload.
585
657
*
0 commit comments