Debugging Playbook
High-level mental model
Searchlite is a single-index, single-writer system with:
- buffered writes (CLI or HTTP)
- durability via WAL + atomic manifest updates
- immutable segments + periodic compaction
- “refresh” semantics for making new commits visible to readers (especially in HTTP mode)
Most “weirdness” is one of:
- you didn’t commit (writes are buffered)
- you didn’t refresh (reader is stale)
- your schema isn’t configured for what you’re asking (e.g., missing
fastfields for filters/aggs) - you’re seeing crash-window replay artifacts (extra segments until compaction)
- you’re hitting HTTP limits (body size, concurrency, timeouts)
10-minute triage checklist
Identify surface area
- CLI? embedded Rust API? HTTP server?
Capture exact versions + features
- git SHA/tag (if known)
- build flags / enabled features (
vectorschanges behavior materially)
Get a minimal reproduction
- schema JSON
- the smallest NDJSON/JSON doc set that reproduces
- the exact search request JSON or CLI flags
Check index state
- CLI:
searchlite inspect <index>andsearchlite compact <index> - HTTP:
GET /inspect,GET /stats, optionallyPOST /compact
- CLI:
Confirm lifecycle steps
- Writes are queued → must call
commit - Depending on config, may need
refreshfor immediate visibility
- Writes are queued → must call
CLI debugging moves
A) “I added docs but search returns nothing”
Common causes:
- Documents missing the required ID field (
doc_id_field, default_id) - You didn’t run
commit - Query is hitting an analyzer mismatch or field mismatch
Checklist:
- Verify you ran:
searchlite add …searchlite commit …
- Run:
searchlite inspect <index>searchlite search <index> --q "..." --return-stored(or request JSON withreturn_stored: true)
- If segments ballooned after a crash, run:
searchlite compact <index>
B) “Filters/aggs don’t work / always empty”
Filters and aggregations depend on fast fields.
- Ensure the target keyword/numeric fields are
"fast": truein schema. - For nested filters, ensure nested subfields are
fasttoo and useNestedfilters properly.
HTTP debugging moves
A) Safety + environment sanity
HTTP server has no auth / no rate limiting; don’t expose it to untrusted networks. When debugging production-ish behavior, always capture:
- bind addr (
--bind/SEARCHLITE_BIND_ADDR) - index path (
--index/SEARCHLITE_INDEX_PATH) - resource limits:
--max-body-bytes--max-concurrency--request-timeout-secs- shutdown grace
- refresh behavior:
--refresh-on-commit
B) Confirm lifecycle and visibility
Writes issued via:
POST /add(NDJSON streaming)POST /bulk(JSON bulk)POST /delete
…are queued and only become durable/searchable after:
POST /commit
Then visibility depends on staleness needs:
- if
--refresh-on-commitis enabled, new data becomes visible immediately - otherwise call:
POST /refresh
C) Use maintenance endpoints for ground truth
POST /refresh— reload readersPOST /compact— merge segments + clean up fragmentationGET /inspect— manifest + segments infoGET /stats— doc/segment counts
D) Interpret HTTP errors
All errors return:
{"error":{"type":"...","reason":"..."}}
If you see a 413/timeout-ish behavior:
- check
--max-body-bytesand--request-timeout-secs - for NDJSON streaming, reduce batch sizes / request size
Ranking correctness + “why did this score happen?”
Search supports explicit debugging aids:
explain: true
- Returns per-hit score breakdowns.
- Use this for correctness (“why did doc X win?”), not for performance runs.
profile: true
- Adds execution stats like:
- candidates examined
- scored docs
- postings advances
- timing buckets (search vs rescore)
- Use this for performance triage (“why is this query slow?”).
Both are off by default for overhead reasons.
“This query is slow” playbook
- Enable
profile: trueon the request and capture the profile blob. - Compare execution modes:
execution=bm25(baseline full scoring)execution=wand(default exact pruning)execution=bmw(block-max pruning)
- Confirm you’re not forcing expensive features:
- rescore windows
- heavy highlighting
- large aggregations on high-cardinality fields
- If using aggregations:
- confirm required fields are
fast - consider sampling knobs where supported
- confirm required fields are
- If using vector search (feature
vectors):- remember ANN is approximate; raising
candidate_size/ef_searchincreases recall but costs CPU
- remember ANN is approximate; raising
Vector search “it errors / results look wrong”
Common causes:
- wrong vector dimension (rejected)
- using cosine without normalization assumptions (Searchlite normalizes cosine vectors automatically)
- expecting exact results from ANN (HNSW is approximate)
Knobs to tune:
candidate_size(oversampling)ef_search(beam width)vector_filter(reduce candidate set)
Nested filter “it matches too much / too little”
Key rule: nested filters are evaluated per object, not across the entire document.
Correct pattern:
- wrap clauses under
Nestedwithpath - for deeper nesting, use a nested
Nestedinside the parent nested filter
Common gotcha:
- forgetting to mark nested filter fields as
fast: true
Storage/durability anomalies
Symptoms:
- “extra segments” after crash
- “duplicate generation” feeling after restart
Remember the documented crash window:
- if the process dies after manifest persisted but before WAL truncation, WAL replay reapplies last batch
- data isn’t lost, but you may see extra segment generations until compaction
Fix:
- run
compact(CLI or HTTP) to merge and clean up
What to attach to a bug report
- schema JSON
- small docs ndjson/json
- exact request JSON (include
execution,profile,explainif relevant) - output of
inspect+stats - server flags/env (HTTP)
- if durability related: note whether crash/restart occurred
Converted and distributed by TomeVault — claim your Tome and manage your conversions.