@cloudbitmaps/core 0.19.1 → 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-RSLVTRJY.js → chunk-IGLIZBR7.js} +982 -281
- package/dist/chunk-IGLIZBR7.js.map +7 -0
- package/dist/core/absent-operands.d.ts +8 -0
- package/dist/core/audit.d.ts +67 -4
- 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 +21 -15
- package/dist/core/concurrency.d.ts +8 -0
- package/dist/core/consistency.d.ts +76 -18
- package/dist/core/crbm/fingerprint.d.ts +23 -0
- package/dist/core/crbm/format.d.ts +2 -5
- package/dist/core/crbm/frame.d.ts +10 -0
- package/dist/core/crbm/plan-reads.d.ts +9 -3
- package/dist/core/crbm/reader.d.ts +51 -12
- 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 +243 -490
- package/dist/core/crbm-write.d.ts +75 -0
- package/dist/core/crypto.d.ts +6 -1
- package/dist/core/engine.d.ts +239 -47
- package/dist/core/erase-id.d.ts +97 -27
- package/dist/core/erasure.d.ts +4 -3
- package/dist/core/errors.d.ts +8 -2
- package/dist/core/generation-gc.d.ts +131 -16
- 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 +9 -1
- package/dist/core/ledger-seed.d.ts +17 -0
- package/dist/core/load.d.ts +17 -36
- package/dist/core/lru.d.ts +2 -15
- package/dist/core/metrics.d.ts +9 -7
- package/dist/core/order.d.ts +2 -0
- package/dist/core/pinned-storage-source.d.ts +7 -7
- package/dist/core/pointer-id.d.ts +19 -0
- package/dist/core/ports.d.ts +210 -45
- package/dist/core/publish.d.ts +207 -0
- package/dist/core/reader-defaults.d.ts +14 -3
- 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 +62 -32
- package/dist/core/summary.d.ts +74 -25
- package/dist/core/token.d.ts +13 -8
- package/dist/core/validate.d.ts +2 -0
- package/dist/driver-kit.d.ts +6 -1
- package/dist/driver-kit.js +90 -10
- 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 +3 -1
- package/dist/drivers/_shared/options.d.ts +19 -0
- package/dist/drivers/_shared/registry.d.ts +31 -19
- 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 +16 -8
- package/dist/drivers/memory.d.ts +14 -9
- package/dist/drivers/retry/retrying-chunk-source.d.ts +1 -5
- package/dist/export/index.d.ts +16 -0
- package/dist/index.d.ts +2 -5
- package/dist/index.js +4303 -2938
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
- package/dist/chunk-RSLVTRJY.js.map +0 -7
- package/dist/core/cost.d.ts +0 -351
- package/dist/core/kept-generations.d.ts +0 -40
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { Yielder } from './cooperative.js';
|
|
2
|
+
import type { CrbmCrypto } from './crypto.js';
|
|
3
|
+
import type { GenerationMetadata, GenKey, IStorageDriver } from './ports.js';
|
|
4
|
+
import type { CodecBitmap, EncodedChunk } from './codec.js';
|
|
5
|
+
/**
|
|
6
|
+
* Write one immutable generation from in-memory bitmaps. Chunks are
|
|
7
|
+
* sorted ascending and empty bitmaps skipped (empty chunks are never stored). Returns the driver's
|
|
8
|
+
* `{ size, sha256 }` for the written object. Pass `options.crypto` to AES-256-GCM-encrypt the generation
|
|
9
|
+
* (built from the segment's DEK, with associated data bound to `(segment, generation)`).
|
|
10
|
+
*/
|
|
11
|
+
export declare function writeCrbmGeneration(driver: IStorageDriver, key: GenKey, chunks: Iterable<{
|
|
12
|
+
chunkKey: number;
|
|
13
|
+
bitmap: CodecBitmap;
|
|
14
|
+
}>, options?: {
|
|
15
|
+
crypto?: CrbmCrypto;
|
|
16
|
+
clock?: Yielder;
|
|
17
|
+
metadata?: GenerationMetadata;
|
|
18
|
+
}): Promise<{
|
|
19
|
+
size: number;
|
|
20
|
+
sha256: string;
|
|
21
|
+
fingerprint: string;
|
|
22
|
+
tailBytes: number;
|
|
23
|
+
}>;
|
|
24
|
+
/** Each non-empty bitmap as the chunk it stores, in the order given. */
|
|
25
|
+
export declare function encodeEach(chunks: Iterable<{
|
|
26
|
+
chunkKey: number;
|
|
27
|
+
bitmap: CodecBitmap;
|
|
28
|
+
}>): Generator<EncodedChunk>;
|
|
29
|
+
/**
|
|
30
|
+
* Write one immutable generation from chunks already encoded, which must ascend by key (the writer refuses one
|
|
31
|
+
* that does not). Returns the driver's `{ size, sha256 }` and a tally of what was written.
|
|
32
|
+
*/
|
|
33
|
+
export declare function writeEncodedChunks(driver: IStorageDriver, key: GenKey, chunks: Iterable<EncodedChunk>, options: {
|
|
34
|
+
crypto?: CrbmCrypto;
|
|
35
|
+
clock?: Yielder;
|
|
36
|
+
metadata?: GenerationMetadata;
|
|
37
|
+
}): Promise<{
|
|
38
|
+
size: number;
|
|
39
|
+
sha256: string;
|
|
40
|
+
chunkCount: number;
|
|
41
|
+
cardinality: number;
|
|
42
|
+
fingerprint: string;
|
|
43
|
+
/** The bytes after the chunk payloads ({@link CrbmReader.tailBytes}). */
|
|
44
|
+
tailBytes: number;
|
|
45
|
+
}>;
|
|
46
|
+
/** What {@link writeCrbmGenerationStream} wrote: the driver's `{ size, sha256 }` + a tally of the generation. */
|
|
47
|
+
interface StreamWriteResult {
|
|
48
|
+
readonly size: number;
|
|
49
|
+
readonly sha256: string;
|
|
50
|
+
/** Non-empty chunk keys written, in ascending order (for the post-write verify, since the stream is consumed). */
|
|
51
|
+
readonly chunkKeys: number[];
|
|
52
|
+
/** Total ids written. */
|
|
53
|
+
readonly cardinality: number;
|
|
54
|
+
/** The fingerprint of what this call wrote ({@link CrbmReader.fingerprint}), which tells it from another object under its key. */
|
|
55
|
+
readonly fingerprint: string;
|
|
56
|
+
/** The bytes after the chunk payloads ({@link CrbmReader.tailBytes}). */
|
|
57
|
+
readonly tailBytes: number;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Streaming variant of {@link writeCrbmGeneration} for a **constant-memory** rewrite (the erasure rewrite, a
|
|
61
|
+
* sorted load): consumes an **already-ascending** async stream of `{ chunkKey, bitmap }`, feeding each to the
|
|
62
|
+
* codec and freeing it, instead of materializing the whole generation. Paired with a streaming storage sink (S3
|
|
63
|
+
* multipart / LocalFs temp file), the writer itself holds ~one chunk + one part. Returns a tally so the caller can verify
|
|
64
|
+
* the re-opened object without re-iterating the (now-consumed) stream. Input **must** be ascending by `chunkKey`
|
|
65
|
+
* — the codec rejects an out-of-order chunk; empty bitmaps are skipped.
|
|
66
|
+
*/
|
|
67
|
+
export declare function writeCrbmGenerationStream(driver: IStorageDriver, key: GenKey, chunks: AsyncIterable<{
|
|
68
|
+
chunkKey: number;
|
|
69
|
+
bitmap: CodecBitmap;
|
|
70
|
+
}>, options?: {
|
|
71
|
+
crypto?: CrbmCrypto;
|
|
72
|
+
clock?: Yielder;
|
|
73
|
+
metadata?: GenerationMetadata;
|
|
74
|
+
}): Promise<StreamWriteResult>;
|
|
75
|
+
export {};
|
package/dist/core/crypto.d.ts
CHANGED
|
@@ -56,7 +56,10 @@ export interface IKeystore {
|
|
|
56
56
|
/**
|
|
57
57
|
* Reconstruct the {@link Aead} from a segment's stored wrappings by unwrapping with any KEK currently held.
|
|
58
58
|
* Throws {@link KeyUnavailableError} if it holds none of the referenced KEKs (lost key, or a shredded
|
|
59
|
-
* segment whose wrappings were deleted).
|
|
59
|
+
* segment whose wrappings were deleted). Throw {@link TransientError} for a fault worth riding out (a throttle, a 5xx,
|
|
60
|
+
* a dropped connection, a request timeout): a rollback below a segment's pointer goes ahead without the key on that
|
|
61
|
+
* error, as on `KeyUnavailableError`, an `IntegrityError` or an `Error` named `TimeoutError`, and is stopped by any
|
|
62
|
+
* other error the keystore throws.
|
|
60
63
|
*/
|
|
61
64
|
openDek(wrapped: readonly WrappedDek[]): Promise<Aead>;
|
|
62
65
|
}
|
|
@@ -82,3 +85,5 @@ export declare function aadFor(ref: {
|
|
|
82
85
|
readonly namespace?: string;
|
|
83
86
|
readonly segment: string;
|
|
84
87
|
}, generation: number, scope: number | 'index' | 'metadata' | 'summary'): Uint8Array;
|
|
88
|
+
/** Whether two rows carry the same wrapped keys. */
|
|
89
|
+
export declare const sameKeys: (a: readonly WrappedDek[] | undefined, b: readonly WrappedDek[]) => boolean;
|
package/dist/core/engine.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { Budget, BudgetOption } from './budget.js';
|
|
|
3
3
|
import type { Clock } from './determinism.js';
|
|
4
4
|
import type { BoundedLru } from './lru.js';
|
|
5
5
|
import type { IMetricsSink } from './metrics.js';
|
|
6
|
-
import type { GenerationMetadata, StorageChunkSource, SegmentRef
|
|
6
|
+
import type { GenerationMetadata, StorageChunkSource, SegmentRef } from './ports.js';
|
|
7
7
|
/**
|
|
8
8
|
* Range requests a stream holds ahead of a combine or `iterate` by default (chunk keys, on a source that reads chunk by
|
|
9
9
|
* chunk, and for `count`'s fallback) — bounds memory and concurrent reads (invariant 6). A read that does not fit
|
|
@@ -13,7 +13,11 @@ import type { GenerationMetadata, StorageChunkSource, SegmentRef, SegmentSize }
|
|
|
13
13
|
export declare const DEFAULT_INTERSECT_CONCURRENCY = 32;
|
|
14
14
|
export interface EngineDeps {
|
|
15
15
|
readonly storage: StorageChunkSource;
|
|
16
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Optional cache of decoded (immutable) Storage chunks. A cache whose `size` is 0 is taken to hold nothing: a read
|
|
18
|
+
* that opens a stream on it looks up the chunk the stream starts at and counts each later chunk a miss without
|
|
19
|
+
* looking it up.
|
|
20
|
+
*/
|
|
17
21
|
readonly cache?: BoundedLru<string, CodecBitmap>;
|
|
18
22
|
/** Per-chunk decode ceiling (invariant 5); defaults to 1 MiB. The `.crbm` reader refuses an entry above its own `maxPayloadBytes` (1 MiB, plus 28 bytes when encrypted) at open, so raise that on the chunk source too. */
|
|
19
23
|
readonly maxBitmapBytes?: number;
|
|
@@ -33,6 +37,19 @@ export interface EngineDeps {
|
|
|
33
37
|
* Read ops refuse before fan-out if they'd exceed it.
|
|
34
38
|
*/
|
|
35
39
|
readonly budget?: Budget | null;
|
|
40
|
+
/**
|
|
41
|
+
* An engine over the same `cache` whose invalidations this one shares: an {@link SegmentEngine.invalidate} through
|
|
42
|
+
* either reaches the reads running on both. It is opt-in, and engines that read through one chunk cache must be
|
|
43
|
+
* built with it: two built without it share nothing, so a read running on one when the other invalidates its
|
|
44
|
+
* segment writes the chunks it read before the invalidation back into the cache, and later reads through it are
|
|
45
|
+
* answered from them, an erased id included, until the cache evicts them. The `CloudRoaring` facade builds the
|
|
46
|
+
* engines of its pins and of its combines with a pinned operand this way.
|
|
47
|
+
*
|
|
48
|
+
* The engines share their requests in flight too: a cache miss on one joins a request the other opened for the same
|
|
49
|
+
* cache key, and gets what that engine's storage, codec and `maxBitmapBytes` made of it, as a cache hit hands over
|
|
50
|
+
* the chunk the other decoded. Throws {@link ValidationError} unless it is a `SegmentEngine` over the same `cache`.
|
|
51
|
+
*/
|
|
52
|
+
readonly sharesInvalidationsWith?: SegmentEngine;
|
|
36
53
|
}
|
|
37
54
|
/**
|
|
38
55
|
* A range of ids for a read to yield: those greater than `after` and no greater than `through`, `(after, through]`.
|
|
@@ -44,8 +61,9 @@ export interface EngineDeps {
|
|
|
44
61
|
* a combine's operands exist.
|
|
45
62
|
*
|
|
46
63
|
* A read with a range fetches only the chunks the range overlaps, on a combine for every operand and every
|
|
47
|
-
* `exclude`. The per-op budget is charged
|
|
48
|
-
*
|
|
64
|
+
* `exclude`. The per-op budget is charged before the first fetch, for every chunk in the range, so with `after` alone
|
|
65
|
+
* it is charged to the end of the segment however early the caller stops; pass `through` to bound it. A read that
|
|
66
|
+
* lists its keys again after a move is charged again, with the chunks it has already taken.
|
|
49
67
|
*/
|
|
50
68
|
export interface IdRange {
|
|
51
69
|
/** Exclusive lower bound: the read yields only ids greater than this. */
|
|
@@ -63,7 +81,7 @@ export interface CombineOptions extends IdRange {
|
|
|
63
81
|
* is larger), about `concurrency × operands` MiB are held, whatever the segment's size. The stream opens 4 ranges wide
|
|
64
82
|
* (or `concurrency`, if lower) and widens as ranges are taken, so a read that stops early has asked for little past
|
|
65
83
|
* where it stopped, and a read that fits one range makes one request however many chunks it needs. A source that
|
|
66
|
-
* reads chunk by chunk (a custom one) is read
|
|
84
|
+
* reads chunk by chunk (a custom one) is read `concurrency` chunk keys at once, opening 8 wide. A running
|
|
67
85
|
* stream holds its operand's reader outside the reader cache's bounds until it ends or the segment moves on.
|
|
68
86
|
*/
|
|
69
87
|
readonly concurrency?: number;
|
|
@@ -94,18 +112,10 @@ export declare class SegmentEngine {
|
|
|
94
112
|
/** Resolved per-op budget (null = disabled); undefined deps ⇒ the generous default. */
|
|
95
113
|
private readonly budget;
|
|
96
114
|
/**
|
|
97
|
-
* The
|
|
98
|
-
*
|
|
99
|
-
* promise per distinct key in flight.
|
|
100
|
-
*/
|
|
101
|
-
private readonly openReads;
|
|
102
|
-
/**
|
|
103
|
-
* The chunk streams open right now. An {@link invalidate} of a segment marks its streams, which then cache no chunk
|
|
104
|
-
* they deliver, as a read whose entry an invalidation dropped does not (streams hold no `openReads` entry).
|
|
115
|
+
* The invalidations this engine shares with every engine over its chunk cache that was built to share them
|
|
116
|
+
* ({@link EngineDeps.sharesInvalidationsWith}), and the chunk reads open on all of them.
|
|
105
117
|
*/
|
|
106
|
-
private readonly
|
|
107
|
-
/** How many times {@link invalidate} has been called: a read compares it with the count it began under. */
|
|
108
|
-
private invalidations;
|
|
118
|
+
private readonly scope;
|
|
109
119
|
/**
|
|
110
120
|
* The cache-key prefixes ({@link segmentPrefix}) of the segments this engine reads, kept rather than encoded for each
|
|
111
121
|
* chunk. A kept prefix is the one {@link invalidate} finds the segment's chunks by.
|
|
@@ -128,26 +138,32 @@ export declare class SegmentEngine {
|
|
|
128
138
|
*/
|
|
129
139
|
count(seg: SegmentRef): Promise<number>;
|
|
130
140
|
/**
|
|
131
|
-
* What the segment's current generation is: its number, its id count
|
|
132
|
-
*
|
|
141
|
+
* What the segment's current generation is: its number, its id count, its metadata and the size of its object in
|
|
142
|
+
* bytes. A source with a `stat` (the `.crbm` source) answers all four from one resolution, so they cannot straddle
|
|
143
|
+
* a publish: from the registry row's summary when it can use it, else from the opened generation's footer and
|
|
144
|
+
* index, with no payload read. A source without one answers from its other methods, each its own resolution: its
|
|
145
|
+
* summary when it has one, else `count()` and the generation it resolves, and its `sizeOf`, or `sizeBytes: null`
|
|
146
|
+
* when it has none. A segment with no generation answers `{ generation: null, cardinality: 0, sizeBytes: null }`.
|
|
133
147
|
*/
|
|
134
148
|
stat(seg: SegmentRef): Promise<{
|
|
135
149
|
generation: number | null;
|
|
136
150
|
cardinality: number;
|
|
137
151
|
metadata?: GenerationMetadata;
|
|
152
|
+
sizeBytes: number | null;
|
|
138
153
|
}>;
|
|
139
|
-
/**
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
/** The chunks of `seg` at `chunkKeys`, read through a window of {@link DEFAULT_INTERSECT_CONCURRENCY}. */
|
|
154
|
+
/** The size of the segment's current generation from the source's `sizeOf`, or `null` when it cannot say. */
|
|
155
|
+
private sizeOf;
|
|
156
|
+
/**
|
|
157
|
+
* The chunks of `seg` at `chunkKeys`, read through a window of {@link DEFAULT_INTERSECT_CONCURRENCY}. A cached chunk
|
|
158
|
+
* that is no longer the segment's version marks `moves`, when given.
|
|
159
|
+
*/
|
|
146
160
|
private chunkWindow;
|
|
147
161
|
/**
|
|
148
162
|
* The chunks of `seg` at `chunkKeys`, taken in ascending key order by a read that may stop at any point: one stream of
|
|
149
163
|
* coalesced ranges when the source has `getChunks`, opening 1, 2, 4 … ranges wide up to {@link DEFAULT_INTERSECT_CONCURRENCY},
|
|
150
|
-
* else the chunk-by-chunk window of that many reads. `close` stops what is left of it.
|
|
164
|
+
* else the chunk-by-chunk window of that many reads. `close` stops what is left of it. A chunk that shows the segment
|
|
165
|
+
* moved marks `moves`: one of the stream named by another version, or one cached under `gen` that is no longer the
|
|
166
|
+
* segment's.
|
|
151
167
|
*/
|
|
152
168
|
private chunkSequence;
|
|
153
169
|
/**
|
|
@@ -157,9 +173,13 @@ export declare class SegmentEngine {
|
|
|
157
173
|
* chunks the range overlaps (see {@link IdRange}). A source that reads chunk by chunk is read through a window of up to
|
|
158
174
|
* 32 chunk fetches that opens 1, 2, 4 … 32 wide.
|
|
159
175
|
*
|
|
176
|
+
* The chunk keys are listed once, before the first fetch. A move of the segment the read meets while it runs (see
|
|
177
|
+
* {@link Moves}) lists them again, from the generation current then, and the read goes on with the keys above the one
|
|
178
|
+
* it last took: it reads a chunk the new generation added, and not one it dropped.
|
|
179
|
+
*
|
|
160
180
|
* Two generators, not one with a branch: a second `yield` site in the full read's generator grows its frame, and
|
|
161
|
-
* every id of a read that asked for no range paid for it (measured at about 5% per id). The full read keeps
|
|
162
|
-
* loop
|
|
181
|
+
* every id of a read that asked for no range paid for it (measured at about 5% per id). The full read keeps one
|
|
182
|
+
* `yield`, in a loop of its own.
|
|
163
183
|
*/
|
|
164
184
|
iterate(seg: SegmentRef, range?: IdRange): AsyncGenerator<number>;
|
|
165
185
|
private iterateAll;
|
|
@@ -167,10 +187,71 @@ export declare class SegmentEngine {
|
|
|
167
187
|
/**
|
|
168
188
|
* {@link iterate}, one `Uint32Array` per chunk instead of one id at a time: the same chunks, in the same order, with
|
|
169
189
|
* the same ids (a range cut at its edges the same way), and no array for a chunk that holds none of them. The
|
|
170
|
-
* reads, the budget charge
|
|
171
|
-
* the caller's to keep.
|
|
190
|
+
* reads, the budget charge, the window and a move met while it runs are the per-id read's. Each array is at most
|
|
191
|
+
* 65,536 ids (256 KiB) and is the caller's to keep.
|
|
172
192
|
*/
|
|
173
193
|
iterateBatches(seg: SegmentRef, range?: IdRange): AsyncGenerator<Uint32Array>;
|
|
194
|
+
/**
|
|
195
|
+
* Plan an `iterate` of the keys of `seg` above `after` and inside `w`: list them with their version
|
|
196
|
+
* ({@link listKeys}), charge the budget one storage fetch for each and for each of the `spent` keys earlier plans
|
|
197
|
+
* took, before any fetch, and open their chunks.
|
|
198
|
+
*/
|
|
199
|
+
private iteratePlan;
|
|
200
|
+
/** The first plan of an `iterate`: every key in `w`. */
|
|
201
|
+
private planIterate;
|
|
202
|
+
/**
|
|
203
|
+
* The chunk at the next key of `run`, and `run.key` that key, or `undefined` once its keys are used up. Not `async`:
|
|
204
|
+
* when nothing has moved and no look is due, the chunk's own promise is all a key costs.
|
|
205
|
+
*/
|
|
206
|
+
private nextChunk;
|
|
207
|
+
private takeNext;
|
|
208
|
+
/**
|
|
209
|
+
* {@link nextChunk} once the read has looked at its segment's version, and planned again if it is not the one the
|
|
210
|
+
* plan was made under. A chunk that suggested a move is confirmed so: a source whose chunks name their version
|
|
211
|
+
* otherwise than its `currentVersion` does costs a look, never a plan.
|
|
212
|
+
*/
|
|
213
|
+
private nextChunkAfterLook;
|
|
214
|
+
/**
|
|
215
|
+
* Plan the rest of `run` again, after a move: the keys above the one it last took, from the generation current now.
|
|
216
|
+
* What the plan's stream still has in flight is dropped, not served.
|
|
217
|
+
*/
|
|
218
|
+
private replanIterate;
|
|
219
|
+
/**
|
|
220
|
+
* A segment's chunk keys, ascending and checked, and the version they are read under, with the invalidation epoch
|
|
221
|
+
* they were listed from. The version is resolved AFTER the shape read (`listChunkKeys`): a non-empty shape read means
|
|
222
|
+
* the source resolved and cached a non-null snapshot, so the version cannot then come back null while storage data is
|
|
223
|
+
* present, and `storageChunk` cannot skip every chunk on a stale null.
|
|
224
|
+
*
|
|
225
|
+
* A write of this store that moves the segment while the two are read would leave the keys of one generation under
|
|
226
|
+
* the version of the next, and nothing later could tell: the read's chunks and its looks would all name that newer
|
|
227
|
+
* version. So when the segment was invalidated in this store's scope while they were read, they are read again. Only
|
|
228
|
+
* that segment's invalidations count, so a store busy invalidating other segments costs a listing nothing more; with
|
|
229
|
+
* none since the listing began the check is a number compare.
|
|
230
|
+
*
|
|
231
|
+
* Honest note on how much the ordering buys, so a future reader does not take an untested claim for a tested one.
|
|
232
|
+
* Against a `CrbmStorageChunkSource`, the only source with a generation to resolve, both reads go through the same
|
|
233
|
+
* resolved-reader memo, so the pathological state (keys non-empty, version null) does not arise and swapping the two
|
|
234
|
+
* is observably identical. The ordering is kept because it is the order that is correct for *any* source satisfying
|
|
235
|
+
* the port (a custom source that resolves its shape and its generation independently can still produce that state),
|
|
236
|
+
* not because a test can currently tell the difference. Within a call shorter than `cache.genTtlMs` the two share one
|
|
237
|
+
* snapshot, so the read is generation-consistent, absent cache-pressure eviction (see `intersect`).
|
|
238
|
+
*/
|
|
239
|
+
private listKeys;
|
|
240
|
+
/**
|
|
241
|
+
* A read's {@link Looks}, from the invalidation epoch `epoch` its plan began at: due at the first key after a later
|
|
242
|
+
* invalidation, or after `every` keys.
|
|
243
|
+
*/
|
|
244
|
+
private looks;
|
|
245
|
+
/**
|
|
246
|
+
* Whether a look is due before the next key: an invalidation since the read last looked, or its keys counted down. The
|
|
247
|
+
* caller counts the key, as it takes one, before it asks.
|
|
248
|
+
*/
|
|
249
|
+
private lookDue;
|
|
250
|
+
/**
|
|
251
|
+
* The read looks now, before the key it takes next: the next look is due `every` keys on, or at the next
|
|
252
|
+
* invalidation. Called before the look awaits, so an invalidation while it waits is met at the next key.
|
|
253
|
+
*/
|
|
254
|
+
private looked;
|
|
174
255
|
/**
|
|
175
256
|
* The ids at 1-based ranks `n`, `2n`, `3n` … counted over the ids in `range`, ascending. The rank of each boundary
|
|
176
257
|
* is placed from the index's per-chunk counts, so only the chunks that hold a boundary are read, each once however
|
|
@@ -219,13 +300,19 @@ export declare class SegmentEngine {
|
|
|
219
300
|
* the result — every chunk read is a whole, checksum-verified, immutable generation. The edge a *long* call can
|
|
220
301
|
* hit is invariant 3's: if it straddles a mid-call `cache.genTtlMs` boundary and a load has published, an
|
|
221
302
|
* operand's not-yet-requested chunks may re-resolve forward to the newer generation (a generation hop within one long
|
|
222
|
-
* call) — the call never crashes or returns a torn object, but may mix generations.
|
|
223
|
-
* waiting for the TTL, so a shorter call can meet them too: **the reader cache evicting an operand mid-call**
|
|
224
|
-
* (`maxOpenSegments`), whose re-read re-resolves fresh
|
|
225
|
-
*
|
|
226
|
-
*
|
|
303
|
+
* call) — the call never crashes or returns a torn object, but may mix generations. Some things hop it without
|
|
304
|
+
* waiting for the TTL, so a shorter call can meet them too: **the reader cache evicting an operand mid-call** on a
|
|
305
|
+
* store with no timed refresh (`maxOpenSegments`), whose re-read re-resolves fresh, and on a store with one the
|
|
306
|
+
* resolution cache letting the operand's resolution go; a sweep deleting the generation it was reading, or another
|
|
307
|
+
* object under its number, met by this read or another, which heals the read forward, as another read's failed open
|
|
308
|
+
* of the operand does; and an invalidation, which this store's own `load`, `rollback` and `eraseSubject` make and
|
|
309
|
+
* `invalidate()` makes on request. Still whole/immutable per read, never torn. An operand's stream resolves its
|
|
227
310
|
* segment again before it serves each chunk, so each of those moves a running read as it would a read of one chunk, and
|
|
228
|
-
* the ranges it had requested of the earlier generation are dropped, not served.
|
|
311
|
+
* the ranges it had requested of the earlier generation are dropped, not served. The read then lists every operand's
|
|
312
|
+
* keys again above the key it last took, from the generation current then, so a key an operand gained is read and one
|
|
313
|
+
* it lost is not asked for. It meets a move through a chunk it is served that comes from another generation than it
|
|
314
|
+
* listed its keys under, and by looking at its operands' generations before the first key after an invalidation, and,
|
|
315
|
+
* with an `exclude` or as a union, every `concurrency` keys.
|
|
229
316
|
*/
|
|
230
317
|
intersect(segs: readonly SegmentRef[], options?: CombineOptions): AsyncGenerator<number>;
|
|
231
318
|
/**
|
|
@@ -270,12 +357,46 @@ export declare class SegmentEngine {
|
|
|
270
357
|
* read and the batch read share it, so the two cannot disagree about what is read or charged.
|
|
271
358
|
*/
|
|
272
359
|
private combinePlan;
|
|
360
|
+
/**
|
|
361
|
+
* Each operand's chunk-key set inside the read's range and above the key `after`, and the version it was listed under
|
|
362
|
+
* ({@link listKeys}), include operands first, each in its order. At most {@link OPERANDS_AT_ONCE} are listed at once;
|
|
363
|
+
* the first listing to fail rejects the plan at once, and no other is begun after it.
|
|
364
|
+
*/
|
|
365
|
+
private combineOperands;
|
|
366
|
+
/**
|
|
367
|
+
* The operands' streams over `keys` and the window that reads them. Each include is read as a stream of coalesced
|
|
368
|
+
* ranges, and so is each exclude that is read with the includes (one include, or a union). An exclude that waits on
|
|
369
|
+
* an AND of two or more includes is read in batches of the keys whose AND is not empty ({@link ExcludeBatch}), since
|
|
370
|
+
* an emptied AND never needs its chunk. The window starts its first reads at once, so a plan is opened only once its
|
|
371
|
+
* budget has passed.
|
|
372
|
+
*/
|
|
373
|
+
private openCombine;
|
|
374
|
+
/**
|
|
375
|
+
* The next key's combined chunk of `run`, `undefined` once its keys are used up. Not `async`: when nothing has moved
|
|
376
|
+
* and no look is due, the window's own promise is all a key costs.
|
|
377
|
+
*/
|
|
378
|
+
private nextSlot;
|
|
379
|
+
/**
|
|
380
|
+
* {@link nextSlot} once the read has looked at every operand's version, and planned again if one is not the version
|
|
381
|
+
* the plan was made under. An operand the keys left do not read would otherwise never show the read its move: an
|
|
382
|
+
* exclude whose keys the survivors do not reach, or an include of a union past its last key. A chunk that suggested a
|
|
383
|
+
* move is confirmed so, as in {@link nextChunkAfterLook}. The versions are looked up at most {@link OPERANDS_AT_ONCE}
|
|
384
|
+
* at once, all of them: a look that finds no move has compared every one.
|
|
385
|
+
*/
|
|
386
|
+
private nextSlotAfterLook;
|
|
387
|
+
/**
|
|
388
|
+
* Plan the rest of `run` again, after a move: every operand's keys above the key it last took, from the generation
|
|
389
|
+
* current now, aligned, charged to the budget with the reads of the keys already taken, and read through new streams
|
|
390
|
+
* and a new window. What the plan it replaces still has in flight is dropped, not served. The absent-operand refusal
|
|
391
|
+
* and the metric are the first plan's alone: a segment dropped while the read runs reads empty from there on.
|
|
392
|
+
*/
|
|
393
|
+
private replanCombine;
|
|
394
|
+
/** Stop the streams of `plan`; the reads its window holds settle on their own and are dropped. */
|
|
395
|
+
private closeCombine;
|
|
273
396
|
private combine;
|
|
274
397
|
private combineBatches;
|
|
275
398
|
/** The chunk read: the plan every combine shares, then each key's result, cut to the range at its edges. */
|
|
276
399
|
private combineChunks;
|
|
277
|
-
/** An edge chunk's bitmap cut to the window, as a new bitmap: the remainders `edgeIds` would yield. */
|
|
278
|
-
private cutToWindow;
|
|
279
400
|
/**
|
|
280
401
|
* The AND (or OR) of one chunk key across all operands, minus the excludes that hold it, or `null` if empty.
|
|
281
402
|
* Operand chunks are fetched **in parallel** (the spec's parallel byte-range reads).
|
|
@@ -287,8 +408,64 @@ export declare class SegmentEngine {
|
|
|
287
408
|
*/
|
|
288
409
|
private combineChunk;
|
|
289
410
|
/**
|
|
290
|
-
*
|
|
291
|
-
*
|
|
411
|
+
* Read `exclude` in batches, when the source can read ranges and the exclude has a generation; one that names the same
|
|
412
|
+
* segment and version as an exclude already opened shares its batch. Nothing is requested until a key asks.
|
|
413
|
+
*/
|
|
414
|
+
private openBatch;
|
|
415
|
+
/**
|
|
416
|
+
* The chunk `exclude` holds at `chunkKey`, for a key whose AND is not empty. One the cache holds, or another read has
|
|
417
|
+
* in flight, is read as a read of that chunk alone is (cache lookup and version check, or joining the read); only a
|
|
418
|
+
* chunk that would be requested waits for the batch.
|
|
419
|
+
*/
|
|
420
|
+
private excludeChunk;
|
|
421
|
+
/**
|
|
422
|
+
* Ask `batch` for the exclude chunk at `key`. A batch is read when it holds half the combine's concurrency in keys; when
|
|
423
|
+
* the asking has gone quiet for {@link BATCH_QUIET_HOPS} turns of the microtask queue (checked as each key asks and as
|
|
424
|
+
* the caller asks for each slot) and the caller is waiting on a slot, or has come within half a
|
|
425
|
+
* window of the oldest key held, so a held key is read at least that far ahead of it; and when every key of the combine
|
|
426
|
+
* has been started (none is left that could still ask).
|
|
427
|
+
* Refused once the combine has stopped or planned again, so a key it was still working on sends nothing more.
|
|
428
|
+
*/
|
|
429
|
+
private askBatch;
|
|
430
|
+
/** Start the wait of `batch` for its asking to go quiet, unless it is running. */
|
|
431
|
+
private settleSoon;
|
|
432
|
+
/**
|
|
433
|
+
* Wait until no key has asked for {@link BATCH_QUIET_HOPS} turns of the microtask queue. If the caller is waiting on a
|
|
434
|
+
* slot, or is within half a window of the oldest key held, or every key of the combine has been started (so no more
|
|
435
|
+
* can ask), the keys held are read; otherwise they are held for the keys the combine has yet to start, until half a
|
|
436
|
+
* window of them is held or the caller comes near. The wait is of microtask turns, so it always ends, and the loop is left rather than spun while
|
|
437
|
+
* keys are held.
|
|
438
|
+
*/
|
|
439
|
+
private settleBatch;
|
|
440
|
+
/** Read the keys `batch` holds now. */
|
|
441
|
+
private flushBatch;
|
|
442
|
+
/**
|
|
443
|
+
* Read the keys that asked: one whose chunk is already being read (by another read, or by an earlier key of this batch
|
|
444
|
+
* that names it again) joins that read, and the rest are one stream of coalesced ranges over those keys alone, so a
|
|
445
|
+
* request is sent for each range of them and never for a chunk nobody asked for. The stream is a
|
|
446
|
+
* {@link StreamedChunks} like an include's, so a chunk is cached under the version it was read from, counted as a cache
|
|
447
|
+
* hit or miss once, checked against the segment's version when it comes from the cache, and marks `moves` when it
|
|
448
|
+
* shows the segment moved. Each key it reads is registered as the open read of its chunk, so a read of that chunk by
|
|
449
|
+
* anything else joins it. Every ask is answered, with its own key's chunk or the error its read raised.
|
|
450
|
+
*/
|
|
451
|
+
private readBatch;
|
|
452
|
+
/**
|
|
453
|
+
* Register the read of the chunk `ref` that a batch's stream makes as its open read, so a read of it by anything else
|
|
454
|
+
* joins it, until it settles. A joiner does not share the stream's failure: a stream ends at its first failed range
|
|
455
|
+
* and fails every take after it, those of keys whose own ranges did not fail included, so when the read fails the
|
|
456
|
+
* entry goes and a joiner reads the chunk on its own, one request for each joined key that failed.
|
|
457
|
+
*/
|
|
458
|
+
private registerRead;
|
|
459
|
+
/**
|
|
460
|
+
* Stop `batch`: the keys still waiting are refused. The streams of the batches in flight are not closed: every range of
|
|
461
|
+
* one was requested when it was read, a read of one of its chunks by another call may be joined to it, and each closes
|
|
462
|
+
* itself once its last key is answered.
|
|
463
|
+
*/
|
|
464
|
+
private closeBatch;
|
|
465
|
+
/**
|
|
466
|
+
* One operand's chunk at `key`: from its stream when it has one, else a read of that chunk on its own, whose cached
|
|
467
|
+
* chunk marks `moves` when it is no longer the operand's version. Not `async`, so a stream's take is made before
|
|
468
|
+
* anything is awaited: concurrent takes of one stream must be made in key order.
|
|
292
469
|
*/
|
|
293
470
|
private operandChunk;
|
|
294
471
|
/**
|
|
@@ -302,6 +479,10 @@ export declare class SegmentEngine {
|
|
|
302
479
|
* not hold. Each later key is counted as a cache hit or miss here, so a key is counted once whichever way it is served.
|
|
303
480
|
*/
|
|
304
481
|
private startStream;
|
|
482
|
+
/**
|
|
483
|
+
* Stop the stream of `streamed`. Nothing else is kept for an open stream, so a read whose caller drops it without
|
|
484
|
+
* closing it leaves nothing behind in the engine.
|
|
485
|
+
*/
|
|
305
486
|
private closeStreamed;
|
|
306
487
|
/**
|
|
307
488
|
* The decoded chunk at `chunkKey` of a streamed read: from the cache while the stream is unopened and the cache holds
|
|
@@ -321,9 +502,10 @@ export declare class SegmentEngine {
|
|
|
321
502
|
* stream does for the chunks it delivers; a chunk served from the cache is checked here, or a read whose chunks are
|
|
322
503
|
* all cached would go on serving the generation it planned under, an erased id included, for as long as it is pulled.
|
|
323
504
|
* Within `cache.genTtlMs` the source answers from its snapshot, so the check is a lookup, not a request. `report`:
|
|
324
|
-
* emit the `cache` event for this lookup, which a key counted when its stream opened does not. `
|
|
325
|
-
* this chunk belongs to
|
|
326
|
-
*
|
|
505
|
+
* emit the `cache` event for this lookup, which a key counted when its stream opened does not. `moves`: where the read
|
|
506
|
+
* this chunk belongs to learns of a move, so it plans its keys again. `streamed`: the read's stream, which a move
|
|
507
|
+
* carries to the version now current, so the rest of it looks there: a read whose stream is not yet open reads the
|
|
508
|
+
* rest as one stream, not a chunk at a time.
|
|
327
509
|
*/
|
|
328
510
|
private cachedIfCurrent;
|
|
329
511
|
/** The cache key of a chunk: by the version it was read under, or by segment and key alone for a source with none. */
|
|
@@ -348,11 +530,20 @@ export declare class SegmentEngine {
|
|
|
348
530
|
* chunks — a segment with data is self-evidently registered — so the check is a registry read exactly when
|
|
349
531
|
* a combine was about to do something suspicious, and no reads at all otherwise. A source that cannot answer
|
|
350
532
|
* `exists` (it has no notion of registration) skips the check rather than guessing. An operand is judged by
|
|
351
|
-
* all its chunks, not those inside a range: one with nothing in range but data elsewhere is not suspicious.
|
|
533
|
+
* all its chunks, not those inside a range: one with nothing in range but data elsewhere is not suspicious. At most
|
|
534
|
+
* {@link OPERANDS_AT_ONCE} are asked after at once.
|
|
352
535
|
*/
|
|
353
536
|
private refuseAbsentOperands;
|
|
354
537
|
/**
|
|
355
|
-
*
|
|
538
|
+
* Forget what was derived from `ref`: drop its decoded chunks from the chunk cache, which every engine sharing this
|
|
539
|
+
* one's invalidations reads through, record the invalidation where they all see it, and tell this engine's Storage
|
|
540
|
+
* source to forget the segment too. An engine sharing the invalidations over another source instance does not tell
|
|
541
|
+
* its own source: engines that read one source, as the facade's do, need only one of them told. A read of the
|
|
542
|
+
* segment already running on any of them caches nothing more of what it began reading before the call, whether a
|
|
543
|
+
* request already in flight or a stream already open; a stream that opens after the call, or a request sent after
|
|
544
|
+
* it, reads afresh and, on a source that names versions (`currentVersion`), caches what it reads. On one that does
|
|
545
|
+
* not, a read that began before the call caches nothing from a stream it opens after it, unless the call landed while
|
|
546
|
+
* the read listed the segment's keys: the read then lists them again, and caches as a read begun after the call.
|
|
356
547
|
*
|
|
357
548
|
* The decoded-chunk cache is keyed by generation, which handles a *publish* (the new generation misses) but
|
|
358
549
|
* not a *destruction*: a segment that was erased from, dropped, shredded or retired has no newer generation
|
|
@@ -394,8 +585,9 @@ export declare class SegmentEngine {
|
|
|
394
585
|
* one): callers read it or clone it, never mutate it.
|
|
395
586
|
*
|
|
396
587
|
* `recheck`: `gen` was planned for a read of many chunks, so a cached chunk is served only while it is still the
|
|
397
|
-
* segment's version ({@link cachedIfCurrent})
|
|
398
|
-
* `report`: emit the `cache` event for this lookup; a key already counted for this
|
|
588
|
+
* segment's version ({@link cachedIfCurrent}), and one that is not marks `moves`. A point read resolves `gen` just
|
|
589
|
+
* before it asks, and passes none. `report`: emit the `cache` event for this lookup; a key already counted for this
|
|
590
|
+
* read does not.
|
|
399
591
|
*/
|
|
400
592
|
private storageChunk;
|
|
401
593
|
/**
|