sliftutils 1.4.86 → 1.4.88

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/index.d.ts CHANGED
@@ -1153,6 +1153,7 @@ declare module "sliftutils/storage/BulkDatabase2/BulkDatabaseFormat" {
1153
1153
  export declare function buildFileBuffer(rows: Record<string, unknown>[], times: number[], targetBytes?: number): BuiltFile[];
1154
1154
  export declare function buildFileBufferRaw(rows: RawRow[], targetBytes?: number): BuiltFile[];
1155
1155
  export type BaseBulkDatabaseReader = {
1156
+ name?: string;
1156
1157
  rowCount: number;
1157
1158
  totalBytes: number;
1158
1159
  minTime: number;
@@ -1194,6 +1195,7 @@ declare module "sliftutils/storage/BulkDatabase2/BulkDatabaseFormat" {
1194
1195
  export declare function loadBulkDatabase(config: {
1195
1196
  totalBytes: number;
1196
1197
  getRange: (start: number, end: number) => Promise<Buffer>;
1198
+ name?: string;
1197
1199
  }): Promise<BaseBulkDatabaseReader>;
1198
1200
 
1199
1201
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sliftutils",
3
- "version": "1.4.86",
3
+ "version": "1.4.88",
4
4
  "main": "index.js",
5
5
  "license": "MIT",
6
6
  "files": [
@@ -41,6 +41,7 @@ export interface BuiltFile {
41
41
  export declare function buildFileBuffer(rows: Record<string, unknown>[], times: number[], targetBytes?: number): BuiltFile[];
42
42
  export declare function buildFileBufferRaw(rows: RawRow[], targetBytes?: number): BuiltFile[];
43
43
  export type BaseBulkDatabaseReader = {
44
+ name?: string;
44
45
  rowCount: number;
45
46
  totalBytes: number;
46
47
  minTime: number;
@@ -82,4 +83,5 @@ export declare function loadBulkHeader(getRange: (start: number, end: number) =>
82
83
  export declare function loadBulkDatabase(config: {
83
84
  totalBytes: number;
84
85
  getRange: (start: number, end: number) => Promise<Buffer>;
86
+ name?: string;
85
87
  }): Promise<BaseBulkDatabaseReader>;
@@ -410,6 +410,9 @@ export function buildFileBufferRaw(rows: RawRow[], targetBytes = TARGET_FILE_BYT
410
410
  }
411
411
 
412
412
  export type BaseBulkDatabaseReader = {
413
+ // Identifies the source this reader came from (the bulk file name, or "(streams)") so the join can
414
+ // name the offending file when one of its reads fails. Undefined for readers built without one.
415
+ name?: string;
413
416
  rowCount: number;
414
417
  totalBytes: number;
415
418
  // Write-time bounds of this reader's data (0 if unknown — old bulk files).
@@ -468,6 +471,7 @@ export async function loadBulkHeader(getRange: (start: number, end: number) => P
468
471
  export async function loadBulkDatabase(config: {
469
472
  totalBytes: number;
470
473
  getRange: (start: number, end: number) => Promise<Buffer>;
474
+ name?: string;
471
475
  }): Promise<BaseBulkDatabaseReader> {
472
476
  const headerLength = (await config.getRange(0, 4)).readUInt32LE(0);
473
477
  if (headerLength <= 0 || headerLength > config.totalBytes) {
@@ -502,6 +506,7 @@ export async function loadBulkDatabase(config: {
502
506
  : keys.map(() => header.maxTime || 0);
503
507
 
504
508
  return {
509
+ name: config.name,
505
510
  rowCount,
506
511
  totalBytes: config.totalBytes,
507
512
  minTime: header.minTime || 0,
@@ -257,7 +257,7 @@ export async function loadFileReader(name: string, storage: FileStorage, f: Bulk
257
257
  const raw = await makeRawGetRange(storage, f.fileName);
258
258
  const fileId = nullJoin(name, f.fileName);
259
259
  const opened = await blockCache.open(fileId, raw.size, raw.rawGetRange);
260
- const reader = await loadBulkDatabase({ totalBytes: opened.uncompressedSize, getRange: opened.getRange });
260
+ const reader = await loadBulkDatabase({ totalBytes: opened.uncompressedSize, getRange: opened.getRange, name: f.fileName });
261
261
  cache.set(f.fileName, reader);
262
262
  return reader;
263
263
  }
@@ -327,6 +327,19 @@ function pruneSubCaches(bulkFiles: BulkFileInfo[], streamFiles: StreamFileInfo[]
327
327
  for (const n of subCaches.stream.keys()) if (!liveStream.has(n)) subCaches.stream.delete(n);
328
328
  }
329
329
 
330
+ // A corrupt/unreadable column in ONE underlying file must not break the whole joined read. When a
331
+ // source's column read throws, we drop that source for that column (falling through to older readers,
332
+ // exactly as if the file never set the column) and warn once per (file, column) so the corruption is
333
+ // visible without flooding the log on every rebuild.
334
+ const warnedCorruptReads = new Set<string>();
335
+ function warnCorruptRead(db: BaseBulkDatabaseReader, column: string, e: unknown): void {
336
+ const fileName = db.name || "(unknown source)";
337
+ const dedupeKey = nullJoin(fileName, column);
338
+ if (warnedCorruptReads.has(dedupeKey)) return;
339
+ warnedCorruptReads.add(dedupeKey);
340
+ console.warn(`${red("corrupt read")}: file ${fileName} column ${JSON.stringify(column)} could not be read - skipping it (its data is dropped from results): ${(e as Error).message}`);
341
+ }
342
+
330
343
  function joinBulkDatabases(databases: BaseBulkDatabaseReader[]): ResolvedReader {
331
344
  const deleteTime = new Map<string, number>();
332
345
  for (const db of databases) {
@@ -367,8 +380,13 @@ function joinBulkDatabases(databases: BaseBulkDatabaseReader[]): ResolvedReader
367
380
  async getColumn(column) {
368
381
  const perReader = await Promise.all(databases.map(async db => {
369
382
  if (!db.columns.some(c => c.column === column)) return undefined;
370
- const entries = await db.getColumn(column);
371
- return new Map(entries.map(e => [e.key, { value: e.value, time: e.time }]));
383
+ try {
384
+ const entries = await db.getColumn(column);
385
+ return new Map(entries.map(e => [e.key, { value: e.value, time: e.time }]));
386
+ } catch (e) {
387
+ warnCorruptRead(db, column, e);
388
+ return undefined;
389
+ }
372
390
  }));
373
391
  return keys.map(key => {
374
392
  let bestTime = -Infinity;
@@ -391,7 +409,13 @@ function joinBulkDatabases(databases: BaseBulkDatabaseReader[]): ResolvedReader
391
409
  let found = false;
392
410
  for (const db of databases) {
393
411
  if (!db.columns.some(c => c.column === column)) continue;
394
- const r = await db.getSingleField(key, column);
412
+ let r;
413
+ try {
414
+ r = await db.getSingleField(key, column);
415
+ } catch (e) {
416
+ warnCorruptRead(db, column, e);
417
+ continue;
418
+ }
395
419
  if (r === ABSENT) continue;
396
420
  if (r.time > bestTime) { bestTime = r.time; bestVal = r.value; found = true; }
397
421
  }
@@ -106,6 +106,7 @@ export function streamReaderFromEntries(entries: StreamEntry[], totalBytes: numb
106
106
  }
107
107
  const keyIndexMap = buildKeyIndex(keys);
108
108
  let reader: BaseBulkDatabaseReader = {
109
+ name: "(streams)",
109
110
  totalBytes,
110
111
  rowCount: keys.length,
111
112
  minTime: times.size ? minTime : 0,
@@ -2,7 +2,6 @@ import cborx from "cbor-x";
2
2
  import { Database, namespaceDatabase } from "./Database";
3
3
  import { TransactionSetStore, transactionRead, transactionMutate, transactionDelete, replayTransactionStore } from "./transactionSet";
4
4
  import { StoredEmbedding, EmbeddingFormat, getCloseness, embeddingToFloat32, releaseFloat32, encodeEmbedding, hashEmbedding } from "../embeddingFormats";
5
- import { isDefined } from "../../misc/types";
6
5
 
7
6
  export type IvfConfig = {
8
7
  model: string;
@@ -241,7 +240,8 @@ export function searchEmbeddings(
241
240
  if (!centroids.size) return [];
242
241
  const probeCellCount = Math.max(1, Math.ceil(options.probeBudget / config.cellTargetSize));
243
242
  const probeCellIds = rankCellsByCloseness(query, centroids).slice(0, probeCellCount);
244
- const stores = probeCellIds.map(cellId => database.readData(root => root.cells[cellId])).filter(isDefined);
243
+ const stores = database.readData(root => probeCellIds.map(cellId => root.cells[cellId]));
244
+ if (!stores) return undefined;
245
245
  for (const store of stores) {
246
246
  const members = replayTransactionStore<StoredEmbedding>(store);
247
247
  for (const ref of members.keys()) {