@@ -8,11 +8,12 @@ import { UploadQueueStats } from '../db/crud/UploadQueueStatus';
8
8
import { PowerSyncBackendConnector } from './connection/PowerSyncBackendConnector' ;
9
9
import {
10
10
AbstractStreamingSyncImplementation ,
11
+ DEFAULT_CRUD_UPLOAD_THROTTLE_MS ,
11
12
StreamingSyncImplementationListener
12
13
} from './sync/stream/AbstractStreamingSyncImplementation' ;
13
14
import { CrudBatch } from './sync/bucket/CrudBatch' ;
14
15
import { CrudTransaction } from './sync/bucket/CrudTransaction' ;
15
- import { BucketStorageAdapter } from './sync/bucket/BucketStorageAdapter' ;
16
+ import { BucketStorageAdapter , PSInternalTable } from './sync/bucket/BucketStorageAdapter' ;
16
17
import { CrudEntry } from './sync/bucket/CrudEntry' ;
17
18
import { mutexRunExclusive } from '../utils/mutex' ;
18
19
import { BaseObserver } from '../utils/BaseObserver' ;
@@ -21,14 +22,29 @@ import { EventIterator } from 'event-iterator';
21
22
export interface PowerSyncDatabaseOptions {
22
23
schema : Schema ;
23
24
database : DBAdapter ;
25
+ /**
26
+ * Delay for retrying sync streaming operations
27
+ * from the PowerSync backend after an error occurs.
28
+ */
24
29
retryDelay ?: number ;
30
+ /**
31
+ * Backend Connector CRUD operations are throttled
32
+ * to occur at most every `crudUploadThrottleMs`
33
+ * milliseconds.
34
+ */
35
+ crudUploadThrottleMs ?: number ;
25
36
logger ?: ILogger ;
26
37
}
27
38
28
39
export interface SQLWatchOptions {
29
40
signal ?: AbortSignal ;
30
41
tables ?: string [ ] ;
31
42
throttleMs ?: number ;
43
+ /**
44
+ * Allows for watching any SQL table
45
+ * by not removing PowerSync table name prefixes
46
+ */
47
+ rawTableNames ?: boolean ;
32
48
}
33
49
34
50
export interface WatchOnChangeEvent {
@@ -45,7 +61,8 @@ export const DEFAULT_WATCH_THROTTLE_MS = 30;
45
61
46
62
export const DEFAULT_POWERSYNC_DB_OPTIONS = {
47
63
retryDelay : 5000 ,
48
- logger : Logger . get ( 'PowerSyncDatabase' )
64
+ logger : Logger . get ( 'PowerSyncDatabase' ) ,
65
+ crudUploadThrottleMs : DEFAULT_CRUD_UPLOAD_THROTTLE_MS
49
66
} ;
50
67
51
68
/**
@@ -135,6 +152,19 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
135
152
this . iterateListeners ( ( cb ) => cb . initialized ?.( ) ) ;
136
153
}
137
154
155
+ /**
156
+ * Queues a CRUD upload when internal CRUD tables have been updated
157
+ */
158
+ protected async watchCrudUploads ( ) {
159
+ for await ( const event of this . onChange ( {
160
+ tables : [ PSInternalTable . CRUD ] ,
161
+ rawTableNames : true ,
162
+ signal : this . abortController ?. signal
163
+ } ) ) {
164
+ this . syncStreamImplementation ?. triggerCrudUpload ( ) ;
165
+ }
166
+ }
167
+
138
168
/**
139
169
* Wait for initialization to complete.
140
170
* While initializing is automatic, this helps to catch and report initialization errors.
@@ -163,6 +193,7 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
163
193
// Begin network stream
164
194
this . syncStreamImplementation . triggerCrudUpload ( ) ;
165
195
this . syncStreamImplementation . streamingSync ( this . abortController . signal ) ;
196
+ this . watchCrudUploads ( ) ;
166
197
}
167
198
168
199
async disconnect ( ) {
@@ -182,9 +213,9 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
182
213
183
214
// TODO DB name, verify this is necessary with extension
184
215
await this . database . writeTransaction ( async ( tx ) => {
185
- await tx . execute ( ' DELETE FROM ps_oplog WHERE 1' ) ;
186
- await tx . execute ( ' DELETE FROM ps_crud WHERE 1' ) ;
187
- await tx . execute ( ' DELETE FROM ps_buckets WHERE 1' ) ;
216
+ await tx . execute ( ` DELETE FROM ${ PSInternalTable . OPLOG } WHERE 1` ) ;
217
+ await tx . execute ( ` DELETE FROM ${ PSInternalTable . CRUD } WHERE 1` ) ;
218
+ await tx . execute ( ` DELETE FROM ${ PSInternalTable . BUCKETS } WHERE 1` ) ;
188
219
189
220
const existingTableRows = await tx . execute (
190
221
"SELECT name FROM sqlite_master WHERE type='table' AND name GLOB 'ps_data_*'"
@@ -220,12 +251,14 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
220
251
async getUploadQueueStats ( includeSize ?: boolean ) : Promise < UploadQueueStats > {
221
252
return this . readTransaction ( async ( tx ) => {
222
253
if ( includeSize ) {
223
- const result = await tx . execute ( 'SELECT SUM(cast(data as blob) + 20) as size, count(*) as count FROM ps_crud' ) ;
254
+ const result = await tx . execute (
255
+ `SELECT SUM(cast(data as blob) + 20) as size, count(*) as count FROM ${ PSInternalTable . CRUD } `
256
+ ) ;
224
257
225
258
const row = result . rows . item ( 0 ) ;
226
259
return new UploadQueueStats ( row ?. count ?? 0 , row ?. size ?? 0 ) ;
227
260
} else {
228
- const result = await tx . execute ( ' SELECT count(*) as count FROM ps_crud' ) ;
261
+ const result = await tx . execute ( ` SELECT count(*) as count FROM ${ PSInternalTable . CRUD } ` ) ;
229
262
const row = result . rows . item ( 0 ) ;
230
263
return new UploadQueueStats ( row ?. count ?? 0 ) ;
231
264
}
@@ -250,9 +283,10 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
250
283
* and a single transaction may be split over multiple batches.
251
284
*/
252
285
async getCrudBatch ( limit : number ) : Promise < CrudBatch | null > {
253
- const result = await this . database . execute ( 'SELECT id, tx_id, data FROM ps_crud ORDER BY id ASC LIMIT ?' , [
254
- limit + 1
255
- ] ) ;
286
+ const result = await this . database . execute (
287
+ `SELECT id, tx_id, data FROM ${ PSInternalTable . CRUD } ORDER BY id ASC LIMIT ?` ,
288
+ [ limit + 1 ]
289
+ ) ;
256
290
257
291
const all : CrudEntry [ ] = result . rows ?. _array ?. map ( ( row ) => CrudEntry . fromRow ( row ) ) ?? [ ] ;
258
292
@@ -268,11 +302,13 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
268
302
const last = all [ all . length - 1 ] ;
269
303
return new CrudBatch ( all , haveMore , async ( writeCheckpoint ?: string ) => {
270
304
await this . writeTransaction ( async ( tx ) => {
271
- await tx . execute ( 'DELETE FROM ps_crud WHERE id <= ?' , [ last . clientId ] ) ;
272
- if ( writeCheckpoint != null && ( await tx . execute ( 'SELECT 1 FROM ps_crud LIMIT 1' ) ) == null ) {
273
- await tx . execute ( "UPDATE ps_buckets SET target_op = ? WHERE name='$local'" , [ writeCheckpoint ] ) ;
305
+ await tx . execute ( `DELETE FROM ${ PSInternalTable . CRUD } WHERE id <= ?` , [ last . clientId ] ) ;
306
+ if ( writeCheckpoint != null && ( await tx . execute ( `SELECT 1 FROM ${ PSInternalTable . CRUD } LIMIT 1` ) ) == null ) {
307
+ await tx . execute ( `UPDATE ${ PSInternalTable . BUCKETS } SET target_op = ? WHERE name='$local'` , [
308
+ writeCheckpoint
309
+ ] ) ;
274
310
} else {
275
- await tx . execute ( " UPDATE ps_buckets SET target_op = ? WHERE name='$local'" , [
311
+ await tx . execute ( ` UPDATE ${ PSInternalTable . BUCKETS } SET target_op = ? WHERE name='$local'` , [
276
312
this . bucketStorageAdapter . getMaxOpId ( )
277
313
] ) ;
278
314
}
@@ -295,7 +331,7 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
295
331
*/
296
332
async getNextCrudTransaction ( ) : Promise < CrudTransaction > {
297
333
return await this . readTransaction ( async ( tx ) => {
298
- const first = await tx . execute ( ' SELECT id, tx_id, data FROM ps_crud ORDER BY id ASC LIMIT 1' ) ;
334
+ const first = await tx . execute ( ` SELECT id, tx_id, data FROM ${ PSInternalTable . CRUD } ORDER BY id ASC LIMIT 1` ) ;
299
335
300
336
if ( ! first . rows . length ) {
301
337
return null ;
@@ -306,7 +342,10 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
306
342
if ( ! txId ) {
307
343
all = [ CrudEntry . fromRow ( first . rows . item ( 0 ) ) ] ;
308
344
} else {
309
- const result = await tx . execute ( 'SELECT id, tx_id, data FROM ps_crud WHERE tx_id = ? ORDER BY id ASC' , [ txId ] ) ;
345
+ const result = await tx . execute (
346
+ `SELECT id, tx_id, data FROM ${ PSInternalTable . CRUD } WHERE tx_id = ? ORDER BY id ASC` ,
347
+ [ txId ]
348
+ ) ;
310
349
all = result . rows . _array . map ( ( row ) => CrudEntry . fromRow ( row ) ) ;
311
350
}
312
351
@@ -316,14 +355,16 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
316
355
all ,
317
356
async ( writeCheckpoint ?: string ) => {
318
357
await this . writeTransaction ( async ( tx ) => {
319
- await tx . execute ( ' DELETE FROM ps_crud WHERE id <= ?' , [ last . clientId ] ) ;
358
+ await tx . execute ( ` DELETE FROM ${ PSInternalTable . CRUD } WHERE id <= ?` , [ last . clientId ] ) ;
320
359
if ( writeCheckpoint ) {
321
- const check = await tx . execute ( ' SELECT 1 FROM ps_crud LIMIT 1' ) ;
360
+ const check = await tx . execute ( ` SELECT 1 FROM ${ PSInternalTable . CRUD } LIMIT 1` ) ;
322
361
if ( ! check . rows ?. length ) {
323
- await tx . execute ( "UPDATE ps_buckets SET target_op = ? WHERE name='$local'" , [ writeCheckpoint ] ) ;
362
+ await tx . execute ( `UPDATE ${ PSInternalTable . BUCKETS } SET target_op = ? WHERE name='$local'` , [
363
+ writeCheckpoint
364
+ ] ) ;
324
365
}
325
366
} else {
326
- await tx . execute ( " UPDATE ps_buckets SET target_op = ? WHERE name='$local'" , [
367
+ await tx . execute ( ` UPDATE ${ PSInternalTable . BUCKETS } SET target_op = ? WHERE name='$local'` , [
327
368
this . bucketStorageAdapter . getMaxOpId ( )
328
369
] ) ;
329
370
}
@@ -339,9 +380,7 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
339
380
*/
340
381
async execute ( sql : string , parameters ?: any [ ] ) {
341
382
await this . waitForReady ( ) ;
342
- const result = await this . database . execute ( sql , parameters ) ;
343
- _ . defer ( ( ) => this . syncStreamImplementation ?. triggerCrudUpload ( ) ) ;
344
- return result ;
383
+ return this . database . execute ( sql , parameters ) ;
345
384
}
346
385
347
386
/**
@@ -386,7 +425,6 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
386
425
await this . waitForReady ( ) ;
387
426
return mutexRunExclusive ( AbstractPowerSyncDatabase . transactionMutex , async ( ) => {
388
427
const res = await callback ( this . database ) ;
389
- _ . defer ( ( ) => this . syncStreamImplementation ?. triggerCrudUpload ( ) ) ;
390
428
return res ;
391
429
} ) ;
392
430
}
@@ -415,7 +453,6 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
415
453
async ( tx ) => {
416
454
const res = await callback ( tx ) ;
417
455
await tx . commit ( ) ;
418
- _ . defer ( ( ) => this . syncStreamImplementation ?. triggerCrudUpload ( ) ) ;
419
456
return res ;
420
457
} ,
421
458
{ timeoutMs : lockTimeout }
@@ -475,10 +512,13 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
475
512
const dispose = this . database . registerListener ( {
476
513
tablesUpdated : async ( update ) => {
477
514
const { table } = update ;
478
- if ( ! table . match ( POWERSYNC_TABLE_MATCH ) ) {
515
+ const { rawTableNames } = options ;
516
+
517
+ if ( ! rawTableNames && ! table . match ( POWERSYNC_TABLE_MATCH ) ) {
479
518
return ;
480
519
}
481
- const tableName = table . replace ( POWERSYNC_TABLE_MATCH , '' ) ;
520
+
521
+ const tableName = rawTableNames ? table : table . replace ( POWERSYNC_TABLE_MATCH , '' ) ;
482
522
throttledTableUpdates . push ( tableName ) ;
483
523
484
524
flushTableUpdates ( ) ;
0 commit comments