@@ -8,11 +8,12 @@ import { UploadQueueStats } from '../db/crud/UploadQueueStatus';
88import { PowerSyncBackendConnector } from './connection/PowerSyncBackendConnector' ;
99import {
1010 AbstractStreamingSyncImplementation ,
11+ DEFAULT_CRUD_UPLOAD_THROTTLE_MS ,
1112 StreamingSyncImplementationListener
1213} from './sync/stream/AbstractStreamingSyncImplementation' ;
1314import { CrudBatch } from './sync/bucket/CrudBatch' ;
1415import { CrudTransaction } from './sync/bucket/CrudTransaction' ;
15- import { BucketStorageAdapter } from './sync/bucket/BucketStorageAdapter' ;
16+ import { BucketStorageAdapter , PSInternalTable } from './sync/bucket/BucketStorageAdapter' ;
1617import { CrudEntry } from './sync/bucket/CrudEntry' ;
1718import { mutexRunExclusive } from '../utils/mutex' ;
1819import { BaseObserver } from '../utils/BaseObserver' ;
@@ -21,14 +22,29 @@ import { EventIterator } from 'event-iterator';
2122export interface PowerSyncDatabaseOptions {
2223 schema : Schema ;
2324 database : DBAdapter ;
25+ /**
26+ * Delay for retrying sync streaming operations
27+ * from the PowerSync backend after an error occurs.
28+ */
2429 retryDelay ?: number ;
30+ /**
31+ * Backend Connector CRUD operations are throttled
32+ * to occur at most every `crudUploadThrottleMs`
33+ * milliseconds.
34+ */
35+ crudUploadThrottleMs ?: number ;
2536 logger ?: ILogger ;
2637}
2738
2839export interface SQLWatchOptions {
2940 signal ?: AbortSignal ;
3041 tables ?: string [ ] ;
3142 throttleMs ?: number ;
43+ /**
44+ * Allows for watching any SQL table
45+ * by not removing PowerSync table name prefixes
46+ */
47+ rawTableNames ?: boolean ;
3248}
3349
3450export interface WatchOnChangeEvent {
@@ -45,7 +61,8 @@ export const DEFAULT_WATCH_THROTTLE_MS = 30;
4561
4662export const DEFAULT_POWERSYNC_DB_OPTIONS = {
4763 retryDelay : 5000 ,
48- logger : Logger . get ( 'PowerSyncDatabase' )
64+ logger : Logger . get ( 'PowerSyncDatabase' ) ,
65+ crudUploadThrottleMs : DEFAULT_CRUD_UPLOAD_THROTTLE_MS
4966} ;
5067
5168/**
@@ -135,6 +152,19 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
135152 this . iterateListeners ( ( cb ) => cb . initialized ?.( ) ) ;
136153 }
137154
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+
138168 /**
139169 * Wait for initialization to complete.
140170 * While initializing is automatic, this helps to catch and report initialization errors.
@@ -163,6 +193,7 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
163193 // Begin network stream
164194 this . syncStreamImplementation . triggerCrudUpload ( ) ;
165195 this . syncStreamImplementation . streamingSync ( this . abortController . signal ) ;
196+ this . watchCrudUploads ( ) ;
166197 }
167198
168199 async disconnect ( ) {
@@ -182,9 +213,9 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
182213
183214 // TODO DB name, verify this is necessary with extension
184215 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` ) ;
188219
189220 const existingTableRows = await tx . execute (
190221 "SELECT name FROM sqlite_master WHERE type='table' AND name GLOB 'ps_data_*'"
@@ -220,12 +251,14 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
220251 async getUploadQueueStats ( includeSize ?: boolean ) : Promise < UploadQueueStats > {
221252 return this . readTransaction ( async ( tx ) => {
222253 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+ ) ;
224257
225258 const row = result . rows . item ( 0 ) ;
226259 return new UploadQueueStats ( row ?. count ?? 0 , row ?. size ?? 0 ) ;
227260 } 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 } ` ) ;
229262 const row = result . rows . item ( 0 ) ;
230263 return new UploadQueueStats ( row ?. count ?? 0 ) ;
231264 }
@@ -250,9 +283,10 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
250283 * and a single transaction may be split over multiple batches.
251284 */
252285 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+ ) ;
256290
257291 const all : CrudEntry [ ] = result . rows ?. _array ?. map ( ( row ) => CrudEntry . fromRow ( row ) ) ?? [ ] ;
258292
@@ -268,11 +302,13 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
268302 const last = all [ all . length - 1 ] ;
269303 return new CrudBatch ( all , haveMore , async ( writeCheckpoint ?: string ) => {
270304 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+ ] ) ;
274310 } 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'` , [
276312 this . bucketStorageAdapter . getMaxOpId ( )
277313 ] ) ;
278314 }
@@ -295,7 +331,7 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
295331 */
296332 async getNextCrudTransaction ( ) : Promise < CrudTransaction > {
297333 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` ) ;
299335
300336 if ( ! first . rows . length ) {
301337 return null ;
@@ -306,7 +342,10 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
306342 if ( ! txId ) {
307343 all = [ CrudEntry . fromRow ( first . rows . item ( 0 ) ) ] ;
308344 } 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+ ) ;
310349 all = result . rows . _array . map ( ( row ) => CrudEntry . fromRow ( row ) ) ;
311350 }
312351
@@ -316,14 +355,16 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
316355 all ,
317356 async ( writeCheckpoint ?: string ) => {
318357 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 ] ) ;
320359 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` ) ;
322361 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+ ] ) ;
324365 }
325366 } 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'` , [
327368 this . bucketStorageAdapter . getMaxOpId ( )
328369 ] ) ;
329370 }
@@ -339,9 +380,7 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
339380 */
340381 async execute ( sql : string , parameters ?: any [ ] ) {
341382 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 ) ;
345384 }
346385
347386 /**
@@ -386,7 +425,6 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
386425 await this . waitForReady ( ) ;
387426 return mutexRunExclusive ( AbstractPowerSyncDatabase . transactionMutex , async ( ) => {
388427 const res = await callback ( this . database ) ;
389- _ . defer ( ( ) => this . syncStreamImplementation ?. triggerCrudUpload ( ) ) ;
390428 return res ;
391429 } ) ;
392430 }
@@ -415,7 +453,6 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
415453 async ( tx ) => {
416454 const res = await callback ( tx ) ;
417455 await tx . commit ( ) ;
418- _ . defer ( ( ) => this . syncStreamImplementation ?. triggerCrudUpload ( ) ) ;
419456 return res ;
420457 } ,
421458 { timeoutMs : lockTimeout }
@@ -475,10 +512,13 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
475512 const dispose = this . database . registerListener ( {
476513 tablesUpdated : async ( update ) => {
477514 const { table } = update ;
478- if ( ! table . match ( POWERSYNC_TABLE_MATCH ) ) {
515+ const { rawTableNames } = options ;
516+
517+ if ( ! rawTableNames && ! table . match ( POWERSYNC_TABLE_MATCH ) ) {
479518 return ;
480519 }
481- const tableName = table . replace ( POWERSYNC_TABLE_MATCH , '' ) ;
520+
521+ const tableName = rawTableNames ? table : table . replace ( POWERSYNC_TABLE_MATCH , '' ) ;
482522 throttledTableUpdates . push ( tableName ) ;
483523
484524 flushTableUpdates ( ) ;
0 commit comments