@gscdump/engine 0.37.2 → 0.37.3

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.
@@ -758,7 +758,10 @@ function createSitemapStore(opts) {
758
758
  }
759
759
  }
760
760
  },
761
- async compactUrls(ctx) {
761
+ async compactUrls(ctx, opts = {}) {
762
+ const startedAt = now();
763
+ const deadlineMs = opts.deadlineMs ?? Number.POSITIVE_INFINITY;
764
+ const maxFeedpaths = opts.maxFeedpaths ?? Number.POSITIVE_INFINITY;
762
765
  const deltaKeys = await ds.list(`${sitemapUrlsPrefix(ctx)}/deltas/`);
763
766
  const deltasByFeed = /* @__PURE__ */ new Map();
764
767
  for (const key of deltaKeys) {
@@ -770,7 +773,10 @@ function createSitemapStore(opts) {
770
773
  list.push(key);
771
774
  deltasByFeed.set(feedpathHash, list);
772
775
  }
776
+ const totalFeedpaths = deltasByFeed.size;
777
+ let compactedFeedpaths = 0;
773
778
  for (const [fpHash, feedDeltaKeys] of deltasByFeed) {
779
+ if (compactedFeedpaths > 0 && (compactedFeedpaths >= maxFeedpaths || now() - startedAt >= deadlineMs)) break;
774
780
  const indexKey = sitemapUrlsIndexKey(ctx, fpHash);
775
781
  const indexBytes = await readOptional(ds, indexKey);
776
782
  const indexRows = indexBytes ? await decodeParquetToRows(indexBytes) : [];
@@ -821,7 +827,12 @@ function createSitemapStore(opts) {
821
827
  });
822
828
  await ds.write(indexKey, bytes);
823
829
  if (consumed.length > 0) await ds.delete(consumed);
830
+ compactedFeedpaths++;
824
831
  }
832
+ return {
833
+ compactedFeedpaths,
834
+ remainingFeedpaths: totalFeedpaths - compactedFeedpaths
835
+ };
825
836
  },
826
837
  async reconcile(ctx, { liveFeedpaths, at: atOpt }) {
827
838
  const at = atOpt ?? now();
@@ -379,6 +379,29 @@ interface ReconcileResult {
379
379
  /** Total URL rows transitioned live → removed across pruned feedpaths. */
380
380
  urlsRemoved: number;
381
381
  }
382
+ /**
383
+ * Bounds on a single `compactUrls` call. `compactUrls` is memory-bounded (one
384
+ * feedpath at a time) but was previously unbounded in TIME: a tenant with a
385
+ * large accumulated delta backlog could run past a caller's execution budget.
386
+ * Both bounds are checked BETWEEN feedpaths and only after at least one has
387
+ * been compacted, so a call always makes forward progress.
388
+ */
389
+ interface CompactUrlsOptions {
390
+ /** Stop before starting another feedpath once this many ms have elapsed. */
391
+ deadlineMs?: number;
392
+ /** Stop after compacting this many feedpaths. */
393
+ maxFeedpaths?: number;
394
+ }
395
+ interface CompactUrlsResult {
396
+ /** Feedpaths whose deltas were folded and deleted by this call. */
397
+ compactedFeedpaths: number;
398
+ /**
399
+ * Feedpaths that still hold outstanding deltas. Call `compactUrls` again to
400
+ * continue — no cursor is needed, because a compacted feedpath's deltas are
401
+ * deleted, so the next call simply doesn't see it.
402
+ */
403
+ remainingFeedpaths: number;
404
+ }
382
405
  interface DeltaEntry {
383
406
  feedpath: string;
384
407
  feedpathHash: string;
@@ -428,8 +451,13 @@ interface SitemapStore {
428
451
  * rewrites each touched feedpath's `by-feed/<hash>/index.parquet` and deletes
429
452
  * the consumed delta files. Bounded per feedpath, so it stays within memory
430
453
  * regardless of total site URL count.
454
+ *
455
+ * Optionally bounded in TIME too (`opts.deadlineMs` / `opts.maxFeedpaths`).
456
+ * A bounded call is safe to stop mid-way: each feedpath's deltas are deleted
457
+ * as soon as its index is rewritten, so the remainder is simply what the next
458
+ * call finds. Callers drive this from `remainingFeedpaths`, not a cursor.
431
459
  */
432
- compactUrls: (ctx: TenantCtx) => Promise<void>;
460
+ compactUrls: (ctx: TenantCtx, opts?: CompactUrlsOptions) => Promise<CompactUrlsResult>;
433
461
  /**
434
462
  * Site-wide convergence: mark every still-live URL whose owning feedpath is
435
463
  * absent from `liveFeedpaths` as removed. `compactUrls`/`snapshotUrls` only
@@ -496,4 +524,4 @@ interface CreateEmptyTypesStoreOptions {
496
524
  now?: () => number;
497
525
  }
498
526
  declare function createEmptyTypesStore(opts: CreateEmptyTypesStoreOptions): EmptyTypesStore;
499
- export { CreateEmptyTypesStoreOptions, CreateIndexingMetadataStoreOptions, CreateInspectionStoreOptions, CreateSitemapStoreOptions, DateRange, DeltaEntry, EmptyTypesDoc, EmptyTypesStore, INSPECTION_EVENT_COLUMNS, INSPECTION_HISTORY_MAX_BYTES, IndexingMetadataIndex, IndexingMetadataRecord, IndexingMetadataStore, InspectionEventRow, InspectionHistoryShard, InspectionIndex, InspectionParquetRow, InspectionRecord, InspectionStore, LoadUrlsOptions, ParsedUrl, QueryDimDeps, QueryDimMeta, QueryDimRecord, QueryDimStore, ReconcileResult, SitemapHistoryDoc, SitemapIndex, SitemapRecord, SitemapStore, SitemapUrlRecord, SnapshotUrlsResult, buildQueryDimRecords, createEmptyTypesStore, createIndexingMetadataStore, createInspectionStore, createQueryDimStore, createSitemapStore, emptyTypesKey, hashUrl, hashUrlList, indexingMetadataIndexKey, inspectionBaseKey, inspectionEventKey, inspectionEventsPrefix, inspectionHistoryPrefix, inspectionHistoryShardKey, inspectionIndexKey, inspectionParquetKey, queryDimMetaKey, queryDimParquetKey, sitemapHistoryKey, sitemapIndexKey, sitemapUrlsDeltaKey, sitemapUrlsIndexKey, sitemapUrlsIndexPrefix };
527
+ export { CompactUrlsOptions, CompactUrlsResult, CreateEmptyTypesStoreOptions, CreateIndexingMetadataStoreOptions, CreateInspectionStoreOptions, CreateSitemapStoreOptions, DateRange, DeltaEntry, EmptyTypesDoc, EmptyTypesStore, INSPECTION_EVENT_COLUMNS, INSPECTION_HISTORY_MAX_BYTES, IndexingMetadataIndex, IndexingMetadataRecord, IndexingMetadataStore, InspectionEventRow, InspectionHistoryShard, InspectionIndex, InspectionParquetRow, InspectionRecord, InspectionStore, LoadUrlsOptions, ParsedUrl, QueryDimDeps, QueryDimMeta, QueryDimRecord, QueryDimStore, ReconcileResult, SitemapHistoryDoc, SitemapIndex, SitemapRecord, SitemapStore, SitemapUrlRecord, SnapshotUrlsResult, buildQueryDimRecords, createEmptyTypesStore, createIndexingMetadataStore, createInspectionStore, createQueryDimStore, createSitemapStore, emptyTypesKey, hashUrl, hashUrlList, indexingMetadataIndexKey, inspectionBaseKey, inspectionEventKey, inspectionEventsPrefix, inspectionHistoryPrefix, inspectionHistoryShardKey, inspectionIndexKey, inspectionParquetKey, queryDimMetaKey, queryDimParquetKey, sitemapHistoryKey, sitemapIndexKey, sitemapUrlsDeltaKey, sitemapUrlsIndexKey, sitemapUrlsIndexPrefix };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gscdump/engine",
3
3
  "type": "module",
4
- "version": "0.37.2",
4
+ "version": "0.37.3",
5
5
  "description": "Append-only Parquet/DuckDB storage engine + planner + adapters for the gscdump pipeline. Node + edge runtimes; opt-in heavy peers.",
6
6
  "author": {
7
7
  "name": "Harlan Wilton",
@@ -181,9 +181,9 @@
181
181
  "dependencies": {
182
182
  "drizzle-orm": "1.0.0-rc.3",
183
183
  "proper-lockfile": "^4.1.2",
184
- "@gscdump/contracts": "0.37.2",
185
- "gscdump": "0.37.2",
186
- "@gscdump/lakehouse": "0.37.2"
184
+ "@gscdump/lakehouse": "0.37.3",
185
+ "@gscdump/contracts": "0.37.3",
186
+ "gscdump": "0.37.3"
187
187
  },
188
188
  "devDependencies": {
189
189
  "@duckdb/duckdb-wasm": "^1.32.0",