diff --git a/modules/module-mongodb-storage/src/storage/implementation/MongoSyncBucketStorage.ts b/modules/module-mongodb-storage/src/storage/implementation/MongoSyncBucketStorage.ts index 6dfdb7e9..603cc20f 100644 --- a/modules/module-mongodb-storage/src/storage/implementation/MongoSyncBucketStorage.ts +++ b/modules/module-mongodb-storage/src/storage/implementation/MongoSyncBucketStorage.ts @@ -9,6 +9,7 @@ import { } from '@powersync/lib-services-framework'; import { BroadcastIterable, + CHECKPOINT_INVALIDATE_ALL, CheckpointChanges, GetCheckpointChangesOptions, getLookupBucketDefinitionName, @@ -884,6 +885,7 @@ export class MongoSyncBucketStorage private async getParameterBucketChanges( options: GetCheckpointChangesOptions ): Promise> { + // TODO: limit max query running time const parameterUpdates = await this.db.bucket_parameters .find( { @@ -921,7 +923,31 @@ export class MongoSyncBucketStorage } }); + private _hasDynamicBucketsCached: boolean | undefined = undefined; + + private hasDynamicBucketQueries(): boolean { + if (this._hasDynamicBucketsCached != null) { + return this._hasDynamicBucketsCached; + } + const syncRules = this.getParsedSyncRules({ + defaultSchema: 'default' // n/a + }); + const hasDynamicBuckets = syncRules.hasDynamicBucketQueries(); + this._hasDynamicBucketsCached = hasDynamicBuckets; + return hasDynamicBuckets; + } + async getCheckpointChanges(options: GetCheckpointChangesOptions): Promise { + if (!this.hasDynamicBucketQueries()) { + // Special case when we have no dynamic parameter queries. + // In this case, we can avoid doing any queries. + return { + invalidateDataBuckets: true, + updatedDataBuckets: [], + invalidateParameterBuckets: false, + updatedParameterBucketDefinitions: [] + }; + } const key = `${options.lastCheckpoint}_${options.nextCheckpoint}`; const result = await this.checkpointChangesCache.fetch(key, { context: { options } }); return result!; diff --git a/packages/sync-rules/src/SqlBucketDescriptor.ts b/packages/sync-rules/src/SqlBucketDescriptor.ts index a0fa59f0..fe715acf 100644 --- a/packages/sync-rules/src/SqlBucketDescriptor.ts +++ b/packages/sync-rules/src/SqlBucketDescriptor.ts @@ -133,6 +133,10 @@ export class SqlBucketDescriptor { return results; } + hasDynamicBucketQueries(): boolean { + return this.parameter_queries.length > 0; + } + getSourceTables(): Set { let result = new Set(); for (let query of this.parameter_queries) { diff --git a/packages/sync-rules/src/SqlSyncRules.ts b/packages/sync-rules/src/SqlSyncRules.ts index 2db268f2..d22be252 100644 --- a/packages/sync-rules/src/SqlSyncRules.ts +++ b/packages/sync-rules/src/SqlSyncRules.ts @@ -326,6 +326,10 @@ export class SqlSyncRules implements SyncRules { return mergeBucketParameterQueriers(queriers); } + hasDynamicBucketQueries() { + return this.bucket_descriptors.some((query) => query.hasDynamicBucketQueries()); + } + getSourceTables(): TablePattern[] { const sourceTables = new Map(); for (const bucket of this.bucket_descriptors) { diff --git a/packages/sync-rules/test/src/sync_rules.test.ts b/packages/sync-rules/test/src/sync_rules.test.ts index 1ffa64cb..243de250 100644 --- a/packages/sync-rules/test/src/sync_rules.test.ts +++ b/packages/sync-rules/test/src/sync_rules.test.ts @@ -37,6 +37,7 @@ bucket_definitions: bucket: 'mybucket[]' } ]); + expect(rules.hasDynamicBucketQueries).toBe(false); expect(rules.getBucketParameterQuerier(normalizeTokenParameters({}))).toMatchObject({ staticBuckets: [{ bucket: 'mybucket[]', priority: 3 }], hasDynamicBuckets: false, @@ -936,6 +937,7 @@ bucket_definitions: ); const bucket = rules.bucket_descriptors[0]; expect(bucket.bucket_parameters).toEqual(['user_id']); + expect(rules.hasDynamicBucketQueries).toBe(true); expect(rules.getBucketParameterQuerier(normalizeTokenParameters({ user_id: 'user1' }))).toMatchObject({ hasDynamicBuckets: true,