@gscdump/lakehouse 0.35.6 → 0.35.7

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.
@@ -121,6 +121,15 @@ interface IcebergConnection {
121
121
  resolver: ReturnType<typeof cachingResolver>;
122
122
  /** The namespace the dataset's table lives under. */
123
123
  namespace: string;
124
+ /**
125
+ * Catalog identity baked into every cross-isolate cache key. Namespaces are
126
+ * NOT unique across catalogs (every per-team catalog uses the same 'gsc'
127
+ * namespace) while deployments share one KV, so a key of (namespace, table)
128
+ * alone lets one team's snapshot pointer/metadata poison every other team's
129
+ * reads. Set by {@link connectIcebergCatalog}; hand-built connections may
130
+ * omit it ONLY when their cache is not shared across catalogs.
131
+ */
132
+ cacheScope?: string;
124
133
  }
125
134
  declare const ICEBERG_TYPE_MAP: Record<IcebergColumnType, IcebergPrimitiveType>;
126
135
  /** Options for {@link connectIcebergCatalog}. */
@@ -139,6 +148,12 @@ interface ConnectIcebergOptions {
139
148
  * `fetch`, no node builtins.
140
149
  */
141
150
  declare function connectIcebergCatalog(config: IcebergCatalogConfig, opts?: ConnectIcebergOptions): Promise<IcebergConnection>;
151
+ /**
152
+ * The catalog-identity component of every cross-isolate cache key. Exported so
153
+ * write paths that only hold the config (not a connection) can invalidate the
154
+ * exact keys readers populate — see {@link invalidateSnapshotRef}.
155
+ */
156
+ declare function catalogCacheScope(config: Pick<IcebergCatalogConfig, 'catalogUri' | 'warehouse'>): string;
142
157
  /**
143
158
  * Ensure the catalog namespace exists. Idempotent — an "already exists"
144
159
  * response from the REST catalog is swallowed.
@@ -246,7 +261,7 @@ interface ResolveIcebergDataFilesOptions {
246
261
  * next read. Best-effort like every cache path here — a failed delete only
247
262
  * means TTL-bounded staleness, never an error.
248
263
  */
249
- declare function invalidateSnapshotRef(cache: CatalogCache, namespace: string, table: string): Promise<void>;
264
+ declare function invalidateSnapshotRef(cache: CatalogCache, namespace: string, table: string, cacheScope?: string): Promise<void>;
250
265
  /**
251
266
  * List the parquet data files in the current snapshot of `table`, filtered to
252
267
  * one partition slice (`matches` + `range`). Generic over the partition spec —
@@ -254,4 +269,4 @@ declare function invalidateSnapshotRef(cache: CatalogCache, namespace: string, t
254
269
  * the def's own spec + identity/dims values.
255
270
  */
256
271
  declare function resolveIcebergDataFiles(conn: IcebergConnection, opts: ResolveIcebergDataFilesOptions): Promise<IcebergListedDataFile[]>;
257
- export { CatalogCache, CommitRetryOptions, ConnectIcebergOptions, ICEBERG_TYPE_MAP, IcebergCatalogConfig, IcebergConnection, IcebergFieldSummary, IcebergListedDataFile, IcebergPartitionSpec, IcebergPartitionSpecField, IcebergPrimitiveType, IcebergSchema, IcebergSchemaField, IcebergSortOrder, IcebergSortOrderField, IcebergTableOpResult, ManifestPartitionFilter, PartitionValueMatch, QueryProfiler, ResolveIcebergDataFilesOptions, buildManifestPartitionFilter, cacheGet, cachePut, connectIcebergCatalog, dropIcebergTables, ensureIcebergNamespace, icebergAppendRetrying, invalidateSnapshotRef, isCommitRateLimited, listIcebergTables, resolveIcebergDataFiles };
272
+ export { CatalogCache, CommitRetryOptions, ConnectIcebergOptions, ICEBERG_TYPE_MAP, IcebergCatalogConfig, IcebergConnection, IcebergFieldSummary, IcebergListedDataFile, IcebergPartitionSpec, IcebergPartitionSpecField, IcebergPrimitiveType, IcebergSchema, IcebergSchemaField, IcebergSortOrder, IcebergSortOrderField, IcebergTableOpResult, ManifestPartitionFilter, PartitionValueMatch, QueryProfiler, ResolveIcebergDataFilesOptions, buildManifestPartitionFilter, cacheGet, cachePut, catalogCacheScope, connectIcebergCatalog, dropIcebergTables, ensureIcebergNamespace, icebergAppendRetrying, invalidateSnapshotRef, isCommitRateLimited, listIcebergTables, resolveIcebergDataFiles };
@@ -116,9 +116,13 @@ async function connectIcebergCatalog(config, opts = {}) {
116
116
  return {
117
117
  catalog,
118
118
  resolver,
119
- namespace: config.namespace
119
+ namespace: config.namespace,
120
+ cacheScope: catalogCacheScope(config)
120
121
  };
121
122
  }
123
+ function catalogCacheScope(config) {
124
+ return `${config.catalogUri}\0${config.warehouse}`;
125
+ }
122
126
  async function ensureIcebergNamespace(conn) {
123
127
  await restCatalogCreateNamespace(conn.catalog, { namespace: conn.namespace }).catch(() => {});
124
128
  }
@@ -201,17 +205,17 @@ const SNAPSHOT_REF_TTL_MS = 1800 * 1e3;
201
205
  const RESOLVED_FILES_TTL_MS = 1440 * 60 * 1e3;
202
206
  const METADATA_TTL_MS = 1440 * 60 * 1e3;
203
207
  const MAX_CACHED_METADATA_BYTES = 2 * 1024 * 1024;
204
- function snapshotRefKey(namespace, table) {
205
- return `lh-snapref\0${namespace}\0${table}`;
208
+ function snapshotRefKey(scope, namespace, table) {
209
+ return `lh-snapref\0${scope}\0${namespace}\0${table}`;
206
210
  }
207
- async function invalidateSnapshotRef(cache, namespace, table) {
208
- await cache.storage.removeItem(snapshotRefKey(namespace, table)).catch(() => {});
211
+ async function invalidateSnapshotRef(cache, namespace, table, cacheScope = "") {
212
+ await cache.storage.removeItem(snapshotRefKey(cacheScope, namespace, table)).catch(() => {});
209
213
  }
210
- function metadataRefKey(namespace, table, snapshotId) {
211
- return `lh-snapmeta\0${namespace}\0${table}\0${snapshotId}`;
214
+ function metadataRefKey(scope, namespace, table, snapshotId) {
215
+ return `lh-snapmeta\0${scope}\0${namespace}\0${table}\0${snapshotId}`;
212
216
  }
213
- function resolvedFilesKey(namespace, table, snapshotId, matches, wantedMonths) {
214
- return `lh-files\0${namespace}\0${table}\0${snapshotId}\0${[...matches].map((m) => `${m.field}=${m.value}`).sort().join(",")}\0${[...wantedMonths].sort((a, b) => a - b).join(",")}`;
217
+ function resolvedFilesKey(scope, namespace, table, snapshotId, matches, wantedMonths) {
218
+ return `lh-files\0${scope}\0${namespace}\0${table}\0${snapshotId}\0${[...matches].map((m) => `${m.field}=${m.value}`).sort().join(",")}\0${[...wantedMonths].sort((a, b) => a - b).join(",")}`;
215
219
  }
216
220
  function monthsInRange(range) {
217
221
  const [sy, sm] = range.start.split("-").map(Number);
@@ -240,8 +244,9 @@ function stripBucket(filePath) {
240
244
  return slash >= 0 ? rest.slice(slash + 1) : rest;
241
245
  }
242
246
  async function loadSnapshotId(conn, namespace, table, cache, now) {
247
+ const scope = conn.cacheScope ?? "";
243
248
  if (cache) {
244
- const cached = await cacheGet(cache, snapshotRefKey(namespace, table), now);
249
+ const cached = await cacheGet(cache, snapshotRefKey(scope, namespace, table), now);
245
250
  if (cached !== void 0) return {
246
251
  snapshotId: cached,
247
252
  metadata: null
@@ -254,9 +259,9 @@ async function loadSnapshotId(conn, namespace, table, cache, now) {
254
259
  const raw = metadata["current-snapshot-id"];
255
260
  const snapshotId = raw == null ? null : String(raw);
256
261
  if (cache) {
257
- await cachePut(cache, snapshotRefKey(namespace, table), snapshotId, SNAPSHOT_REF_TTL_MS, now);
262
+ await cachePut(cache, snapshotRefKey(scope, namespace, table), snapshotId, SNAPSHOT_REF_TTL_MS, now);
258
263
  if (snapshotId != null) {
259
- if (JSON.stringify(metadata).length <= MAX_CACHED_METADATA_BYTES) await cachePut(cache, metadataRefKey(namespace, table, snapshotId), metadata, METADATA_TTL_MS, now);
264
+ if (JSON.stringify(metadata).length <= MAX_CACHED_METADATA_BYTES) await cachePut(cache, metadataRefKey(scope, namespace, table, snapshotId), metadata, METADATA_TTL_MS, now);
260
265
  }
261
266
  }
262
267
  return {
@@ -273,7 +278,8 @@ async function resolveIcebergDataFiles(conn, opts) {
273
278
  let { snapshotId, metadata } = await loadSnapshotId(conn, namespace, table, opts.cache, now);
274
279
  endSnapshot?.({ cached: metadata == null && snapshotId != null });
275
280
  if (snapshotId == null) return [];
276
- const filesKey = resolvedFilesKey(namespace, table, snapshotId, opts.matches, wantedMonths);
281
+ const scope = conn.cacheScope ?? "";
282
+ const filesKey = resolvedFilesKey(scope, namespace, table, snapshotId, opts.matches, wantedMonths);
277
283
  if (opts.cache) {
278
284
  const endCache = profiler?.start("iceberg.cache");
279
285
  const cached = await cacheGet(opts.cache, filesKey, now);
@@ -281,7 +287,7 @@ async function resolveIcebergDataFiles(conn, opts) {
281
287
  if (cached !== void 0) return cached;
282
288
  }
283
289
  if (!metadata && opts.cache) {
284
- const cachedMeta = await cacheGet(opts.cache, metadataRefKey(namespace, table, snapshotId), now);
290
+ const cachedMeta = await cacheGet(opts.cache, metadataRefKey(scope, namespace, table, snapshotId), now);
285
291
  if (cachedMeta != null) metadata = cachedMeta;
286
292
  }
287
293
  if (!metadata) {
@@ -326,9 +332,9 @@ async function resolveIcebergDataFiles(conn, opts) {
326
332
  files: out.length
327
333
  });
328
334
  if (opts.cache) {
329
- const freshKey = resolvedFilesKey(namespace, table, snapshotId, opts.matches, wantedMonths);
335
+ const freshKey = resolvedFilesKey(scope, namespace, table, snapshotId, opts.matches, wantedMonths);
330
336
  await cachePut(opts.cache, freshKey, out, RESOLVED_FILES_TTL_MS, now);
331
337
  }
332
338
  return out;
333
339
  }
334
- export { ICEBERG_TYPE_MAP, buildManifestPartitionFilter, cacheGet, cachePut, connectIcebergCatalog, dropIcebergTables, ensureIcebergNamespace, icebergAppendRetrying, invalidateSnapshotRef, isCommitRateLimited, listIcebergTables, resolveIcebergDataFiles };
340
+ export { ICEBERG_TYPE_MAP, buildManifestPartitionFilter, cacheGet, cachePut, catalogCacheScope, connectIcebergCatalog, dropIcebergTables, ensureIcebergNamespace, icebergAppendRetrying, invalidateSnapshotRef, isCommitRateLimited, listIcebergTables, resolveIcebergDataFiles };
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { CatalogCache, CommitRetryOptions, ConnectIcebergOptions, ICEBERG_TYPE_MAP, IcebergCatalogConfig, IcebergConnection, IcebergFieldSummary, IcebergListedDataFile, IcebergPartitionSpec, IcebergPartitionSpecField, IcebergPrimitiveType, IcebergSchema, IcebergSchemaField, IcebergSortOrder, IcebergSortOrderField, IcebergTableOpResult, ManifestPartitionFilter, PartitionValueMatch, QueryProfiler, ResolveIcebergDataFilesOptions, buildManifestPartitionFilter, cacheGet, cachePut, connectIcebergCatalog, dropIcebergTables, ensureIcebergNamespace, invalidateSnapshotRef, listIcebergTables, resolveIcebergDataFiles } from "./_chunks/catalog.mjs";
1
+ import { CatalogCache, CommitRetryOptions, ConnectIcebergOptions, ICEBERG_TYPE_MAP, IcebergCatalogConfig, IcebergConnection, IcebergFieldSummary, IcebergListedDataFile, IcebergPartitionSpec, IcebergPartitionSpecField, IcebergPrimitiveType, IcebergSchema, IcebergSchemaField, IcebergSortOrder, IcebergSortOrderField, IcebergTableOpResult, ManifestPartitionFilter, PartitionValueMatch, QueryProfiler, ResolveIcebergDataFilesOptions, buildManifestPartitionFilter, cacheGet, cachePut, catalogCacheScope, connectIcebergCatalog, dropIcebergTables, ensureIcebergNamespace, invalidateSnapshotRef, listIcebergTables, resolveIcebergDataFiles } from "./_chunks/catalog.mjs";
2
2
  import { DEFAULT_PARTITION_KEY_ENCODING, IcebergColumn, IcebergColumnType, IcebergPartitionField, IcebergPartitionTransform, IcebergS3Config, IcebergTableSpec, PartitionKeyEncoding } from "./_chunks/schema.mjs";
3
3
  /**
4
4
  * Closed identity shapes (ADR-0021 amendments 2 + 4). `'site-int'` is the
@@ -156,4 +156,4 @@ interface RunPyIcebergWriterOptions {
156
156
  }
157
157
  declare function resolvePyIcebergPython(override?: string): string;
158
158
  declare function runPyIcebergWriter<T extends PyIcebergWriterResult>(options: RunPyIcebergWriterOptions): Promise<T>;
159
- export { type AppendCommitOptions, type AppendResult, type AppendSink, type AppendSinkCloseResult, type AppendSinkOptions, type CatalogCache, type CommitRetryOptions, type ConnectIcebergOptions, DEFAULT_PARTITION_KEY_ENCODING, type DatasetDim, type DatasetIdentity, ICEBERG_TYPE_MAP, type IcebergCatalogConfig, type IcebergColumn, type IcebergColumnType, type IcebergConnection, type IcebergDataset, type IcebergDatasetColumnDef, type IcebergDatasetDef, type IcebergDatasetLedger, type IcebergFieldSummary, type IcebergListedDataFile, type IcebergPartitionField, type IcebergPartitionSpec, type IcebergPartitionSpecField, type IcebergPartitionTransform, type IcebergPrimitiveType, type IcebergS3Config, type IcebergSchema, type IcebergSchemaField, type IcebergSortOrder, type IcebergSortOrderField, type IcebergTableOpResult, type IcebergTableSpec, type ManifestPartitionFilter, type PartitionKeyEncoding, type PartitionValueMatch, type PyIcebergWriterResult, type QueryProfiler, type ResolveDataFilesOptions, type ResolveIcebergDataFilesOptions, type RunPyIcebergWriterOptions, buildManifestPartitionFilter, cacheGet, cachePut, connectIcebergCatalog, defineIcebergDataset, deriveTableSpec, dropIcebergTables, ensureIcebergNamespace, invalidateSnapshotRef, listIcebergTables, resolveIcebergDataFiles, resolvePyIcebergPython, runPyIcebergWriter, toIcebergDayCount };
159
+ export { type AppendCommitOptions, type AppendResult, type AppendSink, type AppendSinkCloseResult, type AppendSinkOptions, type CatalogCache, type CommitRetryOptions, type ConnectIcebergOptions, DEFAULT_PARTITION_KEY_ENCODING, type DatasetDim, type DatasetIdentity, ICEBERG_TYPE_MAP, type IcebergCatalogConfig, type IcebergColumn, type IcebergColumnType, type IcebergConnection, type IcebergDataset, type IcebergDatasetColumnDef, type IcebergDatasetDef, type IcebergDatasetLedger, type IcebergFieldSummary, type IcebergListedDataFile, type IcebergPartitionField, type IcebergPartitionSpec, type IcebergPartitionSpecField, type IcebergPartitionTransform, type IcebergPrimitiveType, type IcebergS3Config, type IcebergSchema, type IcebergSchemaField, type IcebergSortOrder, type IcebergSortOrderField, type IcebergTableOpResult, type IcebergTableSpec, type ManifestPartitionFilter, type PartitionKeyEncoding, type PartitionValueMatch, type PyIcebergWriterResult, type QueryProfiler, type ResolveDataFilesOptions, type ResolveIcebergDataFilesOptions, type RunPyIcebergWriterOptions, buildManifestPartitionFilter, cacheGet, cachePut, catalogCacheScope, connectIcebergCatalog, defineIcebergDataset, deriveTableSpec, dropIcebergTables, ensureIcebergNamespace, invalidateSnapshotRef, listIcebergTables, resolveIcebergDataFiles, resolvePyIcebergPython, runPyIcebergWriter, toIcebergDayCount };
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { icebergCreateTable } from "./_chunks/libs/icebird.mjs";
2
- import { ICEBERG_TYPE_MAP, buildManifestPartitionFilter, cacheGet, cachePut, connectIcebergCatalog, dropIcebergTables, ensureIcebergNamespace, icebergAppendRetrying, invalidateSnapshotRef, listIcebergTables, resolveIcebergDataFiles } from "./_chunks/catalog.mjs";
2
+ import { ICEBERG_TYPE_MAP, buildManifestPartitionFilter, cacheGet, cachePut, catalogCacheScope, connectIcebergCatalog, dropIcebergTables, ensureIcebergNamespace, icebergAppendRetrying, invalidateSnapshotRef, listIcebergTables, resolveIcebergDataFiles } from "./_chunks/catalog.mjs";
3
3
  import process from "node:process";
4
4
  const INT32_MIN = -2147483648;
5
5
  const INT32_MAX = 2147483647;
@@ -353,4 +353,4 @@ async function runPyIcebergWriter(options) {
353
353
  });
354
354
  }
355
355
  const DEFAULT_PARTITION_KEY_ENCODING = "int";
356
- export { DEFAULT_PARTITION_KEY_ENCODING, ICEBERG_TYPE_MAP, buildManifestPartitionFilter, cacheGet, cachePut, connectIcebergCatalog, defineIcebergDataset, deriveTableSpec, dropIcebergTables, ensureIcebergNamespace, invalidateSnapshotRef, listIcebergTables, resolveIcebergDataFiles, resolvePyIcebergPython, runPyIcebergWriter, toIcebergDayCount };
356
+ export { DEFAULT_PARTITION_KEY_ENCODING, ICEBERG_TYPE_MAP, buildManifestPartitionFilter, cacheGet, cachePut, catalogCacheScope, connectIcebergCatalog, defineIcebergDataset, deriveTableSpec, dropIcebergTables, ensureIcebergNamespace, invalidateSnapshotRef, listIcebergTables, resolveIcebergDataFiles, resolvePyIcebergPython, runPyIcebergWriter, toIcebergDayCount };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gscdump/lakehouse",
3
3
  "type": "module",
4
- "version": "0.35.6",
4
+ "version": "0.35.7",
5
5
  "description": "Dataset-agnostic Iceberg lakehouse layer + dataset registry for R2 Data Catalog producers (ADR-0021).",
6
6
  "author": {
7
7
  "name": "Harlan Wilton",