@cloudbitmaps/core 0.1.3 → 0.3.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/dist/index.cjs +102 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +62 -1
- package/dist/index.d.ts +62 -1
- package/dist/index.js +100 -19
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -122,6 +122,29 @@ declare function resolvePerOpBudget(opt: BudgetOption | undefined, storeBudget:
|
|
|
122
122
|
* (disabled).
|
|
123
123
|
*/
|
|
124
124
|
declare function checkBudget(budget: Budget | null, projectedRequests: number, op: string): void;
|
|
125
|
+
/**
|
|
126
|
+
* Drain an async iterable into an array, refusing **as soon as** the budget is exceeded.
|
|
127
|
+
*
|
|
128
|
+
* This exists because the obvious spelling is wrong in a way that is easy to miss:
|
|
129
|
+
*
|
|
130
|
+
* ```ts
|
|
131
|
+
* for await (const rec of registry.list(ns)) recs.push(rec);
|
|
132
|
+
* checkBudget(budget, recs.length, op); // ← too late; the memory is already spent
|
|
133
|
+
* ```
|
|
134
|
+
*
|
|
135
|
+
* That pattern bounds the **fan-out** but not the **enumeration**, so a tight budget refuses the work only
|
|
136
|
+
* after paying for the list — which defeats the purpose. It was measured buffering 20,000 registry records
|
|
137
|
+
* under `budget: { maxRequests: 2 }` before throwing. Draining incrementally bounds resident memory to
|
|
138
|
+
* `O(budget)` instead of `O(fleet)`.
|
|
139
|
+
*
|
|
140
|
+
* **The error message is deliberately vaguer than `checkBudget`'s.** It reports "more than N" rather than an
|
|
141
|
+
* exact total, because knowing the exact total requires finishing the scan — which is precisely the cost being
|
|
142
|
+
* refused. An honest approximation beats an expensive exact number.
|
|
143
|
+
*
|
|
144
|
+
* A `null` budget (`budget: false`) means the caller has explicitly opted out of fan-out limits, so this
|
|
145
|
+
* degrades to a plain drain.
|
|
146
|
+
*/
|
|
147
|
+
declare function collectWithinBudget<T>(source: AsyncIterable<T>, budget: Budget | null, op: string): Promise<T[]>;
|
|
125
148
|
|
|
126
149
|
/**
|
|
127
150
|
* The determinism seam.
|
|
@@ -430,6 +453,15 @@ declare function isTransient(err: unknown): boolean;
|
|
|
430
453
|
*/
|
|
431
454
|
declare function withRetry<T>(op: () => Promise<T>, policy: RetryPolicy, deps: RetryDeps): Promise<T>;
|
|
432
455
|
|
|
456
|
+
/**
|
|
457
|
+
* Default ceiling on resident warm-delta bytes for a single segment scan: 64 MiB.
|
|
458
|
+
*
|
|
459
|
+
* Chosen to be generous enough that no realistic segment trips it — a maximally-wide segment of 65,536 chunks
|
|
460
|
+
* would need ~1 KiB of warm delta per chunk to reach it — while still being small enough to keep a modest
|
|
461
|
+
* container (a 512 MiB Lambda, say) alive rather than OOM-killed when a pathological segment or a hostile
|
|
462
|
+
* caller shows up.
|
|
463
|
+
*/
|
|
464
|
+
declare const DEFAULT_MAX_WARM_SCAN_BYTES: number;
|
|
433
465
|
interface EngineDeps {
|
|
434
466
|
readonly warm: IWarmDriver;
|
|
435
467
|
readonly cold: ColdChunkSource;
|
|
@@ -463,6 +495,19 @@ interface EngineDeps {
|
|
|
463
495
|
* row, so bounded concurrency is safe (`addMany` is already non-atomic on partial failure).
|
|
464
496
|
*/
|
|
465
497
|
readonly writeConcurrency?: number;
|
|
498
|
+
/**
|
|
499
|
+
* Hard ceiling on the bytes one warm scan may hold resident, in bytes. Default {@link DEFAULT_MAX_WARM_SCAN_BYTES}.
|
|
500
|
+
*
|
|
501
|
+
* **Separate from `budget`, and enforced even when `budget: false`.** The budget bounds *cost* — billable
|
|
502
|
+
* requests — which is a different axis from memory, and conflating the two is what let a segment with
|
|
503
|
+
* thousands of warm chunks materialise ~12 MB before a `maxRequests: 2` budget could refuse it. It is also
|
|
504
|
+
* why `intersect` cannot use the budget as a memory bound: its budget is `common keys × operands`, a product
|
|
505
|
+
* that one wide operand can legitimately exceed in row count while staying entirely legal.
|
|
506
|
+
*
|
|
507
|
+
* Always on, because a ceiling that `budget: false` switches off is missing at exactly the moment it is
|
|
508
|
+
* needed. Configurable, because a ceiling you cannot raise is a landmine for a legitimately large segment.
|
|
509
|
+
*/
|
|
510
|
+
readonly maxWarmScanBytes?: number;
|
|
466
511
|
/**
|
|
467
512
|
* Per-op denial-of-wallet budget (07 Decision #3 / T3), already resolved by the facade: a {@link Budget} to
|
|
468
513
|
* enforce, or `null` to disable. Undefined ⇒ {@link DEFAULT_BUDGET} (a direct engine construction still gets
|
|
@@ -485,6 +530,7 @@ declare class SegmentEngine {
|
|
|
485
530
|
/** Consistency for READ-path Warm fetches (precomputed; OCC RMW passes strong explicitly, see `warmGet`). */
|
|
486
531
|
private readonly readOpts;
|
|
487
532
|
private readonly writeConcurrency;
|
|
533
|
+
private readonly maxWarmScanBytes;
|
|
488
534
|
/** Resolved per-op budget (null = disabled); undefined deps ⇒ the generous default. See {@link checkBudget}. */
|
|
489
535
|
private readonly budget;
|
|
490
536
|
constructor(deps: EngineDeps);
|
|
@@ -1791,6 +1837,20 @@ declare function eraseNamespace(namespace: string, deps: EraseDeps, options: {
|
|
|
1791
1837
|
* to confirm a reported tear.
|
|
1792
1838
|
*/
|
|
1793
1839
|
|
|
1840
|
+
/** Default in-flight fan-out for the consistency scan — bounded, no thundering herd. */
|
|
1841
|
+
/**
|
|
1842
|
+
* Default ceiling on how many registry records one consistency check may hold resident: 250,000.
|
|
1843
|
+
*
|
|
1844
|
+
* **Named `maxScanSegments`, not `maxSegments`, on purpose.** Compaction already has a `maxSegments`
|
|
1845
|
+
* (`CR_COMPACT_MAX_SEGMENTS`) that caps how much work one cycle does and then *continues normally*. This one
|
|
1846
|
+
* caps how much a scan may hold and *refuses* past it. Same-sounding names with opposite behaviour at the
|
|
1847
|
+
* limit is a trap; pairing this with `maxWarmScanBytes` instead makes both read as what they are — ceilings on
|
|
1848
|
+
* a scan that fail closed.
|
|
1849
|
+
*
|
|
1850
|
+
* Generous — the compaction docs target 100K+ segment fleets — while still bounding a DR drill's memory to
|
|
1851
|
+
* something a modest operator box survives. Raisable, because a ceiling you cannot lift is a landmine.
|
|
1852
|
+
*/
|
|
1853
|
+
declare const DEFAULT_MAX_SCAN_SEGMENTS = 250000;
|
|
1794
1854
|
interface ConsistencyIssue {
|
|
1795
1855
|
readonly segment: string;
|
|
1796
1856
|
readonly namespace?: string;
|
|
@@ -1830,6 +1890,7 @@ declare function runConsistencyCheck(deps: {
|
|
|
1830
1890
|
}, options?: {
|
|
1831
1891
|
namespace?: string;
|
|
1832
1892
|
concurrency?: number;
|
|
1893
|
+
maxScanSegments?: number;
|
|
1833
1894
|
}): Promise<ConsistencyReport>;
|
|
1834
1895
|
|
|
1835
1896
|
/**
|
|
@@ -1963,4 +2024,4 @@ declare class CrbmWriter {
|
|
|
1963
2024
|
private buildFooter;
|
|
1964
2025
|
}
|
|
1965
2026
|
|
|
1966
|
-
export { AWS_US_EAST_1_ONDEMAND, Aead, AeadSealed, type AuditEvent, type AuditEventKind, BlobReader, BlobSink, BoundedLru, type Budget, BudgetExceededError, type BudgetOption, type BulkLoadResult, CapabilityError, ChunkRef, type Clock, CloudRoaringError, type CodecBitmap, type CodecInterface, ColdCaps, ColdChunkSource, type CompactionCandidate, type CompactionCycleResult, type CompactionDeps, type CompactionOptions, type CompactionResult, type ConsistencyErrorEntry, type ConsistencyIssue, type ConsistencyReport, type CostAdvisory, type CostReport, CountingMetricsSink, CrbmColdChunkSource, type CrbmColdChunkSourceOptions, CrbmCrypto, CrbmReader, type CrbmReaderOptions, CrbmWriter, type CrbmWriterOptions, DEFAULT_BUDGET, DEFAULT_OCC_BACKOFF, DEFAULT_PRICING, DEFAULT_RETRY_POLICY, type DestroyResult, type DiscoveryOptions, type EngineDeps, type EraseDeps, type EstimateInput, type ExportFailure, type ExportFormat, type ExportManifest, type ExportOptions, type ExportSink, type ExportWriter, type ExportedSegment, GenKey, type IAuditSink, IColdDriver, IKeystore, type IMetricsSink, IRegistryDriver, IWarmDriver, InProcessKeystore, type InProcessKeystoreOptions, IntegrityError, KeyUnavailableError, LocalFsColdDriver, LocalFsRegistryDriver, type LocalFsRegistryDriverOptions, LocalFsWarmDriver, MemoryColdChunkSource, MemoryColdDriver, MemoryRegistryDriver, type MemoryRegistryDriverOptions, MemoryWarmDriver, type MetricEvent, type MetricOpName, type MetricsSnapshot, NOOP_AUDIT, NOOP_METRICS, NewRegistryRecord, NoRow, NodeAead, NotFoundError, type PricingProfile, RecordingAuditSink, RegCaps, RegistryPatch, RegistryRecord, type RetryDeps, type RetryPolicy, RetryingColdChunkSource, RetryingColdDriver, type RetryingOptions, RetryingRegistryDriver, RetryingWarmDriver, type Rng, SegmentEngine, SegmentRef, SegmentSize, type SegmentSizing, TimeoutError, Token, type Topology, TransientError, UnsupportedError, ValidationError, WarmReadOptions, WarmRow, type Workload, WrappedDek, WriteConflictError, bulkLoadCrbmGeneration, checkBudget, chunkRefKey, compactSegment, destroySegment, eraseNamespace, estimateCost, findCompactable, gcOrphanGenerations, groundedReport, isCloudRoaringError, isIntegrityError, isNotFoundError, isTransient, isTransientError, isValidationError, isWriteConflictError, joinId, mapWithConcurrency, publishGeneration, resolveBudget, resolvePerOpBudget, runCompactionCycle, runConsistencyCheck, runExport, safeMetrics, segmentKey, splitId, validateCompactionOptions, validateSegmentRef, withRetry, writeCrbmGeneration };
|
|
2027
|
+
export { AWS_US_EAST_1_ONDEMAND, Aead, AeadSealed, type AuditEvent, type AuditEventKind, BlobReader, BlobSink, BoundedLru, type Budget, BudgetExceededError, type BudgetOption, type BulkLoadResult, CapabilityError, ChunkRef, type Clock, CloudRoaringError, type CodecBitmap, type CodecInterface, ColdCaps, ColdChunkSource, type CompactionCandidate, type CompactionCycleResult, type CompactionDeps, type CompactionOptions, type CompactionResult, type ConsistencyErrorEntry, type ConsistencyIssue, type ConsistencyReport, type CostAdvisory, type CostReport, CountingMetricsSink, CrbmColdChunkSource, type CrbmColdChunkSourceOptions, CrbmCrypto, CrbmReader, type CrbmReaderOptions, CrbmWriter, type CrbmWriterOptions, DEFAULT_BUDGET, DEFAULT_MAX_SCAN_SEGMENTS, DEFAULT_MAX_WARM_SCAN_BYTES, DEFAULT_OCC_BACKOFF, DEFAULT_PRICING, DEFAULT_RETRY_POLICY, type DestroyResult, type DiscoveryOptions, type EngineDeps, type EraseDeps, type EstimateInput, type ExportFailure, type ExportFormat, type ExportManifest, type ExportOptions, type ExportSink, type ExportWriter, type ExportedSegment, GenKey, type IAuditSink, IColdDriver, IKeystore, type IMetricsSink, IRegistryDriver, IWarmDriver, InProcessKeystore, type InProcessKeystoreOptions, IntegrityError, KeyUnavailableError, LocalFsColdDriver, LocalFsRegistryDriver, type LocalFsRegistryDriverOptions, LocalFsWarmDriver, MemoryColdChunkSource, MemoryColdDriver, MemoryRegistryDriver, type MemoryRegistryDriverOptions, MemoryWarmDriver, type MetricEvent, type MetricOpName, type MetricsSnapshot, NOOP_AUDIT, NOOP_METRICS, NewRegistryRecord, NoRow, NodeAead, NotFoundError, type PricingProfile, RecordingAuditSink, RegCaps, RegistryPatch, RegistryRecord, type RetryDeps, type RetryPolicy, RetryingColdChunkSource, RetryingColdDriver, type RetryingOptions, RetryingRegistryDriver, RetryingWarmDriver, type Rng, SegmentEngine, SegmentRef, SegmentSize, type SegmentSizing, TimeoutError, Token, type Topology, TransientError, UnsupportedError, ValidationError, WarmReadOptions, WarmRow, type Workload, WrappedDek, WriteConflictError, bulkLoadCrbmGeneration, checkBudget, chunkRefKey, collectWithinBudget, compactSegment, destroySegment, eraseNamespace, estimateCost, findCompactable, gcOrphanGenerations, groundedReport, isCloudRoaringError, isIntegrityError, isNotFoundError, isTransient, isTransientError, isValidationError, isWriteConflictError, joinId, mapWithConcurrency, publishGeneration, resolveBudget, resolvePerOpBudget, runCompactionCycle, runConsistencyCheck, runExport, safeMetrics, segmentKey, splitId, validateCompactionOptions, validateSegmentRef, withRetry, writeCrbmGeneration };
|
package/dist/index.d.ts
CHANGED
|
@@ -122,6 +122,29 @@ declare function resolvePerOpBudget(opt: BudgetOption | undefined, storeBudget:
|
|
|
122
122
|
* (disabled).
|
|
123
123
|
*/
|
|
124
124
|
declare function checkBudget(budget: Budget | null, projectedRequests: number, op: string): void;
|
|
125
|
+
/**
|
|
126
|
+
* Drain an async iterable into an array, refusing **as soon as** the budget is exceeded.
|
|
127
|
+
*
|
|
128
|
+
* This exists because the obvious spelling is wrong in a way that is easy to miss:
|
|
129
|
+
*
|
|
130
|
+
* ```ts
|
|
131
|
+
* for await (const rec of registry.list(ns)) recs.push(rec);
|
|
132
|
+
* checkBudget(budget, recs.length, op); // ← too late; the memory is already spent
|
|
133
|
+
* ```
|
|
134
|
+
*
|
|
135
|
+
* That pattern bounds the **fan-out** but not the **enumeration**, so a tight budget refuses the work only
|
|
136
|
+
* after paying for the list — which defeats the purpose. It was measured buffering 20,000 registry records
|
|
137
|
+
* under `budget: { maxRequests: 2 }` before throwing. Draining incrementally bounds resident memory to
|
|
138
|
+
* `O(budget)` instead of `O(fleet)`.
|
|
139
|
+
*
|
|
140
|
+
* **The error message is deliberately vaguer than `checkBudget`'s.** It reports "more than N" rather than an
|
|
141
|
+
* exact total, because knowing the exact total requires finishing the scan — which is precisely the cost being
|
|
142
|
+
* refused. An honest approximation beats an expensive exact number.
|
|
143
|
+
*
|
|
144
|
+
* A `null` budget (`budget: false`) means the caller has explicitly opted out of fan-out limits, so this
|
|
145
|
+
* degrades to a plain drain.
|
|
146
|
+
*/
|
|
147
|
+
declare function collectWithinBudget<T>(source: AsyncIterable<T>, budget: Budget | null, op: string): Promise<T[]>;
|
|
125
148
|
|
|
126
149
|
/**
|
|
127
150
|
* The determinism seam.
|
|
@@ -430,6 +453,15 @@ declare function isTransient(err: unknown): boolean;
|
|
|
430
453
|
*/
|
|
431
454
|
declare function withRetry<T>(op: () => Promise<T>, policy: RetryPolicy, deps: RetryDeps): Promise<T>;
|
|
432
455
|
|
|
456
|
+
/**
|
|
457
|
+
* Default ceiling on resident warm-delta bytes for a single segment scan: 64 MiB.
|
|
458
|
+
*
|
|
459
|
+
* Chosen to be generous enough that no realistic segment trips it — a maximally-wide segment of 65,536 chunks
|
|
460
|
+
* would need ~1 KiB of warm delta per chunk to reach it — while still being small enough to keep a modest
|
|
461
|
+
* container (a 512 MiB Lambda, say) alive rather than OOM-killed when a pathological segment or a hostile
|
|
462
|
+
* caller shows up.
|
|
463
|
+
*/
|
|
464
|
+
declare const DEFAULT_MAX_WARM_SCAN_BYTES: number;
|
|
433
465
|
interface EngineDeps {
|
|
434
466
|
readonly warm: IWarmDriver;
|
|
435
467
|
readonly cold: ColdChunkSource;
|
|
@@ -463,6 +495,19 @@ interface EngineDeps {
|
|
|
463
495
|
* row, so bounded concurrency is safe (`addMany` is already non-atomic on partial failure).
|
|
464
496
|
*/
|
|
465
497
|
readonly writeConcurrency?: number;
|
|
498
|
+
/**
|
|
499
|
+
* Hard ceiling on the bytes one warm scan may hold resident, in bytes. Default {@link DEFAULT_MAX_WARM_SCAN_BYTES}.
|
|
500
|
+
*
|
|
501
|
+
* **Separate from `budget`, and enforced even when `budget: false`.** The budget bounds *cost* — billable
|
|
502
|
+
* requests — which is a different axis from memory, and conflating the two is what let a segment with
|
|
503
|
+
* thousands of warm chunks materialise ~12 MB before a `maxRequests: 2` budget could refuse it. It is also
|
|
504
|
+
* why `intersect` cannot use the budget as a memory bound: its budget is `common keys × operands`, a product
|
|
505
|
+
* that one wide operand can legitimately exceed in row count while staying entirely legal.
|
|
506
|
+
*
|
|
507
|
+
* Always on, because a ceiling that `budget: false` switches off is missing at exactly the moment it is
|
|
508
|
+
* needed. Configurable, because a ceiling you cannot raise is a landmine for a legitimately large segment.
|
|
509
|
+
*/
|
|
510
|
+
readonly maxWarmScanBytes?: number;
|
|
466
511
|
/**
|
|
467
512
|
* Per-op denial-of-wallet budget (07 Decision #3 / T3), already resolved by the facade: a {@link Budget} to
|
|
468
513
|
* enforce, or `null` to disable. Undefined ⇒ {@link DEFAULT_BUDGET} (a direct engine construction still gets
|
|
@@ -485,6 +530,7 @@ declare class SegmentEngine {
|
|
|
485
530
|
/** Consistency for READ-path Warm fetches (precomputed; OCC RMW passes strong explicitly, see `warmGet`). */
|
|
486
531
|
private readonly readOpts;
|
|
487
532
|
private readonly writeConcurrency;
|
|
533
|
+
private readonly maxWarmScanBytes;
|
|
488
534
|
/** Resolved per-op budget (null = disabled); undefined deps ⇒ the generous default. See {@link checkBudget}. */
|
|
489
535
|
private readonly budget;
|
|
490
536
|
constructor(deps: EngineDeps);
|
|
@@ -1791,6 +1837,20 @@ declare function eraseNamespace(namespace: string, deps: EraseDeps, options: {
|
|
|
1791
1837
|
* to confirm a reported tear.
|
|
1792
1838
|
*/
|
|
1793
1839
|
|
|
1840
|
+
/** Default in-flight fan-out for the consistency scan — bounded, no thundering herd. */
|
|
1841
|
+
/**
|
|
1842
|
+
* Default ceiling on how many registry records one consistency check may hold resident: 250,000.
|
|
1843
|
+
*
|
|
1844
|
+
* **Named `maxScanSegments`, not `maxSegments`, on purpose.** Compaction already has a `maxSegments`
|
|
1845
|
+
* (`CR_COMPACT_MAX_SEGMENTS`) that caps how much work one cycle does and then *continues normally*. This one
|
|
1846
|
+
* caps how much a scan may hold and *refuses* past it. Same-sounding names with opposite behaviour at the
|
|
1847
|
+
* limit is a trap; pairing this with `maxWarmScanBytes` instead makes both read as what they are — ceilings on
|
|
1848
|
+
* a scan that fail closed.
|
|
1849
|
+
*
|
|
1850
|
+
* Generous — the compaction docs target 100K+ segment fleets — while still bounding a DR drill's memory to
|
|
1851
|
+
* something a modest operator box survives. Raisable, because a ceiling you cannot lift is a landmine.
|
|
1852
|
+
*/
|
|
1853
|
+
declare const DEFAULT_MAX_SCAN_SEGMENTS = 250000;
|
|
1794
1854
|
interface ConsistencyIssue {
|
|
1795
1855
|
readonly segment: string;
|
|
1796
1856
|
readonly namespace?: string;
|
|
@@ -1830,6 +1890,7 @@ declare function runConsistencyCheck(deps: {
|
|
|
1830
1890
|
}, options?: {
|
|
1831
1891
|
namespace?: string;
|
|
1832
1892
|
concurrency?: number;
|
|
1893
|
+
maxScanSegments?: number;
|
|
1833
1894
|
}): Promise<ConsistencyReport>;
|
|
1834
1895
|
|
|
1835
1896
|
/**
|
|
@@ -1963,4 +2024,4 @@ declare class CrbmWriter {
|
|
|
1963
2024
|
private buildFooter;
|
|
1964
2025
|
}
|
|
1965
2026
|
|
|
1966
|
-
export { AWS_US_EAST_1_ONDEMAND, Aead, AeadSealed, type AuditEvent, type AuditEventKind, BlobReader, BlobSink, BoundedLru, type Budget, BudgetExceededError, type BudgetOption, type BulkLoadResult, CapabilityError, ChunkRef, type Clock, CloudRoaringError, type CodecBitmap, type CodecInterface, ColdCaps, ColdChunkSource, type CompactionCandidate, type CompactionCycleResult, type CompactionDeps, type CompactionOptions, type CompactionResult, type ConsistencyErrorEntry, type ConsistencyIssue, type ConsistencyReport, type CostAdvisory, type CostReport, CountingMetricsSink, CrbmColdChunkSource, type CrbmColdChunkSourceOptions, CrbmCrypto, CrbmReader, type CrbmReaderOptions, CrbmWriter, type CrbmWriterOptions, DEFAULT_BUDGET, DEFAULT_OCC_BACKOFF, DEFAULT_PRICING, DEFAULT_RETRY_POLICY, type DestroyResult, type DiscoveryOptions, type EngineDeps, type EraseDeps, type EstimateInput, type ExportFailure, type ExportFormat, type ExportManifest, type ExportOptions, type ExportSink, type ExportWriter, type ExportedSegment, GenKey, type IAuditSink, IColdDriver, IKeystore, type IMetricsSink, IRegistryDriver, IWarmDriver, InProcessKeystore, type InProcessKeystoreOptions, IntegrityError, KeyUnavailableError, LocalFsColdDriver, LocalFsRegistryDriver, type LocalFsRegistryDriverOptions, LocalFsWarmDriver, MemoryColdChunkSource, MemoryColdDriver, MemoryRegistryDriver, type MemoryRegistryDriverOptions, MemoryWarmDriver, type MetricEvent, type MetricOpName, type MetricsSnapshot, NOOP_AUDIT, NOOP_METRICS, NewRegistryRecord, NoRow, NodeAead, NotFoundError, type PricingProfile, RecordingAuditSink, RegCaps, RegistryPatch, RegistryRecord, type RetryDeps, type RetryPolicy, RetryingColdChunkSource, RetryingColdDriver, type RetryingOptions, RetryingRegistryDriver, RetryingWarmDriver, type Rng, SegmentEngine, SegmentRef, SegmentSize, type SegmentSizing, TimeoutError, Token, type Topology, TransientError, UnsupportedError, ValidationError, WarmReadOptions, WarmRow, type Workload, WrappedDek, WriteConflictError, bulkLoadCrbmGeneration, checkBudget, chunkRefKey, compactSegment, destroySegment, eraseNamespace, estimateCost, findCompactable, gcOrphanGenerations, groundedReport, isCloudRoaringError, isIntegrityError, isNotFoundError, isTransient, isTransientError, isValidationError, isWriteConflictError, joinId, mapWithConcurrency, publishGeneration, resolveBudget, resolvePerOpBudget, runCompactionCycle, runConsistencyCheck, runExport, safeMetrics, segmentKey, splitId, validateCompactionOptions, validateSegmentRef, withRetry, writeCrbmGeneration };
|
|
2027
|
+
export { AWS_US_EAST_1_ONDEMAND, Aead, AeadSealed, type AuditEvent, type AuditEventKind, BlobReader, BlobSink, BoundedLru, type Budget, BudgetExceededError, type BudgetOption, type BulkLoadResult, CapabilityError, ChunkRef, type Clock, CloudRoaringError, type CodecBitmap, type CodecInterface, ColdCaps, ColdChunkSource, type CompactionCandidate, type CompactionCycleResult, type CompactionDeps, type CompactionOptions, type CompactionResult, type ConsistencyErrorEntry, type ConsistencyIssue, type ConsistencyReport, type CostAdvisory, type CostReport, CountingMetricsSink, CrbmColdChunkSource, type CrbmColdChunkSourceOptions, CrbmCrypto, CrbmReader, type CrbmReaderOptions, CrbmWriter, type CrbmWriterOptions, DEFAULT_BUDGET, DEFAULT_MAX_SCAN_SEGMENTS, DEFAULT_MAX_WARM_SCAN_BYTES, DEFAULT_OCC_BACKOFF, DEFAULT_PRICING, DEFAULT_RETRY_POLICY, type DestroyResult, type DiscoveryOptions, type EngineDeps, type EraseDeps, type EstimateInput, type ExportFailure, type ExportFormat, type ExportManifest, type ExportOptions, type ExportSink, type ExportWriter, type ExportedSegment, GenKey, type IAuditSink, IColdDriver, IKeystore, type IMetricsSink, IRegistryDriver, IWarmDriver, InProcessKeystore, type InProcessKeystoreOptions, IntegrityError, KeyUnavailableError, LocalFsColdDriver, LocalFsRegistryDriver, type LocalFsRegistryDriverOptions, LocalFsWarmDriver, MemoryColdChunkSource, MemoryColdDriver, MemoryRegistryDriver, type MemoryRegistryDriverOptions, MemoryWarmDriver, type MetricEvent, type MetricOpName, type MetricsSnapshot, NOOP_AUDIT, NOOP_METRICS, NewRegistryRecord, NoRow, NodeAead, NotFoundError, type PricingProfile, RecordingAuditSink, RegCaps, RegistryPatch, RegistryRecord, type RetryDeps, type RetryPolicy, RetryingColdChunkSource, RetryingColdDriver, type RetryingOptions, RetryingRegistryDriver, RetryingWarmDriver, type Rng, SegmentEngine, SegmentRef, SegmentSize, type SegmentSizing, TimeoutError, Token, type Topology, TransientError, UnsupportedError, ValidationError, WarmReadOptions, WarmRow, type Workload, WrappedDek, WriteConflictError, bulkLoadCrbmGeneration, checkBudget, chunkRefKey, collectWithinBudget, compactSegment, destroySegment, eraseNamespace, estimateCost, findCompactable, gcOrphanGenerations, groundedReport, isCloudRoaringError, isIntegrityError, isNotFoundError, isTransient, isTransientError, isValidationError, isWriteConflictError, joinId, mapWithConcurrency, publishGeneration, resolveBudget, resolvePerOpBudget, runCompactionCycle, runConsistencyCheck, runExport, safeMetrics, segmentKey, splitId, validateCompactionOptions, validateSegmentRef, withRetry, writeCrbmGeneration };
|
package/dist/index.js
CHANGED
|
@@ -117,6 +117,18 @@ function checkBudget(budget, projectedRequests, op) {
|
|
|
117
117
|
);
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
|
+
async function collectWithinBudget(source, budget, op) {
|
|
121
|
+
const out = [];
|
|
122
|
+
for await (const item of source) {
|
|
123
|
+
out.push(item);
|
|
124
|
+
if (budget !== null && out.length > budget.maxRequests) {
|
|
125
|
+
throw new BudgetExceededError(
|
|
126
|
+
`${op} would fan out to more than ${budget.maxRequests} units, over the per-op budget \u2014 the scan was abandoned at that point rather than completed, so no exact total is available. Raise \`budget.maxRequests\`, override it per-op, or set \`budget: false\``
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return out;
|
|
131
|
+
}
|
|
120
132
|
|
|
121
133
|
// src/core/keys.ts
|
|
122
134
|
var FIELD = " ";
|
|
@@ -310,6 +322,7 @@ var ZeroRng = class {
|
|
|
310
322
|
return 0;
|
|
311
323
|
}
|
|
312
324
|
};
|
|
325
|
+
var DEFAULT_MAX_WARM_SCAN_BYTES = 64 * 1024 * 1024;
|
|
313
326
|
var SegmentEngine = class {
|
|
314
327
|
warm;
|
|
315
328
|
cold;
|
|
@@ -325,6 +338,7 @@ var SegmentEngine = class {
|
|
|
325
338
|
/** Consistency for READ-path Warm fetches (precomputed; OCC RMW passes strong explicitly, see `warmGet`). */
|
|
326
339
|
readOpts;
|
|
327
340
|
writeConcurrency;
|
|
341
|
+
maxWarmScanBytes;
|
|
328
342
|
/** Resolved per-op budget (null = disabled); undefined deps ⇒ the generous default. See {@link checkBudget}. */
|
|
329
343
|
budget;
|
|
330
344
|
constructor(deps) {
|
|
@@ -339,6 +353,12 @@ var SegmentEngine = class {
|
|
|
339
353
|
this.occBackoff = deps.occBackoff ?? DEFAULT_OCC_BACKOFF;
|
|
340
354
|
this.readOpts = { consistent: (deps.warmReadConsistency ?? "strong") !== "eventual" };
|
|
341
355
|
this.writeConcurrency = deps.writeConcurrency ?? 1;
|
|
356
|
+
this.maxWarmScanBytes = deps.maxWarmScanBytes ?? DEFAULT_MAX_WARM_SCAN_BYTES;
|
|
357
|
+
if (!Number.isFinite(this.maxWarmScanBytes) || this.maxWarmScanBytes < 1) {
|
|
358
|
+
throw new ValidationError(
|
|
359
|
+
`maxWarmScanBytes must be a finite number >= 1 (got ${String(deps.maxWarmScanBytes)})`
|
|
360
|
+
);
|
|
361
|
+
}
|
|
342
362
|
this.budget = deps.budget === void 0 ? DEFAULT_BUDGET : deps.budget;
|
|
343
363
|
this.metrics = safeMetrics(deps.metrics ?? NOOP_METRICS);
|
|
344
364
|
this.metricsOn = this.metrics !== NOOP_METRICS;
|
|
@@ -370,7 +390,7 @@ var SegmentEngine = class {
|
|
|
370
390
|
return cold ? cold.has(remainder) : false;
|
|
371
391
|
}
|
|
372
392
|
async count(seg) {
|
|
373
|
-
const warmRows = await this.collectWarm(seg);
|
|
393
|
+
const warmRows = await this.collectWarm(seg, "count", this.budget?.maxRequests ?? null);
|
|
374
394
|
const cardinalities = this.cold.cardinalities ? await this.cold.cardinalities(seg) : null;
|
|
375
395
|
if (!cardinalities) {
|
|
376
396
|
const chunkKeys = await this.chunkKeys(seg, warmRows);
|
|
@@ -402,7 +422,7 @@ var SegmentEngine = class {
|
|
|
402
422
|
return this.cold.sizeOf ? this.cold.sizeOf(seg) : Promise.resolve(null);
|
|
403
423
|
}
|
|
404
424
|
async *iterate(seg) {
|
|
405
|
-
const warmRows = await this.collectWarm(seg);
|
|
425
|
+
const warmRows = await this.collectWarm(seg, "iterate", this.budget?.maxRequests ?? null);
|
|
406
426
|
const chunkKeys = await this.chunkKeys(seg, warmRows);
|
|
407
427
|
checkBudget(this.budget, chunkKeys.length, "iterate");
|
|
408
428
|
const gen = await this.currentGen(seg);
|
|
@@ -449,7 +469,7 @@ var SegmentEngine = class {
|
|
|
449
469
|
const budget = resolvePerOpBudget(options?.budget, this.budget);
|
|
450
470
|
const operands = await Promise.all(
|
|
451
471
|
segs.map(async (seg) => {
|
|
452
|
-
const warmRows = await this.collectWarm(seg);
|
|
472
|
+
const warmRows = await this.collectWarm(seg, "intersect", null);
|
|
453
473
|
const keys = await this.chunkKeys(seg, warmRows);
|
|
454
474
|
const gen = await this.currentGen(seg);
|
|
455
475
|
return { seg, warmRows, keys: new Set(keys), gen };
|
|
@@ -590,12 +610,22 @@ var SegmentEngine = class {
|
|
|
590
610
|
}
|
|
591
611
|
return row;
|
|
592
612
|
}
|
|
593
|
-
async collectWarm(seg) {
|
|
613
|
+
async collectWarm(seg, op, rowCeiling) {
|
|
594
614
|
const rows = /* @__PURE__ */ new Map();
|
|
595
615
|
let bytes = 0;
|
|
596
616
|
for await (const row of this.warm.listChunks(seg, this.readOpts)) {
|
|
597
617
|
rows.set(row.chunkKey, row.bytes);
|
|
598
618
|
bytes += row.bytes.length;
|
|
619
|
+
if (bytes > this.maxWarmScanBytes) {
|
|
620
|
+
throw new BudgetExceededError(
|
|
621
|
+
`${op} would hold more than ${this.maxWarmScanBytes} bytes of warm deltas resident for one segment \u2014 the scan was abandoned there rather than completed. Raise \`maxWarmScanBytes\`, narrow the segment, or compact it so fewer chunks carry warm deltas. (This ceiling is independent of \`budget\` and stays in force when \`budget: false\`.)`
|
|
622
|
+
);
|
|
623
|
+
}
|
|
624
|
+
if (rowCeiling !== null && rows.size > rowCeiling) {
|
|
625
|
+
throw new BudgetExceededError(
|
|
626
|
+
`${op} would read more than ${rowCeiling} warm chunk rows, over the per-op budget \u2014 the scan was abandoned at that point rather than completed, so no exact total is available. Raise \`budget.maxRequests\`, override it per-op, or set \`budget: false\``
|
|
627
|
+
);
|
|
628
|
+
}
|
|
599
629
|
}
|
|
600
630
|
if (this.metricsOn) {
|
|
601
631
|
this.metrics.onEvent({
|
|
@@ -1083,22 +1113,44 @@ function aadFor(ref, generation, scope) {
|
|
|
1083
1113
|
}
|
|
1084
1114
|
|
|
1085
1115
|
// src/core/crbm/crc32c.ts
|
|
1086
|
-
var
|
|
1116
|
+
var TABLES = (() => {
|
|
1087
1117
|
const POLY = 2197175160;
|
|
1088
|
-
const
|
|
1118
|
+
const t0 = new Uint32Array(256);
|
|
1089
1119
|
for (let n = 0; n < 256; n++) {
|
|
1090
1120
|
let c = n;
|
|
1091
|
-
for (let k = 0; k < 8; k++)
|
|
1092
|
-
|
|
1121
|
+
for (let k = 0; k < 8; k++) c = c & 1 ? POLY ^ c >>> 1 : c >>> 1;
|
|
1122
|
+
t0[n] = c >>> 0;
|
|
1123
|
+
}
|
|
1124
|
+
const tables = [t0];
|
|
1125
|
+
for (let k = 1; k < 8; k++) {
|
|
1126
|
+
const prev = tables[k - 1];
|
|
1127
|
+
const next = new Uint32Array(256);
|
|
1128
|
+
for (let n = 0; n < 256; n++) {
|
|
1129
|
+
const v = prev[n];
|
|
1130
|
+
next[n] = (v >>> 8 ^ t0[v & 255]) >>> 0;
|
|
1093
1131
|
}
|
|
1094
|
-
|
|
1132
|
+
tables.push(next);
|
|
1095
1133
|
}
|
|
1096
|
-
return
|
|
1134
|
+
return tables;
|
|
1097
1135
|
})();
|
|
1136
|
+
var T0 = TABLES[0];
|
|
1137
|
+
var T1 = TABLES[1];
|
|
1138
|
+
var T2 = TABLES[2];
|
|
1139
|
+
var T3 = TABLES[3];
|
|
1140
|
+
var T4 = TABLES[4];
|
|
1141
|
+
var T5 = TABLES[5];
|
|
1142
|
+
var T6 = TABLES[6];
|
|
1143
|
+
var T7 = TABLES[7];
|
|
1098
1144
|
function crc32c(bytes, seed = 0) {
|
|
1099
1145
|
let crc = (seed ^ 4294967295) >>> 0;
|
|
1100
|
-
|
|
1101
|
-
|
|
1146
|
+
let i = 0;
|
|
1147
|
+
const n8 = bytes.length - bytes.length % 8;
|
|
1148
|
+
for (; i < n8; i += 8) {
|
|
1149
|
+
crc ^= bytes[i] | bytes[i + 1] << 8 | bytes[i + 2] << 16 | bytes[i + 3] << 24;
|
|
1150
|
+
crc = (T7[crc & 255] ^ T6[crc >>> 8 & 255] ^ T5[crc >>> 16 & 255] ^ T4[crc >>> 24 & 255] ^ T3[bytes[i + 4]] ^ T2[bytes[i + 5]] ^ T1[bytes[i + 6]] ^ T0[bytes[i + 7]]) >>> 0;
|
|
1151
|
+
}
|
|
1152
|
+
for (; i < bytes.length; i++) {
|
|
1153
|
+
crc = (T0[(crc ^ bytes[i]) & 255] ^ crc >>> 8) >>> 0;
|
|
1102
1154
|
}
|
|
1103
1155
|
return (crc ^ 4294967295) >>> 0;
|
|
1104
1156
|
}
|
|
@@ -1609,6 +1661,7 @@ function coldBlobReader(driver, key) {
|
|
|
1609
1661
|
};
|
|
1610
1662
|
}
|
|
1611
1663
|
var DEFAULT_CURRENT_GEN_TTL_MS = 2e3;
|
|
1664
|
+
var BULK_FLUSH_IDS = 1 << 20;
|
|
1612
1665
|
var DEFAULT_MAX_OPEN_SEGMENTS = 1024;
|
|
1613
1666
|
var DEFAULT_MAX_OPEN_INDEX_BYTES = 64 * 1024 * 1024;
|
|
1614
1667
|
var CrbmColdChunkSource = class {
|
|
@@ -1886,15 +1939,29 @@ async function bulkLoadCrbmGeneration(driver, key, ids, options = {}) {
|
|
|
1886
1939
|
}
|
|
1887
1940
|
const codec = requireCodec(options.codec, "bulkLoadCrbmGeneration");
|
|
1888
1941
|
const byChunk = /* @__PURE__ */ new Map();
|
|
1942
|
+
const pendingByChunk = /* @__PURE__ */ new Map();
|
|
1943
|
+
let pending = 0;
|
|
1944
|
+
const flushPending = () => {
|
|
1945
|
+
for (const [chunkKey, rems] of pendingByChunk) {
|
|
1946
|
+
if (rems.length === 0) continue;
|
|
1947
|
+
const existingBitmap = byChunk.get(chunkKey);
|
|
1948
|
+
if (existingBitmap === void 0) byChunk.set(chunkKey, codec.fromValues(rems));
|
|
1949
|
+
else existingBitmap.addMany(rems);
|
|
1950
|
+
rems.length = 0;
|
|
1951
|
+
}
|
|
1952
|
+
pending = 0;
|
|
1953
|
+
};
|
|
1889
1954
|
for await (const id of toAsyncIterable(ids)) {
|
|
1890
1955
|
const { chunkKey, remainder } = splitId(id);
|
|
1891
|
-
let
|
|
1892
|
-
if (
|
|
1893
|
-
|
|
1894
|
-
|
|
1956
|
+
let bucket = pendingByChunk.get(chunkKey);
|
|
1957
|
+
if (bucket === void 0) {
|
|
1958
|
+
bucket = [];
|
|
1959
|
+
pendingByChunk.set(chunkKey, bucket);
|
|
1895
1960
|
}
|
|
1896
|
-
|
|
1961
|
+
bucket.push(remainder);
|
|
1962
|
+
if (++pending >= BULK_FLUSH_IDS) flushPending();
|
|
1897
1963
|
}
|
|
1964
|
+
flushPending();
|
|
1898
1965
|
let cardinality = 0;
|
|
1899
1966
|
const chunks = [];
|
|
1900
1967
|
for (const [chunkKey, bitmap] of byChunk) {
|
|
@@ -3472,6 +3539,7 @@ async function eraseWarm(warm, ref) {
|
|
|
3472
3539
|
}
|
|
3473
3540
|
|
|
3474
3541
|
// src/core/consistency.ts
|
|
3542
|
+
var DEFAULT_MAX_SCAN_SEGMENTS = 25e4;
|
|
3475
3543
|
var DEFAULT_CHECK_CONCURRENCY = 8;
|
|
3476
3544
|
async function listGenerations(cold, ref) {
|
|
3477
3545
|
const present = /* @__PURE__ */ new Set();
|
|
@@ -3483,8 +3551,21 @@ async function runConsistencyCheck(deps, options = {}) {
|
|
|
3483
3551
|
if (!Number.isInteger(concurrency) || concurrency < 1) {
|
|
3484
3552
|
throw new ValidationError(`concurrency must be a positive integer; got ${concurrency}`);
|
|
3485
3553
|
}
|
|
3554
|
+
const maxScanSegments = options.maxScanSegments ?? DEFAULT_MAX_SCAN_SEGMENTS;
|
|
3555
|
+
if (!Number.isFinite(maxScanSegments) || maxScanSegments < 1) {
|
|
3556
|
+
throw new ValidationError(
|
|
3557
|
+
`maxScanSegments must be a finite number >= 1; got ${String(options.maxScanSegments)}`
|
|
3558
|
+
);
|
|
3559
|
+
}
|
|
3486
3560
|
const recs = [];
|
|
3487
|
-
for await (const rec of deps.registry.list(options.namespace))
|
|
3561
|
+
for await (const rec of deps.registry.list(options.namespace)) {
|
|
3562
|
+
recs.push(rec);
|
|
3563
|
+
if (recs.length > maxScanSegments) {
|
|
3564
|
+
throw new BudgetExceededError(
|
|
3565
|
+
`checkConsistency would enumerate more than ${maxScanSegments} segments \u2014 the scan was abandoned there rather than completed. Narrow it with \`namespace\`, or raise \`maxScanSegments\` if the fleet really is that large and the memory is available.`
|
|
3566
|
+
);
|
|
3567
|
+
}
|
|
3568
|
+
}
|
|
3488
3569
|
const results = await mapWithConcurrency(recs, concurrency, async (rec) => {
|
|
3489
3570
|
if (rec.status === "destroyed") return { kind: "ok" };
|
|
3490
3571
|
const ref = { segment: rec.segment, namespace: rec.namespace };
|
|
@@ -3676,6 +3757,6 @@ var RetryingRegistryDriver = class {
|
|
|
3676
3757
|
}
|
|
3677
3758
|
};
|
|
3678
3759
|
|
|
3679
|
-
export { AWS_US_EAST_1_ONDEMAND, BoundedLru, BufferReader, BufferSink, CountingMetricsSink, CrbmColdChunkSource, CrbmReader, CrbmWriter, DEFAULT_BUDGET, DEFAULT_OCC_BACKOFF, DEFAULT_PRICING, DEFAULT_RETRY_POLICY, InProcessKeystore, LocalFsColdDriver, LocalFsRegistryDriver, LocalFsWarmDriver, MemoryColdChunkSource, MemoryColdDriver, MemoryRegistryDriver, MemoryWarmDriver, NOOP_AUDIT, NOOP_METRICS, NodeAead, RecordingAuditSink, RetryingColdChunkSource, RetryingColdDriver, RetryingRegistryDriver, RetryingWarmDriver, SegmentEngine, aadFor, bulkLoadCrbmGeneration, checkBudget, chunkRefKey, compactSegment, destroySegment, eraseNamespace, estimateCost, findCompactable, gcOrphanGenerations, groundedReport, isTransient, joinId, publishGeneration, resolveBudget, resolvePerOpBudget, runCompactionCycle, runConsistencyCheck, runExport, safeMetrics, segmentKey, splitId, validateCompactionOptions, withRetry, writeCrbmGeneration };
|
|
3760
|
+
export { AWS_US_EAST_1_ONDEMAND, BoundedLru, BufferReader, BufferSink, CountingMetricsSink, CrbmColdChunkSource, CrbmReader, CrbmWriter, DEFAULT_BUDGET, DEFAULT_MAX_SCAN_SEGMENTS, DEFAULT_MAX_WARM_SCAN_BYTES, DEFAULT_OCC_BACKOFF, DEFAULT_PRICING, DEFAULT_RETRY_POLICY, InProcessKeystore, LocalFsColdDriver, LocalFsRegistryDriver, LocalFsWarmDriver, MemoryColdChunkSource, MemoryColdDriver, MemoryRegistryDriver, MemoryWarmDriver, NOOP_AUDIT, NOOP_METRICS, NodeAead, RecordingAuditSink, RetryingColdChunkSource, RetryingColdDriver, RetryingRegistryDriver, RetryingWarmDriver, SegmentEngine, aadFor, bulkLoadCrbmGeneration, checkBudget, chunkRefKey, collectWithinBudget, compactSegment, destroySegment, eraseNamespace, estimateCost, findCompactable, gcOrphanGenerations, groundedReport, isTransient, joinId, publishGeneration, resolveBudget, resolvePerOpBudget, runCompactionCycle, runConsistencyCheck, runExport, safeMetrics, segmentKey, splitId, validateCompactionOptions, withRetry, writeCrbmGeneration };
|
|
3680
3761
|
//# sourceMappingURL=index.js.map
|
|
3681
3762
|
//# sourceMappingURL=index.js.map
|