@harperfast/harper-pro 5.0.22 → 5.0.24
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/core/DESIGN.md +2 -0
- package/core/bin/copyDb.ts +21 -2
- package/core/package-lock.json +82 -70
- package/core/resources/RecordEncoder.ts +21 -3
- package/core/resources/Table.ts +18 -6
- package/core/resources/blob.ts +18 -4
- package/core/resources/databases.ts +18 -0
- package/core/utility/logging/logRotator.js +3 -4
- package/dist/core/bin/copyDb.js +22 -1
- package/dist/core/bin/copyDb.js.map +1 -1
- package/dist/core/resources/RecordEncoder.js +14 -2
- package/dist/core/resources/RecordEncoder.js.map +1 -1
- package/dist/core/resources/Table.js +19 -6
- package/dist/core/resources/Table.js.map +1 -1
- package/dist/core/resources/blob.js +19 -4
- package/dist/core/resources/blob.js.map +1 -1
- package/dist/core/resources/databases.js +20 -0
- package/dist/core/resources/databases.js.map +1 -1
- package/dist/core/utility/logging/logRotator.js +3 -3
- package/dist/core/utility/logging/logRotator.js.map +1 -1
- package/npm-shrinkwrap.json +82 -70
- package/package.json +3 -3
- package/studio/web/assets/{index-CKW3SZJG.js → index-C5Er24-c.js} +16 -16
- package/studio/web/assets/index-C5Er24-c.js.map +1 -0
- package/studio/web/assets/index-j0M5ZXh5.css +1 -0
- package/studio/web/assets/{index.lazy-D9dMKjQH.js → index.lazy-C5LLrJkX.js} +2 -2
- package/studio/web/assets/{index.lazy-D9dMKjQH.js.map → index.lazy-C5LLrJkX.js.map} +1 -1
- package/studio/web/assets/{profile-DAsdweRg.js → profile-DdGCk1RA.js} +2 -2
- package/studio/web/assets/{profile-DAsdweRg.js.map → profile-DdGCk1RA.js.map} +1 -1
- package/studio/web/assets/{status-KVqwJsbk.js → status-t7ZjUhlN.js} +2 -2
- package/studio/web/assets/{status-KVqwJsbk.js.map → status-t7ZjUhlN.js.map} +1 -1
- package/studio/web/index.html +2 -2
- package/studio/web/assets/index-BGocx0nU.css +0 -1
- package/studio/web/assets/index-CKW3SZJG.js.map +0 -1
package/core/DESIGN.md
CHANGED
|
@@ -38,6 +38,8 @@ When `migrateOnStart` opens a source LMDB primary store to read records out for
|
|
|
38
38
|
|
|
39
39
|
Harper's normal `databases.ts` path already does this (search for `dbiInit.compression = primaryKeyAttribute.compression`); the migration path in `bin/copyDb.ts` has to match.
|
|
40
40
|
|
|
41
|
+
The same source-dbi open has a second non-obvious requirement: assign `sourceDbi.encoder.rootStore = sourceRootStore` for the primary store. The primary dbi decodes through a `RecordEncoder`, and decoding a record that holds a file-backed blob reference resolves that reference against `rootStore` (it locates the blob file). With `rootStore` unset, the `Blob` msgpackr extension throws `No store specified, cannot load blob from storage`; `RecordEncoder.decode` swallows the error and yields `null`, and `copyDbToRocks` then skips the `null` value — so every record with a file-backed blob is silently dropped from the migration. The runtime path gets `rootStore` from `handleLocalTimeForGets`; the migration path opens the source dbi raw and must set it explicitly (issue #857).
|
|
42
|
+
|
|
41
43
|
## Schema migration and `runIndexing` internals (`databases.ts`)
|
|
42
44
|
|
|
43
45
|
When `table()` is called with an attribute newly marked `indexed: true` (or with any change that requires re-building the secondary index), `runIndexing` is launched asynchronously and `Table.indexingOperation` is set to its promise. While running:
|
package/core/bin/copyDb.ts
CHANGED
|
@@ -376,7 +376,7 @@ export async function migrateOnStart() {
|
|
|
376
376
|
}
|
|
377
377
|
}
|
|
378
378
|
|
|
379
|
-
async function copyDbToRocks(sourceRootStore, sourceDatabase: string, targetPath: string) {
|
|
379
|
+
export async function copyDbToRocks(sourceRootStore, sourceDatabase: string, targetPath: string) {
|
|
380
380
|
console.log(`Migrating database ${sourceDatabase} to RocksDB at ${targetPath}`);
|
|
381
381
|
const sourceDbisDb = sourceRootStore.dbisDb;
|
|
382
382
|
|
|
@@ -413,6 +413,11 @@ async function copyDbToRocks(sourceRootStore, sourceDatabase: string, targetPath
|
|
|
413
413
|
const dbiInit = new OpenDBIObject(!isPrimary, isPrimary);
|
|
414
414
|
dbiInit.compression = attribute.compression;
|
|
415
415
|
const sourceDbi = sourceRootStore.openDB(key, dbiInit);
|
|
416
|
+
// The primary dbi uses a RecordEncoder, whose decode resolves file-backed blob references
|
|
417
|
+
// against `rootStore`. Without it, decoding any record that holds a blob throws "No store
|
|
418
|
+
// specified, cannot load blob from storage", the error is swallowed (record decodes to null),
|
|
419
|
+
// and the record is silently dropped from the migration (HarperFast/harper#857).
|
|
420
|
+
if (isPrimary && sourceDbi.encoder) sourceDbi.encoder.rootStore = sourceRootStore;
|
|
416
421
|
|
|
417
422
|
let targetDbi;
|
|
418
423
|
if (!isPrimary) {
|
|
@@ -425,9 +430,23 @@ async function copyDbToRocks(sourceRootStore, sourceDatabase: string, targetPath
|
|
|
425
430
|
existingEncoder.isRocksDB = true;
|
|
426
431
|
existingEncoder.rootStore = targetRootStore;
|
|
427
432
|
const tempEncoder = new RecordEncoder({ name: key }) as any;
|
|
433
|
+
// msgpackr's pack closure captures `packr = this` at construction, so during
|
|
434
|
+
// re-encoding the structure callbacks resolve to tempEncoder's getStructures/
|
|
435
|
+
// saveStructures (invoked with this === tempEncoder), not existingEncoder's.
|
|
436
|
+
// tempEncoder must therefore carry the RocksDB wiring too, or getStructures hits
|
|
437
|
+
// the non-RocksDB branch where the captured super is undefined and throws.
|
|
438
|
+
tempEncoder.name = key;
|
|
439
|
+
tempEncoder.isRocksDB = true;
|
|
440
|
+
tempEncoder.rootStore = targetRootStore;
|
|
428
441
|
existingEncoder.encode = tempEncoder.encode;
|
|
429
|
-
existingEncoder.saveStructures = tempEncoder.saveStructures;
|
|
430
442
|
existingEncoder.getStructures = tempEncoder.getStructures;
|
|
443
|
+
// The shared structures dictionary is copied verbatim from the source by
|
|
444
|
+
// copyStructures() below, so re-encoding never needs to persist new structures.
|
|
445
|
+
// A no-op saveStructures avoids opening a targetRootStore.transactionSync() in the
|
|
446
|
+
// middle of each record's encode, which otherwise discards the targetDbi record writes.
|
|
447
|
+
const noopSaveStructures = () => true;
|
|
448
|
+
existingEncoder.saveStructures = noopSaveStructures;
|
|
449
|
+
tempEncoder.saveStructures = noopSaveStructures;
|
|
431
450
|
}
|
|
432
451
|
|
|
433
452
|
copyStructures(sourceDbi, key);
|
package/core/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "harper",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.24",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "harper",
|
|
9
|
-
"version": "5.0.
|
|
9
|
+
"version": "5.0.24",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@aws-sdk/client-s3": "^3.1012.0",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"@fastify/cors": "^11.2.0",
|
|
19
19
|
"@fastify/static": "^9.1.3",
|
|
20
20
|
"@harperfast/extended-iterable": "^1.0.1",
|
|
21
|
-
"@harperfast/rocksdb-js": "^1.
|
|
21
|
+
"@harperfast/rocksdb-js": "^1.4.0",
|
|
22
22
|
"@turf/area": "6.5.0",
|
|
23
23
|
"@turf/boolean-contains": "6.5.0",
|
|
24
24
|
"@turf/boolean-disjoint": "6.5.0",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"json-bigint-fixes": "1.1.0",
|
|
54
54
|
"jsonata": "1.8.7",
|
|
55
55
|
"jsonwebtoken": "9.0.3",
|
|
56
|
-
"lmdb": "3.5.
|
|
56
|
+
"lmdb": "3.5.5",
|
|
57
57
|
"lodash": "^4.17.23",
|
|
58
58
|
"mathjs": "11.12.0",
|
|
59
59
|
"micromatch": "^4.0.8",
|
|
@@ -2218,9 +2218,9 @@
|
|
|
2218
2218
|
"license": "Apache-2.0"
|
|
2219
2219
|
},
|
|
2220
2220
|
"node_modules/@harperfast/rocksdb-js": {
|
|
2221
|
-
"version": "1.
|
|
2222
|
-
"resolved": "https://registry.npmjs.org/@harperfast/rocksdb-js/-/rocksdb-js-1.
|
|
2223
|
-
"integrity": "sha512-
|
|
2221
|
+
"version": "1.4.0",
|
|
2222
|
+
"resolved": "https://registry.npmjs.org/@harperfast/rocksdb-js/-/rocksdb-js-1.4.0.tgz",
|
|
2223
|
+
"integrity": "sha512-Qa62jWYG5GO+OI3gEHJI3WyhzgA6OdFLthbmB7TRJJMa2Zu8s3psKuAnM0t2v963ohRFF4UbyRw24zt5PX3k3A==",
|
|
2224
2224
|
"license": "Apache-2.0",
|
|
2225
2225
|
"dependencies": {
|
|
2226
2226
|
"@harperfast/extended-iterable": "1.0.3",
|
|
@@ -2234,20 +2234,20 @@
|
|
|
2234
2234
|
"node": ">=18"
|
|
2235
2235
|
},
|
|
2236
2236
|
"optionalDependencies": {
|
|
2237
|
-
"@harperfast/rocksdb-js-darwin-arm64": "1.
|
|
2238
|
-
"@harperfast/rocksdb-js-darwin-x64": "1.
|
|
2239
|
-
"@harperfast/rocksdb-js-linux-arm64-glibc": "1.
|
|
2240
|
-
"@harperfast/rocksdb-js-linux-arm64-musl": "1.
|
|
2241
|
-
"@harperfast/rocksdb-js-linux-x64-glibc": "1.
|
|
2242
|
-
"@harperfast/rocksdb-js-linux-x64-musl": "1.
|
|
2243
|
-
"@harperfast/rocksdb-js-win32-arm64": "1.
|
|
2244
|
-
"@harperfast/rocksdb-js-win32-x64": "1.
|
|
2237
|
+
"@harperfast/rocksdb-js-darwin-arm64": "1.4.0",
|
|
2238
|
+
"@harperfast/rocksdb-js-darwin-x64": "1.4.0",
|
|
2239
|
+
"@harperfast/rocksdb-js-linux-arm64-glibc": "1.4.0",
|
|
2240
|
+
"@harperfast/rocksdb-js-linux-arm64-musl": "1.4.0",
|
|
2241
|
+
"@harperfast/rocksdb-js-linux-x64-glibc": "1.4.0",
|
|
2242
|
+
"@harperfast/rocksdb-js-linux-x64-musl": "1.4.0",
|
|
2243
|
+
"@harperfast/rocksdb-js-win32-arm64": "1.4.0",
|
|
2244
|
+
"@harperfast/rocksdb-js-win32-x64": "1.4.0"
|
|
2245
2245
|
}
|
|
2246
2246
|
},
|
|
2247
2247
|
"node_modules/@harperfast/rocksdb-js-darwin-arm64": {
|
|
2248
|
-
"version": "1.
|
|
2249
|
-
"resolved": "https://registry.npmjs.org/@harperfast/rocksdb-js-darwin-arm64/-/rocksdb-js-darwin-arm64-1.
|
|
2250
|
-
"integrity": "sha512
|
|
2248
|
+
"version": "1.4.0",
|
|
2249
|
+
"resolved": "https://registry.npmjs.org/@harperfast/rocksdb-js-darwin-arm64/-/rocksdb-js-darwin-arm64-1.4.0.tgz",
|
|
2250
|
+
"integrity": "sha512-78nT13T3P0Am0UfppPkMgUcTqS5Xx2ftXGl9IsmjWfrDawM1HOJt00n0GtPLoqOqrs80QVGbH9qgBZ22Bpty6Q==",
|
|
2251
2251
|
"cpu": [
|
|
2252
2252
|
"arm64"
|
|
2253
2253
|
],
|
|
@@ -2261,9 +2261,9 @@
|
|
|
2261
2261
|
}
|
|
2262
2262
|
},
|
|
2263
2263
|
"node_modules/@harperfast/rocksdb-js-darwin-x64": {
|
|
2264
|
-
"version": "1.
|
|
2265
|
-
"resolved": "https://registry.npmjs.org/@harperfast/rocksdb-js-darwin-x64/-/rocksdb-js-darwin-x64-1.
|
|
2266
|
-
"integrity": "sha512-
|
|
2264
|
+
"version": "1.4.0",
|
|
2265
|
+
"resolved": "https://registry.npmjs.org/@harperfast/rocksdb-js-darwin-x64/-/rocksdb-js-darwin-x64-1.4.0.tgz",
|
|
2266
|
+
"integrity": "sha512-Ck+m4bfaiGikq1I0IAHS8AELnRL+GeKB99hR8ASR7DRAvQqYGYbPtUQQUKIQk9/8S8LviKUfto/cla1nRAcslg==",
|
|
2267
2267
|
"cpu": [
|
|
2268
2268
|
"x64"
|
|
2269
2269
|
],
|
|
@@ -2277,12 +2277,15 @@
|
|
|
2277
2277
|
}
|
|
2278
2278
|
},
|
|
2279
2279
|
"node_modules/@harperfast/rocksdb-js-linux-arm64-glibc": {
|
|
2280
|
-
"version": "1.
|
|
2281
|
-
"resolved": "https://registry.npmjs.org/@harperfast/rocksdb-js-linux-arm64-glibc/-/rocksdb-js-linux-arm64-glibc-1.
|
|
2282
|
-
"integrity": "sha512-
|
|
2280
|
+
"version": "1.4.0",
|
|
2281
|
+
"resolved": "https://registry.npmjs.org/@harperfast/rocksdb-js-linux-arm64-glibc/-/rocksdb-js-linux-arm64-glibc-1.4.0.tgz",
|
|
2282
|
+
"integrity": "sha512-1yJkGMYR5Zh45j6joCu0cgQ+LPlsU0hjiPfv0f0cmXdjPiccXu7TemADjf3fnLz6p4p2VLP46s1fwJL7W+4lFw==",
|
|
2283
2283
|
"cpu": [
|
|
2284
2284
|
"arm64"
|
|
2285
2285
|
],
|
|
2286
|
+
"libc": [
|
|
2287
|
+
"glibc"
|
|
2288
|
+
],
|
|
2286
2289
|
"license": "Apache-2.0",
|
|
2287
2290
|
"optional": true,
|
|
2288
2291
|
"os": [
|
|
@@ -2293,12 +2296,15 @@
|
|
|
2293
2296
|
}
|
|
2294
2297
|
},
|
|
2295
2298
|
"node_modules/@harperfast/rocksdb-js-linux-arm64-musl": {
|
|
2296
|
-
"version": "1.
|
|
2297
|
-
"resolved": "https://registry.npmjs.org/@harperfast/rocksdb-js-linux-arm64-musl/-/rocksdb-js-linux-arm64-musl-1.
|
|
2298
|
-
"integrity": "sha512-
|
|
2299
|
+
"version": "1.4.0",
|
|
2300
|
+
"resolved": "https://registry.npmjs.org/@harperfast/rocksdb-js-linux-arm64-musl/-/rocksdb-js-linux-arm64-musl-1.4.0.tgz",
|
|
2301
|
+
"integrity": "sha512-wIUqCl95Fs7WL0Ecm2j6Lu9TAFMbeDageMn2MfzqTtjx6fcQRL9dPH9o8HfIOfQ6Q0T0HTPpRwBTAtryYLmcFA==",
|
|
2299
2302
|
"cpu": [
|
|
2300
2303
|
"arm64"
|
|
2301
2304
|
],
|
|
2305
|
+
"libc": [
|
|
2306
|
+
"musl"
|
|
2307
|
+
],
|
|
2302
2308
|
"license": "Apache-2.0",
|
|
2303
2309
|
"optional": true,
|
|
2304
2310
|
"os": [
|
|
@@ -2309,12 +2315,15 @@
|
|
|
2309
2315
|
}
|
|
2310
2316
|
},
|
|
2311
2317
|
"node_modules/@harperfast/rocksdb-js-linux-x64-glibc": {
|
|
2312
|
-
"version": "1.
|
|
2313
|
-
"resolved": "https://registry.npmjs.org/@harperfast/rocksdb-js-linux-x64-glibc/-/rocksdb-js-linux-x64-glibc-1.
|
|
2314
|
-
"integrity": "sha512-
|
|
2318
|
+
"version": "1.4.0",
|
|
2319
|
+
"resolved": "https://registry.npmjs.org/@harperfast/rocksdb-js-linux-x64-glibc/-/rocksdb-js-linux-x64-glibc-1.4.0.tgz",
|
|
2320
|
+
"integrity": "sha512-J8anAA31Dv6+h+Uu/mDlszQOoEZiTOGqrLVbWQ7/W5cET2pKZ9IC6NJjvG3rjkkFT82G+spqJ+aESY2yuEJv5Q==",
|
|
2315
2321
|
"cpu": [
|
|
2316
2322
|
"x64"
|
|
2317
2323
|
],
|
|
2324
|
+
"libc": [
|
|
2325
|
+
"glibc"
|
|
2326
|
+
],
|
|
2318
2327
|
"license": "Apache-2.0",
|
|
2319
2328
|
"optional": true,
|
|
2320
2329
|
"os": [
|
|
@@ -2325,12 +2334,15 @@
|
|
|
2325
2334
|
}
|
|
2326
2335
|
},
|
|
2327
2336
|
"node_modules/@harperfast/rocksdb-js-linux-x64-musl": {
|
|
2328
|
-
"version": "1.
|
|
2329
|
-
"resolved": "https://registry.npmjs.org/@harperfast/rocksdb-js-linux-x64-musl/-/rocksdb-js-linux-x64-musl-1.
|
|
2330
|
-
"integrity": "sha512-
|
|
2337
|
+
"version": "1.4.0",
|
|
2338
|
+
"resolved": "https://registry.npmjs.org/@harperfast/rocksdb-js-linux-x64-musl/-/rocksdb-js-linux-x64-musl-1.4.0.tgz",
|
|
2339
|
+
"integrity": "sha512-bHJvxKlx/UqeG++aLpEcm6DDzNeGvDWvBZ+LKstVmF4mNZnR/QZ8TX9dYEGHFHMB+/PVrotRzUaMA5jgjfaMFQ==",
|
|
2331
2340
|
"cpu": [
|
|
2332
2341
|
"x64"
|
|
2333
2342
|
],
|
|
2343
|
+
"libc": [
|
|
2344
|
+
"musl"
|
|
2345
|
+
],
|
|
2334
2346
|
"license": "Apache-2.0",
|
|
2335
2347
|
"optional": true,
|
|
2336
2348
|
"os": [
|
|
@@ -2341,9 +2353,9 @@
|
|
|
2341
2353
|
}
|
|
2342
2354
|
},
|
|
2343
2355
|
"node_modules/@harperfast/rocksdb-js-win32-arm64": {
|
|
2344
|
-
"version": "1.
|
|
2345
|
-
"resolved": "https://registry.npmjs.org/@harperfast/rocksdb-js-win32-arm64/-/rocksdb-js-win32-arm64-1.
|
|
2346
|
-
"integrity": "sha512-
|
|
2356
|
+
"version": "1.4.0",
|
|
2357
|
+
"resolved": "https://registry.npmjs.org/@harperfast/rocksdb-js-win32-arm64/-/rocksdb-js-win32-arm64-1.4.0.tgz",
|
|
2358
|
+
"integrity": "sha512-r6F+ZQWZmppu4mkuZwlyYDaXWXZ/AQldP2qdIRhICxXNfxcvQUxW0+CKmfnor3X3GNk46ThIaP7tM08fu7/Zag==",
|
|
2347
2359
|
"cpu": [
|
|
2348
2360
|
"arm64"
|
|
2349
2361
|
],
|
|
@@ -2357,9 +2369,9 @@
|
|
|
2357
2369
|
}
|
|
2358
2370
|
},
|
|
2359
2371
|
"node_modules/@harperfast/rocksdb-js-win32-x64": {
|
|
2360
|
-
"version": "1.
|
|
2361
|
-
"resolved": "https://registry.npmjs.org/@harperfast/rocksdb-js-win32-x64/-/rocksdb-js-win32-x64-1.
|
|
2362
|
-
"integrity": "sha512-
|
|
2372
|
+
"version": "1.4.0",
|
|
2373
|
+
"resolved": "https://registry.npmjs.org/@harperfast/rocksdb-js-win32-x64/-/rocksdb-js-win32-x64-1.4.0.tgz",
|
|
2374
|
+
"integrity": "sha512-msYlg3D2sm33sw7Uyiuxw04Jt8mrzcKOhjHFZbGAM9bqlUE0lar3txcO03oHwcKYVyoIEsnYgyddQFHdRLAU+A==",
|
|
2363
2375
|
"cpu": [
|
|
2364
2376
|
"x64"
|
|
2365
2377
|
],
|
|
@@ -2607,9 +2619,9 @@
|
|
|
2607
2619
|
}
|
|
2608
2620
|
},
|
|
2609
2621
|
"node_modules/@lmdb/lmdb-darwin-arm64": {
|
|
2610
|
-
"version": "3.5.
|
|
2611
|
-
"resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.5.
|
|
2612
|
-
"integrity": "sha512-
|
|
2622
|
+
"version": "3.5.5",
|
|
2623
|
+
"resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.5.5.tgz",
|
|
2624
|
+
"integrity": "sha512-hcSIUgSblRtwEuPzV/88cTbEPxaWfxVSrTaMIVUreyAA02ysE3S9tsiQdmT7KNmUaXlwgRGs+x+GaVDCuk7Prg==",
|
|
2613
2625
|
"cpu": [
|
|
2614
2626
|
"arm64"
|
|
2615
2627
|
],
|
|
@@ -2620,9 +2632,9 @@
|
|
|
2620
2632
|
]
|
|
2621
2633
|
},
|
|
2622
2634
|
"node_modules/@lmdb/lmdb-darwin-x64": {
|
|
2623
|
-
"version": "3.5.
|
|
2624
|
-
"resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-3.5.
|
|
2625
|
-
"integrity": "sha512-
|
|
2635
|
+
"version": "3.5.5",
|
|
2636
|
+
"resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-3.5.5.tgz",
|
|
2637
|
+
"integrity": "sha512-cLlp9ypk3W9Hr4fuTYwg1kL2vPfz887v9NXiHQE1raSKtJ8/hnghekqUu62wUQdQPgThLV8Om+Ran08wWuM/3w==",
|
|
2626
2638
|
"cpu": [
|
|
2627
2639
|
"x64"
|
|
2628
2640
|
],
|
|
@@ -2633,9 +2645,9 @@
|
|
|
2633
2645
|
]
|
|
2634
2646
|
},
|
|
2635
2647
|
"node_modules/@lmdb/lmdb-linux-arm": {
|
|
2636
|
-
"version": "3.5.
|
|
2637
|
-
"resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-3.5.
|
|
2638
|
-
"integrity": "sha512-
|
|
2648
|
+
"version": "3.5.5",
|
|
2649
|
+
"resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-3.5.5.tgz",
|
|
2650
|
+
"integrity": "sha512-eDeK9vZww0dIXlRCZa7pvbEXSUpUNIJKUs13FxDzazL8CRLDLggbeirm5m0hZvJcRgZzTYGESE8UUEBqHcdZ9Q==",
|
|
2639
2651
|
"cpu": [
|
|
2640
2652
|
"arm"
|
|
2641
2653
|
],
|
|
@@ -2646,9 +2658,9 @@
|
|
|
2646
2658
|
]
|
|
2647
2659
|
},
|
|
2648
2660
|
"node_modules/@lmdb/lmdb-linux-arm64": {
|
|
2649
|
-
"version": "3.5.
|
|
2650
|
-
"resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-3.5.
|
|
2651
|
-
"integrity": "sha512-
|
|
2661
|
+
"version": "3.5.5",
|
|
2662
|
+
"resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-3.5.5.tgz",
|
|
2663
|
+
"integrity": "sha512-tn8KSYdvX5sietpuGyJJSXocvQQtWlenzquap/Mzr+wWx5jrjK5I3nEAIMFxvTtuMMrpPTGHGNuE/Hanh3o0EQ==",
|
|
2652
2664
|
"cpu": [
|
|
2653
2665
|
"arm64"
|
|
2654
2666
|
],
|
|
@@ -2659,9 +2671,9 @@
|
|
|
2659
2671
|
]
|
|
2660
2672
|
},
|
|
2661
2673
|
"node_modules/@lmdb/lmdb-linux-x64": {
|
|
2662
|
-
"version": "3.5.
|
|
2663
|
-
"resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-3.5.
|
|
2664
|
-
"integrity": "sha512-
|
|
2674
|
+
"version": "3.5.5",
|
|
2675
|
+
"resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-3.5.5.tgz",
|
|
2676
|
+
"integrity": "sha512-o6VRcJjZ6Zc67MnymLEa3AtbYSreFzm6Um6LMHh4lkynlE5Qk/XUHXU2beTZBdB3QuZBiMoXO2gjPSgP0tYUKg==",
|
|
2665
2677
|
"cpu": [
|
|
2666
2678
|
"x64"
|
|
2667
2679
|
],
|
|
@@ -2672,9 +2684,9 @@
|
|
|
2672
2684
|
]
|
|
2673
2685
|
},
|
|
2674
2686
|
"node_modules/@lmdb/lmdb-win32-arm64": {
|
|
2675
|
-
"version": "3.5.
|
|
2676
|
-
"resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-arm64/-/lmdb-win32-arm64-3.5.
|
|
2677
|
-
"integrity": "sha512-
|
|
2687
|
+
"version": "3.5.5",
|
|
2688
|
+
"resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-arm64/-/lmdb-win32-arm64-3.5.5.tgz",
|
|
2689
|
+
"integrity": "sha512-k0k/CwywhymTAJT7Uj5PdehH31Vem2ov/Jp0rn5McuJEHgzj7vbE0cmnByhjrQv+VeHLALI8wUZNicqwocPB4Q==",
|
|
2678
2690
|
"cpu": [
|
|
2679
2691
|
"arm64"
|
|
2680
2692
|
],
|
|
@@ -2685,9 +2697,9 @@
|
|
|
2685
2697
|
]
|
|
2686
2698
|
},
|
|
2687
2699
|
"node_modules/@lmdb/lmdb-win32-x64": {
|
|
2688
|
-
"version": "3.5.
|
|
2689
|
-
"resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-3.5.
|
|
2690
|
-
"integrity": "sha512-
|
|
2700
|
+
"version": "3.5.5",
|
|
2701
|
+
"resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-3.5.5.tgz",
|
|
2702
|
+
"integrity": "sha512-dkV3r04ro8MfhBaPj4ZsARy0LIP3sK+rm2LAuVe2QLCepBIpSzRx8p8RLWfRfkKV5YNyJ3c0NUJxGNMqtTB6Fg==",
|
|
2691
2703
|
"cpu": [
|
|
2692
2704
|
"x64"
|
|
2693
2705
|
],
|
|
@@ -7535,9 +7547,9 @@
|
|
|
7535
7547
|
"license": "MIT"
|
|
7536
7548
|
},
|
|
7537
7549
|
"node_modules/lmdb": {
|
|
7538
|
-
"version": "3.5.
|
|
7539
|
-
"resolved": "https://registry.npmjs.org/lmdb/-/lmdb-3.5.
|
|
7540
|
-
"integrity": "sha512-
|
|
7550
|
+
"version": "3.5.5",
|
|
7551
|
+
"resolved": "https://registry.npmjs.org/lmdb/-/lmdb-3.5.5.tgz",
|
|
7552
|
+
"integrity": "sha512-e2q1RtHdJoDaQQNMJ55wJnD5BqpvzoJIEL3NBxfZXNBjYxY0QjCh3RSL/sqmi+y9l97cERdO4WwGFfYYVft1fA==",
|
|
7541
7553
|
"hasInstallScript": true,
|
|
7542
7554
|
"license": "MIT",
|
|
7543
7555
|
"dependencies": {
|
|
@@ -7552,13 +7564,13 @@
|
|
|
7552
7564
|
"download-lmdb-prebuilds": "bin/download-prebuilds.js"
|
|
7553
7565
|
},
|
|
7554
7566
|
"optionalDependencies": {
|
|
7555
|
-
"@lmdb/lmdb-darwin-arm64": "3.5.
|
|
7556
|
-
"@lmdb/lmdb-darwin-x64": "3.5.
|
|
7557
|
-
"@lmdb/lmdb-linux-arm": "3.5.
|
|
7558
|
-
"@lmdb/lmdb-linux-arm64": "3.5.
|
|
7559
|
-
"@lmdb/lmdb-linux-x64": "3.5.
|
|
7560
|
-
"@lmdb/lmdb-win32-arm64": "3.5.
|
|
7561
|
-
"@lmdb/lmdb-win32-x64": "3.5.
|
|
7567
|
+
"@lmdb/lmdb-darwin-arm64": "3.5.5",
|
|
7568
|
+
"@lmdb/lmdb-darwin-x64": "3.5.5",
|
|
7569
|
+
"@lmdb/lmdb-linux-arm": "3.5.5",
|
|
7570
|
+
"@lmdb/lmdb-linux-arm64": "3.5.5",
|
|
7571
|
+
"@lmdb/lmdb-linux-x64": "3.5.5",
|
|
7572
|
+
"@lmdb/lmdb-win32-arm64": "3.5.5",
|
|
7573
|
+
"@lmdb/lmdb-win32-x64": "3.5.5"
|
|
7562
7574
|
}
|
|
7563
7575
|
},
|
|
7564
7576
|
"node_modules/lmdb/node_modules/node-addon-api": {
|
|
@@ -17,7 +17,14 @@ import {
|
|
|
17
17
|
} from './auditStore.ts';
|
|
18
18
|
import * as harperLogger from '../utility/logging/harper_logger.js';
|
|
19
19
|
import './blob.ts';
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
blobsWereEncoded,
|
|
22
|
+
decodeFromDatabase,
|
|
23
|
+
deleteBlobsInObject,
|
|
24
|
+
encodeBlobsWithFilePath,
|
|
25
|
+
findBlobsInObject,
|
|
26
|
+
getFileId,
|
|
27
|
+
} from './blob.ts';
|
|
21
28
|
import { getThisNodeId } from './nodeIdMapping.ts';
|
|
22
29
|
import { recordAction } from './analytics/write.ts';
|
|
23
30
|
import { RocksDatabase } from '@harperfast/rocksdb-js';
|
|
@@ -606,8 +613,19 @@ export function recordUpdater(store, tableId, auditStore) {
|
|
|
606
613
|
// we use resolveRecord outside of transaction, so must explicitly make it conditional
|
|
607
614
|
if (resolveRecord) putOptions.ifVersion = ifVersion = existingEntry?.version ?? null;
|
|
608
615
|
if (existingEntry && existingEntry.value && type !== 'message' && existingEntry.metadataFlags & HAS_BLOBS) {
|
|
609
|
-
//
|
|
610
|
-
|
|
616
|
+
// Delete the prior row's blob files — except any the new record still references.
|
|
617
|
+
// Without the retention check, updating an unrelated attribute on a row that
|
|
618
|
+
// carries a file-backed blob unlinks the blob ~deletionDelay ms later, leaving
|
|
619
|
+
// the new (otherwise valid) row pointing at a missing file. See HarperFast/harper#641
|
|
620
|
+
// (deployment tracking) for the production repro.
|
|
621
|
+
let retainedFileIds: Set<string> | undefined;
|
|
622
|
+
if (record) {
|
|
623
|
+
findBlobsInObject(record, (blob) => {
|
|
624
|
+
const fileId = getFileId(blob);
|
|
625
|
+
if (fileId) (retainedFileIds ??= new Set()).add(fileId);
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
deleteBlobsInObject(existingEntry.value, retainedFileIds);
|
|
611
629
|
}
|
|
612
630
|
let result: Promise<void>;
|
|
613
631
|
if (record !== undefined) {
|
package/core/resources/Table.ts
CHANGED
|
@@ -1441,6 +1441,7 @@ export function makeTable(options) {
|
|
|
1441
1441
|
const lmdbTransaction = txnForContext({ transaction: new DatabaseTransaction() });
|
|
1442
1442
|
let transaction = lmdbTransaction.getReadTxn();
|
|
1443
1443
|
let options = { transaction };
|
|
1444
|
+
let committed = false;
|
|
1444
1445
|
try {
|
|
1445
1446
|
if (hasSourceGet || audit) {
|
|
1446
1447
|
if (!existingRecord) return;
|
|
@@ -1460,19 +1461,30 @@ export function makeTable(options) {
|
|
|
1460
1461
|
primaryStore.ifVersion?.(id, existingVersion, () => {
|
|
1461
1462
|
updateIndices(id, existingRecord, null);
|
|
1462
1463
|
});
|
|
1463
|
-
|
|
1464
|
+
removeEntry(primaryStore, entry ?? primaryStore.getEntry(id), existingVersion);
|
|
1464
1465
|
} else {
|
|
1465
1466
|
updateIndices(id, existingRecord, null, options);
|
|
1466
|
-
|
|
1467
|
+
removeEntry(primaryStore, entry ?? primaryStore.getEntry(id), options);
|
|
1467
1468
|
}
|
|
1468
|
-
|
|
1469
|
+
committed = true;
|
|
1469
1470
|
if (primaryStore.ifVersion) {
|
|
1470
1471
|
// LMDB: committing the wrapper calls doneReadTxn(), removing it from trackedTxns
|
|
1471
1472
|
return lmdbTransaction.commit();
|
|
1472
1473
|
}
|
|
1473
1474
|
// RocksDB: eviction writes went directly into the raw transaction via options;
|
|
1474
|
-
// commit it directly, as DatabaseTransaction.commit() would abort it (no tracked writes)
|
|
1475
|
-
return
|
|
1475
|
+
// commit it directly, as DatabaseTransaction.commit() would abort it (no tracked writes).
|
|
1476
|
+
// Wrap in Promise.resolve so callers can rely on a thenable return regardless of engine.
|
|
1477
|
+
return Promise.resolve((transaction as any).commit());
|
|
1478
|
+
} finally {
|
|
1479
|
+
if (!committed) {
|
|
1480
|
+
// Skip path or thrown error: abort instead of committing so we don't apply
|
|
1481
|
+
// partial work and the txn handle is released.
|
|
1482
|
+
if (primaryStore.ifVersion) {
|
|
1483
|
+
(lmdbTransaction as any).abort?.();
|
|
1484
|
+
} else {
|
|
1485
|
+
(transaction as any)?.abort?.();
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1476
1488
|
}
|
|
1477
1489
|
}
|
|
1478
1490
|
/**
|
|
@@ -4459,7 +4471,7 @@ export function makeTable(options) {
|
|
|
4459
4471
|
const { key, value: record, version, expiresAt, metadataFlags } = entry;
|
|
4460
4472
|
// if there is no auditing cleanup and we are tracking deletion, need to do cleanup of
|
|
4461
4473
|
// these deletion entries (LMDB audit cleanup has its own scheduled job for this)
|
|
4462
|
-
let resolution: Promise<void
|
|
4474
|
+
let resolution: Promise<void> | undefined;
|
|
4463
4475
|
if (record === null && removeDeletedRecords && version + auditRetention < Date.now()) {
|
|
4464
4476
|
// make sure it is still deleted when we do the removal
|
|
4465
4477
|
resolution = removeEntry(primaryStore, entry, version);
|
package/core/resources/blob.ts
CHANGED
|
@@ -970,12 +970,26 @@ export function decodeFromDatabase<T>(callback: () => T, rootStore: LMDBStore) {
|
|
|
970
970
|
}
|
|
971
971
|
|
|
972
972
|
/**
|
|
973
|
-
* Delete blobs in an object, recursively searching for blobs
|
|
973
|
+
* Delete blobs in an object, recursively searching for blobs. When `retainedFileIds`
|
|
974
|
+
* is supplied, blobs whose fileId is in that set are skipped — used by the update path
|
|
975
|
+
* to avoid deleting a blob the new record still references.
|
|
976
|
+
*
|
|
977
|
+
* Background: `RecordEncoder` calls this with the *prior* row's value on every update.
|
|
978
|
+
* If a caller updates an unrelated attribute on a row that still carries a file-backed
|
|
979
|
+
* blob, the unfiltered delete unlinks the blob file ~`deletionDelay` ms later, even
|
|
980
|
+
* though the new row still references the same fileId. The retainedFileIds set
|
|
981
|
+
* prevents that data loss.
|
|
982
|
+
*
|
|
974
983
|
* @param object
|
|
984
|
+
* @param retainedFileIds optional set of fileIds the caller wants to keep on disk
|
|
975
985
|
*/
|
|
976
|
-
export function deleteBlobsInObject(object) {
|
|
977
|
-
findBlobsInObject(object, (
|
|
978
|
-
|
|
986
|
+
export function deleteBlobsInObject(object: any, retainedFileIds?: Set<string>): void {
|
|
987
|
+
findBlobsInObject(object, (blob) => {
|
|
988
|
+
if (retainedFileIds?.size) {
|
|
989
|
+
const fileId = getFileId(blob);
|
|
990
|
+
if (fileId && retainedFileIds.has(fileId)) return;
|
|
991
|
+
}
|
|
992
|
+
deleteBlob(blob);
|
|
979
993
|
});
|
|
980
994
|
}
|
|
981
995
|
|
|
@@ -1317,6 +1317,24 @@ async function runIndexing(Table, attributes, indicesToRemove) {
|
|
|
1317
1317
|
}
|
|
1318
1318
|
} catch (error) {
|
|
1319
1319
|
logger.error('Error in indexing', error);
|
|
1320
|
+
// Persist indexingFailed so the next restart re-triggers the rebuild from an
|
|
1321
|
+
// explicitly failed state rather than silently looping. Without this,
|
|
1322
|
+
// indexingPID (written before runIndexing was called) stays in the descriptor
|
|
1323
|
+
// but indexingFailed is never set, leaving isIndexing stuck with no recovery
|
|
1324
|
+
// signal. Mirrors the hadIndexingErrors path. harper#843
|
|
1325
|
+
try {
|
|
1326
|
+
const puts: Promise<unknown>[] = [];
|
|
1327
|
+
for (const attribute of attributes) {
|
|
1328
|
+
attribute.indexingFailed = true;
|
|
1329
|
+
puts.push(Table.dbisDB.put(attribute.key, attribute));
|
|
1330
|
+
attribute.dbi.isIndexing = true;
|
|
1331
|
+
const activeDbi = Table.indices[attribute.name];
|
|
1332
|
+
if (activeDbi) activeDbi.isIndexing = true;
|
|
1333
|
+
}
|
|
1334
|
+
await Promise.all(puts);
|
|
1335
|
+
} catch (persistError) {
|
|
1336
|
+
logger.warn('Failed to persist indexing failure state', persistError);
|
|
1337
|
+
}
|
|
1320
1338
|
}
|
|
1321
1339
|
}
|
|
1322
1340
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { promises: fsProm, createReadStream, createWriteStream } = require('fs');
|
|
3
|
+
const { promises: fsProm, createReadStream, createWriteStream, mkdirSync } = require('fs');
|
|
4
4
|
const { createGzip } = require('zlib');
|
|
5
5
|
const { promisify } = require('util');
|
|
6
6
|
const { pipeline } = require('stream');
|
|
@@ -17,8 +17,6 @@ const { onStorageReclamation } = require('../../server/storageReclamation.ts');
|
|
|
17
17
|
const LOG_AUDIT_INTERVAL = 60000;
|
|
18
18
|
const INT_SIZE_UNDEFINED_MSG =
|
|
19
19
|
"'interval' and 'maxSize' are both undefined, to enable logging rotation at least one of these values must be defined in harperdb-config.yaml";
|
|
20
|
-
const PATH_UNDEFINED_MSG =
|
|
21
|
-
"'logging.rotation.path' is undefined, to enable logging rotation set this value in harperdb-config.yaml";
|
|
22
20
|
|
|
23
21
|
let lastRotationTime;
|
|
24
22
|
let setIntervalId;
|
|
@@ -47,8 +45,9 @@ function logRotator({ logger, maxSize, interval, retention, enabled, path: rotat
|
|
|
47
45
|
}
|
|
48
46
|
|
|
49
47
|
if (!rotatedLogDir) {
|
|
50
|
-
|
|
48
|
+
rotatedLogDir = path.join(path.dirname(logger.path), 'rotated');
|
|
51
49
|
}
|
|
50
|
+
mkdirSync(rotatedLogDir, { recursive: true });
|
|
52
51
|
|
|
53
52
|
// Convert maxSize param to bytes.
|
|
54
53
|
let maxBytes;
|
package/dist/core/bin/copyDb.js
CHANGED
|
@@ -39,6 +39,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
39
39
|
exports.compactOnStart = compactOnStart;
|
|
40
40
|
exports.copyDb = copyDb;
|
|
41
41
|
exports.migrateOnStart = migrateOnStart;
|
|
42
|
+
exports.copyDbToRocks = copyDbToRocks;
|
|
42
43
|
const databases_ts_1 = require("../resources/databases.js");
|
|
43
44
|
const lmdb_1 = require("lmdb");
|
|
44
45
|
const path_1 = require("path");
|
|
@@ -423,6 +424,12 @@ async function copyDbToRocks(sourceRootStore, sourceDatabase, targetPath) {
|
|
|
423
424
|
const dbiInit = new OpenDBIObject_js_1.OpenDBIObject(!isPrimary, isPrimary);
|
|
424
425
|
dbiInit.compression = attribute.compression;
|
|
425
426
|
const sourceDbi = sourceRootStore.openDB(key, dbiInit);
|
|
427
|
+
// The primary dbi uses a RecordEncoder, whose decode resolves file-backed blob references
|
|
428
|
+
// against `rootStore`. Without it, decoding any record that holds a blob throws "No store
|
|
429
|
+
// specified, cannot load blob from storage", the error is swallowed (record decodes to null),
|
|
430
|
+
// and the record is silently dropped from the migration (HarperFast/harper#857).
|
|
431
|
+
if (isPrimary && sourceDbi.encoder)
|
|
432
|
+
sourceDbi.encoder.rootStore = sourceRootStore;
|
|
426
433
|
let targetDbi;
|
|
427
434
|
if (!isPrimary) {
|
|
428
435
|
targetDbi = openRocksDb(targetPath, { dupSort: true, name: key });
|
|
@@ -435,9 +442,23 @@ async function copyDbToRocks(sourceRootStore, sourceDatabase, targetPath) {
|
|
|
435
442
|
existingEncoder.isRocksDB = true;
|
|
436
443
|
existingEncoder.rootStore = targetRootStore;
|
|
437
444
|
const tempEncoder = new RecordEncoder_ts_1.RecordEncoder({ name: key });
|
|
445
|
+
// msgpackr's pack closure captures `packr = this` at construction, so during
|
|
446
|
+
// re-encoding the structure callbacks resolve to tempEncoder's getStructures/
|
|
447
|
+
// saveStructures (invoked with this === tempEncoder), not existingEncoder's.
|
|
448
|
+
// tempEncoder must therefore carry the RocksDB wiring too, or getStructures hits
|
|
449
|
+
// the non-RocksDB branch where the captured super is undefined and throws.
|
|
450
|
+
tempEncoder.name = key;
|
|
451
|
+
tempEncoder.isRocksDB = true;
|
|
452
|
+
tempEncoder.rootStore = targetRootStore;
|
|
438
453
|
existingEncoder.encode = tempEncoder.encode;
|
|
439
|
-
existingEncoder.saveStructures = tempEncoder.saveStructures;
|
|
440
454
|
existingEncoder.getStructures = tempEncoder.getStructures;
|
|
455
|
+
// The shared structures dictionary is copied verbatim from the source by
|
|
456
|
+
// copyStructures() below, so re-encoding never needs to persist new structures.
|
|
457
|
+
// A no-op saveStructures avoids opening a targetRootStore.transactionSync() in the
|
|
458
|
+
// middle of each record's encode, which otherwise discards the targetDbi record writes.
|
|
459
|
+
const noopSaveStructures = () => true;
|
|
460
|
+
existingEncoder.saveStructures = noopSaveStructures;
|
|
461
|
+
tempEncoder.saveStructures = noopSaveStructures;
|
|
441
462
|
}
|
|
442
463
|
copyStructures(sourceDbi, key);
|
|
443
464
|
console.log('migrating', key, 'from', sourceDatabase, 'to RocksDB');
|