From c73cb9708152e78ab538ffe575bd45e6ad1c460e Mon Sep 17 00:00:00 2001 From: justrach <54503978+justrach@users.noreply.github.com> Date: Sun, 27 Sep 2026 17:54:33 +0800 Subject: [PATCH] release: 0.2.5860 faster hosted semantic index builds semantic-index sends 100-item batches with eight requests in flight by default, cutting round trips for hosted index builds. Index builds now wait out hosted rate limits with a doubling backoff capped at 30 seconds instead of failing after a few quick retries. Interactive context calls still fail fast to local results. --- CHANGELOG.md | 5 +++++ README.md | 2 +- build.zig.zon | 2 +- docs/cli.md | 4 ++-- docs/releases/0.2.5860.md | 17 +++++++++++++++++ npm/package.json | 2 +- src/release_info.zig | 2 +- src/semantic.zig | 39 ++++++++++++++++++++++++++++++--------- src/semantic_index.zig | 26 +++++++++++--------------- 9 files changed, 69 insertions(+), 30 deletions(-) create mode 100644 docs/releases/0.2.5860.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 2414dd05..4aa4dabf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## Unreleased +## 0.2.5860 - 2026-09-27 + +- `codedb semantic-index` sends larger batches with more requests in flight, so hosted index builds finish faster. +- Index builds wait out hosted rate limits with a longer capped backoff instead of failing after a few quick retries. Interactive `codedb context` calls still fail fast to local results. + ## 0.2.5859 - 2026-09-27 - Fix `codedb context` reporting `semantic lane: unavailable` with the default hosted configuration. The hosted default model id is now `codedb-hosted-v1`. Local ANN sidecars built by earlier releases are rebuilt once in the background; custom endpoints and model overrides are unchanged. diff --git a/README.md b/README.md index 82e25563..313a2133 100644 --- a/README.md +++ b/README.md @@ -489,7 +489,7 @@ endpoints/model overrides and new repositories are never auto-built; set never mixes vectors from different models. It splits already-indexable files into bounded 832-byte source chunks, uses -four concurrent 25-item requests by default (explicitly configurable from one +eight concurrent 100-item requests by default (explicitly configurable from one to eight), and writes a small `semantic-chunks-v3.meta` mapping plus a generation-named `.hmls` mmap slab only in codedb's per-project local data directory. The directory and diff --git a/build.zig.zon b/build.zig.zon index 9d2d2ba1..611aa501 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,7 +1,7 @@ .{ .name = .codedb2, .fingerprint = 0x6e18d96ca2a31757, - .version = "0.2.5859", + .version = "0.2.5860", .minimum_zig_version = "0.17.0-dev.813+2153f8143", .dependencies = .{ .nanoregex = .{ diff --git a/docs/cli.md b/docs/cli.md index 5ecefd66..45ecbbcb 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -100,7 +100,7 @@ command returns the local result and does not run an embedding model on CPU. default; `--local` (or `--no-semantic`) is the explicit on-device-only mode. `semantic-index` is the explicit ANN build trigger. It sends bounded 832-byte -code chunks using four concurrent 25-item requests by default (configurable +code chunks using eight concurrent 100-item requests by default (configurable from one to eight) and stores a small mapping plus a generation-named, validated `.hmls` mmap slab under codedb's local per-project data directory (0700/0600 on POSIX). @@ -161,7 +161,7 @@ codedb-cli stop # stop daemon | `CODEDB_EMBEDDINGS_DIMENSIONS` | managed | Requested custom-provider dimensions (64-4096) | | `CODEDB_EMBEDDINGS_TOKEN` | unset | Optional legacy bearer token for protected/custom endpoints; the hosted lane enrolls automatically | | `CODEDB_EMBEDDINGS_TIMEOUT_MS` | `15000` | Per-request deadline in milliseconds (10-120000) | -| `CODEDB_SEMANTIC_INDEX_CONCURRENCY` | `4` | Parallel 25-item index batches (clamped to 1-8) | +| `CODEDB_SEMANTIC_INDEX_CONCURRENCY` | `8` | Parallel 100-item index batches (clamped to 1-8) | ## Performance diff --git a/docs/releases/0.2.5860.md b/docs/releases/0.2.5860.md new file mode 100644 index 00000000..9feabfcf --- /dev/null +++ b/docs/releases/0.2.5860.md @@ -0,0 +1,17 @@ +# CodeDB 0.2.5860 + +Faster hosted semantic index builds. + +## Hosted semantic retrieval + +`codedb semantic-index` now sends 100-item batches with eight requests in flight by default. `CODEDB_SEMANTIC_INDEX_CONCURRENCY` still overrides the concurrency (1-8). + +Index builds wait out hosted rate limits with a doubling backoff capped at 30 seconds, instead of failing after a few quick retries. Interactive `codedb context` calls are unchanged and still fall back to local results immediately. + +Local retrieval, the watcher, and the installer are unchanged. + +## Verification + +```sh +zig build test --summary all +``` diff --git a/npm/package.json b/npm/package.json index 90522bab..28dd0001 100644 --- a/npm/package.json +++ b/npm/package.json @@ -1,6 +1,6 @@ { "name": "codedeebee", - "version": "0.2.5859", + "version": "0.2.5860", "description": "Zig code intelligence MCP server — npx launcher for the codedb native binary", "license": "MIT", "author": "justrach", diff --git a/src/release_info.zig b/src/release_info.zig index 6398cd27..9c741984 100644 --- a/src/release_info.zig +++ b/src/release_info.zig @@ -1 +1 @@ -pub const semver = "0.2.5859"; +pub const semver = "0.2.5860"; diff --git a/src/semantic.zig b/src/semantic.zig index 873e97e0..5621837e 100644 --- a/src/semantic.zig +++ b/src/semantic.zig @@ -21,19 +21,22 @@ pub const default_timeout_ms: u32 = 15_000; pub const min_timeout_ms: u32 = 10; pub const max_timeout_ms: u32 = 120_000; pub const max_documents: usize = 24; -// The free public codedb lane accepts at most 25 inputs per request. Indexing -// may fill all 25 slots because, unlike query reranking, it does not prepend a -// query vector to the batch. -pub const max_index_documents: usize = 25; +// The hosted lane accepts at most 100 inputs per request, the provider's own +// batch ceiling. Larger batches cut per-item latency and round trips; query +// reranking stays bounded separately by `max_documents`. +pub const max_index_documents: usize = 100; pub const max_document_bytes: usize = 2 * 1024; -// Keep the serialized anonymous request below Caddy's 64 KiB raw-body cap even +// Keep the serialized request below the hosted edge's 256 KiB body cap even // when source punctuation/control bytes expand during JSON escaping. pub const max_batch_text_bytes: usize = 8 * 1024; -pub const max_request_bytes: usize = 60 * 1024; +pub const max_request_bytes: usize = 240 * 1024; pub const max_response_bytes: usize = 2 * 1024 * 1024; -pub const max_index_batch_text_bytes: usize = 25 * 1024; -pub const max_index_embedding_attempts: usize = 3; +pub const max_index_batch_text_bytes: usize = 100 * 1024; +// Hosted quota windows are per minute. Ten attempts with doubling backoff +// capped at 30s wait about 92s in total, enough to outlast one window. +pub const max_index_embedding_attempts: usize = 10; pub const index_embedding_retry_base_ms: u32 = 250; +pub const index_embedding_retry_max_ms: u32 = 30_000; pub const default_rrf_k: f32 = 60; pub const default_semantic_weight: f32 = 0.05; // Accuracy-oriented Jina hybrid policy; see ADR 0008 for live-service @@ -494,7 +497,7 @@ pub fn embedIndexRemoteTexts( err == error.EmbeddingProviderUnavailable or err == error.EmbeddingTimeout; if (!retryable or attempt + 1 == max_index_embedding_attempts) return err; - const delay_ms = index_embedding_retry_base_ms << @intCast(attempt); + const delay_ms = indexRetryDelayMs(attempt); io.sleep(.fromMilliseconds(delay_ms), .awake) catch {}; continue; }; @@ -502,6 +505,24 @@ pub fn embedIndexRemoteTexts( unreachable; } +pub fn indexRetryDelayMs(attempt: usize) u32 { + const shift: u5 = @intCast(@min(attempt, 16)); + const scaled = @as(u64, index_embedding_retry_base_ms) << shift; + return @intCast(@min(scaled, index_embedding_retry_max_ms)); +} + +test "index retry backoff doubles, caps, and outlasts a one-minute quota window" { + const testing = std.testing; + try testing.expectEqual(@as(u32, 250), indexRetryDelayMs(0)); + try testing.expectEqual(@as(u32, 500), indexRetryDelayMs(1)); + try testing.expectEqual(index_embedding_retry_max_ms, indexRetryDelayMs(8)); + try testing.expectEqual(index_embedding_retry_max_ms, indexRetryDelayMs(40)); + var total: u64 = 0; + for (0..max_index_embedding_attempts - 1) |attempt| total += indexRetryDelayMs(attempt); + try testing.expect(total >= 60_000); + try testing.expect(total <= 120_000); +} + pub fn embedQueryRemote( io: std.Io, allocator: std.mem.Allocator, diff --git a/src/semantic_index.zig b/src/semantic_index.zig index 10bb7aa6..9915b06e 100644 --- a/src/semantic_index.zig +++ b/src/semantic_index.zig @@ -26,12 +26,10 @@ pub const slab_file_suffix = ".hmls"; pub const max_chunk_card_bytes: usize = 1024; pub const chunk_source_bytes: usize = 832; pub const max_chunks_per_file: usize = 256; -// Hosted-lane hill climbs: 256 chunks (2026-08-26) gave c1=12.4s, c2=9.4s, -// c4=6.1s, c8=16.7s. A 6,713-chunk React subset (2026-08-30) confirmed -// c1=180.8s, c2=98.9s, c4=68.8s, while c8 was aborted after 338s in retry/ -// queue contention. Four remains the Pareto point; differently provisioned -// endpoints can still use the explicit override. -pub const default_parallel_batches: usize = 4; +// Hosted 100-item batches (2026-09-27, 1,570 chunks): c4=10.7s, c8=5.9s +// against 17.8s for the former 25-item c4 default. Repositories beyond the +// hosted per-minute quota are paced by the index retry backoff instead. +pub const default_parallel_batches: usize = 8; pub const max_parallel_batches: usize = 8; pub const default_search_results: usize = 24; pub const max_records: usize = 250_000; @@ -1657,15 +1655,13 @@ test "semantic ANN replacement removes only the previously referenced slab" { try std.Io.Dir.cwd().access(io, new_path, .{}); } -test "semantic ANN wave uses the public 25-item batch boundary" { - // This is the hosted-lane Pareto point measured against the production - // TurboAPI/TEI queue. Raising the default to the supported maximum of 8 - // regressed the 256-chunk end-to-end run from 6.1s to 16.7s. Keep the - // override for custom endpoints, but make an accidental default drift a - // release-gating test failure. - try std.testing.expectEqual(@as(usize, 4), default_parallel_batches); - try std.testing.expectEqual(@as(usize, 25), semantic.max_index_documents); - try std.testing.expectEqual(@as(usize, 100), default_parallel_batches * semantic.max_index_documents); +test "semantic ANN wave uses the hosted 100-item batch boundary" { + // Hosted batches carry 100 items with eight in flight (800-item waves). + // Keep the override for custom endpoints, but make an accidental default + // drift a release-gating test failure. + try std.testing.expectEqual(@as(usize, 8), default_parallel_batches); + try std.testing.expectEqual(@as(usize, 100), semantic.max_index_documents); + try std.testing.expectEqual(@as(usize, 800), default_parallel_batches * semantic.max_index_documents); try std.testing.expectEqual(@as(usize, 1), embeddingBatchCount(semantic.max_index_documents)); try std.testing.expectEqual(@as(usize, 2), embeddingBatchCount(semantic.max_index_documents + 1)); try std.testing.expectEqual(