Skip to content

Documentation

One database for news search and ad matching

ysearch stores articles, traffic, and campaigns, and matches every article to every campaign with scores computed at read time, so statistics can change daily without rewriting anything on S3.

Status
Pre-v1
fork done; YS1–YS3 in progress
Workload
News + ad matching
articles, traffic, campaigns
Protocols
gRPC + RESP
no HTTP API, no SQL
Storage
S3 + KV plane
segments on S3, hot state on SSD

How ysearch works

Scores at read time

Matches store statistics-free features. Every node folds the same statistics epoch from the catalog, and a reader scores under one pinned epoch, so statistics can change daily without rewriting S3.

Pinned statistics epochs

Reach is a dial

Each campaign has a dial (α_c, τ_c) that is read when the ad server asks. Widening or narrowing reach writes one record, never below the lowest cutoff at which the answer stays exact.

Matching and dials

Two planes, one binary

Immutable segments, match cells and tables live on S3. Documents, traffic, campaigns and hot match rows live in a convergent KV plane on SSD, ported from marekvs.

Architecture

ysearch is one Go database system for a news and ad-matching workload. It keeps a large daily volume of articles, their traffic, and the advertising campaigns that want to run next to them. For every article, it answers one question fast: which campaigns match it right now?

It is a fork of the yolosearch search engine with a second storage plane added: a replicated, convergent key-value store whose design is ported from marekvs.

Two planes, one binary

The segment plane is on S3. Immutable index segments, match results, and cold documents live in object storage. Local SSD holds only metadata and caches, so a query node owns nothing it cannot rebuild. This part runs today: it is yolosearch's engine. See segments and the catalog.

The KV plane is on SSD. Documents, per-article traffic, campaigns, reach dials, and the hot part of the match tables live in an eventually consistent store replicated three ways. Every record type merges as a CRDT, so replicas converge without coordination. Its building blocks are on main; the plane itself is milestone YS5. See the KV plane.

Scores computed at read time

A match stores statistics-free features, such as term frequencies, field lengths, and cosines, never a finished score. Every node folds the same statistics epoch from the catalog. A reader pins one epoch and computes the score on the spot.

Epochs can therefore change every day, or with every new catalog generation, without rewriting a single stored match. A drift bound keeps the stored set exact as statistics move. See pinned statistics epochs.

Reach is a dial

Each campaign has a dial (α_c, τ_c): α_c weighs the lexical score against the vector score, and τ_c is the cutoff. The dial is read when the ad server asks, so widening or narrowing a campaign's reach rewrites nothing. A controller can set τ_c from a threshold, a count, an impression forecast, or a pacing goal, and never below the lowest cutoff at which the answer is still exact. See matching and dials.

Fresh within 30 seconds

New articles arrive from Kafka-protocol topics as protobuf records. The target is under 30 seconds from write to matched: the partition's owner indexes each new article in memory and matches it provisionally against every campaign, long before its segment reaches S3. See streams.

Protocols and extensions

  • gRPC and RESP only. There is no HTTP API and no SQL. Prometheus metrics and health probes are the only plain-HTTP surface.
  • WASM apps, not Lua. Write scripts, pipeline processors, and custom matchers are WebAssembly components against ysearch-defined interfaces. A matcher app emits integer features, and the host computes the score. See extensions.
  • Deterministic scoring. Scores are bit-identical on amd64 and arm64, so a floor decided on one node and a threshold applied on another always agree.

What you can run today

The inherited engine builds, ingests, and serves lexical and vector search over S3-compatible storage or a local directory:

zsh
just build
./bin/ysearch-local

The quick start takes it from a source checkout to a served query. The guides cover schemas, ingest, both query grammars, vectors, and the local development stack, which already includes Redpanda and the marekvs sidecar the matcher prototype uses.

Where to start

Start here