Farnsworth
Farnsworth is a Rust search engine prototype, exploring whether (and how) OpenSearch could be replaced for Woosmap localities/POIs/addresses search.
Status: all 7 roadmap items done
Read-only against real exported data, no write/patch-path port, no prod cutover planned. Text search, geo search, and address interpolation all validated against real data at production scale, proven to compose into one working search path (32/32 tests passing). See architecture for what's built and working.
Why - and how the premise changed
The original hypothesis was that OpenSearch's p99 latency tail (autocomplete: 21ms p50 vs 542ms p99, a 25-40x blowup on ~2.4% of requests) was a JVM garbage-collection problem, fixable by removing the JVM entirely.
That hypothesis was disproven with real CloudWatch data: zero old-gen GC collections in a 24h window, JVM memory pressure never above 70%, CPU maxing at 39%. The actual root cause, confirmed by correlating latency against the app's own N_TOKENS tag: query-plan cost that scales with input word count, driven by OpenSearch's per-language dis_max/multi_match fan-out (~10 language subfields, each with several clauses, multiplied per token). p50 and p99 both climb roughly 13x from 1 to 10+ tokens.
This reframes the whole project: the case isn't "remove the JVM," it's "can a different query architecture avoid the fan-out cost structurally." That's directly testable, and early results on the merged-multi-language-field approach (see architecture) are promising.
OpenSearch's actual feature usage in localities is closer to ~40-50% of its surface - a full text-analysis chain (ICU normalization/folding, synonym graphs, word-delimiter, search-as-you-type shingles), geo_point/geo_shape queries, custom scoring (rank_feature/distance_feature) and multi-index fan-out - not the aggregation/percolator/ILM/cluster-coordination machinery that justifies OpenSearch's distributed-cluster cost.
The dataset (~26.4M docs across localities/pois/addresses, ~15.7G compressed) fits comfortably in RAM on a single box, which removes the need for OpenSearch's distributed-cluster model entirely.
Approach
- ~~Baseline current OpenSearch p50/p99 latency~~ - done, reframed the whole premise (see above).
- ~~Evaluate Meilisearch's
milliengine as an embeddable alternative~~ - done, not viable (internal-only API, see architecture). - ~~Prototype on tantivy for text search~~ - done: prefix matching, typo tolerance, synonym expansion, and word-delimiter parity all validated at full 13.9M-doc scale, plus a real query-latency bug found and fixed (~2.6x faster).
- ~~Layer a custom geo index (R-tree/H3) for geo_distance + geo_shape polygon relations~~ - done, clean win at full scale, no surprises.
- ~~Wire protocol~~ - done: a deliberate non-fix, serialization format doesn't move the needle at this scale.
- ~~Combine text + geo into one working search path~~ - done, proven with tests against real data.
- ~~Validate address-number interpolation~~ - done: reproduced localities' real algorithm exactly, holdout-tested against real French data (48,312 predictions, median 11m error). Full 11-country schema migration and the write path remain deliberately out of scope - this is a learning project, not scoped for prod cutover.
Out of scope
Aggregations, percolator, ingest pipelines, ILM, snapshots, cross-cluster search, scroll/PIT, nested queries - confirmed unused in the current codebase. The live document patch/write path stays OpenSearch's job.