grepmax 0.26.12 → 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.
|
@@ -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
|
@@ -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
|
|
@@ -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) {
|
|
@@ -500,6 +515,16 @@ 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)...");
|
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",
|
|
@@ -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);
|
|
@@ -1093,6 +1162,15 @@ class VectorDB {
|
|
|
1093
1162
|
if (this.lastOptimizeDidWork) {
|
|
1094
1163
|
const table = yield this.openExistingTableUnsafe();
|
|
1095
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
|
+
}
|
|
1096
1174
|
const stats = yield table.stats();
|
|
1097
1175
|
const diskSize = this.getDirectorySize(this.lancedbDir);
|
|
1098
1176
|
const logicalSize = stats.totalBytes;
|
package/package.json
CHANGED