@cloudbitmaps/core 0.20.0 → 0.21.0
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/README.md +85 -0
- package/dist/{chunk-YSC7NJWV.js → chunk-IGLIZBR7.js} +740 -137
- package/dist/chunk-IGLIZBR7.js.map +7 -0
- package/dist/core/absent-operands.d.ts +8 -0
- package/dist/core/audit.d.ts +12 -2
- package/dist/core/bit-route.d.ts +2 -1
- package/dist/core/blob.d.ts +12 -1
- package/dist/core/bulk-load.d.ts +148 -0
- package/dist/core/cas-retry.d.ts +27 -0
- package/dist/core/check.d.ts +22 -0
- package/dist/core/chunk-checks.d.ts +6 -1
- package/dist/core/chunk-window.d.ts +11 -4
- package/dist/core/combine-expr.d.ts +1 -3
- package/dist/core/combine-feed.d.ts +2 -16
- package/dist/core/combine-many.d.ts +12 -9
- package/dist/core/concurrency.d.ts +8 -0
- package/dist/core/consistency.d.ts +69 -17
- package/dist/core/crbm/fingerprint.d.ts +5 -0
- package/dist/core/crbm/format.d.ts +0 -1
- package/dist/core/crbm/plan-reads.d.ts +9 -3
- package/dist/core/crbm/reader.d.ts +52 -6
- package/dist/core/crbm/varint.d.ts +2 -1
- package/dist/core/crbm/writer.d.ts +5 -1
- package/dist/core/crbm-open.d.ts +154 -0
- package/dist/core/crbm-storage-source.d.ts +65 -452
- package/dist/core/crbm-write.d.ts +75 -0
- package/dist/core/crypto.d.ts +6 -1
- package/dist/core/engine.d.ts +222 -33
- package/dist/core/erase-id.d.ts +42 -7
- package/dist/core/erasure.d.ts +4 -3
- package/dist/core/errors.d.ts +8 -2
- package/dist/core/generation-gc.d.ts +129 -15
- package/dist/core/generation-list.d.ts +11 -0
- package/dist/core/id-window.d.ts +46 -0
- package/dist/core/invalidation-scope.d.ts +77 -0
- package/dist/core/known-generations-shape.d.ts +13 -0
- package/dist/core/known-generations.d.ts +147 -0
- package/dist/core/leases.d.ts +4 -3
- package/dist/core/ledger-seed.d.ts +17 -0
- package/dist/core/load.d.ts +17 -38
- package/dist/core/lru.d.ts +2 -15
- package/dist/core/metrics.d.ts +4 -2
- package/dist/core/order.d.ts +2 -0
- package/dist/core/pinned-storage-source.d.ts +4 -4
- package/dist/core/ports.d.ts +89 -16
- package/dist/core/publish.d.ts +207 -0
- package/dist/core/registry-scan.d.ts +0 -2
- package/dist/core/retention-sweep.d.ts +69 -65
- package/dist/core/retention.d.ts +1 -1
- package/dist/core/retry.d.ts +12 -19
- package/dist/core/rollback.d.ts +60 -31
- package/dist/core/summary.d.ts +50 -18
- package/dist/core/token.d.ts +2 -2
- package/dist/core/validate.d.ts +2 -0
- package/dist/driver-kit.d.ts +4 -0
- package/dist/driver-kit.js +80 -7
- package/dist/driver-kit.js.map +3 -3
- package/dist/drivers/_shared/bytes.d.ts +6 -0
- package/dist/drivers/_shared/faults.d.ts +15 -0
- package/dist/drivers/_shared/object-keys.d.ts +18 -0
- package/dist/drivers/_shared/object-registry-keys.d.ts +0 -2
- package/dist/drivers/_shared/object-registry.d.ts +2 -0
- package/dist/drivers/_shared/options.d.ts +19 -0
- package/dist/drivers/_shared/registry.d.ts +13 -10
- package/dist/drivers/localfs/exact-case.d.ts +0 -3
- package/dist/drivers/localfs/fs-util.d.ts +22 -0
- package/dist/drivers/localfs/registry.d.ts +10 -4
- package/dist/drivers/localfs/storage.d.ts +9 -2
- package/dist/drivers/memory.d.ts +7 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3647 -2410
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
- package/dist/chunk-YSC7NJWV.js.map +0 -7
- package/dist/core/kept-generations.d.ts +0 -40
|
@@ -1,14 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import type {
|
|
4
|
-
import type { Clock, Rng } from './determinism.js';
|
|
5
|
-
import type { PublishedKept } from './generation-gc.js';
|
|
6
|
-
import type { Aead, CrbmCrypto, IKeystore, WrappedDek } from './crypto.js';
|
|
7
|
-
import type { ChunkRef, ChunkRead, ReadChunksOptions, StorageChunkSource, GenerationMetadata, GenerationSummary, GenKey, IStorageDriver, IRegistryDriver, RegistryRecord, RegistrySummary, SegmentRef, SegmentSize, Token } from './ports.js';
|
|
8
|
-
import { CrbmReader } from './crbm/reader.js';
|
|
1
|
+
import type { Clock } from './determinism.js';
|
|
2
|
+
import type { IKeystore } from './crypto.js';
|
|
3
|
+
import type { ChunkRef, ChunkRead, ReadChunksOptions, StorageChunkSource, GenerationSummary, IStorageDriver, IRegistryDriver, RegistryRecord, SegmentRef, SegmentSize, Token } from './ports.js';
|
|
9
4
|
import type { CrbmReaderOptions } from './crbm/reader.js';
|
|
10
|
-
import type { CodecBitmap, CodecInterface } from './codec.js';
|
|
11
|
-
import { ChunkLoadInput, DecodedLoadInput } from './load-input.js';
|
|
12
5
|
export interface CrbmStorageChunkSourceOptions extends CrbmReaderOptions {
|
|
13
6
|
/**
|
|
14
7
|
* Optional {@link IRegistryDriver}. When provided, the current generation is resolved via the registry's
|
|
@@ -45,7 +38,7 @@ export interface CrbmStorageChunkSourceOptions extends CrbmReaderOptions {
|
|
|
45
38
|
* How long (ms) a resolved `currentGen` is trusted before the next read re-resolves it (default 2000) — the
|
|
46
39
|
* bound on read staleness after a load publishes: a reader may serve the prior generation for up to this long,
|
|
47
40
|
* then converges. Lazy (checked on read — no timer); ≤ one cheap registry read per segment per window, and a
|
|
48
|
-
* new {@link CrbmReader} is opened only when the
|
|
41
|
+
* new {@link CrbmReader} is opened only when the row's pointer was renewed. `0` turns the refresh off, as a
|
|
49
42
|
* missing clock or registry does: {@link clock} says what that changes, and what it does not.
|
|
50
43
|
*/
|
|
51
44
|
readonly currentGenTtlMs?: number;
|
|
@@ -53,7 +46,7 @@ export interface CrbmStorageChunkSourceOptions extends CrbmReaderOptions {
|
|
|
53
46
|
* Hard ceiling on how many segments' readers (each holding a fully-parsed `.crbm` index) are cached at once
|
|
54
47
|
* (default 1024) — a memory bound for a long-running server. Past it, the least-recently-used
|
|
55
48
|
* segment's reader is evicted; the next read of an evicted segment re-opens it (one cheap tail GET, since
|
|
56
|
-
* generations are immutable). Raise it for a big cache working set of small segments. A source with a timed refresh
|
|
49
|
+
* generations are immutable). Raise it for a big cache working set of small segments, and raise {@link maxOpenIndexBytes} with it: each open reader counts about 3.75 KiB there, so past about 17,000 tiny readers the byte ceiling binds first. A source with a timed refresh
|
|
57
50
|
* also keeps up to 8 times this many segments' resolutions apart from their readers, for the TTL: the re-open then
|
|
58
51
|
* reads no row, and comes only when a read needs the reader, for the index or for a chunk.
|
|
59
52
|
*/
|
|
@@ -64,24 +57,25 @@ export interface CrbmStorageChunkSourceOptions extends CrbmReaderOptions {
|
|
|
64
57
|
* **second half of that memory bound**. `maxOpenSegments` alone bounds by *count*, but a wide/dense segment's
|
|
65
58
|
* parsed index can reach about 1.3 MB, so 1024 wide indices could pin over a GB and blow a small heap (e.g. a 128 MB
|
|
66
59
|
* Lambda) while the count is nominally "in bounds". This caps the summed {@link CrbmReader.retainedBytes} (the
|
|
67
|
-
* parsed index, any metadata a generation carries, and any kept chunk bytes)
|
|
60
|
+
* parsed index, any metadata a generation carries, and any kept chunk bytes) plus a fixed 3.75 KiB for the objects each open reader
|
|
61
|
+
* holds around them, across cached readers, so that many tiny segments are bounded too; the least-recently-used reader is evicted once the total would exceed it — whichever
|
|
68
62
|
* of the count/byte bounds binds first. A reader whose open read the whole object keeps a copy of its chunk region when
|
|
69
63
|
* that region is at most this ceiling divided by {@link maxOpenSegments} (64 KiB by default) — its fair share — so a small generation's
|
|
70
64
|
* chunks are served from memory with no further request, and a bigger one is read by range. Only a source with a timed
|
|
71
65
|
* pointer refresh ({@link currentGenTtlMs} above 0, a registry and a clock) keeps chunk bytes. Lower it for memory-tight deployments with wide segments; a single
|
|
72
|
-
* segment whose
|
|
66
|
+
* segment whose weight alone exceeds the budget is still cached (it can't be shrunk) but nothing else alongside. A
|
|
73
67
|
* sixteenth of it bounds the resolutions a source with a timed refresh keeps besides (4 MiB by default).
|
|
74
68
|
*/
|
|
75
69
|
readonly maxOpenIndexBytes?: number;
|
|
76
70
|
}
|
|
77
71
|
/**
|
|
78
72
|
* What a pin holds of the object it pinned: the version its cache entries are keyed by, and the object's
|
|
79
|
-
* fingerprint, which a pinned read checks each time it opens the generation. `pinGeneration` always records
|
|
80
|
-
*
|
|
73
|
+
* fingerprint, which a pinned read checks each time it opens the generation. `pinGeneration` always records both,
|
|
74
|
+
* and a pinned read given one without both as strings throws a `ValidationError`.
|
|
81
75
|
*/
|
|
82
76
|
export interface PinnedObject {
|
|
83
77
|
readonly version: string;
|
|
84
|
-
readonly fingerprint
|
|
78
|
+
readonly fingerprint: string;
|
|
85
79
|
}
|
|
86
80
|
/**
|
|
87
81
|
* What a live version's resolution named, without the object's fingerprint: `<generation>:<pointerId>`, or
|
|
@@ -99,6 +93,15 @@ export interface PinLease {
|
|
|
99
93
|
readonly token: Token;
|
|
100
94
|
} | 'moved'>;
|
|
101
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* The fixed heap an open reader's snapshot holds beyond {@link CrbmReader.retainedBytes}: the snapshot, the reader, its
|
|
98
|
+
* five index arrays as objects, and the closures and promises that tie them. It is added to a reader's weight in the
|
|
99
|
+
* reader cache, so that the byte ceiling bounds many tiny segments as it does a few wide ones. Measured as the heap that
|
|
100
|
+
* stayed (full collections until stable) per segment after a source opened 2,000 one-chunk segments, minus the 20 bytes
|
|
101
|
+
* of index the weight already counts: about 3.7 KiB on Node 24, rounded up to 3.75 KiB.
|
|
102
|
+
* `tests/core/reader-cache-heap.test.ts` holds the counted weight to that heap.
|
|
103
|
+
*/
|
|
104
|
+
export declare const OPEN_READER_BYTES = 3840;
|
|
102
105
|
export declare class CrbmStorageChunkSource implements StorageChunkSource {
|
|
103
106
|
private readonly driver;
|
|
104
107
|
/**
|
|
@@ -151,7 +154,12 @@ export declare class CrbmStorageChunkSource implements StorageChunkSource {
|
|
|
151
154
|
* counts every segment's invalidations: a read that waited through another segment's only costs a later read the
|
|
152
155
|
* check again.
|
|
153
156
|
*/
|
|
154
|
-
private invalidations;
|
|
157
|
+
private get invalidations();
|
|
158
|
+
/**
|
|
159
|
+
* Which segments {@link invalidate} has been called for, the latest invalidation of up to 1,024 of them: a pinned
|
|
160
|
+
* stream asks it whether its own segment was invalidated since it last looked.
|
|
161
|
+
*/
|
|
162
|
+
private readonly invalidated;
|
|
155
163
|
/**
|
|
156
164
|
* Generations whose row summary this process found to disagree with their object, by segment and generation. A
|
|
157
165
|
* summary here is not used again: a count of that generation reads the object. Bounded as the readers are, so a
|
|
@@ -409,9 +417,9 @@ export declare class CrbmStorageChunkSource implements StorageChunkSource {
|
|
|
409
417
|
/** Size of a specific generation. `held` as for {@link getChunkAt}. */
|
|
410
418
|
sizeOfAt(ref: SegmentRef, generation: number, held?: PinnedObject): Promise<SegmentSize | null>;
|
|
411
419
|
/**
|
|
412
|
-
* Whether the segment is a registered name at all. With a registry that is
|
|
413
|
-
*
|
|
414
|
-
*
|
|
420
|
+
* Whether the segment is a registered name at all. With a registry: a row that is not a `destroyed` tombstone,
|
|
421
|
+
* including one with no generation yet (minted by `setRetention` before the first load), which somebody deliberately
|
|
422
|
+
* created. A tombstone holds nothing, so it answers `false`. Without a registry there is no registration to
|
|
415
423
|
* consult, so the honest answer is whether the bucket holds any generation of it.
|
|
416
424
|
*
|
|
417
425
|
* Deliberately NOT served from the snapshot memo: the memo resolves to `null` for both states this exists to
|
|
@@ -437,6 +445,22 @@ export declare class CrbmStorageChunkSource implements StorageChunkSource {
|
|
|
437
445
|
* it should hold the same count and metadata, or the summary is not believed again in this process
|
|
438
446
|
* ({@link distrusted}). Neither check costs a request. A row with no summary it can use names no object, and its
|
|
439
447
|
* generation's object is opened as it is.
|
|
448
|
+
*
|
|
449
|
+
* An encrypted generation's tail read is sent first, beside the key, because the key (a keystore call, a network
|
|
450
|
+
* round trip for a KMS keystore) and the tail are independent: only the index needs the key, and it comes out of the
|
|
451
|
+
* tail. The row's summary of an encrypted generation is sealed under the same key, so it is read after the key. The
|
|
452
|
+
* open then holds the footer to the summary and decrypts, as an open that waited for the key does, and nothing of the
|
|
453
|
+
* tail is read before then. The key is awaited first, so a keystore fault reaches the caller before a storage fault,
|
|
454
|
+
* and the request already sent is not cancelled: it is billed, and its answer or failure is dropped when the key
|
|
455
|
+
* fails.
|
|
456
|
+
*
|
|
457
|
+
* A cleartext generation's tail read is sized from the `tailBytes` of that summary ({@link sizedTailBytes}), so a
|
|
458
|
+
* generation whose index is longer than the default tail comes in the one read, with no range read for the index
|
|
459
|
+
* after it. Only a summary this process still believes sizes a read ({@link trustedSummary}): one found to disagree
|
|
460
|
+
* with its object is not used again, so a wrong number costs one extra read for the process, not for each open. An
|
|
461
|
+
* encrypted generation is not sized: its summary is sealed under the key, so sizing from it would send the tail read
|
|
462
|
+
* after the key, which the early read above avoids where no earlier step has already waited for the key (a cold
|
|
463
|
+
* `has()` has).
|
|
440
464
|
*/
|
|
441
465
|
private openLive;
|
|
442
466
|
private distrustKey;
|
|
@@ -478,6 +502,16 @@ export declare class CrbmStorageChunkSource implements StorageChunkSource {
|
|
|
478
502
|
* {@link getChunks} of a specific generation, the pinned shape read. It yields the chunks without a version: the
|
|
479
503
|
* caller that pinned the generation knows it. It never moves to another generation; `held` as for
|
|
480
504
|
* {@link getChunkAt}.
|
|
505
|
+
*
|
|
506
|
+
* Before it hands out a chunk after an {@link invalidate}, it looks its generation up as a read of that chunk alone
|
|
507
|
+
* would. Where the invalidation was of its segment, which forgets the pinned reader, it opens the object again, and
|
|
508
|
+
* fails as that read would, with `NotFoundError` once the object is gone or replaced, or its row is gone or
|
|
509
|
+
* destroyed. An object still there is the one it was reading, since an object is immutable and the open is held to
|
|
510
|
+
* the pin, so the stream carries on with the ranges it holds. Another segment's invalidation costs it nothing, the
|
|
511
|
+
* pin's reader still in the reader cache or not, and with no invalidation since it last looked the check is a
|
|
512
|
+
* compare. The one exception is a store that invalidates more than 1,024 other segments while the stream runs: the
|
|
513
|
+
* record of invalidations then counts the stream's segment as invalidated too, and the stream looks its pin up
|
|
514
|
+
* again, at no cost where the reader cache still holds the pin's reader and with one open where it has let it go.
|
|
481
515
|
*/
|
|
482
516
|
getChunksAt(ref: SegmentRef, generation: number, keys: readonly number[], held?: PinnedObject, options?: ReadChunksOptions): AsyncGenerator<Omit<ChunkRead, 'version'>>;
|
|
483
517
|
listChunkKeys(ref: SegmentRef): Promise<number[]>;
|
|
@@ -539,11 +573,20 @@ export declare class CrbmStorageChunkSource implements StorageChunkSource {
|
|
|
539
573
|
* is the documented outcome rather than a failure.
|
|
540
574
|
*
|
|
541
575
|
* **Bounded to exactly two resolve-and-open round trips.** That is a cost contract, not a detail: the retry
|
|
542
|
-
*
|
|
576
|
+
* re-reads the *registry*, so an unbounded one would hammer the shared, throttle-prone resource in a
|
|
543
577
|
* tight loop on a segment whose pointer names an object that is permanently absent — and an N-way
|
|
544
578
|
* `intersect` would do it N times. Gated in `tests/core/storage-source-heal-open.test.ts` by counting calls.
|
|
545
579
|
*/
|
|
546
580
|
private withFreshSnapshot;
|
|
581
|
+
/** The live snapshot of `ref` and the reader it is opening, as a read holds them while it resolves. */
|
|
582
|
+
private resolution;
|
|
583
|
+
/**
|
|
584
|
+
* The reader `at` resolves to. A caller that retries has the resolution and open run through its `retry`; a failed
|
|
585
|
+
* one forgets itself, so each try resolves anew and `at` is moved to the snapshot that try resolved.
|
|
586
|
+
*/
|
|
587
|
+
private resolveReader;
|
|
588
|
+
/** The options a chunk stream is read with: the caller's tuning, its retry for each range, and this source's clock. */
|
|
589
|
+
private readOptionsOf;
|
|
547
590
|
/**
|
|
548
591
|
* Whether `err`, raised by a read through the snapshot's reader, came from an object that is no longer the one the
|
|
549
592
|
* reader opened. A generation number can be taken again once its object is deleted (an erasure removes the
|
|
@@ -556,433 +599,3 @@ export declare class CrbmStorageChunkSource implements StorageChunkSource {
|
|
|
556
599
|
*/
|
|
557
600
|
private replacedUnder;
|
|
558
601
|
}
|
|
559
|
-
/**
|
|
560
|
-
* Write one immutable generation from in-memory bitmaps. Chunks are
|
|
561
|
-
* sorted ascending and empty bitmaps skipped (empty chunks are never stored). Returns the driver's
|
|
562
|
-
* `{ size, sha256 }` for the written object. Pass `options.crypto` to AES-256-GCM-encrypt the generation
|
|
563
|
-
* (built from the segment's DEK, with associated data bound to `(segment, generation)`).
|
|
564
|
-
*/
|
|
565
|
-
export declare function writeCrbmGeneration(driver: IStorageDriver, key: GenKey, chunks: Iterable<{
|
|
566
|
-
chunkKey: number;
|
|
567
|
-
bitmap: CodecBitmap;
|
|
568
|
-
}>, options?: {
|
|
569
|
-
crypto?: CrbmCrypto;
|
|
570
|
-
clock?: Yielder;
|
|
571
|
-
metadata?: GenerationMetadata;
|
|
572
|
-
}): Promise<{
|
|
573
|
-
size: number;
|
|
574
|
-
sha256: string;
|
|
575
|
-
fingerprint: string;
|
|
576
|
-
}>;
|
|
577
|
-
/**
|
|
578
|
-
* The object under `key`, when it is provably the one `fingerprint` names ({@link CrbmReader.fingerprint}), from one
|
|
579
|
-
* read of its footer, with the version the driver reported on that read (`undefined` from a driver that reports none).
|
|
580
|
-
* `null` when it is another object, when it is gone, and when the read fails in any way: a caller that deletes what
|
|
581
|
-
* this proves deletes nothing it cannot prove is its own, and passes the version as the delete's `ifVersion`, so a
|
|
582
|
-
* number taken again between the read and the delete keeps the object put under it since.
|
|
583
|
-
*/
|
|
584
|
-
export declare function provenObject(storage: IStorageDriver, key: GenKey, fingerprint: string): Promise<{
|
|
585
|
-
readonly version: string | undefined;
|
|
586
|
-
} | null>;
|
|
587
|
-
/**
|
|
588
|
-
* Whether the object under `key` is still the one `fingerprint` names ({@link CrbmReader.fingerprint}), from one read
|
|
589
|
-
* of its footer. Unlike {@link provenObject}, a fault is not an answer: `NotFoundError` when the key holds no object,
|
|
590
|
-
* and any other failure of the read, thrown as it is.
|
|
591
|
-
*/
|
|
592
|
-
export declare function sameObjectUnder(storage: IStorageDriver, key: GenKey, fingerprint: string): Promise<boolean>;
|
|
593
|
-
/**
|
|
594
|
-
* Whether the object under `key` says it is encrypted, from one read of its footer, with no key. `noteVersion` is shown
|
|
595
|
-
* the version the driver reported on that read, when it reported one: a caller that deletes the object for what its
|
|
596
|
-
* footer says passes it as the delete's `ifVersion`.
|
|
597
|
-
*/
|
|
598
|
-
export declare function objectIsEncrypted(storage: IStorageDriver, key: GenKey, noteVersion?: (version: string) => void): Promise<boolean>;
|
|
599
|
-
/**
|
|
600
|
-
* What one tail read of the object under `key` tells a rollback about it: whether the footer says it is encrypted, and,
|
|
601
|
-
* when the object and the key agree (an encrypted object opened with `crypto`, a cleartext one with none), the reader
|
|
602
|
-
* the open gives, which holds its count and metadata. No reader when they disagree, which the caller refuses as a
|
|
603
|
-
* target the row cannot use, and none for an encrypted object when the caller has no key. A footer that fails its own
|
|
604
|
-
* checks, an object another generation's, and an index or metadata that do not open are an {@link IntegrityError}.
|
|
605
|
-
*/
|
|
606
|
-
export declare function openRollbackTarget(storage: IStorageDriver, key: GenKey, crypto: CrbmCrypto | undefined): Promise<{
|
|
607
|
-
encrypted: boolean;
|
|
608
|
-
reader: CrbmReader | undefined;
|
|
609
|
-
}>;
|
|
610
|
-
/** What {@link writeCrbmGenerationStream} wrote: the driver's `{ size, sha256 }` + a tally of the generation. */
|
|
611
|
-
export interface StreamWriteResult {
|
|
612
|
-
readonly size: number;
|
|
613
|
-
readonly sha256: string;
|
|
614
|
-
/** Non-empty chunk keys written, in ascending order (for the post-write verify, since the stream is consumed). */
|
|
615
|
-
readonly chunkKeys: number[];
|
|
616
|
-
/** Total ids written. */
|
|
617
|
-
readonly cardinality: number;
|
|
618
|
-
/** The fingerprint of what this call wrote ({@link CrbmReader.fingerprint}), which tells it from another object under its key. */
|
|
619
|
-
readonly fingerprint: string;
|
|
620
|
-
}
|
|
621
|
-
/**
|
|
622
|
-
* Streaming variant of {@link writeCrbmGeneration} for a **constant-memory** rewrite (the erasure rewrite, a
|
|
623
|
-
* sorted load): consumes an **already-ascending** async stream of `{ chunkKey, bitmap }`, feeding each to the
|
|
624
|
-
* codec and freeing it, instead of materializing the whole generation. Paired with a streaming storage sink (S3
|
|
625
|
-
* multipart / LocalFs temp file), the writer itself holds ~one chunk + one part. Returns a tally so the caller can verify
|
|
626
|
-
* the re-opened object without re-iterating the (now-consumed) stream. Input **must** be ascending by `chunkKey`
|
|
627
|
-
* — the codec rejects an out-of-order chunk; empty bitmaps are skipped.
|
|
628
|
-
*/
|
|
629
|
-
export declare function writeCrbmGenerationStream(driver: IStorageDriver, key: GenKey, chunks: AsyncIterable<{
|
|
630
|
-
chunkKey: number;
|
|
631
|
-
bitmap: CodecBitmap;
|
|
632
|
-
}>, options?: {
|
|
633
|
-
crypto?: CrbmCrypto;
|
|
634
|
-
clock?: Yielder;
|
|
635
|
-
metadata?: GenerationMetadata;
|
|
636
|
-
}): Promise<StreamWriteResult>;
|
|
637
|
-
/**
|
|
638
|
-
* Point a segment's registry `currentGen` at `key.generation` — the publish step that makes a freshly-written
|
|
639
|
-
* generation the authoritative latest (so registry-aware readers see it). **Forward-only and idempotent:**
|
|
640
|
-
* if the registry has no row it creates one; if it's already at/ahead of `key.generation` it's a no-op (an
|
|
641
|
-
* out-of-order/duplicate publish never regresses the pointer); otherwise it advances via compare-and-swap,
|
|
642
|
-
* retrying a few times under contention. Separated from the Storage write so callers can publish atomically
|
|
643
|
-
* after the immutable object is durable (write-then-publish).
|
|
644
|
-
*
|
|
645
|
-
* **Throws** rather than returning, in two cases, because each would otherwise create a state no reader can
|
|
646
|
-
* recover from: a `destroyed` (crypto-shredded) row, where advancing the pointer would name an object encrypted
|
|
647
|
-
* under a shredded key; and an advance carrying **new** `wrappedDeks`, which would either strand that key or make
|
|
648
|
-
* the row advertise encryption over cleartext generations. Contention that never clears throws
|
|
649
|
-
* {@link WriteConflictError} after the retries.
|
|
650
|
-
*
|
|
651
|
-
* **`expectFrom` turns forward-only into read-modify-write.** Forward-only is the right rule for a writer whose
|
|
652
|
-
* content does not depend on what was current — a load computes its ids upstream, so publishing over a newer
|
|
653
|
-
* generation loses nothing the loader knew about. It is the WRONG rule for a writer that *derived* its content
|
|
654
|
-
* from a particular generation: the erasure rewrite streams generation `from` and clears one bit, so publishing
|
|
655
|
-
* it over a newer generation silently discards whatever that generation added. Passing `expectFrom` makes the
|
|
656
|
-
* publish land only while the pointer is still exactly there, and return `false` otherwise, so the caller can
|
|
657
|
-
* report `superseded` and re-derive. The compare-and-swap itself carries the token read in the same iteration,
|
|
658
|
-
* so the check and the write see the same row.
|
|
659
|
-
*
|
|
660
|
-
* **`expectToken` fences the LINEAGE, and a derived writer needs both.** A generation number identifies a
|
|
661
|
-
* generation only within one incarnation of a name: the numbering restarts at `0` once the row is purged and
|
|
662
|
-
* the bucket empty, so a name that is retired and re-created has a *different* segment wearing the *same*
|
|
663
|
-
* `currentGen`. `expectFrom` alone matched it — and an erasure rewrite then published one incarnation's content
|
|
664
|
-
* over another's, deleted the live objects with its `keep: 0` collection, and returned `erased: true`. The row's
|
|
665
|
-
* OCC token is the identity that survives this: a shipped registry's later `create` gets a token never issued before
|
|
666
|
-
* under that name, but for a collision of probability 2^-128 per pair of incarnations, so no token is reused across them.
|
|
667
|
-
*
|
|
668
|
-
* Pass the token read alongside `expectFrom` and the publish lands only on the same row it was derived from.
|
|
669
|
-
* The check is deliberately **conservative**: the token also changes on writes that are not supersessions at
|
|
670
|
-
* all (a `setRetention`, a due-index reindex), so such a write makes a derived publish report `superseded` and
|
|
671
|
-
* the caller re-derive. That costs a re-run on a rare, unrelated write; the alternative costs a segment.
|
|
672
|
-
*
|
|
673
|
-
* **The driver sends a registry write once, and an unanswered one is reconciled by its effect.** A `create` or
|
|
674
|
-
* `compareAndSwap` that fails with {@link TransientError} (a throttle, a lost response, a timeout) may or may not
|
|
675
|
-
* have landed, and may still be in flight. The publish reads the row once more and decides from what it holds:
|
|
676
|
-
*
|
|
677
|
-
* - the pointer names `key.generation`, on the incarnation the write was made against (the row's `createdAt`), and
|
|
678
|
-
* `holdsOwnObject` proves the object under it the caller's: the write landed, and the publish returns `true`. Only
|
|
679
|
-
* the writer of a write-once object publishes its number, and a number is never taken while an object holds it;
|
|
680
|
-
* - the row is still the one the write was made against (the same token, or still no row): the write may land yet. The
|
|
681
|
-
* publish then waits, on the injected clock, and sends a **new** write from the row it just read: a request of its
|
|
682
|
-
* own, not a replay of the driver's, carrying the same version, so under the registry's fence at most one of the two
|
|
683
|
-
* lands and the other meets a conflict, which the next read settles. At most {@link UNANSWERED_RESENDS} follow, after
|
|
684
|
-
* a wait under 500 ms, then 1 s, then 2 s (full jitter when an `rng` is given); each is settled the same way. Still
|
|
685
|
-
* unanswered, or with no clock to wait on, this throws the registry's own `TransientError` and writes nothing more.
|
|
686
|
-
* A caller must not delete its object on it;
|
|
687
|
-
* - anything else: the row has moved past the state the write was conditioned on, so that write can never land, and
|
|
688
|
-
* the publish goes on as after a lost race, from the row it just read. A pointer at `key.generation` that is not over
|
|
689
|
-
* the caller's own object, or is on another incarnation, is a lost race too and answers `false`, never an already
|
|
690
|
-
* current re-publish.
|
|
691
|
-
*
|
|
692
|
-
* A failed read throws `TransientError` too. The same check of the pointer runs after a {@link WriteConflictError},
|
|
693
|
-
* so a write that landed and then met itself (a registry whose client re-sent it) is not taken for a lost race.
|
|
694
|
-
*
|
|
695
|
-
* **A `summary` goes in the same write that moves the pointer**, so a reader that sees this generation as current
|
|
696
|
-
* sees the count and metadata that describe it, and every attempt sends the same one. It must be the shape the row's
|
|
697
|
-
* keys call for: sealed for an object written with a key, clear for one written without.
|
|
698
|
-
*/
|
|
699
|
-
export declare function publishGenerationKept(registry: IRegistryDriver, key: GenKey, options?: {
|
|
700
|
-
wrappedDeks?: readonly WrappedDek[];
|
|
701
|
-
expectFrom?: number;
|
|
702
|
-
expectToken?: Token;
|
|
703
|
-
/**
|
|
704
|
-
* The row `expectToken` names, as the caller read it. With it, a row whose token has moved but which differs from
|
|
705
|
-
* this one only in its leases (a reader took or released one) does not refuse the publish: it goes on against the
|
|
706
|
-
* fresh row, after a jittered wait and without the caller redoing its work, up to a bound. Any other difference
|
|
707
|
-
* refuses as ever. Without it, a moved token refuses.
|
|
708
|
-
*/
|
|
709
|
-
expectRow?: RegistryRecord | null;
|
|
710
|
-
/**
|
|
711
|
-
* Publish only while the segment still has **no registry row**.
|
|
712
|
-
*
|
|
713
|
-
* The fence for a caller whose decision rests on the row being ABSENT: every load that found no row sets it,
|
|
714
|
-
* guarded or not. `expectFrom` and `expectToken` cannot express it: both compare against a value read from a
|
|
715
|
-
* row, so when there was no row there is nothing to compare and both are simply omitted — leaving the publish a
|
|
716
|
-
* bare forward-only advance that happily lands over whatever appeared in the meantime.
|
|
717
|
-
*
|
|
718
|
-
* Without it, a guarded load into a segment that did not exist yet would judge "no row", pass its empty and
|
|
719
|
-
* `minRetained` bounds vacuously, and land an EMPTY generation over the ids a concurrent writer had just loaded,
|
|
720
|
-
* reporting `published: true`. An unguarded load would move the pointer over another first load's generation or
|
|
721
|
-
* onto a row `setRetention` made, and, where an erasure had deleted its object above that row's pointer, onto an
|
|
722
|
-
* object that is gone. Materialising into a destination that does not exist yet is the ordinary first run of a
|
|
723
|
-
* pipeline, so the `*Into` verbs reach this as often as `loadSegment` does.
|
|
724
|
-
*/
|
|
725
|
-
expectAbsent?: boolean;
|
|
726
|
-
/**
|
|
727
|
-
* The generation's summary (see {@link RegistrySummary}), written by the write that moves the pointer to it. It
|
|
728
|
-
* names `key.generation`. Absent, the row is left with none for this generation.
|
|
729
|
-
*/
|
|
730
|
-
summary?: RegistrySummary;
|
|
731
|
-
/**
|
|
732
|
-
* The segment's row as the caller already read it (`null`: it found none). The first attempt acts on it
|
|
733
|
-
* instead of reading the row again, which is sound because that attempt writes only under the row's own
|
|
734
|
-
* fence: its compare-and-swap carries this row's token, and with no row it creates create-only, so a row
|
|
735
|
-
* that changed or appeared since makes the write lose and the next attempt reads the row afresh. Only the
|
|
736
|
-
* idempotent "already current" answer writes nothing, so a reused row never gives it.
|
|
737
|
-
*/
|
|
738
|
-
row?: RegistryRecord | null;
|
|
739
|
-
/**
|
|
740
|
-
* The object was written without encryption. Its publish is refused, with {@link KeyUnavailableError}, onto a row
|
|
741
|
-
* that carries key material: a reader of that segment opens every generation with its key, and a later
|
|
742
|
-
* `destroySegment` would attest that shredding it made a readable object unreadable. A caller decides its key
|
|
743
|
-
* from the row it read before the write, so this closes the window in which another writer creates the
|
|
744
|
-
* segment encrypted meanwhile.
|
|
745
|
-
*/
|
|
746
|
-
cleartext?: boolean;
|
|
747
|
-
/**
|
|
748
|
-
* Whether the object under `key` is the one the caller wrote, and throws when it cannot tell. Asked only when a
|
|
749
|
-
* registry write failed and the row then names `key.generation`, to tell this publish's write from another
|
|
750
|
-
* writer's at the same number. Every caller that writes the object passes it. Without it, a write that ended
|
|
751
|
-
* without an answer over a row that names `key.generation` cannot be settled: the publish throws
|
|
752
|
-
* {@link TransientError} and the caller keeps its object.
|
|
753
|
-
*/
|
|
754
|
-
holdsOwnObject?: () => Promise<boolean>;
|
|
755
|
-
/**
|
|
756
|
-
* What a publish waits on before it sends a fresh compare-and-swap, after a write that ended without an answer and
|
|
757
|
-
* left the row as it was. Without a `sleep`, such a write throws at once instead of being sent again.
|
|
758
|
-
*/
|
|
759
|
-
clock?: Yielder;
|
|
760
|
-
/** Spreads that wait (full jitter) so concurrent publishers do not retry together. Without it the wait is the bound. */
|
|
761
|
-
rng?: Rng;
|
|
762
|
-
/**
|
|
763
|
-
* Called once for each registry write that ended without an answer, before the row is read back. A caller whose
|
|
764
|
-
* publish then answers `false` can say that it cannot tell whether its own write landed first: the row may have
|
|
765
|
-
* held its generation for a while before another writer moved on.
|
|
766
|
-
*/
|
|
767
|
-
onUnanswered?: () => void;
|
|
768
|
-
/**
|
|
769
|
-
* The clock that judges whether a lease has ended. With it, a publish over a row that holds ended leases drops
|
|
770
|
-
* them in the write that moves the pointer, which rewrites the row anyway. Without it, leases are left as they are.
|
|
771
|
-
*/
|
|
772
|
-
leasesNow?: () => number;
|
|
773
|
-
/**
|
|
774
|
-
* Clear the row's leases in the write that moves the pointer: the caller's collection deletes every generation
|
|
775
|
-
* below the new pointer whatever a lease says, as a subject erasure's does.
|
|
776
|
-
*/
|
|
777
|
-
clearLeases?: boolean;
|
|
778
|
-
/**
|
|
779
|
-
* How many generations below the new pointer the caller keeps. With it, the write that moves the pointer also
|
|
780
|
-
* records the window in the row (`keptGens`: the newest `keep` of the row's own list and the generation that was
|
|
781
|
-
* current), derived from the very row the write is made against, and the result names the generations the window
|
|
782
|
-
* dropped. Without it, or above the most a row records, the row records none, and a collection lists.
|
|
783
|
-
*/
|
|
784
|
-
keep?: number;
|
|
785
|
-
}): Promise<PublishResult>;
|
|
786
|
-
/** What a publish did: whether the pointer is at the generation, and what the write recorded in the row. */
|
|
787
|
-
export interface PublishResult {
|
|
788
|
-
readonly published: boolean;
|
|
789
|
-
/**
|
|
790
|
-
* What the write that moved the pointer recorded: the list, the generations the window dropped, and the new row's
|
|
791
|
-
* token. Absent when no write of this call is known to have landed (the pointer was already there, or the outcome
|
|
792
|
-
* was settled without knowing which write it was), and a collection then lists.
|
|
793
|
-
*/
|
|
794
|
-
readonly kept?: PublishedKept;
|
|
795
|
-
/**
|
|
796
|
-
* A token of the row whose pointer is at the generation, when it is published: the one the write was given, or the
|
|
797
|
-
* row read that showed the pointer there. Every write of a row keeps its incarnation, so this names the incarnation
|
|
798
|
-
* the publish landed in, for the audit event.
|
|
799
|
-
*/
|
|
800
|
-
readonly token?: Token;
|
|
801
|
-
}
|
|
802
|
-
/**
|
|
803
|
-
* {@link publishGenerationKept}'s answer to "did the pointer move", for a caller with no use for what it recorded:
|
|
804
|
-
* a publish with no `keep` leaves the row recording no list.
|
|
805
|
-
*/
|
|
806
|
-
export declare function publishGeneration(registry: IRegistryDriver, key: GenKey, options?: Parameters<typeof publishGenerationKept>[2]): Promise<boolean>;
|
|
807
|
-
/**
|
|
808
|
-
* Whether the object under `key` is the one `fingerprint` names, from one read of its footer: false when it is another
|
|
809
|
-
* object or gone. Unlike {@link provenObject}, a read that cannot tell throws, for a caller that must not take "could
|
|
810
|
-
* not look" for "not this object".
|
|
811
|
-
*/
|
|
812
|
-
export declare function provesOwnObject(storage: IStorageDriver, key: GenKey, fingerprint: string): Promise<boolean>;
|
|
813
|
-
/** What a bulk-load wrote — the driver's `{ size, sha256 }` plus a summary of the built generation. */
|
|
814
|
-
export interface BulkLoadResult {
|
|
815
|
-
/** Bytes written to the Storage object. */
|
|
816
|
-
readonly size: number;
|
|
817
|
-
/** The driver's content hash of the object. */
|
|
818
|
-
readonly sha256: string;
|
|
819
|
-
/** Distinct non-empty chunks written (≤ 65536). */
|
|
820
|
-
readonly chunkCount: number;
|
|
821
|
-
/**
|
|
822
|
-
* The DEK freshly minted for this generation, wrapped — present only when the segment is encrypted AND this
|
|
823
|
-
* call minted one (an existing segment reuses its key, which is already on the row).
|
|
824
|
-
*
|
|
825
|
-
* Returned so a caller that deliberately does **not** pass `registry` here — writing now and publishing later,
|
|
826
|
-
* which is how a load applies a guard while the old generation is still authoritative — can carry the key onto
|
|
827
|
-
* its own `publishGeneration`. Without it that publish stores no DEK and the generation it makes current is
|
|
828
|
-
* unreadable: the bytes are encrypted with a key nothing recorded.
|
|
829
|
-
*/
|
|
830
|
-
readonly wrappedDeks?: readonly WrappedDek[];
|
|
831
|
-
/** Whether the object was written encrypted: under the segment's key, or under one this call minted. */
|
|
832
|
-
readonly encrypted: boolean;
|
|
833
|
-
/** The written object's fingerprint, which tells it from an object stored later under the same key. */
|
|
834
|
-
readonly fingerprint: string;
|
|
835
|
-
/** Total distinct ids in the generation (post-dedup). */
|
|
836
|
-
readonly cardinality: number;
|
|
837
|
-
/**
|
|
838
|
-
* The summary of what was written, for the write that makes it current: its id count and metadata, sealed under the
|
|
839
|
-
* segment's key when the object was written with one. A caller that defers the publish (`publish: false`) passes it
|
|
840
|
-
* to {@link publishGeneration}.
|
|
841
|
-
*/
|
|
842
|
-
readonly summary: RegistrySummary;
|
|
843
|
-
/**
|
|
844
|
-
* Whether this generation is now the segment's **current** one — `undefined` when no `registry` was wired, so
|
|
845
|
-
* there is no pointer and no publish step to report on.
|
|
846
|
-
*
|
|
847
|
-
* `false` means the object is durable but a **concurrent writer published a higher generation first**, so this
|
|
848
|
-
* one is an orphan that no reader will ever resolve. The write succeeded and the *load* did not: whatever this
|
|
849
|
-
* call was asked to make the segment contain, the segment does not contain. That is not a detail a caller can
|
|
850
|
-
* be left to infer — a `*Into` materialisation that reported the generation it wrote as "the destination's new
|
|
851
|
-
* current generation" on this path would state a plain untruth — so the flag is on the result and the verbs that
|
|
852
|
-
* promise a published generation check it.
|
|
853
|
-
*/
|
|
854
|
-
readonly becameCurrent?: boolean;
|
|
855
|
-
}
|
|
856
|
-
/**
|
|
857
|
-
* **Bulk-load** a whole immutable generation from a flat id source — the batch "seed/rebuild" entry point.
|
|
858
|
-
* Unlike {@link writeCrbmGeneration} (which takes pre-grouped bitmaps), this
|
|
859
|
-
* consumes an arbitrary, **unsorted** stream of u32 ids (sync or async) and routes each into its chunk's
|
|
860
|
-
* Roaring bitmap as it goes. The input is consumed lazily and ids dedup on insert, so memory is bounded by
|
|
861
|
-
* the **built generation** — the in-memory Roaring representation of the *distinct* set (≤ one bitmap per
|
|
862
|
-
* non-empty chunk) — not by the input length: you can stream a billion duplicate-heavy ids and hold only the
|
|
863
|
-
* distinct result. Note this is `O(distinct set)`, **not** window-bounded like {@link SegmentEngine.intersect}:
|
|
864
|
-
* bulk-load holds the whole generation in RAM, which suits batch seed/rebuild jobs but not an
|
|
865
|
-
* unbounded-cardinality stream (a segment larger than RAM needs external-merge / pre-sorted input — later phase).
|
|
866
|
-
*
|
|
867
|
-
* Each id must be an integer in `[0, 2³²)` ({@link splitId} throws {@link ValidationError} otherwise) — the
|
|
868
|
-
* source is consumed lazily, so a bad id aborts mid-stream without writing a partial object (the driver
|
|
869
|
-
* commits only after the callback resolves). An empty source writes a valid empty generation.
|
|
870
|
-
*
|
|
871
|
-
* Writes a fresh full snapshot of a segment. The caller picks the generation number in `key` — `nextGeneration`
|
|
872
|
-
* computes one from the registry and the bucket, as `nextLoadGeneration` does for a load — and re-using an existing
|
|
873
|
-
* generation throws
|
|
874
|
-
* {@link WriteConflictError} (write-once). Without a registry a `StorageChunkSource` serves the **highest**
|
|
875
|
-
* generation present, so a too-high number silently shadows real data; with one, `publishGeneration` decides.
|
|
876
|
-
*/
|
|
877
|
-
export declare function bulkLoadCrbmGeneration(driver: IStorageDriver, key: GenKey, ids: Iterable<number> | AsyncIterable<number> | DecodedLoadInput | ChunkLoadInput, options?: {
|
|
878
|
-
registry?: IRegistryDriver;
|
|
879
|
-
keystore?: IKeystore;
|
|
880
|
-
requireEncryption?: boolean;
|
|
881
|
-
audit?: IAuditSink;
|
|
882
|
-
/** Bitmap codec. Optional in the type; a **flavor** package binds it ({@link requireCodec}). */
|
|
883
|
-
codec?: CodecInterface;
|
|
884
|
-
/**
|
|
885
|
-
* Injected clock. Supplying one makes bulk-load **cooperative**: it yields the event loop periodically so a
|
|
886
|
-
* long load does not stall everything else on the process. Without it the load still completes, just
|
|
887
|
-
* without yielding — which is the pre-existing behaviour, kept so this is purely additive.
|
|
888
|
-
*
|
|
889
|
-
* `@cloudbitmaps/roaring` supplies a real clock by default, so flavor users get cooperative behaviour with
|
|
890
|
-
* no wiring. `core/` cannot default it: it is timer-free by lint, which is exactly why waiting goes through
|
|
891
|
-
* this seam rather than `setTimeout`.
|
|
892
|
-
*/
|
|
893
|
-
clock?: Clock;
|
|
894
|
-
/**
|
|
895
|
-
* Write the object but do **not** advance the pointer (default: publish when a `registry` is wired).
|
|
896
|
-
*
|
|
897
|
-
* For a caller that has to inspect what it wrote before deciding whether it should become current — a
|
|
898
|
-
* guarded {@link loadSegment} is the one in-tree case — because the check is only meaningful while the old
|
|
899
|
-
* generation is still authoritative. The `registry` is still required for an encrypted segment and still
|
|
900
|
-
* consulted: it is where an existing segment's DEK lives, and reusing that key is not optional. The wrapped
|
|
901
|
-
* DEK comes back on the result so the deferred publish can store it.
|
|
902
|
-
*
|
|
903
|
-
* A caller that defers the publish owns what it wrote: an unpublished object sits ABOVE `currentGen`, where
|
|
904
|
-
* generation collection deliberately never looks, so nothing reclaims it until a later generation above it is
|
|
905
|
-
* current.
|
|
906
|
-
*/
|
|
907
|
-
publish?: boolean;
|
|
908
|
-
/**
|
|
909
|
-
* The generation's metadata (see {@link GenerationMetadata}), written into the object and carried in the summary.
|
|
910
|
-
* The caller has checked it against the metadata rules; the writer checks it again, and refuses a record that breaks
|
|
911
|
-
* one before a byte is written.
|
|
912
|
-
*/
|
|
913
|
-
metadata?: GenerationMetadata;
|
|
914
|
-
/**
|
|
915
|
-
* The segment's row as the caller already read it (`null`: it found none), for a caller that defers the
|
|
916
|
-
* publish (`publish: false`) and fences it on that same row. A present cleartext row is then used as read,
|
|
917
|
-
* rather than read again: a row that changes in between (a publish, a drop, a purge and re-create) makes the
|
|
918
|
-
* fenced publish lose, so the object is never published on the strength of the stale read. A row read as
|
|
919
|
-
* absent, or one carrying key material, is read again after the ids, as with no row passed: a first load must
|
|
920
|
-
* see a row another writer created meanwhile, and an encrypted segment's key is unwrapped only from a row read
|
|
921
|
-
* after the ids, so a segment shredded while they streamed is refused before its key is used.
|
|
922
|
-
*/
|
|
923
|
-
row?: RegistryRecord | null;
|
|
924
|
-
}): Promise<BulkLoadResult>;
|
|
925
|
-
/**
|
|
926
|
-
* What a load has already started when it calls {@link bulkLoadAhead}: its number, and the segment's data key, each
|
|
927
|
-
* still in flight so their round trips overlap the encoding. Both are joined where the write needs them, and neither
|
|
928
|
-
* may be left to reject unobserved.
|
|
929
|
-
*/
|
|
930
|
-
export interface LoadAhead {
|
|
931
|
-
/** The number the generation takes (see {@link nextLoadGeneration}); joined just before the object is written. */
|
|
932
|
-
readonly generation: PromiseLike<number>;
|
|
933
|
-
/**
|
|
934
|
-
* The key a caller already asked the keystore to unwrap, with the wrappings it asked about. Used only when the
|
|
935
|
-
* row the write reads after the ids carries exactly these wrappings; otherwise the write unwraps for itself.
|
|
936
|
-
*/
|
|
937
|
-
readonly unwrapped?: {
|
|
938
|
-
readonly wrapped: readonly WrappedDek[];
|
|
939
|
-
readonly aead: PromiseLike<Aead>;
|
|
940
|
-
};
|
|
941
|
-
}
|
|
942
|
-
/** What a load with `requireEncryption` meets on a segment that already has a cleartext generation. */
|
|
943
|
-
export declare function cleartextUnderRequiredEncryption(ref: SegmentRef, currentGen: number): ValidationError;
|
|
944
|
-
/**
|
|
945
|
-
* {@link bulkLoadCrbmGeneration} for a load whose number, and perhaps its key, are still being fetched: the same
|
|
946
|
-
* options, with the same meaning, and the same writes. The number is joined only where the object is written.
|
|
947
|
-
*/
|
|
948
|
-
export declare function bulkLoadAhead(driver: IStorageDriver, ref: SegmentRef, ahead: LoadAhead, ids: Parameters<typeof bulkLoadCrbmGeneration>[2], options?: NonNullable<Parameters<typeof bulkLoadCrbmGeneration>[3]>): Promise<BulkLoadResult>;
|
|
949
|
-
/**
|
|
950
|
-
* Open a {@link CrbmReader} on one generation over the storage driver's range/tail reads (decrypting if `crypto`).
|
|
951
|
-
* The reader the write paths use to re-read what they wrote, and the erasure rewrite uses to stream the old
|
|
952
|
-
* generation; the engine's read path goes through {@link CrbmStorageChunkSource} instead, which caches these. With
|
|
953
|
-
* `named`, the fingerprint the row's summary records, the object must be that one: another is {@link NotFoundError}.
|
|
954
|
-
* The version of the object it opened, from the tail read the open made, is what {@link objectVersionOf} answers.
|
|
955
|
-
*/
|
|
956
|
-
export declare function openGenerationReader(storage: IStorageDriver, key: GenKey, crypto: CrbmCrypto | undefined, options?: Omit<CrbmReaderOptions, 'crypto'>, named?: string): Promise<CrbmReader>;
|
|
957
|
-
/**
|
|
958
|
-
* {@link openGenerationReader}, with `noteVersion` shown the version {@link objectVersionOf} would answer as soon as the
|
|
959
|
-
* tail read that carries it returns, before the open goes on: a caller whose open then fails, on an object sealed under
|
|
960
|
-
* another key, say, still knows which object it met, and can condition a delete of it on that read.
|
|
961
|
-
*/
|
|
962
|
-
export declare function openGenerationReaderNoting(storage: IStorageDriver, key: GenKey, crypto: CrbmCrypto | undefined, noteVersion: (version: string) => void): Promise<CrbmReader>;
|
|
963
|
-
/**
|
|
964
|
-
* The version of the object `reader` was opened on, as the driver reported it on the tail read the open made, for a
|
|
965
|
-
* delete conditioned on that object ({@link StorageDeleteOptions.ifVersion}); `undefined` for a reader
|
|
966
|
-
* {@link openGenerationReader} did not open, or a driver that reports none. Held beside the reader rather than on it, so
|
|
967
|
-
* nothing is added to the reader's public surface, and no read the engine makes pays for it.
|
|
968
|
-
*/
|
|
969
|
-
export declare function objectVersionOf(reader: CrbmReader): string | undefined;
|
|
970
|
-
/**
|
|
971
|
-
* {@link openGenerationReader}, of the object `fingerprint` names: `undefined` when the object under the key is another
|
|
972
|
-
* one, found from its footer before its index is opened, so an object sealed under another key is told apart as well.
|
|
973
|
-
*/
|
|
974
|
-
export declare function openNamedGeneration(storage: IStorageDriver, key: GenKey, crypto: CrbmCrypto | undefined, fingerprint: string): Promise<CrbmReader | undefined>;
|
|
975
|
-
/**
|
|
976
|
-
* Re-open a freshly written generation and assert it round-trips exactly what was streamed into it: the same
|
|
977
|
-
* per-chunk key set *and* the same total cardinality (on top of the codec's own per-chunk CRC + footer checks), and the
|
|
978
|
-
* metadata it was given. `expected` is the streaming writer's tally (the stream is consumed, so it can't be
|
|
979
|
-
* re-iterated) — the key-set comparison catches a dropped/extra chunk that a cardinality-only check could miss when
|
|
980
|
-
* two errors cancel out. The count and the metadata are what the generation's row summary is about to claim, so the
|
|
981
|
-
* summary is held to the object it describes before it is published.
|
|
982
|
-
* Throws {@link IntegrityError}: the object is on disk but must not be published.
|
|
983
|
-
*/
|
|
984
|
-
export declare function verifyGeneration(storage: IStorageDriver, key: GenKey, expected: {
|
|
985
|
-
readonly chunkKeys: readonly number[];
|
|
986
|
-
readonly cardinality: number;
|
|
987
|
-
readonly metadata?: GenerationMetadata | undefined;
|
|
988
|
-
}, crypto: CrbmCrypto | undefined): Promise<void>;
|