Build1 publisher3 min readPublished
PlanetScale's TIN searches a Postgres row the moment its transaction commits
The extension puts BM25 ranking behind an ordinary Postgres index, so a newly committed row is searchable without a trip to an Elasticsearch cluster. PlanetScale's own benchmark reports 199 queries per second against ParadeDB's 7.9.
The Engineer · Build desk

What happened
- PlanetScale contributors Eric Ridge and Patrick Reynolds launched TIN on September 16th, a full-text search extension for Postgres with BM25 ranking, now generally available across the company's Postgres and Neki products.
- Search results are subject to Postgres transaction visibility, and the index works with joins, ordinary WHERE conditions, replication, backups and continuous updates.
- PlanetScale's comparison ran an 85 GB Stack Exchange corpus of 150 million documents against 1,719 synthetic queries built from substrings of two to 15 terms.
- TIN built a 50.7 GB index in 8 minutes and 10 seconds, while the GIN index shipped with Postgres 18.6 took 2 hours, 9 minutes and 4 seconds.
- In the mixed top-10 workload PlanetScale reported 199 queries per second for TIN and 7.9 for ParadeDB, roughly 25x, with p99 latency 26 times lower.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- capability Sync lag between Postgres and a search cluster stops being a failure mode to monitor, because a row's visibility to a search query follows the same transaction as its visibility to a SELECT.
- constraint One index per column or expression means multi-field relevance is assembled from several indexes with per-field boosts, and the single-column benchmark leaves the cost of that assembly unmeasured.
- decision Anyone currently sizing an Elasticsearch cluster is now weighing that against a vendor-run comparison whose queries were generated out of the same corpus being searched.
- cost Development, CI and staging run on a deliberately slow substitute, so a team pays for production-grade hardware to find out whether its own latency targets hold.
A TIN index is created with `CREATE INDEX posts_body_tin ON posts USING tin(body)` and queried with a new operator, `==>` [4]. Boolean, phrase and span searches are supported, along with fuzzy terms, wildcards, regular expressions and case and accent folding, and a query can return exact counts or BM25 scores [5]. Because the index lives in the database, it is covered by the same replication and backup path as the table it indexes [6]. Patrick Reynolds, one of the two contributors credited with the launch, founded GitHub's Blackbird code-search project before joining PlanetScale [21].
PlanetScale's documentation says each TIN index covers one text column or expression [7]. Ranking a post by title and body therefore means two indexes, combined at query time with per-field boosts, plus partial or expression indexes for narrower cases [7]. The default tokenizer folds case and accents and indexes emoji as searchable terms [8].
The 1,719 queries in the benchmark were generated from substrings of two to 15 terms taken out of the corpus itself [10]. Every query matches something by construction, and query term frequencies follow document term frequencies. Production query logs contain misspellings, terms that are not in the corpus at all, and a head-heavy distribution that warms a cache differently. Only TIN and ParadeDB completed every workload; GIN and pg_textsearch dropped out of some comparisons after running out of memory or failing to support the query shape [19].
Converted to seconds, the two extreme build times are 490 and 7,744, a factor of 15.8 [1]. At 490 seconds for 150 million documents, that is roughly 306,000 documents indexed per second [2]. ParadeDB built a slightly larger index, 52.1 GB, in 19 minutes 20 seconds [14]. PlanetScale raised the memory limit for the three competing systems during index construction after they failed at 32 GB, then returned every system to 32 GB for query testing [12].
The 50.7 GB index exceeds the 32 GB query-phase cap by 18.7 GB [3], so the query phase read from storage on the i7i.8xlarge instance [11]. On a box where the index fits in page cache, the ratios come out differently.
In the conjunction-and-phrase workload PlanetScale reported 242 queries per second for TIN, 24 for ParadeDB and 0.4 for GIN [18]. Those are read measurements against an index that was already built. PlanetScale did not publish query throughput under sustained ingest, and that is the case that decides whether one system can stand in for two.
Runtimewire's account of the launch describes Lead as a compatible but deliberately slow substitute for local development, CI and staging [20]. Relevance and correctness can be checked there, while latency regressions will only show up against a production-grade instance.
What to watch
- An independent rerun on real query logs, with ingest running concurrently, would show whether 199 queries per second survives contact with a production workload.
- Whether PlanetScale publishes write-visibility latency and query throughput under sustained update load; the current benchmark measures neither.
- Whether ParadeDB or Tiger Data respond with their own runs at equal memory for index construction.