@powersync/service-core 0.0.0-dev-20241016162519 → 0.0.0-dev-20241018075839
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -8
- package/dist/db/mongo.d.ts +6 -0
- package/dist/db/mongo.js +6 -0
- package/dist/db/mongo.js.map +1 -1
- package/dist/storage/BucketStorage.d.ts +1 -2
- package/dist/storage/BucketStorage.js.map +1 -1
- package/dist/storage/MongoBucketStorage.js +28 -14
- package/dist/storage/MongoBucketStorage.js.map +1 -1
- package/dist/storage/mongo/MongoSyncBucketStorage.d.ts +1 -1
- package/dist/storage/mongo/MongoSyncBucketStorage.js +26 -17
- package/dist/storage/mongo/MongoSyncBucketStorage.js.map +1 -1
- package/dist/storage/mongo/db.d.ts +9 -0
- package/dist/storage/mongo/db.js +11 -0
- package/dist/storage/mongo/db.js.map +1 -1
- package/package.json +5 -5
- package/src/db/mongo.ts +7 -0
- package/src/storage/BucketStorage.ts +1 -3
- package/src/storage/MongoBucketStorage.ts +27 -14
- package/src/storage/mongo/MongoSyncBucketStorage.ts +26 -21
- package/src/storage/mongo/db.ts +12 -0
- package/test/src/data_storage.test.ts +22 -0
- package/test/src/sync.test.ts +2 -16
- package/test/src/util.ts +14 -3
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -31,6 +31,8 @@ import { BucketDataDocument, BucketDataKey, SourceKey, SyncRuleState } from './m
|
|
|
31
31
|
import { MongoBucketBatch } from './MongoBucketBatch.js';
|
|
32
32
|
import { MongoCompactor } from './MongoCompactor.js';
|
|
33
33
|
import { BSON_DESERIALIZE_OPTIONS, idPrefixFilter, mapOpEntry, readSingleBatch, serializeLookup } from './util.js';
|
|
34
|
+
import { logger } from '@powersync/lib-services-framework';
|
|
35
|
+
import * as timers from 'timers/promises';
|
|
34
36
|
|
|
35
37
|
export class MongoSyncBucketStorage
|
|
36
38
|
extends DisposableObserver<SyncRulesBucketStorageListener>
|
|
@@ -459,10 +461,28 @@ export class MongoSyncBucketStorage
|
|
|
459
461
|
}
|
|
460
462
|
|
|
461
463
|
async clear(): Promise<void> {
|
|
464
|
+
while (true) {
|
|
465
|
+
try {
|
|
466
|
+
await this.clearIteration();
|
|
467
|
+
return;
|
|
468
|
+
} catch (e: unknown) {
|
|
469
|
+
if (e instanceof mongo.MongoServerError && e.codeName == 'MaxTimeMSExpired') {
|
|
470
|
+
logger.info(
|
|
471
|
+
`Clearing took longer than ${db.mongo.MONGO_CLEAR_OPERATION_TIMEOUT_MS}ms, waiting and triggering another iteration.`
|
|
472
|
+
);
|
|
473
|
+
await timers.setTimeout(db.mongo.MONGO_CLEAR_OPERATION_TIMEOUT_MS / 5);
|
|
474
|
+
continue;
|
|
475
|
+
} else {
|
|
476
|
+
throw e;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
private async clearIteration(): Promise<void> {
|
|
462
483
|
// Individual operations here may time out with the maxTimeMS option.
|
|
463
484
|
// It is expected to still make progress, and continue on the next try.
|
|
464
485
|
|
|
465
|
-
// TODO: Transactional?
|
|
466
486
|
await this.db.sync_rules.updateOne(
|
|
467
487
|
{
|
|
468
488
|
_id: this.group_id
|
|
@@ -476,48 +496,33 @@ export class MongoSyncBucketStorage
|
|
|
476
496
|
no_checkpoint_before: null
|
|
477
497
|
}
|
|
478
498
|
},
|
|
479
|
-
{ maxTimeMS: db.mongo.
|
|
499
|
+
{ maxTimeMS: db.mongo.MONGO_CLEAR_OPERATION_TIMEOUT_MS }
|
|
480
500
|
);
|
|
481
501
|
await this.db.bucket_data.deleteMany(
|
|
482
502
|
{
|
|
483
503
|
_id: idPrefixFilter<BucketDataKey>({ g: this.group_id }, ['b', 'o'])
|
|
484
504
|
},
|
|
485
|
-
{ maxTimeMS: db.mongo.
|
|
505
|
+
{ maxTimeMS: db.mongo.MONGO_CLEAR_OPERATION_TIMEOUT_MS }
|
|
486
506
|
);
|
|
487
507
|
await this.db.bucket_parameters.deleteMany(
|
|
488
508
|
{
|
|
489
509
|
key: idPrefixFilter<SourceKey>({ g: this.group_id }, ['t', 'k'])
|
|
490
510
|
},
|
|
491
|
-
{ maxTimeMS: db.mongo.
|
|
511
|
+
{ maxTimeMS: db.mongo.MONGO_CLEAR_OPERATION_TIMEOUT_MS }
|
|
492
512
|
);
|
|
493
513
|
|
|
494
514
|
await this.db.current_data.deleteMany(
|
|
495
515
|
{
|
|
496
516
|
_id: idPrefixFilter<SourceKey>({ g: this.group_id }, ['t', 'k'])
|
|
497
517
|
},
|
|
498
|
-
{ maxTimeMS: db.mongo.
|
|
518
|
+
{ maxTimeMS: db.mongo.MONGO_CLEAR_OPERATION_TIMEOUT_MS }
|
|
499
519
|
);
|
|
500
520
|
|
|
501
521
|
await this.db.source_tables.deleteMany(
|
|
502
522
|
{
|
|
503
523
|
group_id: this.group_id
|
|
504
524
|
},
|
|
505
|
-
{ maxTimeMS: db.mongo.
|
|
506
|
-
);
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
async setSnapshotDone(lsn: string): Promise<void> {
|
|
510
|
-
await this.db.sync_rules.updateOne(
|
|
511
|
-
{
|
|
512
|
-
_id: this.group_id
|
|
513
|
-
},
|
|
514
|
-
{
|
|
515
|
-
$set: {
|
|
516
|
-
snapshot_done: true,
|
|
517
|
-
persisted_lsn: lsn,
|
|
518
|
-
last_checkpoint_ts: new Date()
|
|
519
|
-
}
|
|
520
|
-
}
|
|
525
|
+
{ maxTimeMS: db.mongo.MONGO_CLEAR_OPERATION_TIMEOUT_MS }
|
|
521
526
|
);
|
|
522
527
|
}
|
|
523
528
|
|
package/src/storage/mongo/db.ts
CHANGED
|
@@ -62,6 +62,9 @@ export class PowerSyncMongo {
|
|
|
62
62
|
this.locks = this.db.collection('locks');
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Clear all collections.
|
|
67
|
+
*/
|
|
65
68
|
async clear() {
|
|
66
69
|
await this.current_data.deleteMany({});
|
|
67
70
|
await this.bucket_data.deleteMany({});
|
|
@@ -73,4 +76,13 @@ export class PowerSyncMongo {
|
|
|
73
76
|
await this.instance.deleteOne({});
|
|
74
77
|
await this.locks.deleteMany({});
|
|
75
78
|
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Drop the entire database.
|
|
82
|
+
*
|
|
83
|
+
* Primarily for tests.
|
|
84
|
+
*/
|
|
85
|
+
async drop() {
|
|
86
|
+
await this.db.dropDatabase();
|
|
87
|
+
}
|
|
76
88
|
}
|
|
@@ -1438,4 +1438,26 @@ bucket_definitions:
|
|
|
1438
1438
|
expect(errorCaught).true;
|
|
1439
1439
|
expect(isDisposed).true;
|
|
1440
1440
|
});
|
|
1441
|
+
|
|
1442
|
+
test('empty storage metrics', async () => {
|
|
1443
|
+
const f = await factory({ dropAll: true });
|
|
1444
|
+
|
|
1445
|
+
const metrics = await f.getStorageMetrics();
|
|
1446
|
+
expect(metrics).toEqual({
|
|
1447
|
+
operations_size_bytes: 0,
|
|
1448
|
+
parameters_size_bytes: 0,
|
|
1449
|
+
replication_size_bytes: 0
|
|
1450
|
+
});
|
|
1451
|
+
|
|
1452
|
+
const r = await f.configureSyncRules('bucket_definitions: {}');
|
|
1453
|
+
const storage = f.getInstance(r.persisted_sync_rules!);
|
|
1454
|
+
await storage.autoActivate();
|
|
1455
|
+
|
|
1456
|
+
const metrics2 = await f.getStorageMetrics();
|
|
1457
|
+
expect(metrics2).toEqual({
|
|
1458
|
+
operations_size_bytes: 0,
|
|
1459
|
+
parameters_size_bytes: 0,
|
|
1460
|
+
replication_size_bytes: 0
|
|
1461
|
+
});
|
|
1462
|
+
});
|
|
1441
1463
|
}
|
package/test/src/sync.test.ts
CHANGED
|
@@ -5,15 +5,7 @@ import { JSONBig } from '@powersync/service-jsonbig';
|
|
|
5
5
|
import { RequestParameters } from '@powersync/service-sync-rules';
|
|
6
6
|
import * as timers from 'timers/promises';
|
|
7
7
|
import { describe, expect, test } from 'vitest';
|
|
8
|
-
import {
|
|
9
|
-
BATCH_OPTIONS,
|
|
10
|
-
makeTestTable,
|
|
11
|
-
MONGO_STORAGE_FACTORY,
|
|
12
|
-
PARSE_OPTIONS,
|
|
13
|
-
StorageFactory,
|
|
14
|
-
ZERO_LSN
|
|
15
|
-
} from './util.js';
|
|
16
|
-
import { ParseSyncRulesOptions, StartBatchOptions } from '@/storage/BucketStorage.js';
|
|
8
|
+
import { BATCH_OPTIONS, makeTestTable, MONGO_STORAGE_FACTORY, PARSE_OPTIONS, StorageFactory } from './util.js';
|
|
17
9
|
|
|
18
10
|
describe('sync - mongodb', function () {
|
|
19
11
|
defineTests(MONGO_STORAGE_FACTORY);
|
|
@@ -38,8 +30,7 @@ function defineTests(factory: StorageFactory) {
|
|
|
38
30
|
content: BASIC_SYNC_RULES
|
|
39
31
|
});
|
|
40
32
|
|
|
41
|
-
const storage =
|
|
42
|
-
await storage.setSnapshotDone(ZERO_LSN);
|
|
33
|
+
const storage = f.getInstance(syncRules);
|
|
43
34
|
await storage.autoActivate();
|
|
44
35
|
|
|
45
36
|
const result = await storage.startBatch(BATCH_OPTIONS, async (batch) => {
|
|
@@ -91,7 +82,6 @@ function defineTests(factory: StorageFactory) {
|
|
|
91
82
|
});
|
|
92
83
|
|
|
93
84
|
const storage = await f.getInstance(syncRules);
|
|
94
|
-
await storage.setSnapshotDone(ZERO_LSN);
|
|
95
85
|
await storage.autoActivate();
|
|
96
86
|
|
|
97
87
|
const result = await storage.startBatch(BATCH_OPTIONS, async (batch) => {
|
|
@@ -136,7 +126,6 @@ function defineTests(factory: StorageFactory) {
|
|
|
136
126
|
});
|
|
137
127
|
|
|
138
128
|
const storage = await f.getInstance(syncRules);
|
|
139
|
-
await storage.setSnapshotDone(ZERO_LSN);
|
|
140
129
|
await storage.autoActivate();
|
|
141
130
|
|
|
142
131
|
const stream = streamResponse({
|
|
@@ -164,7 +153,6 @@ function defineTests(factory: StorageFactory) {
|
|
|
164
153
|
});
|
|
165
154
|
|
|
166
155
|
const storage = await f.getInstance(syncRules);
|
|
167
|
-
await storage.setSnapshotDone(ZERO_LSN);
|
|
168
156
|
await storage.autoActivate();
|
|
169
157
|
|
|
170
158
|
const stream = streamResponse({
|
|
@@ -226,7 +214,6 @@ function defineTests(factory: StorageFactory) {
|
|
|
226
214
|
});
|
|
227
215
|
|
|
228
216
|
const storage = await f.getInstance(syncRules);
|
|
229
|
-
await storage.setSnapshotDone(ZERO_LSN);
|
|
230
217
|
await storage.autoActivate();
|
|
231
218
|
|
|
232
219
|
const exp = Date.now() / 1000 + 0.1;
|
|
@@ -265,7 +252,6 @@ function defineTests(factory: StorageFactory) {
|
|
|
265
252
|
});
|
|
266
253
|
|
|
267
254
|
const storage = await f.getInstance(syncRules);
|
|
268
|
-
await storage.setSnapshotDone(ZERO_LSN);
|
|
269
255
|
await storage.autoActivate();
|
|
270
256
|
|
|
271
257
|
await storage.startBatch(BATCH_OPTIONS, async (batch) => {
|
package/test/src/util.ts
CHANGED
|
@@ -24,11 +24,22 @@ await Metrics.initialise({
|
|
|
24
24
|
});
|
|
25
25
|
Metrics.getInstance().resetCounters();
|
|
26
26
|
|
|
27
|
-
export
|
|
27
|
+
export interface StorageOptions {
|
|
28
|
+
/**
|
|
29
|
+
* By default, collections are only cleared/
|
|
30
|
+
* Setting this to true will drop the collections completely.
|
|
31
|
+
*/
|
|
32
|
+
dropAll?: boolean;
|
|
33
|
+
}
|
|
34
|
+
export type StorageFactory = (options?: StorageOptions) => Promise<BucketStorageFactory>;
|
|
28
35
|
|
|
29
|
-
export const MONGO_STORAGE_FACTORY: StorageFactory = async () => {
|
|
36
|
+
export const MONGO_STORAGE_FACTORY: StorageFactory = async (options?: StorageOptions) => {
|
|
30
37
|
const db = await connectMongo();
|
|
31
|
-
|
|
38
|
+
if (options?.dropAll) {
|
|
39
|
+
await db.drop();
|
|
40
|
+
} else {
|
|
41
|
+
await db.clear();
|
|
42
|
+
}
|
|
32
43
|
return new MongoBucketStorage(db, { slot_name_prefix: 'test_' });
|
|
33
44
|
};
|
|
34
45
|
|