@cloudbitmaps/roaring 0.22.0 → 0.23.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/PRIVACY.md +23 -17
- package/README.md +13 -4
- package/dist/bin/export-segments.js +471 -173
- package/dist/bin/export-segments.js.map +3 -3
- package/dist/index.d.ts +261 -60
- package/dist/index.js +462 -169
- package/dist/index.js.map +3 -3
- package/dist/roaring-codec.d.ts +23 -22
- package/package.json +4 -3
package/dist/index.d.ts
CHANGED
|
@@ -25,9 +25,36 @@
|
|
|
25
25
|
*/
|
|
26
26
|
import type { BudgetOption, GenerationEntry, GenerationMetadata, LoadGuard, LoadInput, LoadOptions, LoadRefusal, LoadResult, RollbackResult, Clock, Entropy, ConsistencyReport, DropResult, ExportManifest, ExportOptions, ExportSink, IAuditSink, IMetricsSink, RetentionPolicy, RetireExpiredOptions, RetireExpiredResult, RetryPolicy, Rng, SetRetentionResult, PinnedAt, SegmentRef } from '@cloudbitmaps/core';
|
|
27
27
|
import type { IKeystore, IStorageDriver, StorageBackend, StorageChunkSource } from '@cloudbitmaps/core/driver-kit';
|
|
28
|
+
import type { DestroyResult, EraseDeps } from '@cloudbitmaps/core';
|
|
28
29
|
import type { SegmentInfo } from '@cloudbitmaps/core';
|
|
30
|
+
import type { PreflightResult } from '@cloudbitmaps/core';
|
|
29
31
|
import type { Expr, MaterializeManyFeedRecord, MaterializeManyStats } from '@cloudbitmaps/core';
|
|
30
32
|
import { deserializePortable } from './roaring-codec.js';
|
|
33
|
+
/**
|
|
34
|
+
* Crypto-shred one segment. It cannot be undone through the library (a registry restore to a point before it can).
|
|
35
|
+
* `confirmSegment` must equal `ref.segment` (a guard against an accidental destroy). A non-encrypted segment is
|
|
36
|
+
* rejected unless you opt in via `allowCleartext`: it has no key to shred, its Storage bytes stay readable, and the
|
|
37
|
+
* call emits a `segment.tombstone` audit event instead of `segment.erase`. A write that gets no answer is settled by
|
|
38
|
+
* reading the row back, waiting between reads on `deps.clock` (the system clock by default).
|
|
39
|
+
*/
|
|
40
|
+
export declare function destroySegment(ref: SegmentRef, deps: EraseDeps, options: {
|
|
41
|
+
confirmSegment: string;
|
|
42
|
+
allowCleartext?: boolean;
|
|
43
|
+
audit?: IAuditSink;
|
|
44
|
+
}): Promise<DestroyResult>;
|
|
45
|
+
/**
|
|
46
|
+
* Crypto-shred every segment in a namespace. It cannot be undone through the library. `confirmNamespace` must equal
|
|
47
|
+
* `namespace`. Returns a per-segment result, in the order the registry listed the segments, which is unspecified. A
|
|
48
|
+
* write that gets no answer is settled as {@link destroySegment}'s is.
|
|
49
|
+
*/
|
|
50
|
+
export declare function eraseNamespace(namespace: string, deps: EraseDeps, options: {
|
|
51
|
+
confirmNamespace: string;
|
|
52
|
+
allowCleartext?: boolean;
|
|
53
|
+
audit?: IAuditSink;
|
|
54
|
+
maxScanSegments?: number;
|
|
55
|
+
}): Promise<{
|
|
56
|
+
destroyed: DestroyResult[];
|
|
57
|
+
}>;
|
|
31
58
|
/**
|
|
32
59
|
* Wiring for a {@link CloudRoaring} store. **Only `storage` is required** — the minimal call is
|
|
33
60
|
* `new CloudRoaring({ storage: backend })`. A backend resolves generations with one strong read, reads
|
|
@@ -76,7 +103,8 @@ export interface CloudRoaringOptions {
|
|
|
76
103
|
* attempt, capped at 2 s, with full jitter): `has`, `count`,
|
|
77
104
|
* `iterate` and the combines, the `*Into` verbs' reads of their operands included, a pinned handle's reads and
|
|
78
105
|
* `pin()` itself, and the reads a write makes along the way: a load's guard read of the current generation, and an
|
|
79
|
-
* erasure's reads of the generation it rewrites, of the one it wrote and of any other that may still hold the id
|
|
106
|
+
* erasure's reads of the generation it rewrites, of the one it wrote and of any other that may still hold the id, and
|
|
107
|
+
* the search `subjectReport` and `eraseSubject` make under a destroyed segment (its listing of the bucket included).
|
|
80
108
|
* This option does not govern writes, nor the calls that read the registry or list the bucket directly (`exists`,
|
|
81
109
|
* `segments`, `generations`, `getRetention`, and the registry scan `subjectReport`, `exportSegments` and
|
|
82
110
|
* `checkConsistency` start from): they report a transient fault to their caller, because a conditional write that
|
|
@@ -114,9 +142,12 @@ export interface CloudRoaringOptions {
|
|
|
114
142
|
}
|
|
115
143
|
/** {@link CloudRoaringOptions.cache} — the memory and staleness bounds. */
|
|
116
144
|
export interface CacheOptions {
|
|
117
|
-
/** Ceiling on decoded Storage chunks held in RAM (default 1024)
|
|
145
|
+
/** Ceiling on decoded Storage chunks held in RAM (default 1024): an integer of at least 1, else `ValidationError`. */
|
|
118
146
|
readonly maxChunks?: number;
|
|
119
|
-
/**
|
|
147
|
+
/**
|
|
148
|
+
* Optional TTL on cached chunks (ms): a finite number above 0, else `ValidationError`. Omit to keep a chunk until it is
|
|
149
|
+
* evicted by the count bound.
|
|
150
|
+
*/
|
|
120
151
|
readonly ttlMs?: number;
|
|
121
152
|
/**
|
|
122
153
|
* How long (ms) the store trusts a segment's resolved `currentGen` before re-resolving it on the next read
|
|
@@ -144,7 +175,8 @@ export interface CacheOptions {
|
|
|
144
175
|
readonly genTtlMs?: number;
|
|
145
176
|
/**
|
|
146
177
|
* Ceiling on how many segments' `.crbm` readers (each holding a parsed index) the store keeps open at once
|
|
147
|
-
* (default 1024) — the steady-state memory bound for a long-running
|
|
178
|
+
* (default 1024; an integer of at least 1, else `ValidationError`) — the steady-state memory bound for a long-running
|
|
179
|
+
* server that reads across many segments.
|
|
148
180
|
* Past it the least-recently-used segment's reader is evicted; re-opening it later is one cheap tail GET. With a
|
|
149
181
|
* timed refresh ({@link CacheOptions.genTtlMs} above 0, and a backend) the store also keeps up to 8 times this many
|
|
150
182
|
* segments' resolutions (what it read of each segment's row: never an unwrapped key, though the wrapped keys are in
|
|
@@ -167,7 +199,7 @@ export interface CacheOptions {
|
|
|
167
199
|
* heap (e.g. a 128 MB Lambda); this evicts the least-recently-used reader once the summed weight
|
|
168
200
|
* would exceed the ceiling — whichever of the count/byte bounds binds first. A sixteenth of it (4 MiB by default)
|
|
169
201
|
* bounds the segments' resolutions a store with a timed refresh keeps besides. Lower it for memory-tight
|
|
170
|
-
* deployments that read across wide segments. A ceiling below two readers' weight (about 7.7 KB for tiny ones) keeps one reader open at a time. Applies whenever the store builds its own read path.
|
|
202
|
+
* deployments that read across wide segments. An integer of at least 1, else `ValidationError`. A ceiling below two readers' weight (about 7.7 KB for tiny ones) keeps one reader open at a time. Applies whenever the store builds its own read path.
|
|
171
203
|
*/
|
|
172
204
|
readonly readerMaxBytes?: number;
|
|
173
205
|
}
|
|
@@ -181,11 +213,13 @@ export interface EncryptionOptions {
|
|
|
181
213
|
readonly keystore?: IKeystore;
|
|
182
214
|
/**
|
|
183
215
|
* Refuse to touch a **cleartext** segment — a guard against silently reading, or writing, data that should be
|
|
184
|
-
* encrypted. Needs a backend, for the same reason as {@link EncryptionOptions.keystore}. Off by default.
|
|
216
|
+
* encrypted. Needs a backend, for the same reason as {@link EncryptionOptions.keystore}. Off by default. Exactly a
|
|
217
|
+
* boolean: any other value, a truthy string included, throws `ValidationError`.
|
|
185
218
|
*
|
|
186
219
|
* It refuses **writes** as well as reads, which is easy to miss: the `*Into` verbs and `eraseSubject` both
|
|
187
220
|
* carry it into their write path, so on a cleartext segment a materialisation throws and an erasure records
|
|
188
|
-
* `note: 'error: requireEncryption: …'` in its ledger rather than erasing
|
|
221
|
+
* `note: 'error: KeyUnavailableError: requireEncryption: …'` in its ledger rather than erasing, whatever id it erases and
|
|
222
|
+
* whether or not the id is in the segment: it does not read the segment. And since a segment's encryption is
|
|
189
223
|
* decided at its **first** generation, this cannot be switched on for a segment that already has one — load
|
|
190
224
|
* into a new encrypted segment and drop the old one.
|
|
191
225
|
*/
|
|
@@ -229,24 +263,39 @@ export interface SeamOptions {
|
|
|
229
263
|
* passed here is refused with {@link ValidationError}.
|
|
230
264
|
*/
|
|
231
265
|
export interface SegmentOptions {
|
|
266
|
+
/** The segment's namespace, or `undefined` for the default one. */
|
|
232
267
|
readonly namespace?: string;
|
|
233
268
|
}
|
|
234
269
|
/** A segment reference in a subject report / erasure ledger. */
|
|
235
270
|
export interface SubjectSegmentRef {
|
|
271
|
+
/** The segment's name. */
|
|
236
272
|
readonly segment: string;
|
|
273
|
+
/** The segment's namespace, or `undefined` for the default one. */
|
|
237
274
|
readonly namespace?: string;
|
|
238
275
|
}
|
|
239
|
-
/** Result of {@link CloudRoaring.subjectReport}
|
|
276
|
+
/** Result of {@link CloudRoaring.subjectReport}: the segments an id is a member of, and the destroyed segments whose leftover objects hold it. */
|
|
240
277
|
export interface SubjectReport {
|
|
278
|
+
/** The id the report is about. */
|
|
241
279
|
readonly id: number;
|
|
242
280
|
/** The registered segments the id is currently a member of. */
|
|
243
281
|
readonly segments: SubjectSegmentRef[];
|
|
282
|
+
/**
|
|
283
|
+
* The destroyed segments whose leftover objects still hold the id in the clear: one destroyed with `allowCleartext`,
|
|
284
|
+
* dropped while a load wrote an object its sweep did not catch, or crypto-shredded over an object a cleartext writer
|
|
285
|
+
* left. No read resolves them, so the id is a member of none, but its bytes are in the bucket, and `eraseSubject`
|
|
286
|
+
* deletes them. A crypto-shredded segment's own objects are sealed under a key no row holds and are never a holder,
|
|
287
|
+
* so it is listed only when an object left under it in the clear holds the id. A destroyed segment with no
|
|
288
|
+
* object left is not listed either. `[]` when none.
|
|
289
|
+
*/
|
|
290
|
+
readonly tombstones: SubjectSegmentRef[];
|
|
244
291
|
/** How many registered segments were scanned (the completeness denominator). */
|
|
245
292
|
readonly scannedSegments: number;
|
|
246
293
|
}
|
|
247
294
|
/** One segment's entry in an erasure ledger (see {@link EraseSubjectResult}). */
|
|
248
295
|
export interface SubjectErasureEntry {
|
|
296
|
+
/** The segment's name. */
|
|
249
297
|
readonly segment: string;
|
|
298
|
+
/** The segment's namespace, or `undefined` for the default one. */
|
|
250
299
|
readonly namespace?: string;
|
|
251
300
|
/**
|
|
252
301
|
* True iff the id **was** in the segment and **no generation of it holds the id now** — not the current one, not
|
|
@@ -269,15 +318,15 @@ export interface SubjectErasureEntry {
|
|
|
269
318
|
* no pointer (a purge and re-creation, or a hand-made write), while the call was in flight, so **this call** did
|
|
270
319
|
* not erase the id;
|
|
271
320
|
* re-run against the new generation, which erases it if it is still there and reports nothing for the segment
|
|
272
|
-
* if the racing writer already removed it. `` `error: <message>` `` — an isolated per-segment fault
|
|
321
|
+
* if the racing writer already removed it. `` `error: <class>: <message>` `` — an isolated per-segment fault, named by its error's class
|
|
273
322
|
* (per-segment faults are recorded so one segment can't discard the whole ledger); re-run after fixing the
|
|
274
323
|
* fault. A budget refusal is one of these: the segment's generations did not fit `budget`, so it was not finished;
|
|
275
324
|
* re-run with a higher `budget`. A fault that landed once part of the work was already done — a Storage `delete` fault, a collect that
|
|
276
325
|
* could not prove the segment was still the same one, or a generation still holding the id when the bucket was
|
|
277
326
|
* listed at the end, whether or not a rewrite was published first — also re-runs, but **read what the re-run
|
|
278
327
|
* says**: it usually reports `erased: true` against the generation it found the id in, it reports
|
|
279
|
-
* nothing at all if a racing collector took that generation first
|
|
280
|
-
* receipt for it), and if the segment's row has since been purged it is no longer scanned at all — anything
|
|
328
|
+
* nothing at all if a racing collector took that generation first, or this call's own delete of it landed after a
|
|
329
|
+
* fault the call could not settle (the bit is gone, but no run holds a receipt for it), and if the segment's row has since been purged it is no longer scanned at all — anything
|
|
281
330
|
* left in its bucket is an orphan, which `store.generations(ref)` lists and `store.dropSegment(ref, {
|
|
282
331
|
* confirmSegment })` deletes; `checkConsistency` and the collection a load runs read rows, so neither reaches it.
|
|
283
332
|
* **Segments the id is not in
|
|
@@ -291,6 +340,7 @@ export interface SubjectErasureEntry {
|
|
|
291
340
|
* return value only (no library-side persistence): persist it / route it to your audit sink as you see fit.
|
|
292
341
|
*/
|
|
293
342
|
export interface EraseSubjectResult {
|
|
343
|
+
/** The id that was erased. */
|
|
294
344
|
readonly id: number;
|
|
295
345
|
/** Per-segment records for the segments the id was found in (absent segments are not listed). */
|
|
296
346
|
readonly erasedFrom: SubjectErasureEntry[];
|
|
@@ -413,6 +463,10 @@ export type MaterializeManyOutcome = MaterializeResult | {
|
|
|
413
463
|
export interface MaterializeManyRun {
|
|
414
464
|
/** One per output, in the order given: one output's refusal or error never stops another. */
|
|
415
465
|
readonly outputs: MaterializeManyOutcome[];
|
|
466
|
+
/**
|
|
467
|
+
* What the call did: the groups it ran, its requests by class, its budget and memory, and the figures of each operand
|
|
468
|
+
* and output.
|
|
469
|
+
*/
|
|
416
470
|
readonly stats: MaterializeManyStats;
|
|
417
471
|
}
|
|
418
472
|
/**
|
|
@@ -420,6 +474,7 @@ export interface MaterializeManyRun {
|
|
|
420
474
|
* judged against its `dest` as its publish would be, and nothing is written. See {@link CloudRoaring.materializeMany}.
|
|
421
475
|
*/
|
|
422
476
|
export interface MaterializeManyDryRunOptions extends MaterializeManyOptions {
|
|
477
|
+
/** `true` makes the call a dry run: every output is computed and judged, and nothing is written. */
|
|
423
478
|
readonly dryRun: true;
|
|
424
479
|
}
|
|
425
480
|
/**
|
|
@@ -428,7 +483,9 @@ export interface MaterializeManyDryRunOptions extends MaterializeManyOptions {
|
|
|
428
483
|
* run for a publish.
|
|
429
484
|
*/
|
|
430
485
|
export interface MaterializeDryRunResult {
|
|
486
|
+
/** Always `true`: the output was computed and judged, not published. */
|
|
431
487
|
readonly dryRun: true;
|
|
488
|
+
/** Always `false`: nothing was written. */
|
|
432
489
|
readonly published: false;
|
|
433
490
|
/** Distinct ids the output would hold. */
|
|
434
491
|
readonly cardinality: number;
|
|
@@ -449,6 +506,10 @@ export type MaterializeManyDryRunOutcome = MaterializeDryRunResult | {
|
|
|
449
506
|
export interface MaterializeManyDryRun {
|
|
450
507
|
/** One per output, in the order given. */
|
|
451
508
|
readonly outputs: MaterializeManyDryRunOutcome[];
|
|
509
|
+
/**
|
|
510
|
+
* What the dry run did: the groups it ran, its requests by class, its budget and memory, and the figures of each
|
|
511
|
+
* operand and output.
|
|
512
|
+
*/
|
|
452
513
|
readonly stats: MaterializeManyStats;
|
|
453
514
|
}
|
|
454
515
|
/** What an `*Into` verb wrote: the new generation of the destination, and whether it became current. */
|
|
@@ -512,6 +573,7 @@ export declare class CloudRoaring {
|
|
|
512
573
|
private readonly metrics;
|
|
513
574
|
private readonly storageDriver;
|
|
514
575
|
private readonly registry;
|
|
576
|
+
private readonly backend;
|
|
515
577
|
private readonly keystore;
|
|
516
578
|
private readonly requireEncryption;
|
|
517
579
|
/** Resolved store-level per-op budget (null = disabled); the admin scans use it, with a per-op override. */
|
|
@@ -535,6 +597,19 @@ export declare class CloudRoaring {
|
|
|
535
597
|
* no `next` or a keystore with no `createDek` builds a store that throws a raw `TypeError` at its first load or read.
|
|
536
598
|
*/
|
|
537
599
|
private static rejectHollowSeams;
|
|
600
|
+
/**
|
|
601
|
+
* Refuse a `cache` or `encryption` value that can never be honoured, by the name the caller wrote. The sizes reach
|
|
602
|
+
* the caches under their own internal names, so left to them an error would name `maxEntries` or `maxBytes`; and
|
|
603
|
+
* `encryption.required` must be exactly a boolean, since the read path took a truthy string for `true` while every
|
|
604
|
+
* write path did not, which refused cleartext reads and still wrote cleartext.
|
|
605
|
+
*/
|
|
606
|
+
private static rejectBadValues;
|
|
607
|
+
/**
|
|
608
|
+
* Builds a store. Only `storage` is required: pass a backend such as `S3Storage` or `MemoryStorage`. `cache`,
|
|
609
|
+
* `encryption`, `retry`, `metrics`, `budget` and `seams` tune it. An option it does not take, a `cache` size or `ttlMs`
|
|
610
|
+
* that is not a usable number, an `encryption.required` that is not a boolean, or a seam missing a method the store
|
|
611
|
+
* calls, throws `ValidationError`.
|
|
612
|
+
*/
|
|
538
613
|
constructor(options: CloudRoaringOptions);
|
|
539
614
|
/**
|
|
540
615
|
* The write-side deps, from the store's own drivers, for the lifecycle helpers and the `*Into` verbs. Requires
|
|
@@ -743,21 +818,50 @@ export declare class CloudRoaring {
|
|
|
743
818
|
* tax every load for a rare request; this admin scan is `O(registered segments)` and touches no hot path. Needs
|
|
744
819
|
* a storage backend (throws {@link UnsupportedError} otherwise).
|
|
745
820
|
*
|
|
821
|
+
* **Scope: one `namespace`, or `{ allNamespaces: true }`, never both** (both throws `ValidationError`, as does
|
|
822
|
+
* neither). A segment loaded with no `namespace` is in no namespace you can name: only `{ allNamespaces: true }`
|
|
823
|
+
* reaches it. `'_default'`, the label the export tool and the disaster-recovery samples print, and the bucket's
|
|
824
|
+
* object keys use, for such a segment, is an ordinary namespace name here, and passing it scans a namespace of that name, not those segments.
|
|
825
|
+
*
|
|
746
826
|
* **Current as of the registry row, not as of the reader's cache.** Each segment's resolved generation is
|
|
747
827
|
* compared with the row the scan listed, and a segment whose cached generation differs is re-resolved before the
|
|
748
828
|
* read, so a load or an erasure from another process shows at once — whatever `cache.genTtlMs` is, `0` included.
|
|
749
829
|
* The comparison is on the generation **and** the row's token, so a segment retired, purged and loaded again from
|
|
750
830
|
* generation 0 while this store held its old generation 0 is told apart too.
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
831
|
+
*
|
|
832
|
+
* **A destroyed segment is searched as {@link CloudRoaring.eraseSubject} searches it.** No read resolves a
|
|
833
|
+
* tombstone, so the id is a member of none, but a cleartext `destroySegment` (`allowCleartext`), or a `dropSegment`
|
|
834
|
+
* whose sweep missed an object a load was still writing, leaves objects anyone with bucket access can read. Each
|
|
835
|
+
* object left under the tombstone is opened newest first until one holds the id, and the segment is then listed in
|
|
836
|
+
* `tombstones`, never in `segments`. A crypto-shredded segment's own objects are sealed under a key no row holds and are
|
|
837
|
+
* never a holder: it is listed only when an object left under it in the clear holds the id. The cost is one listing per destroyed segment in scope (1 request when nothing is left), and for each
|
|
838
|
+
* object opened a tail read and, where its index lists the id's chunk, a chunk read (two tail reads for an encrypted
|
|
839
|
+
* object, which never holds the id): 7 requests for a cleartext destroy with 3 objects, 21 for 10. The budget is
|
|
840
|
+
* charged one unit for each object opened past the one the row names, as `eraseSubject` charges. A report that runs
|
|
841
|
+
* out, or whose listing or read fails after the store's retry, rejects rather than under-reporting, so one leftover
|
|
842
|
+
* object that cannot be opened makes every report over its scope reject until it is removed (`generations(ref)` lists
|
|
843
|
+
* what is left, `dropSegment` deletes it). A report refused for budget while it searches a destroyed segment reports
|
|
844
|
+
* `spent` as `limit + 1`, a lower bound.
|
|
845
|
+
*/
|
|
846
|
+
subjectReport(id: number, options: {
|
|
755
847
|
concurrency?: number;
|
|
756
848
|
budget?: BudgetOption;
|
|
757
|
-
}
|
|
849
|
+
} & ({
|
|
850
|
+
namespace: string;
|
|
851
|
+
allNamespaces?: false;
|
|
852
|
+
} | {
|
|
853
|
+
allNamespaces: true;
|
|
854
|
+
namespace?: undefined;
|
|
855
|
+
})): Promise<SubjectReport>;
|
|
758
856
|
/**
|
|
759
857
|
* **Subject erasure (GDPR Art. 17): remove an id from every segment it's in, with the bit physically gone
|
|
760
|
-
* from the bucket on return
|
|
858
|
+
* from the bucket on return for each segment the ledger reports `erased: true`.** A noncurrent version, a replica
|
|
859
|
+
* or a backup keeps the old object until its own lifecycle removes it.
|
|
860
|
+
*
|
|
861
|
+
* **Scope: one `namespace`, or `{ allNamespaces: true }`, never both** (both throws `ValidationError`, as does
|
|
862
|
+
* neither). A segment loaded with no `namespace` is in no namespace you can name: only `{ allNamespaces: true }`
|
|
863
|
+
* reaches it. `'_default'`, the label the export tool and the disaster-recovery samples print, and the bucket's
|
|
864
|
+
* object keys use, for such a segment, is an ordinary namespace name here, and passing it scans a namespace of that name, not those segments.
|
|
761
865
|
*
|
|
762
866
|
* For each **registered** segment the id is a member of, `eraseIdFromSegment` rewrites the current generation
|
|
763
867
|
* without the id — every chunk streamed through, one bit cleared — publishes the rewrite **fenced on the
|
|
@@ -822,21 +926,27 @@ export declare class CloudRoaring {
|
|
|
822
926
|
* `O(registered segments + superseded generations)`, no hot-path cost: a segment whose current generation lacks
|
|
823
927
|
* the id is searched generation by generation, one open for each other generation in its bucket, and with the
|
|
824
928
|
* keep-everything default of the `*Into` verbs that is one per generation the segment ever had. The per-op
|
|
825
|
-
* `budget` is charged one unit for each segment and one for each generation opened beyond the one its row names,
|
|
826
|
-
*
|
|
929
|
+
* `budget` is charged one unit for each segment and one for each generation opened beyond the one its row names, so
|
|
930
|
+
* a fleet with long histories can exhaust it where the segment count alone would not. A generation is charged at its
|
|
931
|
+
* first open only: a later open of it in the call, a retry of a failed read included, costs no more units. A segment
|
|
932
|
+
* that exhausts the budget is
|
|
827
933
|
* reported `erased: false` with an `error:` note, before it deletes anything (the one refusal that can come after a
|
|
828
934
|
* rewrite is the last check for a generation a concurrent writer left behind), and the
|
|
829
935
|
* rest of the scan continues: the call itself does not throw for it. Per-subject crypto-shred is infeasible (a subject's bit is
|
|
830
936
|
* co-mingled in a shared container), so this is the single-subject erasure route; whole-segment/tenant erasure
|
|
831
937
|
* is `dropSegment` / the `destroySegment`/`eraseNamespace` free functions.
|
|
832
938
|
*/
|
|
833
|
-
eraseSubject(id: number, options
|
|
834
|
-
namespace?: string;
|
|
835
|
-
allNamespaces?: boolean;
|
|
939
|
+
eraseSubject(id: number, options: {
|
|
836
940
|
audit?: IAuditSink;
|
|
837
941
|
concurrency?: number;
|
|
838
942
|
budget?: BudgetOption;
|
|
839
|
-
}
|
|
943
|
+
} & ({
|
|
944
|
+
namespace: string;
|
|
945
|
+
allNamespaces?: false;
|
|
946
|
+
} | {
|
|
947
|
+
allNamespaces: true;
|
|
948
|
+
namespace?: undefined;
|
|
949
|
+
})): Promise<EraseSubjectResult>;
|
|
840
950
|
private eraseSubjectRun;
|
|
841
951
|
/**
|
|
842
952
|
* Every generation still in the bucket for this segment, ascending, with the current one marked.
|
|
@@ -846,6 +956,7 @@ export declare class CloudRoaring {
|
|
|
846
956
|
* choose from, which is the reason to look at it. One registry read and one listing; it does not open the objects.
|
|
847
957
|
* Each entry says whether the row knows the generation (`known`): a known generation needs no `allowUnknown`; an unknown
|
|
848
958
|
* one does, except below a row's pointer when the row has no usable record or the target is older than a full one reaches.
|
|
959
|
+
* No entry is `current` on a destroyed segment: its row keeps the pointer it had, but no read resolves through it.
|
|
849
960
|
*
|
|
850
961
|
* Needs a backend.
|
|
851
962
|
*/
|
|
@@ -925,8 +1036,9 @@ export declare class CloudRoaring {
|
|
|
925
1036
|
* {@link ValidationError} before any request to storage, and a known one whose object carries another checksum than
|
|
926
1037
|
* the recorded one throws {@link IntegrityError}, with or without a keystore, since the footer is in the clear. A
|
|
927
1038
|
* generation not in the bucket (collected, or never written) throws `NotFoundError` naming what *is* available, a
|
|
928
|
-
* crypto-shredded
|
|
929
|
-
*
|
|
1039
|
+
* destroyed segment (crypto-shredded, destroyed in the clear or dropped) throws {@link ValidationError}, because no
|
|
1040
|
+
* generation resolves through a destroyed row, and a target that is cleartext under an encrypted segment, or
|
|
1041
|
+
* encrypted under a cleartext one, throws {@link IntegrityError} from
|
|
930
1042
|
* one tail read of it, because every read would refuse it. Rolling to the generation already current is a no-op that
|
|
931
1043
|
* reports itself, once one footer read finds its object in the bucket (and the one the row recorded); when the object is
|
|
932
1044
|
* gone it throws `NotFoundError` naming what is.
|
|
@@ -937,8 +1049,10 @@ export declare class CloudRoaring {
|
|
|
937
1049
|
* key (it holds none of the key's KEKs, one it holds does not unwrap it, or it throws `TransientError` or times out
|
|
938
1050
|
* as `AbortSignal.timeout()` and the AWS SDK report a timeout), a rollback onto a known generation still happens,
|
|
939
1051
|
* opening nothing, and the row carries no summary of the target. Any other failure of the keystore is
|
|
940
|
-
* thrown. A swap of the pointer that gets no answer is settled by reading the row: one
|
|
941
|
-
*
|
|
1052
|
+
* thrown. A swap of the pointer that gets no answer, or that meets a conflict, is settled by reading the row: one
|
|
1053
|
+
* that landed, the row's pointer being at the target over a write this call sent (the write token, on a registry that
|
|
1054
|
+
* reports them), is checked as any is, and otherwise its `TransientError` is thrown, or a `WriteConflictError` after
|
|
1055
|
+
* a conflict, with no move recorded. After the swap the target's
|
|
942
1056
|
* footer is read once more, and the pointer put back if the object is gone or is another.
|
|
943
1057
|
*
|
|
944
1058
|
* It deletes nothing. The generations above the new pointer stay put — which is what makes this reversible —
|
|
@@ -978,8 +1092,14 @@ export declare class CloudRoaring {
|
|
|
978
1092
|
* with `serialize('portable')` such as `roaring`'s `RoaringBitmap32`, or `{ serialized }`, portable Roaring bytes.
|
|
979
1093
|
* A bitmap is checked (size cap, structure, safe deserializer) before the first request, written from its own
|
|
980
1094
|
* containers with no per-id work, and gives the generation chunk for chunk what its ids would. A bare
|
|
981
|
-
* `RoaringBitmap32` passed as ids loads as `{ bitmap }`. A
|
|
982
|
-
* `ValidationError`, since each byte would load as an id:
|
|
1095
|
+
* `RoaringBitmap32`, or a bitmap `deserializePortable` returned, passed as ids loads as `{ bitmap }`. A
|
|
1096
|
+
* `Uint8Array` or `Buffer` passed as ids is refused with `ValidationError`, since each byte would load as an id:
|
|
1097
|
+
* pass bytes as `{ serialized }`.
|
|
1098
|
+
*
|
|
1099
|
+
* Ids are held whole in memory before the object is written: the generation's bitmaps and a buffer of up to about 28
|
|
1100
|
+
* MB. Ids that already ascend can be loaded with `sorted: true` instead, which writes them one chunk at a time while
|
|
1101
|
+
* the object uploads, so the load holds one chunk and the writer's index (up to about 7 MiB, about 8 MiB for an
|
|
1102
|
+
* encrypted segment, derived), whatever the segment's size, and writes the same object ({@link LoadOptions.sorted}).
|
|
983
1103
|
*
|
|
984
1104
|
* The whole write path in one call: take the next generation number, write the object, check the result is
|
|
985
1105
|
* plausible, move the pointer, collect what the move superseded. Composed by hand those are four functions and
|
|
@@ -1038,7 +1158,14 @@ export declare class CloudRoaring {
|
|
|
1038
1158
|
* does the first load of a row that records none, which then records it. {@link LoadResult.collected} then names
|
|
1039
1159
|
* what the pass deleted by name, and one name may have been gone already.
|
|
1040
1160
|
*
|
|
1041
|
-
*
|
|
1161
|
+
* `input` cannot be a stream of this library (`iterate`, `everyNth`, `intersect`, `union`, `andNot`, as returned) that
|
|
1162
|
+
* read the segment being loaded, from this store or one built over the same registry driver: a load publishes over
|
|
1163
|
+
* whatever the segment is by then, so it would replace another writer's newer load with a result built from an older
|
|
1164
|
+
* one, and it throws {@link ValidationError}. Ids read from another store over another backend load as any ids do.
|
|
1165
|
+
* Two separate store instances over one bucket cannot be told apart, so this does not cover them; this release has no
|
|
1166
|
+
* fenced in-place update, so write the result into a new segment and point readers at it.
|
|
1167
|
+
*
|
|
1168
|
+
* What it **throws** for is a fault rather than an outcome: invalid options or ids, and a destroyed
|
|
1042
1169
|
* segment (`ValidationError`); a key the keystore cannot provide (`KeyUnavailableError`); a current generation
|
|
1043
1170
|
* that will not open when a guard has to read its size, which it does only when the row has no usable summary of it
|
|
1044
1171
|
* (`IntegrityError`); a driver failure; and a fault in the collection's own reads, listing or deletes, or in the write
|
|
@@ -1062,11 +1189,14 @@ export declare class CloudRoaring {
|
|
|
1062
1189
|
*/
|
|
1063
1190
|
load(ref: SegmentRef, input: LoadInput, options?: LoadOptions): Promise<LoadResult>;
|
|
1064
1191
|
/**
|
|
1065
|
-
* **Dispose of a segment — tombstone it, then delete its storage objects.**
|
|
1192
|
+
* **Dispose of a segment — tombstone it, then delete its storage objects.** It cannot be undone through the
|
|
1193
|
+
* library (a restore from the registry's and the bucket's versions or backups can).
|
|
1066
1194
|
*
|
|
1067
|
-
* The operation a rolling window needs: `destroySegment` crypto-shreds (
|
|
1068
|
-
*
|
|
1069
|
-
*
|
|
1195
|
+
* The operation a rolling window needs: `destroySegment` crypto-shreds (it removes the key from the segment's
|
|
1196
|
+
* current registry row, so the bytes are unreadable everywhere, backups included, once no retained copy of the row
|
|
1197
|
+
* holds the key or every KEK that wrapped it is destroyed; the objects still sit in your bucket and are still
|
|
1198
|
+
* billed, and it requires encryption), while this one removes the storage and works on a cleartext segment. On an
|
|
1199
|
+
* encrypted segment it does both.
|
|
1070
1200
|
*
|
|
1071
1201
|
* Pass `{ dryRun: true }` first — it reports the generations it *would* delete and changes nothing. That
|
|
1072
1202
|
* matters more than the `confirmSegment` guard for anything automated, because in a loop the guard is the same
|
|
@@ -1112,8 +1242,9 @@ export declare class CloudRoaring {
|
|
|
1112
1242
|
audit?: IAuditSink;
|
|
1113
1243
|
}): Promise<DropResult>;
|
|
1114
1244
|
/**
|
|
1115
|
-
* **Record when this segment becomes eligible for retirement.** One
|
|
1116
|
-
*
|
|
1245
|
+
* **Record when this segment becomes eligible for retirement.** One policy write, plus a best-effort write of its
|
|
1246
|
+
* due-index pointer (and removal of the old pointer when the expiry day moves); no data is deleted here, and
|
|
1247
|
+
* nothing starts running. `retireExpired` is what acts on the policy, and **you** decide when that runs —
|
|
1117
1248
|
* an EventBridge rule, a CronJob, a queue consumer, whatever your deployment already has. This library starts
|
|
1118
1249
|
* no background thread (it has to work identically in a Lambda, an edge isolate and a long-lived server).
|
|
1119
1250
|
*
|
|
@@ -1135,7 +1266,7 @@ export declare class CloudRoaring {
|
|
|
1135
1266
|
* A value in the past is legal and means "eligible on the next sweep" — backfilling a policy onto existing
|
|
1136
1267
|
* buckets is normal. A value below `1_000_000_000_000` (2001-09-09) is rejected: it is almost certainly epoch
|
|
1137
1268
|
* **seconds**, which would read as long-expired and retire the segment on the next pass. Needs a storage
|
|
1138
|
-
* backend (throws {@link UnsupportedError} otherwise), and refuses a
|
|
1269
|
+
* backend (throws {@link UnsupportedError} otherwise), and refuses a destroyed segment.
|
|
1139
1270
|
*/
|
|
1140
1271
|
setRetention(ref: SegmentRef, policy: RetentionPolicy): Promise<SetRetentionResult>;
|
|
1141
1272
|
/**
|
|
@@ -1146,8 +1277,10 @@ export declare class CloudRoaring {
|
|
|
1146
1277
|
*/
|
|
1147
1278
|
getRetention(ref: SegmentRef): Promise<RetentionPolicy | null | 'invalid'>;
|
|
1148
1279
|
/**
|
|
1149
|
-
* **Cancel a segment's expiry** so no sweep retires it. Returns whether
|
|
1150
|
-
* when there was none
|
|
1280
|
+
* **Cancel a segment's expiry** so no sweep retires it. Returns whether this call removed a policy (`false`
|
|
1281
|
+
* when there was none, and also when a write that got no answer cannot be shown to be this call's and the
|
|
1282
|
+
* row holds no expiry: the expiry is gone either way, and its due-index pointer is removed. A later write that set an
|
|
1283
|
+
* expiry is cleared too, and the answer is `true`). A separate verb from `setRetention` on purpose: "never expire" as a magic value passed
|
|
1151
1284
|
* to the setter is how a typo becomes a deletion.
|
|
1152
1285
|
*/
|
|
1153
1286
|
clearRetention(ref: SegmentRef): Promise<boolean>;
|
|
@@ -1169,20 +1302,22 @@ export declare class CloudRoaring {
|
|
|
1169
1302
|
*
|
|
1170
1303
|
* const swept = await store.retireExpired({ namespace: 'active-daily' });
|
|
1171
1304
|
* for (const e of swept.entries) {
|
|
1172
|
-
*
|
|
1305
|
+
* // invalid-policy / policy-changed / tombstone-not-empty / failed: …
|
|
1306
|
+
* if (e.action === 'skipped') console.warn(`${e.segment}: ${e.reason}`);
|
|
1173
1307
|
* if (e.action === 'retired' && e.result.generationsRemaining.length > 0) {
|
|
1174
1308
|
* console.warn(`${e.segment}: storage not fully reclaimed — re-run`);
|
|
1175
1309
|
* }
|
|
1176
1310
|
* }
|
|
1177
|
-
* if (swept.limited) scheduleAnotherPassSoon(); // more
|
|
1311
|
+
* if (swept.limited) scheduleAnotherPassSoon(); // more work (retirements or purges) remains
|
|
1178
1312
|
* ```
|
|
1179
1313
|
*
|
|
1180
1314
|
* **Read the ledger.** A per-segment *fault* is an entry, not an exception — a throw from the middle of a fleet
|
|
1181
1315
|
* sweep would leave the caller unable to say which segments were retired, having already retired some. (A bad
|
|
1182
1316
|
* argument does throw, and so does a fleet larger than `maxScanSegments`.) `limited: true` means the per-cycle
|
|
1183
|
-
* `limit` (default 100) cut the pass short and more
|
|
1184
|
-
*
|
|
1185
|
-
*
|
|
1317
|
+
* `limit` (default 100) cut the pass short, and more work (retirements or purges) remains: `limit` caps the
|
|
1318
|
+
* retirements and tombstone purges one call attempts, together, and a segment it deferred gets no entry. That cap
|
|
1319
|
+
* is charged on **attempts**, not successes, which is what makes it a real bound: `dropSegment` writes the
|
|
1320
|
+
* tombstone before sweeping Storage, so a fault in the Storage phase is a segment that is already retired. Retirements are sequential, so `limit` is a
|
|
1186
1321
|
* wall-clock knob too, and `retired` counts deletions only — a dry run reports `wouldRetire` instead, so a
|
|
1187
1322
|
* dashboard summing `retired` can never show a phantom deletion.
|
|
1188
1323
|
*
|
|
@@ -1192,9 +1327,11 @@ export declare class CloudRoaring {
|
|
|
1192
1327
|
* charged to `limit`, so it never holds the retirements behind it, and purging stops for the rest of the call after
|
|
1193
1328
|
* three refused purges in a row (a purge that succeeds starts the count again).
|
|
1194
1329
|
*
|
|
1195
|
-
* It also **deletes the tombstone rows its own past retirements left**, after `tombstoneGraceMs` (default 24 h)
|
|
1330
|
+
* It also **deletes the tombstone rows its own past retirements left**, after `tombstoneGraceMs` (default 24 h; at least one hour, else `ValidationError`)
|
|
1196
1331
|
* and only once that segment's Storage generations are provably gone — collecting a straggler generation itself
|
|
1197
|
-
* first, since nothing else
|
|
1332
|
+
* first, since nothing else runs that collection on its own for a tombstoned segment (an erasure collects there
|
|
1333
|
+
* only
|
|
1334
|
+
* when an object holds its id, and a re-run drop sweeps it). Attribution is a **positive marker the sweep
|
|
1198
1335
|
* stamps on its own retirements**, not an inference from "destroyed + an expired policy": a crypto-shred leaves
|
|
1199
1336
|
* `retention` untouched, so setting a policy and then honouring a right-to-erasure request mid-window produces
|
|
1200
1337
|
* exactly that row, and deleting it would destroy the Art. 17 attestation and un-fence the name. By default a
|
|
@@ -1294,8 +1431,9 @@ export declare class CloudRoaring {
|
|
|
1294
1431
|
* throw). Read-only, bounded fan-out; run it at startup after a restore. Returns `{ checked, inconsistent,
|
|
1295
1432
|
* errored }` — `inconsistent` empty ⇒ coherent; otherwise it names the segments to recover (restore the object
|
|
1296
1433
|
* store, or roll the registry back to a coherent point). Needs the store built with a **backend**
|
|
1297
|
-
* (throws {@link UnsupportedError} otherwise). `destroyed` (crypto-shredded
|
|
1298
|
-
*
|
|
1434
|
+
* (throws {@link UnsupportedError} otherwise). `destroyed` segments (crypto-shredded, destroyed in the clear or
|
|
1435
|
+
* dropped) are skipped. Pair
|
|
1436
|
+
* it with the DR runbook (docs/guide/disaster-recovery.md).
|
|
1299
1437
|
*
|
|
1300
1438
|
* A segment whose pointer names a missing object is the torn restore this reports; a cold `count()` of it still
|
|
1301
1439
|
* answers the number its row records, while a read of the object throws, so the count alone never shows it. With
|
|
@@ -1324,6 +1462,39 @@ export declare class CloudRoaring {
|
|
|
1324
1462
|
summaries?: boolean;
|
|
1325
1463
|
generations?: boolean;
|
|
1326
1464
|
}): Promise<ConsistencyReport>;
|
|
1465
|
+
/**
|
|
1466
|
+
* **First-run check of the store's storage and registry.** Call it once at deploy time, or on a first run, before
|
|
1467
|
+
* the first load: it finds a missing permission, a storage that does not refuse a second write to a key, a listing
|
|
1468
|
+
* that lags, or a lifecycle rule that would delete the store's objects, while nothing depends on them yet.
|
|
1469
|
+
*
|
|
1470
|
+
* It runs seven checks and always lists all seven, in this order: `registry` (one read of a row that must be
|
|
1471
|
+
* absent, and the registry able to write), `write` (a new object), `write-once` (a second write to the same key must
|
|
1472
|
+
* be refused), `read` (the object reads back as written), `list` (the listing names it), `delete` (this run's own
|
|
1473
|
+
* object is removed, with the version the read gave where the driver applies one) and `lifecycle` (the backend's
|
|
1474
|
+
* lifecycle check). The probe is one small object at `cbm.due.preflight/segments/probe.<number>.crbm` under the
|
|
1475
|
+
* store's prefix; no registry row is written, and no segment of yours can share the name. A probe left by a run that
|
|
1476
|
+
* was cut off is named in the `list` detail and never deleted, since it could be a run in progress.
|
|
1477
|
+
*
|
|
1478
|
+
* It **never throws over a finding**. A failed step, a transient fault included, is a `problem` whose `detail`
|
|
1479
|
+
* says to run again when the fault was transient, and the checks that cannot run without it are `unchecked`. The
|
|
1480
|
+
* exception is the `lifecycle` step, whose failures, its timeout included, are `unchecked`. `ok` is true when no
|
|
1481
|
+
* check is a `problem`: read an `unchecked` check yourself, as `ok` does not vouch for it. The `lifecycle` check is
|
|
1482
|
+
* `unchecked` for a backend whose check says so (Azure Blob always), for a backend that has no such method (a
|
|
1483
|
+
* third-party backend, a `brandAsBackend` wrapper that does not forward it), when the identity lacks the optional
|
|
1484
|
+
* permission to read the lifecycle rules (`s3:GetLifecycleConfiguration`, `storage.buckets.get`), and when the check
|
|
1485
|
+
* takes longer than 30 seconds of the store's clock. The other steps are the store's ordinary calls and keep their
|
|
1486
|
+
* timing.
|
|
1487
|
+
*
|
|
1488
|
+
* **Not checked:** read-after-delete, the registry's `If-Match` on compare-and-swap, multipart abort permission,
|
|
1489
|
+
* versioning, backups, the keystore's backup and an S3 SDK from 3.641.0 to 3.699.0 (an older SDK shows as a
|
|
1490
|
+
* `write-once` problem; these drop `If-Match`, which only a registry write refuses). It makes no kind of request that a
|
|
1491
|
+
* load does not, though the lifecycle read needs its own optional permission. A run costs about seven requests (a
|
|
1492
|
+
* first run on GCS makes eight, the eighth a second listing that checks the bucket exists, and Azure Blob always
|
|
1493
|
+
* eight): two writes and a delete, the rest reads and one listing.
|
|
1494
|
+
*
|
|
1495
|
+
* Throws {@link UnsupportedError}, and only that, for a store with no backend, since there is nothing to check.
|
|
1496
|
+
*/
|
|
1497
|
+
preflight(): Promise<PreflightResult>;
|
|
1327
1498
|
/**
|
|
1328
1499
|
* **Export ("eject") every registered segment's current generation** through the injected `sink`, using only
|
|
1329
1500
|
* public read APIs — so your data is readable **without CloudRoaring** (the exit path; see the README's "Your
|
|
@@ -1332,8 +1503,9 @@ export declare class CloudRoaring {
|
|
|
1332
1503
|
* Enumerates via the store's **own** registry (needs one — throws {@link UnsupportedError} otherwise) so the
|
|
1333
1504
|
* enumeration and the read path provably share one registry. Encrypted segments are decrypted transparently
|
|
1334
1505
|
* **iff** this store was wired with their keystore — the export is therefore **cleartext**; protect it.
|
|
1335
|
-
*
|
|
1336
|
-
* (the run continues), so "a manifest exists" means the run finished —
|
|
1506
|
+
* Destroyed segments (crypto-shredded, destroyed in the clear or dropped) are skipped. A segment that can't be read
|
|
1507
|
+
* is isolated into the manifest's `failed[]` (the run continues), so "a manifest exists" means the run finished —
|
|
1508
|
+
* check `failed`. The `export-segments`
|
|
1337
1509
|
* CLI wraps this with a filesystem sink. The `'roaring'` format's bitmaps are built by the store's own codec.
|
|
1338
1510
|
*/
|
|
1339
1511
|
exportSegments(sink: ExportSink, options?: ExportOptions): Promise<ExportManifest>;
|
|
@@ -1533,6 +1705,7 @@ export interface PinAt {
|
|
|
1533
1705
|
export interface SegmentStat {
|
|
1534
1706
|
/** The generation read, or `null` when the segment has none. */
|
|
1535
1707
|
readonly generation: number | null;
|
|
1708
|
+
/** The number of ids in the generation read, or `0` when the segment has none. */
|
|
1536
1709
|
readonly cardinality: number;
|
|
1537
1710
|
/** The metadata the generation was loaded with; absent when it has none. */
|
|
1538
1711
|
readonly metadata?: GenerationMetadata;
|
|
@@ -1601,7 +1774,7 @@ export declare class Segment {
|
|
|
1601
1774
|
*
|
|
1602
1775
|
* **One call reads a segment at one generation**, so a combine that holds this segment at two —
|
|
1603
1776
|
* `snap0.andNot([snap1])`, or `live.intersect([snap])` — throws {@link ValidationError} when it is read, and an
|
|
1604
|
-
* `*Into` of one throws before it reads anything. Materialise one side first, with `
|
|
1777
|
+
* `*Into` of one throws before it reads anything. Materialise one side first, with `materializeMany({ operands: { src: seg }, outputs: [{ dest, expr: { or: ['src'] } }], keep })`.
|
|
1605
1778
|
*
|
|
1606
1779
|
* **A pin knows its object, not only its number.** A name purged and loaded again starts again at generation
|
|
1607
1780
|
* 0, so a pin of the old segment never reads the new one: what it has already read still answers, as the
|
|
@@ -1702,10 +1875,10 @@ export declare class Segment {
|
|
|
1702
1875
|
* `at.generation` instead of the current one. Pass what the earlier pin recorded, `{ generation, fingerprint }`
|
|
1703
1876
|
* from its {@link Segment.pinnedAt}. A generation number alone is not an identity, since a purged and re-created
|
|
1704
1877
|
* name starts again at 0, so the fingerprint is required: a bare number, a malformed fingerprint or a key other
|
|
1705
|
-
* than `generation` and `
|
|
1878
|
+
* than `generation`, `fingerprint` and `version` is a {@link ValidationError}. Identify a pin by its `generation` and
|
|
1706
1879
|
* `fingerprint`: the handle's `pinnedAt.version` can differ from the earlier pin's.
|
|
1707
1880
|
*
|
|
1708
|
-
* Throws {@link NotFoundError} when the generation is gone (collected, purged, or on a
|
|
1881
|
+
* Throws {@link NotFoundError} when the generation is gone (collected, purged, or on a destroyed segment),
|
|
1709
1882
|
* is above the row's pointer, or is another object than the fingerprint names, and never reads empty. A rollback
|
|
1710
1883
|
* deletes nothing, so once a later load moves the pointer past a generation it rolled back from, that one can be
|
|
1711
1884
|
* reopened while its object is stored. Nothing keeps a generation alive unless you lease it: a later load's
|
|
@@ -1718,6 +1891,10 @@ export declare class Segment {
|
|
|
1718
1891
|
* then reads as one from `pin()` does, including its failure on a chunk it has not cached once the generation is
|
|
1719
1892
|
* swept. Works for an encrypted segment. The first argument is an object so that fields can join it later; the lease
|
|
1720
1893
|
* is in `options`, the second.
|
|
1894
|
+
*
|
|
1895
|
+
* It takes what {@link Segment.pinnedAt} holds: once `generation` is not `null`, the record passes as it is (its
|
|
1896
|
+
* `version` is not read), and `{ generation, fingerprint }` does as well. A pin of a segment that had no generation
|
|
1897
|
+
* has nothing to reopen and throws {@link ValidationError}.
|
|
1721
1898
|
*/
|
|
1722
1899
|
pinAt(at: PinAt, options?: PinOptions): Promise<Segment>;
|
|
1723
1900
|
/**
|
|
@@ -1728,15 +1905,25 @@ export declare class Segment {
|
|
|
1728
1905
|
*/
|
|
1729
1906
|
key(): string;
|
|
1730
1907
|
/**
|
|
1731
|
-
* Refuse an `*Into`
|
|
1732
|
-
* segment of another store
|
|
1733
|
-
*
|
|
1908
|
+
* Refuse an `*Into` that cannot run, before any request, in this order: anything that is not a segment or is a
|
|
1909
|
+
* segment of another store, an empty operand list for `intersectInto`, and a `dest` that is also a source (all
|
|
1910
|
+
* {@link ValidationError}); then a handle (this one, `dest`, an operand or an exclude) whose lease has ended or was
|
|
1911
|
+
* released ({@link LeaseExpiredError}). A call with both a lapsed lease and one of the first three refusals throws the
|
|
1912
|
+
* first. Called by the three `*Into` verbs only.
|
|
1734
1913
|
*
|
|
1735
1914
|
* The verbs are `async` so this surfaces as a **rejected promise**, like every other validation in the facade —
|
|
1736
1915
|
* a synchronous throw out of a promise-returning method escapes a caller who attached `.catch()` instead of
|
|
1737
1916
|
* awaiting.
|
|
1738
1917
|
*/
|
|
1739
1918
|
private assertIntoHandles;
|
|
1919
|
+
/**
|
|
1920
|
+
* Refuse an `*Into` whose destination is also one of its sources (this segment, an operand or an exclude, by the key
|
|
1921
|
+
* a handle gives, so a pin of the source and a ref spelled with the default namespace count), before any request, as
|
|
1922
|
+
* `materializeMany` refuses it. The combine would read the destination at the generation this store last resolved,
|
|
1923
|
+
* up to `cache.genTtlMs` old, while the publish is fenced on the row as it stands: another process's newer load of
|
|
1924
|
+
* the destination would be overwritten by a result built from the one before it, with no conflict to report.
|
|
1925
|
+
*/
|
|
1926
|
+
private refuseSourceDestination;
|
|
1740
1927
|
/** `result` as is, or, when a handle of the call holds a lease, checking that lease each time it reads a chunk. */
|
|
1741
1928
|
private guarded;
|
|
1742
1929
|
/**
|
|
@@ -1871,7 +2058,8 @@ export declare class Segment {
|
|
|
1871
2058
|
*
|
|
1872
2059
|
* Of {@link CombineOptions} it takes the range, `concurrency`, `budget`, `allowAbsentOperands` and `exclude`; a write
|
|
1873
2060
|
* option (`audit`, `allowEmpty`, `guard`, `metadata`, `keep`) fails the stream with {@link ValidationError} when it is
|
|
1874
|
-
* first read, before any request.
|
|
2061
|
+
* first read, before any request. So does an empty `others`: an intersection over nothing has no meaning, and
|
|
2062
|
+
* to read every id of one segment, use {@link iterate}. (`union` and `andNot` read an empty list as `this`.)
|
|
1875
2063
|
*/
|
|
1876
2064
|
intersect(others: Segment[], options?: CombineOptions): IdStream;
|
|
1877
2065
|
/**
|
|
@@ -1893,6 +2081,15 @@ export declare class Segment {
|
|
|
1893
2081
|
* materialisation that silently did not take effect is the one thing a caller cannot detect on its own.
|
|
1894
2082
|
*
|
|
1895
2083
|
* A call involving a handle whose lease has ended, or anything that is not a segment, is refused before any of this.
|
|
2084
|
+
* So is a `dest` that is also `this`, an operand or an exclude (the same segment by name and namespace, however the
|
|
2085
|
+
* handle was made), and, for `intersectInto` alone, an empty `others` (`unionInto` and `andNotInto` publish `this` for an
|
|
2086
|
+
* empty list): each throws {@link ValidationError} before any request. A destination that
|
|
2087
|
+
* is also a source would publish a result built from the generation this store last resolved over a newer load by
|
|
2088
|
+
* another process, with no conflict to report. This release has no fenced in-place update: write the result into a
|
|
2089
|
+
* new segment and point readers at it; a single writer that knows nothing else writes the segment may load the result
|
|
2090
|
+
* back, unfenced. To copy `src` into `dest`, call
|
|
2091
|
+
* `materializeMany({ operands: { src }, outputs: [{ dest, expr: { or: ['src'] } }], keep })`, which reads each chunk
|
|
2092
|
+
* once, at one instant.
|
|
1896
2093
|
* It takes every field of {@link CombineOptions}, as `unionInto` does.
|
|
1897
2094
|
*/
|
|
1898
2095
|
intersectInto(dest: Segment, others: Segment[], options?: CombineOptions): Promise<MaterializeResult>;
|
|
@@ -1904,7 +2101,8 @@ export declare class Segment {
|
|
|
1904
2101
|
* `intersect` prunes any key missing from any operand; union has nothing to prune. It is charged against the
|
|
1905
2102
|
* same per-op budget, so a wide union is refused rather than quietly billed — pass `budget` to raise it
|
|
1906
2103
|
* deliberately. If you find yourself unioning the same segments on every read, materializing the combined
|
|
1907
|
-
* segment once (`unionInto`, or a load) is the cheaper shape. It takes the options {@link intersect} takes
|
|
2104
|
+
* segment once (`unionInto`, or a load) is the cheaper shape. It takes the options {@link intersect} takes, and an
|
|
2105
|
+
* empty `others` reads `this` itself.
|
|
1908
2106
|
*/
|
|
1909
2107
|
union(others: Segment[], options?: CombineOptions): IdStream;
|
|
1910
2108
|
/** Materialize `this ∪ others…` (minus `exclude`) as a **new generation of `dest`** — see {@link intersectInto}. */
|
|
@@ -1938,6 +2136,9 @@ export declare class Segment {
|
|
|
1938
2136
|
/** The one body of the three `*Into` verbs, after each has checked its own options. */
|
|
1939
2137
|
private combineInto;
|
|
1940
2138
|
}
|
|
2139
|
+
export { MemoryStorage, LocalFsStorage, InProcessKeystore } from '@cloudbitmaps/core/node';
|
|
2140
|
+
export type { InProcessKeystoreOptions, LocalFsStorageOptions, MemoryStorageOptions, } from '@cloudbitmaps/core/node';
|
|
1941
2141
|
export { deserializePortable };
|
|
1942
|
-
export {
|
|
1943
|
-
export
|
|
2142
|
+
export type { ReadonlyBitmap } from './roaring-codec.js';
|
|
2143
|
+
export { excludingReservedRows, readRetentionPolicy, CloudRoaringError, ValidationError, WriteConflictError, IntegrityError, NotFoundError, UnsupportedError, CapabilityError, TransientError, KeyUnavailableError, BudgetExceededError, LeaseExpiredError, LeaseLimitError, StaleOperandError, errorKind, CountingMetricsSink, RecordingAuditSink, } from '@cloudbitmaps/core';
|
|
2144
|
+
export type { AuditEvent, Budget, BudgetOption, Clock, ConsistencyErrorEntry, ConsistencyIssue, ConsistencyReport, DestroyResult, DropResult, Entropy, EraseDeps, ErrorKind, ExportFailure, ExportFormat, ExportManifest, ExportOptions, ExportSink, ExportSkipped, ExportWriter, ExportedSegment, Expr, GenerationEntry, GenerationMetadata, IAuditSink, IMetricsSink, LoadGuard, LoadInput, LoadOptions, LoadRefusal, LoadResult, MaterializeManyFeedRecord, MaterializeManyOperandStats, MaterializeManyOutputStats, MaterializeManyStats, MetricEvent, MetricOpName, MetricsSnapshot, PinnedAt, PortableBitmap, PreflightCheck, PreflightResult, RetentionPolicy, RetireEntry, RetireExpiredOptions, RetireExpiredResult, RetryPolicy, Rng, RollbackResult, SegmentInfo, SegmentRef, SetRetentionResult, } from '@cloudbitmaps/core';
|