@powersync/service-module-mongodb-storage 0.12.0 → 0.12.2
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 +20 -0
- package/dist/migrations/db/migrations/1741697235857-bucket-state-index.js +1 -4
- package/dist/migrations/db/migrations/1741697235857-bucket-state-index.js.map +1 -1
- package/dist/storage/MongoBucketStorage.d.ts +3 -2
- package/dist/storage/MongoBucketStorage.js +4 -2
- package/dist/storage/MongoBucketStorage.js.map +1 -1
- package/dist/storage/implementation/MongoChecksums.d.ts +66 -0
- package/dist/storage/implementation/MongoChecksums.js +287 -0
- package/dist/storage/implementation/MongoChecksums.js.map +1 -0
- package/dist/storage/implementation/MongoCompactor.d.ts +9 -2
- package/dist/storage/implementation/MongoCompactor.js +116 -39
- package/dist/storage/implementation/MongoCompactor.js.map +1 -1
- package/dist/storage/implementation/MongoSyncBucketStorage.d.ts +7 -4
- package/dist/storage/implementation/MongoSyncBucketStorage.js +14 -132
- package/dist/storage/implementation/MongoSyncBucketStorage.js.map +1 -1
- package/dist/storage/implementation/MongoTestStorageFactoryGenerator.d.ts +2 -0
- package/dist/storage/implementation/MongoTestStorageFactoryGenerator.js +4 -3
- package/dist/storage/implementation/MongoTestStorageFactoryGenerator.js.map +1 -1
- package/dist/storage/implementation/db.d.ts +4 -0
- package/dist/storage/implementation/db.js +10 -0
- package/dist/storage/implementation/db.js.map +1 -1
- package/dist/storage/implementation/models.d.ts +5 -1
- package/dist/storage/implementation/util.js.map +1 -1
- package/package.json +4 -4
- package/src/migrations/db/migrations/1741697235857-bucket-state-index.ts +1 -7
- package/src/storage/MongoBucketStorage.ts +4 -3
- package/src/storage/implementation/MongoChecksums.ts +342 -0
- package/src/storage/implementation/MongoCompactor.ts +156 -64
- package/src/storage/implementation/MongoSyncBucketStorage.ts +21 -152
- package/src/storage/implementation/MongoTestStorageFactoryGenerator.ts +7 -4
- package/src/storage/implementation/db.ts +14 -0
- package/src/storage/implementation/models.ts +5 -1
- package/src/storage/implementation/util.ts +1 -1
- package/test/src/__snapshots__/storage.test.ts.snap +17 -1
- package/test/src/storage.test.ts +38 -1
- package/test/src/storage_compacting.test.ts +120 -5
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -7,12 +7,13 @@ export const MongoTestStorageFactoryGenerator = (factoryOptions) => {
|
|
|
7
7
|
if (!(await db.db.listCollections({ name: db.bucket_parameters.collectionName }).hasNext())) {
|
|
8
8
|
await db.db.createCollection('bucket_parameters');
|
|
9
9
|
}
|
|
10
|
-
// Full migrations are not currently run for tests, so we manually create this
|
|
11
|
-
await db.createCheckpointEventsCollection();
|
|
12
10
|
if (!options?.doNotClear) {
|
|
13
11
|
await db.clear();
|
|
14
12
|
}
|
|
15
|
-
|
|
13
|
+
// Full migrations are not currently run for tests, so we manually create the important ones
|
|
14
|
+
await db.createCheckpointEventsCollection();
|
|
15
|
+
await db.createBucketStateIndex();
|
|
16
|
+
return new MongoBucketStorage(db, { slot_name_prefix: 'test_' }, factoryOptions.internalOptions);
|
|
16
17
|
};
|
|
17
18
|
};
|
|
18
19
|
//# sourceMappingURL=MongoTestStorageFactoryGenerator.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MongoTestStorageFactoryGenerator.js","sourceRoot":"","sources":["../../../src/storage/implementation/MongoTestStorageFactoryGenerator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"MongoTestStorageFactoryGenerator.js","sourceRoot":"","sources":["../../../src/storage/implementation/MongoTestStorageFactoryGenerator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AASjD,MAAM,CAAC,MAAM,gCAAgC,GAAG,CAAC,cAAuC,EAAE,EAAE;IAC1F,OAAO,KAAK,EAAE,OAA4B,EAAE,EAAE;QAC5C,MAAM,EAAE,GAAG,oBAAoB,CAAC,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC,IAAI,CAAC,CAAC;QAEzE,8EAA8E;QAC9E,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,eAAe,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,iBAAiB,CAAC,cAAc,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;YAC5F,MAAM,EAAE,CAAC,EAAE,CAAC,gBAAgB,CAAC,mBAAmB,CAAC,CAAC;QACpD,CAAC;QAED,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,CAAC;YACzB,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC;QACnB,CAAC;QAED,4FAA4F;QAC5F,MAAM,EAAE,CAAC,gCAAgC,EAAE,CAAC;QAC5C,MAAM,EAAE,CAAC,sBAAsB,EAAE,CAAC;QAElC,OAAO,IAAI,kBAAkB,CAAC,EAAE,EAAE,EAAE,gBAAgB,EAAE,OAAO,EAAE,EAAE,cAAc,CAAC,eAAe,CAAC,CAAC;IACnG,CAAC,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -44,5 +44,9 @@ export declare class PowerSyncMongo {
|
|
|
44
44
|
* Only use in migrations and tests.
|
|
45
45
|
*/
|
|
46
46
|
createCheckpointEventsCollection(): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Only use in migrations and tests.
|
|
49
|
+
*/
|
|
50
|
+
createBucketStateIndex(): Promise<void>;
|
|
47
51
|
}
|
|
48
52
|
export declare function createPowerSyncMongo(config: MongoStorageConfig, options?: lib_mongo.MongoConnectionOptions): PowerSyncMongo;
|
|
@@ -95,6 +95,16 @@ export class PowerSyncMongo {
|
|
|
95
95
|
max: 50 // max number of documents
|
|
96
96
|
});
|
|
97
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* Only use in migrations and tests.
|
|
100
|
+
*/
|
|
101
|
+
async createBucketStateIndex() {
|
|
102
|
+
// TODO: Implement a better mechanism to use migrations in tests
|
|
103
|
+
await this.bucket_state.createIndex({
|
|
104
|
+
'_id.g': 1,
|
|
105
|
+
last_op: 1
|
|
106
|
+
}, { name: 'bucket_updates', unique: true });
|
|
107
|
+
}
|
|
98
108
|
}
|
|
99
109
|
export function createPowerSyncMongo(config, options) {
|
|
100
110
|
return new PowerSyncMongo(lib_mongo.createMongoClient(config, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.js","sourceRoot":"","sources":["../../../src/storage/implementation/db.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../../../src/storage/implementation/db.ts"],"names":[],"mappings":"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"}
|
|
@@ -90,6 +90,10 @@ export interface BucketStateDocument {
|
|
|
90
90
|
g: number;
|
|
91
91
|
b: string;
|
|
92
92
|
};
|
|
93
|
+
/**
|
|
94
|
+
* Important: There is an unique index on {'_id.g': 1, last_op: 1}.
|
|
95
|
+
* That means the last_op must match an actual op in the bucket, and not the commit checkpoint.
|
|
96
|
+
*/
|
|
93
97
|
last_op: bigint;
|
|
94
98
|
/**
|
|
95
99
|
* If set, this can be treated as "cache" of a checksum at a specific point.
|
|
@@ -99,7 +103,7 @@ export interface BucketStateDocument {
|
|
|
99
103
|
op_id: InternalOpId;
|
|
100
104
|
count: number;
|
|
101
105
|
checksum: bigint;
|
|
102
|
-
bytes: number;
|
|
106
|
+
bytes: number | null;
|
|
103
107
|
};
|
|
104
108
|
estimate_since_compact?: {
|
|
105
109
|
count: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.js","sourceRoot":"","sources":["../../../src/storage/implementation/util.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AACjC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACvD,OAAO,
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../../../src/storage/implementation/util.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@powersync/service-module-mongodb-storage",
|
|
3
3
|
"repository": "https://github.com/powersync-ja/powersync-service",
|
|
4
4
|
"types": "dist/index.d.ts",
|
|
5
|
-
"version": "0.12.
|
|
5
|
+
"version": "0.12.2",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"license": "FSL-1.1-ALv2",
|
|
8
8
|
"type": "module",
|
|
@@ -27,15 +27,15 @@
|
|
|
27
27
|
"lru-cache": "^10.2.2",
|
|
28
28
|
"ts-codec": "^1.3.0",
|
|
29
29
|
"uuid": "^11.1.0",
|
|
30
|
-
"@powersync/lib-service-mongodb": "0.6.
|
|
30
|
+
"@powersync/lib-service-mongodb": "0.6.5",
|
|
31
31
|
"@powersync/lib-services-framework": "0.7.3",
|
|
32
|
-
"@powersync/service-core": "1.15.
|
|
32
|
+
"@powersync/service-core": "1.15.2",
|
|
33
33
|
"@powersync/service-jsonbig": "0.17.11",
|
|
34
34
|
"@powersync/service-sync-rules": "0.29.0",
|
|
35
35
|
"@powersync/service-types": "0.13.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@powersync/service-core-tests": "0.12.
|
|
38
|
+
"@powersync/service-core-tests": "0.12.2"
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "tsc -b",
|
|
@@ -11,13 +11,7 @@ export const up: migrations.PowerSyncMigrationFunction = async (context) => {
|
|
|
11
11
|
const db = storage.createPowerSyncMongo(configuration.storage as MongoStorageConfig);
|
|
12
12
|
|
|
13
13
|
try {
|
|
14
|
-
await db.
|
|
15
|
-
{
|
|
16
|
-
'_id.g': 1,
|
|
17
|
-
last_op: 1
|
|
18
|
-
},
|
|
19
|
-
{ name: INDEX_NAME, unique: true }
|
|
20
|
-
);
|
|
14
|
+
await db.createBucketStateIndex();
|
|
21
15
|
} finally {
|
|
22
16
|
await db.client.close();
|
|
23
17
|
}
|
|
@@ -11,7 +11,7 @@ import { mongo } from '@powersync/lib-service-mongodb';
|
|
|
11
11
|
import { PowerSyncMongo } from './implementation/db.js';
|
|
12
12
|
import { SyncRuleDocument } from './implementation/models.js';
|
|
13
13
|
import { MongoPersistedSyncRulesContent } from './implementation/MongoPersistedSyncRulesContent.js';
|
|
14
|
-
import { MongoSyncBucketStorage } from './implementation/MongoSyncBucketStorage.js';
|
|
14
|
+
import { MongoSyncBucketStorage, MongoSyncBucketStorageOptions } from './implementation/MongoSyncBucketStorage.js';
|
|
15
15
|
import { generateSlotName } from './implementation/util.js';
|
|
16
16
|
|
|
17
17
|
export class MongoBucketStorage
|
|
@@ -31,7 +31,8 @@ export class MongoBucketStorage
|
|
|
31
31
|
db: PowerSyncMongo,
|
|
32
32
|
options: {
|
|
33
33
|
slot_name_prefix: string;
|
|
34
|
-
}
|
|
34
|
+
},
|
|
35
|
+
private internalOptions?: MongoSyncBucketStorageOptions
|
|
35
36
|
) {
|
|
36
37
|
super();
|
|
37
38
|
this.client = db.client;
|
|
@@ -49,7 +50,7 @@ export class MongoBucketStorage
|
|
|
49
50
|
if ((typeof id as any) == 'bigint') {
|
|
50
51
|
id = Number(id);
|
|
51
52
|
}
|
|
52
|
-
const storage = new MongoSyncBucketStorage(this, id, syncRules, slot_name);
|
|
53
|
+
const storage = new MongoSyncBucketStorage(this, id, syncRules, slot_name, undefined, this.internalOptions);
|
|
53
54
|
if (!options?.skipLifecycleHooks) {
|
|
54
55
|
this.iterateListeners((cb) => cb.syncStorageCreated?.(storage));
|
|
55
56
|
}
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import * as lib_mongo from '@powersync/lib-service-mongodb';
|
|
2
|
+
import {
|
|
3
|
+
addPartialChecksums,
|
|
4
|
+
bson,
|
|
5
|
+
BucketChecksum,
|
|
6
|
+
ChecksumCache,
|
|
7
|
+
ChecksumMap,
|
|
8
|
+
FetchPartialBucketChecksum,
|
|
9
|
+
InternalOpId,
|
|
10
|
+
isPartialChecksum,
|
|
11
|
+
PartialChecksum,
|
|
12
|
+
PartialChecksumMap,
|
|
13
|
+
PartialOrFullChecksum
|
|
14
|
+
} from '@powersync/service-core';
|
|
15
|
+
import { PowerSyncMongo } from './db.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Checksum calculation options, primarily for tests.
|
|
19
|
+
*/
|
|
20
|
+
export interface MongoChecksumOptions {
|
|
21
|
+
/**
|
|
22
|
+
* How many buckets to process in a batch when calculating checksums.
|
|
23
|
+
*/
|
|
24
|
+
bucketBatchLimit?: number;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Limit on the number of documents to calculate a checksum on at a time.
|
|
28
|
+
*/
|
|
29
|
+
operationBatchLimit?: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const DEFAULT_BUCKET_BATCH_LIMIT = 200;
|
|
33
|
+
const DEFAULT_OPERATION_BATCH_LIMIT = 50_000;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Checksum query implementation.
|
|
37
|
+
*
|
|
38
|
+
* General implementation flow is:
|
|
39
|
+
* 1. getChecksums() -> check cache for (partial) matches. If not found or partial match, query the remainder using computePartialChecksums().
|
|
40
|
+
* 2. computePartialChecksums() -> query bucket_state for partial matches. Query the remainder using computePartialChecksumsDirect().
|
|
41
|
+
* 3. computePartialChecksumsDirect() -> split into batches of 200 buckets at a time -> computePartialChecksumsInternal()
|
|
42
|
+
* 4. computePartialChecksumsInternal() -> aggregate over 50_000 operations in bucket_data at a time
|
|
43
|
+
*/
|
|
44
|
+
export class MongoChecksums {
|
|
45
|
+
private cache = new ChecksumCache({
|
|
46
|
+
fetchChecksums: (batch) => {
|
|
47
|
+
return this.computePartialChecksums(batch);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
constructor(
|
|
52
|
+
private db: PowerSyncMongo,
|
|
53
|
+
private group_id: number,
|
|
54
|
+
private options?: MongoChecksumOptions
|
|
55
|
+
) {}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Calculate checksums, utilizing the cache for partial checkums, and querying the remainder from
|
|
59
|
+
* the database (bucket_state + bucket_data).
|
|
60
|
+
*/
|
|
61
|
+
async getChecksums(checkpoint: InternalOpId, buckets: string[]): Promise<ChecksumMap> {
|
|
62
|
+
return this.cache.getChecksumMap(checkpoint, buckets);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
clearCache() {
|
|
66
|
+
this.cache.clear();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Calculate (partial) checksums from bucket_state (pre-aggregated) and bucket_data (individual operations).
|
|
71
|
+
*
|
|
72
|
+
* Results are not cached here. This method is only called by {@link ChecksumCache.getChecksumMap},
|
|
73
|
+
* which is responsible for caching its result.
|
|
74
|
+
*
|
|
75
|
+
* As long as data is compacted regularly, this should be fast. Large buckets without pre-compacted bucket_state
|
|
76
|
+
* can be slow.
|
|
77
|
+
*/
|
|
78
|
+
private async computePartialChecksums(batch: FetchPartialBucketChecksum[]): Promise<PartialChecksumMap> {
|
|
79
|
+
if (batch.length == 0) {
|
|
80
|
+
return new Map();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const preFilters: any[] = [];
|
|
84
|
+
for (let request of batch) {
|
|
85
|
+
if (request.start == null) {
|
|
86
|
+
preFilters.push({
|
|
87
|
+
_id: {
|
|
88
|
+
g: this.group_id,
|
|
89
|
+
b: request.bucket
|
|
90
|
+
},
|
|
91
|
+
'compacted_state.op_id': { $exists: true, $lte: request.end }
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const preStates = new Map<string, { opId: InternalOpId; checksum: BucketChecksum }>();
|
|
97
|
+
|
|
98
|
+
if (preFilters.length > 0) {
|
|
99
|
+
// For un-cached bucket checksums, attempt to use the compacted state first.
|
|
100
|
+
const states = await this.db.bucket_state
|
|
101
|
+
.find({
|
|
102
|
+
$or: preFilters
|
|
103
|
+
})
|
|
104
|
+
.toArray();
|
|
105
|
+
for (let state of states) {
|
|
106
|
+
const compactedState = state.compacted_state!;
|
|
107
|
+
preStates.set(state._id.b, {
|
|
108
|
+
opId: compactedState.op_id,
|
|
109
|
+
checksum: {
|
|
110
|
+
bucket: state._id.b,
|
|
111
|
+
checksum: Number(compactedState.checksum),
|
|
112
|
+
count: compactedState.count
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const mappedRequests = batch.map((request) => {
|
|
119
|
+
let start = request.start;
|
|
120
|
+
if (start == null) {
|
|
121
|
+
const preState = preStates.get(request.bucket);
|
|
122
|
+
if (preState != null) {
|
|
123
|
+
start = preState.opId;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
...request,
|
|
128
|
+
start
|
|
129
|
+
};
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const queriedChecksums = await this.computePartialChecksumsDirect(mappedRequests);
|
|
133
|
+
|
|
134
|
+
return new Map<string, PartialOrFullChecksum>(
|
|
135
|
+
batch.map((request) => {
|
|
136
|
+
const bucket = request.bucket;
|
|
137
|
+
// Could be null if this is either (1) a partial request, or (2) no compacted checksum was available
|
|
138
|
+
const preState = preStates.get(bucket);
|
|
139
|
+
// Could be null if we got no data
|
|
140
|
+
const partialChecksum = queriedChecksums.get(bucket);
|
|
141
|
+
const merged = addPartialChecksums(bucket, preState?.checksum ?? null, partialChecksum ?? null);
|
|
142
|
+
|
|
143
|
+
return [bucket, merged];
|
|
144
|
+
})
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Calculate (partial) checksums from the data collection directly, bypassing the cache and bucket_state.
|
|
150
|
+
*
|
|
151
|
+
* Can be used directly in cases where the cache should be bypassed, such as from a compact job.
|
|
152
|
+
*
|
|
153
|
+
* Internally, we do calculations in smaller batches of buckets as appropriate.
|
|
154
|
+
*
|
|
155
|
+
* For large buckets, this can be slow, but should not time out as the underlying queries are performed in
|
|
156
|
+
* smaller batches.
|
|
157
|
+
*/
|
|
158
|
+
public async computePartialChecksumsDirect(batch: FetchPartialBucketChecksum[]): Promise<PartialChecksumMap> {
|
|
159
|
+
// Limit the number of buckets we query for at a time.
|
|
160
|
+
const bucketBatchLimit = this.options?.bucketBatchLimit ?? DEFAULT_BUCKET_BATCH_LIMIT;
|
|
161
|
+
|
|
162
|
+
if (batch.length < bucketBatchLimit) {
|
|
163
|
+
// Single batch - no need for splitting the batch and merging results
|
|
164
|
+
return await this.computePartialChecksumsInternal(batch);
|
|
165
|
+
}
|
|
166
|
+
// Split the batch and merge results
|
|
167
|
+
let results = new Map<string, PartialOrFullChecksum>();
|
|
168
|
+
for (let i = 0; i < batch.length; i += bucketBatchLimit) {
|
|
169
|
+
const bucketBatch = batch.slice(i, i + bucketBatchLimit);
|
|
170
|
+
const batchResults = await this.computePartialChecksumsInternal(bucketBatch);
|
|
171
|
+
for (let r of batchResults.values()) {
|
|
172
|
+
results.set(r.bucket, r);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return results;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Query a batch of checksums.
|
|
180
|
+
*
|
|
181
|
+
* We limit the number of operations that the query aggregates in each sub-batch, to avoid potential query timeouts.
|
|
182
|
+
*
|
|
183
|
+
* `batch` must be limited to DEFAULT_BUCKET_BATCH_LIMIT buckets before calling this.
|
|
184
|
+
*/
|
|
185
|
+
private async computePartialChecksumsInternal(batch: FetchPartialBucketChecksum[]): Promise<PartialChecksumMap> {
|
|
186
|
+
const batchLimit = this.options?.operationBatchLimit ?? DEFAULT_OPERATION_BATCH_LIMIT;
|
|
187
|
+
|
|
188
|
+
// Map requests by bucket. We adjust this as we get partial results.
|
|
189
|
+
let requests = new Map<string, FetchPartialBucketChecksum>();
|
|
190
|
+
for (let request of batch) {
|
|
191
|
+
requests.set(request.bucket, request);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const partialChecksums = new Map<string, PartialOrFullChecksum>();
|
|
195
|
+
|
|
196
|
+
while (requests.size > 0) {
|
|
197
|
+
const filters: any[] = [];
|
|
198
|
+
for (let request of requests.values()) {
|
|
199
|
+
filters.push({
|
|
200
|
+
_id: {
|
|
201
|
+
$gt: {
|
|
202
|
+
g: this.group_id,
|
|
203
|
+
b: request.bucket,
|
|
204
|
+
o: request.start ?? new bson.MinKey()
|
|
205
|
+
},
|
|
206
|
+
$lte: {
|
|
207
|
+
g: this.group_id,
|
|
208
|
+
b: request.bucket,
|
|
209
|
+
o: request.end
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Aggregate over a max of `batchLimit` operations at a time.
|
|
216
|
+
// Let's say we have 3 buckets (A, B, C), each with 10 operations, and our batch limit is 12.
|
|
217
|
+
// Then we'll do three batches:
|
|
218
|
+
// 1. Query: A[1-end], B[1-end], C[1-end]
|
|
219
|
+
// Returns: A[1-10], B[1-2]
|
|
220
|
+
// 2. Query: B[3-end], C[1-end]
|
|
221
|
+
// Returns: B[3-10], C[1-4]
|
|
222
|
+
// 3. Query: C[5-end]
|
|
223
|
+
// Returns: C[5-10]
|
|
224
|
+
const aggregate = await this.db.bucket_data
|
|
225
|
+
.aggregate(
|
|
226
|
+
[
|
|
227
|
+
{
|
|
228
|
+
$match: {
|
|
229
|
+
$or: filters
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
// sort and limit _before_ grouping
|
|
233
|
+
{ $sort: { _id: 1 } },
|
|
234
|
+
{ $limit: batchLimit },
|
|
235
|
+
{
|
|
236
|
+
$group: {
|
|
237
|
+
_id: '$_id.b',
|
|
238
|
+
// Historically, checksum may be stored as 'int' or 'double'.
|
|
239
|
+
// More recently, this should be a 'long'.
|
|
240
|
+
// $toLong ensures that we always sum it as a long, avoiding inaccuracies in the calculations.
|
|
241
|
+
checksum_total: { $sum: { $toLong: '$checksum' } },
|
|
242
|
+
count: { $sum: 1 },
|
|
243
|
+
has_clear_op: {
|
|
244
|
+
$max: {
|
|
245
|
+
$cond: [{ $eq: ['$op', 'CLEAR'] }, 1, 0]
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
last_op: { $max: '$_id.o' }
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
],
|
|
252
|
+
{ session: undefined, readConcern: 'snapshot', maxTimeMS: lib_mongo.MONGO_CHECKSUM_TIMEOUT_MS }
|
|
253
|
+
)
|
|
254
|
+
.toArray()
|
|
255
|
+
.catch((e) => {
|
|
256
|
+
throw lib_mongo.mapQueryError(e, 'while reading checksums');
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
let batchCount = 0;
|
|
260
|
+
let limitReached = false;
|
|
261
|
+
for (let doc of aggregate) {
|
|
262
|
+
const bucket = doc._id;
|
|
263
|
+
const checksum = checksumFromAggregate(doc);
|
|
264
|
+
|
|
265
|
+
const existing = partialChecksums.get(bucket);
|
|
266
|
+
if (existing != null) {
|
|
267
|
+
partialChecksums.set(bucket, addPartialChecksums(bucket, existing, checksum));
|
|
268
|
+
} else {
|
|
269
|
+
partialChecksums.set(bucket, checksum);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
batchCount += doc.count;
|
|
273
|
+
if (batchCount == batchLimit) {
|
|
274
|
+
// Limit reached. Request more in the next batch.
|
|
275
|
+
// Note that this only affects the _last_ bucket in a batch.
|
|
276
|
+
limitReached = true;
|
|
277
|
+
const req = requests.get(bucket);
|
|
278
|
+
requests.set(bucket, {
|
|
279
|
+
bucket,
|
|
280
|
+
start: doc.last_op,
|
|
281
|
+
end: req!.end
|
|
282
|
+
});
|
|
283
|
+
} else {
|
|
284
|
+
// All done for this bucket
|
|
285
|
+
requests.delete(bucket);
|
|
286
|
+
}
|
|
287
|
+
batchCount++;
|
|
288
|
+
}
|
|
289
|
+
if (!limitReached) {
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return new Map<string, PartialOrFullChecksum>(
|
|
295
|
+
batch.map((request) => {
|
|
296
|
+
const bucket = request.bucket;
|
|
297
|
+
// Could be null if we got no data
|
|
298
|
+
let partialChecksum = partialChecksums.get(bucket);
|
|
299
|
+
if (partialChecksum == null) {
|
|
300
|
+
partialChecksum = {
|
|
301
|
+
bucket,
|
|
302
|
+
partialCount: 0,
|
|
303
|
+
partialChecksum: 0
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
if (request.start == null && isPartialChecksum(partialChecksum)) {
|
|
307
|
+
partialChecksum = {
|
|
308
|
+
bucket,
|
|
309
|
+
count: partialChecksum.partialCount,
|
|
310
|
+
checksum: partialChecksum.partialChecksum
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return [bucket, partialChecksum];
|
|
315
|
+
})
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Convert output of the $group stage into a checksum.
|
|
322
|
+
*/
|
|
323
|
+
function checksumFromAggregate(doc: bson.Document): PartialOrFullChecksum {
|
|
324
|
+
const partialChecksum = Number(BigInt(doc.checksum_total) & 0xffffffffn) & 0xffffffff;
|
|
325
|
+
const bucket = doc._id;
|
|
326
|
+
|
|
327
|
+
if (doc.has_clear_op == 1) {
|
|
328
|
+
return {
|
|
329
|
+
// full checksum - replaces any previous one
|
|
330
|
+
bucket,
|
|
331
|
+
checksum: partialChecksum,
|
|
332
|
+
count: doc.count
|
|
333
|
+
} satisfies BucketChecksum;
|
|
334
|
+
} else {
|
|
335
|
+
return {
|
|
336
|
+
// partial checksum - is added to a previous one
|
|
337
|
+
bucket,
|
|
338
|
+
partialCount: doc.count,
|
|
339
|
+
partialChecksum
|
|
340
|
+
} satisfies PartialChecksum;
|
|
341
|
+
}
|
|
342
|
+
}
|