grepmax 0.26.11 → 0.26.13
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/.claude-plugin/marketplace.json +1 -1
- package/dist/commands/doctor.js +67 -12
- package/dist/config.js +14 -1
- package/dist/lib/daemon/daemon.js +22 -0
- package/dist/lib/daemon/ipc-handler.js +3 -0
- package/dist/lib/store/vector-db.js +87 -0
- package/package.json +1 -1
- package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"name": "grepmax",
|
|
11
11
|
"source": "./plugins/grepmax",
|
|
12
12
|
"description": "Semantic code search for Claude Code. Automatically indexes your project and provides intelligent search capabilities.",
|
|
13
|
-
"version": "0.26.
|
|
13
|
+
"version": "0.26.13",
|
|
14
14
|
"author": {
|
|
15
15
|
"name": "Robert Owens",
|
|
16
16
|
"email": "robowens@me.com"
|
package/dist/commands/doctor.js
CHANGED
|
@@ -88,7 +88,7 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
88
88
|
.option("--fix", "Auto-fix detected issues (compact, prune, remove stale locks)", false)
|
|
89
89
|
.option("--agent", "Compact output for AI agents", false)
|
|
90
90
|
.action((opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
91
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
91
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
92
92
|
if (!opts.agent)
|
|
93
93
|
console.log("gmax Doctor\n");
|
|
94
94
|
const root = config_1.PATHS.globalRoot;
|
|
@@ -198,7 +198,7 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
198
198
|
let staleLock = false;
|
|
199
199
|
const orphanedProjects = [];
|
|
200
200
|
try {
|
|
201
|
-
const { VectorDB } = yield Promise.resolve().then(() => __importStar(require("../lib/store/vector-db")));
|
|
201
|
+
const { VectorDB, findStaleLanceTempFiles, sweepStaleLanceTempFiles } = yield Promise.resolve().then(() => __importStar(require("../lib/store/vector-db")));
|
|
202
202
|
const db = new VectorDB(config_1.PATHS.lancedbDir);
|
|
203
203
|
const table = yield db.ensureTable();
|
|
204
204
|
const totalChunks = yield table.countRows();
|
|
@@ -258,7 +258,16 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
258
258
|
}
|
|
259
259
|
}
|
|
260
260
|
// Compute warning flags
|
|
261
|
-
|
|
261
|
+
//
|
|
262
|
+
// Abandoned `.tmp*` scratch files are disk that optimize provably cannot
|
|
263
|
+
// reclaim (no manifest entry, so `deleteUnverified` will not touch them).
|
|
264
|
+
// They get their own remediation — a sweep — and are excluded from the
|
|
265
|
+
// ratio that decides whether to run a whole-table optimize, because
|
|
266
|
+
// counting them there prescribes a slow rewrite for bytes it cannot free.
|
|
267
|
+
const staleTempFiles = findStaleLanceTempFiles(config_1.PATHS.lancedbDir);
|
|
268
|
+
const staleTempBytes = staleTempFiles.reduce((n, f) => n + f.size, 0);
|
|
269
|
+
const reclaimableDiskSize = Math.max(0, diskSize - staleTempBytes);
|
|
270
|
+
const bloatRatio = logicalSize > 0 ? reclaimableDiskSize / logicalSize : 0;
|
|
262
271
|
if (bloatRatio > 2.0)
|
|
263
272
|
needsOptimize = true;
|
|
264
273
|
// Track the compactor's own threshold. Warning (and worse, --fix forcing a
|
|
@@ -281,7 +290,7 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
281
290
|
? "LOW"
|
|
282
291
|
: "ok";
|
|
283
292
|
}
|
|
284
|
-
catch (
|
|
293
|
+
catch (_p) { }
|
|
285
294
|
const staleChunkerProjects = projects.filter((p) => {
|
|
286
295
|
var _a;
|
|
287
296
|
return p.status === "indexed" &&
|
|
@@ -304,6 +313,8 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
304
313
|
`lock=${lockStatus.split(" ")[0]}`,
|
|
305
314
|
`daemon=${daemonUp ? "running" : "stopped"}`,
|
|
306
315
|
`orphaned=${orphanedProjects.length}`,
|
|
316
|
+
`stale_temp=${staleTempFiles.length}`,
|
|
317
|
+
`stale_temp_bytes=${staleTempBytes}`,
|
|
307
318
|
`stale_chunker=${staleChunkerProjects.length}`,
|
|
308
319
|
`stale_embedding=${staleEmbeddingProjects.length}`,
|
|
309
320
|
`legacy_embedding=${legacyEmbeddingCount}`,
|
|
@@ -380,11 +391,15 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
380
391
|
console.log(`ok Disk: ${formatSize(availBytes)} available`);
|
|
381
392
|
}
|
|
382
393
|
// Storage
|
|
394
|
+
const storageLine = `${totalChunks.toLocaleString()} rows, ${formatSize(logicalSize)} logical, ${formatSize(diskSize)} disk`;
|
|
383
395
|
if (bloatRatio > 2.0) {
|
|
384
|
-
console.log(`WARN Storage: ${
|
|
396
|
+
console.log(`WARN Storage: ${storageLine} (${bloatRatio.toFixed(1)}x — orphaned files)`);
|
|
385
397
|
}
|
|
386
398
|
else {
|
|
387
|
-
console.log(`ok Storage: ${
|
|
399
|
+
console.log(`ok Storage: ${storageLine}`);
|
|
400
|
+
}
|
|
401
|
+
if (staleTempFiles.length > 0) {
|
|
402
|
+
console.log(`WARN Abandoned temp files: ${staleTempFiles.length} (${formatSize(staleTempBytes)}) — leftover from interrupted compaction, run with --fix`);
|
|
388
403
|
}
|
|
389
404
|
// Fragments
|
|
390
405
|
if (numSmallFragments > config_1.FRAGMENT_COMPACT_THRESHOLD) {
|
|
@@ -486,7 +501,7 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
486
501
|
yield mc.close();
|
|
487
502
|
}
|
|
488
503
|
}
|
|
489
|
-
catch (
|
|
504
|
+
catch (_q) { }
|
|
490
505
|
}
|
|
491
506
|
}
|
|
492
507
|
// --fix auto-remediation
|
|
@@ -500,12 +515,52 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
500
515
|
console.log("ok Removed stale lock");
|
|
501
516
|
fixed++;
|
|
502
517
|
}
|
|
518
|
+
// Sweep before optimize: these bytes are unreachable by optimize, and
|
|
519
|
+
// clearing them first means any bloat still visible afterwards is real.
|
|
520
|
+
// Unlike optimize this needs no single-writer routing — the age gate in
|
|
521
|
+
// findStaleLanceTempFiles already excludes anything a writer could hold.
|
|
522
|
+
if (staleTempFiles.length > 0) {
|
|
523
|
+
const swept = sweepStaleLanceTempFiles(config_1.PATHS.lancedbDir);
|
|
524
|
+
if (!opts.agent)
|
|
525
|
+
console.log(`ok Removed ${swept.filesRemoved} abandoned temp file(s), freed ${formatSize(swept.bytesFreed)}`);
|
|
526
|
+
fixed++;
|
|
527
|
+
}
|
|
503
528
|
if (needsOptimize) {
|
|
504
529
|
if (!opts.agent)
|
|
505
530
|
console.log("... Running optimize (compact + prune)...");
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
531
|
+
// Prefer the daemon. Lance's optimize deletes unverified files, which
|
|
532
|
+
// is only safe with a single writer on the store — optimizing here
|
|
533
|
+
// while the daemon compacts made this command a second writer. With a
|
|
534
|
+
// daemon up it owns compaction; without one we are the sole process
|
|
535
|
+
// and can do it in-process safely.
|
|
536
|
+
const { sendDaemonCommand } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/daemon-client")));
|
|
537
|
+
const resp = yield sendDaemonCommand({ cmd: "optimize" }, { timeoutMs: 10 * 60 * 1000 });
|
|
538
|
+
let via = "daemon";
|
|
539
|
+
if (!resp.ok) {
|
|
540
|
+
// Only fall back when the daemon could not take it. A daemon that
|
|
541
|
+
// answered with a real failure should surface, not be retried here
|
|
542
|
+
// as a competing writer.
|
|
543
|
+
const reason = String((_k = resp.error) !== null && _k !== void 0 ? _k : "");
|
|
544
|
+
// Fall back ONLY when nothing is listening. A daemon that is merely
|
|
545
|
+
// too old ("unknown command") or busy ("timeout") is still a live
|
|
546
|
+
// writer on the store, so optimizing here would recreate the
|
|
547
|
+
// two-writer case this routing exists to prevent.
|
|
548
|
+
const daemonAbsent = reason.includes("ECONNREFUSED") || reason.includes("ENOENT");
|
|
549
|
+
if (daemonAbsent) {
|
|
550
|
+
yield db.optimize(3, 0);
|
|
551
|
+
via = "in-process (no daemon)";
|
|
552
|
+
}
|
|
553
|
+
else if (reason.includes("unknown command")) {
|
|
554
|
+
via = "";
|
|
555
|
+
console.log("WARN Optimize skipped: the running daemon predates the optimize command. Restart it (gmax watch --daemon -b) and re-run.");
|
|
556
|
+
}
|
|
557
|
+
else {
|
|
558
|
+
via = "";
|
|
559
|
+
console.log(`WARN Optimize failed via daemon: ${reason}`);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
if (via && !opts.agent)
|
|
563
|
+
console.log(`ok Optimize complete (${via})`);
|
|
509
564
|
fixed++;
|
|
510
565
|
}
|
|
511
566
|
if (orphanedProjects.length > 0) {
|
|
@@ -533,7 +588,7 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
533
588
|
const done = yield sendStreamingCommand({ cmd: "index", root: p.root, reset: true }, () => { }).catch((e) => ({ ok: false, error: String(e) }));
|
|
534
589
|
if (done.ok &&
|
|
535
590
|
(!("degraded" in done) || done.degraded !== true)) {
|
|
536
|
-
const chunks = (
|
|
591
|
+
const chunks = (_l = done.indexed) !== null && _l !== void 0 ? _l : 0;
|
|
537
592
|
if (opts.agent) {
|
|
538
593
|
console.log(`stale_chunker_reindexed\tname=${name}\tchunks=${chunks}`);
|
|
539
594
|
}
|
|
@@ -545,7 +600,7 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
545
600
|
else if (!opts.agent) {
|
|
546
601
|
const degraded = "degraded" in done && done.degraded === true;
|
|
547
602
|
const reason = degraded
|
|
548
|
-
? `${(
|
|
603
|
+
? `${(_m = ("scanErrors" in done ? done.scanErrors : 0)) !== null && _m !== void 0 ? _m : 0} scan errors, ${(_o = ("failedFiles" in done ? done.failedFiles : 0)) !== null && _o !== void 0 ? _o : 0} file failures`
|
|
549
604
|
: done.error;
|
|
550
605
|
console.log(`FAIL ${name}: reindex failed (${reason})`);
|
|
551
606
|
}
|
package/dist/config.js
CHANGED
|
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.INDEXABLE_EXTENSIONS = exports.VERSION_PRUNE_THRESHOLD = exports.FRAGMENT_COMPACT_THRESHOLD = exports.DISK_LOW_BYTES = exports.DISK_CRITICAL_BYTES = exports.MAX_FILE_SIZE_BYTES = exports.PATHS = exports.MAX_WORKER_MEMORY_MB = exports.WORKER_BOOT_TIMEOUT_MS = exports.WORKER_TIMEOUT_MS = exports.REBUILD_COMMAND = exports.CHUNKER_VERSION_HISTORY = exports.CONFIG = exports.MODEL_IDS = exports.DEFAULT_MODEL_TIER = exports.MODEL_TIERS = void 0;
|
|
36
|
+
exports.INDEXABLE_EXTENSIONS = exports.STALE_TEMP_FILE_AGE_MS = exports.VERSION_PRUNE_THRESHOLD = exports.FRAGMENT_COMPACT_THRESHOLD = exports.DISK_LOW_BYTES = exports.DISK_CRITICAL_BYTES = exports.MAX_FILE_SIZE_BYTES = exports.PATHS = exports.MAX_WORKER_MEMORY_MB = exports.WORKER_BOOT_TIMEOUT_MS = exports.WORKER_TIMEOUT_MS = exports.REBUILD_COMMAND = exports.CHUNKER_VERSION_HISTORY = exports.CONFIG = exports.MODEL_IDS = exports.DEFAULT_MODEL_TIER = exports.MODEL_TIERS = void 0;
|
|
37
37
|
exports.describeChunkerGap = describeChunkerGap;
|
|
38
38
|
exports.describeEmbeddingGap = describeEmbeddingGap;
|
|
39
39
|
exports.describeSchemaDimGap = describeSchemaDimGap;
|
|
@@ -281,6 +281,19 @@ exports.FRAGMENT_COMPACT_THRESHOLD = 400;
|
|
|
281
281
|
// fire on every healthy store — and make `doctor --fix` force the whole-table
|
|
282
282
|
// rewrite that the fragment threshold exists to avoid.
|
|
283
283
|
exports.VERSION_PRUNE_THRESHOLD = 2000;
|
|
284
|
+
// Age past which a Lance `.tmp*` scratch file in a `*.lance/data` directory is
|
|
285
|
+
// considered abandoned rather than in flight. Lance writes a new fragment to a
|
|
286
|
+
// dot-prefixed temp file and renames it on success, so a killed optimize leaves
|
|
287
|
+
// the temp behind forever: it is in no manifest, so `optimize({deleteUnverified})`
|
|
288
|
+
// never collects it, and the dot prefix hides it from a plain `ls`. Three such
|
|
289
|
+
// files (7.3 GB, up to 2.5 months old) were what kept this store pinned at a 2.1x
|
|
290
|
+
// disk/logical ratio that repeated optimize passes could not move.
|
|
291
|
+
//
|
|
292
|
+
// The age gate is the whole safety argument: an in-flight write keeps bumping its
|
|
293
|
+
// temp file's mtime, so anything this stale has no writer, and deleting it is safe
|
|
294
|
+
// without knowing how many processes are on the store. Observed optimize runs on a
|
|
295
|
+
// 325k-row store peak at ~1.4 min, so an hour is ~40x the worst case seen.
|
|
296
|
+
exports.STALE_TEMP_FILE_AGE_MS = 60 * 60 * 1000;
|
|
284
297
|
// Extensions we consider for indexing to avoid binary noise and improve relevance.
|
|
285
298
|
exports.INDEXABLE_EXTENSIONS = new Set([
|
|
286
299
|
".ts",
|
|
@@ -486,6 +486,28 @@ class Daemon {
|
|
|
486
486
|
runSharedOperation(name, signal, fn) {
|
|
487
487
|
return this.operations.runShared(name, signal, fn);
|
|
488
488
|
}
|
|
489
|
+
/**
|
|
490
|
+
* Compact + prune on behalf of a CLI caller.
|
|
491
|
+
*
|
|
492
|
+
* Lance's optimize runs with `deleteUnverified: true`, which its own docs say
|
|
493
|
+
* is only safe when no other process is working on the dataset. That held by
|
|
494
|
+
* accident, not by design: the daemon serializes its own maintenance, but
|
|
495
|
+
* `gmax doctor --fix` optimized in-process, so a user running it during a
|
|
496
|
+
* compaction was a second writer on a store that assumes one. Routing the
|
|
497
|
+
* request here makes it structural — with a daemon up, the daemon is the only
|
|
498
|
+
* optimizer, sharing the same serialization as the maintenance loop. Callers
|
|
499
|
+
* fall back to in-process only when no daemon answers, and are then the sole
|
|
500
|
+
* writer themselves.
|
|
501
|
+
*/
|
|
502
|
+
runOptimize() {
|
|
503
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
504
|
+
const db = this.vectorDb;
|
|
505
|
+
if (!db)
|
|
506
|
+
throw new Error("daemon resources not ready");
|
|
507
|
+
yield this.runSharedOperation("store-maintenance", undefined, () => db.runMaintenance({ force: true }));
|
|
508
|
+
return { ok: true };
|
|
509
|
+
});
|
|
510
|
+
}
|
|
489
511
|
operationStatus() {
|
|
490
512
|
return this.operations.status;
|
|
491
513
|
}
|
|
@@ -173,6 +173,9 @@ function handleCommand(daemon, cmd, conn) {
|
|
|
173
173
|
diskPressure: daemon.getDiskPressure(),
|
|
174
174
|
mlx: daemon.getMlxStatus(),
|
|
175
175
|
};
|
|
176
|
+
case "optimize":
|
|
177
|
+
// Keeps compaction single-writer: see Daemon.runOptimize.
|
|
178
|
+
return yield daemon.runOptimize();
|
|
176
179
|
case "project-stats": {
|
|
177
180
|
const root = String(cmd.root || "");
|
|
178
181
|
if (!root)
|
|
@@ -43,6 +43,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
43
43
|
};
|
|
44
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
45
|
exports.VectorDB = exports.DiskPressureError = void 0;
|
|
46
|
+
exports.findStaleLanceTempFiles = findStaleLanceTempFiles;
|
|
47
|
+
exports.sweepStaleLanceTempFiles = sweepStaleLanceTempFiles;
|
|
46
48
|
exports.isLanceCorruptionError = isLanceCorruptionError;
|
|
47
49
|
exports.isMissingTableError = isMissingTableError;
|
|
48
50
|
const fs = __importStar(require("node:fs"));
|
|
@@ -55,6 +57,73 @@ const filter_builder_1 = require("../utils/filter-builder");
|
|
|
55
57
|
const logger_1 = require("../utils/logger");
|
|
56
58
|
const ann_config_1 = require("./ann-config");
|
|
57
59
|
const store_lease_1 = require("./store-lease");
|
|
60
|
+
/**
|
|
61
|
+
* Find abandoned Lance scratch files under `<lancedbDir>/ *.lance/data`.
|
|
62
|
+
*
|
|
63
|
+
* Lance stages a new fragment in a dot-prefixed `.tmp*` file and renames it into
|
|
64
|
+
* place on success. A killed or panicking optimize leaves the temp behind, and
|
|
65
|
+
* nothing ever collects it: it appears in no manifest, so `deleteUnverified`
|
|
66
|
+
* cannot prove it is garbage, and the leading dot hides it from `ls`. The result
|
|
67
|
+
* is permanent disk bloat that no amount of optimizing reclaims.
|
|
68
|
+
*
|
|
69
|
+
* Only files older than `minAgeMs` are reported. An in-flight write keeps
|
|
70
|
+
* bumping its temp file's mtime, so the age gate — not a writer count — is what
|
|
71
|
+
* makes deleting the result safe from any process.
|
|
72
|
+
*/
|
|
73
|
+
function findStaleLanceTempFiles(lancedbDir, minAgeMs = config_1.STALE_TEMP_FILE_AGE_MS) {
|
|
74
|
+
const found = [];
|
|
75
|
+
const cutoff = Date.now() - minAgeMs;
|
|
76
|
+
let tables;
|
|
77
|
+
try {
|
|
78
|
+
tables = fs.readdirSync(lancedbDir);
|
|
79
|
+
}
|
|
80
|
+
catch (_a) {
|
|
81
|
+
return found;
|
|
82
|
+
}
|
|
83
|
+
for (const tableDir of tables) {
|
|
84
|
+
if (!tableDir.endsWith(".lance"))
|
|
85
|
+
continue;
|
|
86
|
+
const dataDir = `${lancedbDir}/${tableDir}/data`;
|
|
87
|
+
let entries;
|
|
88
|
+
try {
|
|
89
|
+
entries = fs.readdirSync(dataDir);
|
|
90
|
+
}
|
|
91
|
+
catch (_b) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
for (const entry of entries) {
|
|
95
|
+
if (!entry.startsWith(".tmp"))
|
|
96
|
+
continue;
|
|
97
|
+
const full = `${dataDir}/${entry}`;
|
|
98
|
+
try {
|
|
99
|
+
const s = fs.statSync(full);
|
|
100
|
+
if (!s.isFile() || s.mtimeMs > cutoff)
|
|
101
|
+
continue;
|
|
102
|
+
found.push({ path: full, size: s.size, mtimeMs: s.mtimeMs });
|
|
103
|
+
}
|
|
104
|
+
catch (_c) { }
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return found;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Delete abandoned Lance scratch files. Returns bytes actually reclaimed —
|
|
111
|
+
* a file that vanishes between the scan and the unlink is not counted, so the
|
|
112
|
+
* number reported is never larger than what was freed.
|
|
113
|
+
*/
|
|
114
|
+
function sweepStaleLanceTempFiles(lancedbDir, minAgeMs = config_1.STALE_TEMP_FILE_AGE_MS) {
|
|
115
|
+
let filesRemoved = 0;
|
|
116
|
+
let bytesFreed = 0;
|
|
117
|
+
for (const f of findStaleLanceTempFiles(lancedbDir, minAgeMs)) {
|
|
118
|
+
try {
|
|
119
|
+
fs.unlinkSync(f.path);
|
|
120
|
+
filesRemoved++;
|
|
121
|
+
bytesFreed += f.size;
|
|
122
|
+
}
|
|
123
|
+
catch (_a) { }
|
|
124
|
+
}
|
|
125
|
+
return { filesRemoved, bytesFreed };
|
|
126
|
+
}
|
|
58
127
|
class DiskPressureError extends Error {
|
|
59
128
|
constructor(message = "Disk critically low — writes suspended") {
|
|
60
129
|
super(message);
|
|
@@ -956,6 +1025,15 @@ class VectorDB {
|
|
|
956
1025
|
yield this.drainWrites();
|
|
957
1026
|
try {
|
|
958
1027
|
const done = (0, logger_1.timer)("vectordb", "optimize");
|
|
1028
|
+
// deleteUnverified deletes files Lance cannot prove are unreferenced.
|
|
1029
|
+
// LanceDB's own docs: only safe "if you can guarantee that no other
|
|
1030
|
+
// process is currently working on this dataset. Otherwise the dataset
|
|
1031
|
+
// could be put into a corrupted state." gmax is multi-process, so that
|
|
1032
|
+
// guarantee comes from routing compaction to a single writer — the
|
|
1033
|
+
// daemon serializes it (Daemon.runOptimize + the maintenance loop),
|
|
1034
|
+
// and CLI callers go through it rather than optimizing alongside it.
|
|
1035
|
+
// Anything new that calls optimize() must preserve that: either be the
|
|
1036
|
+
// daemon, or be the only process on the store.
|
|
959
1037
|
const stats = yield table.optimize({
|
|
960
1038
|
cleanupOlderThan: cutoff,
|
|
961
1039
|
deleteUnverified: true,
|
|
@@ -1084,6 +1162,15 @@ class VectorDB {
|
|
|
1084
1162
|
if (this.lastOptimizeDidWork) {
|
|
1085
1163
|
const table = yield this.openExistingTableUnsafe();
|
|
1086
1164
|
if (table) {
|
|
1165
|
+
// Collect abandoned scratch files before measuring. They are pure
|
|
1166
|
+
// disk with no manifest entry, so they inflate the ratio while being
|
|
1167
|
+
// invisible to optimize — measuring first would blame the compactor
|
|
1168
|
+
// for bytes it has no way to reach and send us into a second
|
|
1169
|
+
// full-table pass that reclaims nothing.
|
|
1170
|
+
const swept = sweepStaleLanceTempFiles(this.lancedbDir);
|
|
1171
|
+
if (swept.filesRemoved > 0) {
|
|
1172
|
+
(0, logger_1.log)("vectordb", `Swept ${swept.filesRemoved} abandoned temp file(s), freed ${(swept.bytesFreed / 1024 / 1024).toFixed(1)}MB`);
|
|
1173
|
+
}
|
|
1087
1174
|
const stats = yield table.stats();
|
|
1088
1175
|
const diskSize = this.getDirectorySize(this.lancedbDir);
|
|
1089
1176
|
const logicalSize = stats.totalBytes;
|
package/package.json
CHANGED