By 16:30 UTC on September 19, a Hacker News discussion about Tin had reached 191 points. It formed around a Postgres extension that most developers cannot download. PlanetScale's production Tin engine is private and runs on its managed databases. The public substitute, Lead, keeps the same SQL interface by scanning every row and explicitly warns against production use. The point count is a signal that developers care about search inside Postgres. It does not verify PlanetScale's performance claims.
For a developer, that split matters as much as the speed. PlanetScale announced Tin as generally available on September 16 for its Postgres and Neki databases. Tin supplies ranked full-text search, exact counts, phrases, fuzzy terms, wildcards and regular expressions without a separate search cluster. It also turns the choice of search engine into a choice of database host.
The SQL stays inside Postgres
Tin looks like a normal extension from the application side. A developer enables it, builds an index over a text column and uses the ==> operator in a query:
CREATE EXTENSION IF NOT EXISTS tin;
CREATE INDEX posts_body_tin ON posts USING tin (body);
SELECT id, tin.score(ctid) AS score, body
FROM posts
WHERE body ==> 'postgres AND search'
ORDER BY score DESC
LIMIT 10;
The getting-started documentation says each index covers one text column or expression. Tin ranks matches with BM25, can combine scores from separately indexed columns and uses one query language, TINQL, for Boolean, phrase and proximity searches. Case and accents are folded by default, while emoji are indexed as terms.
Keeping search in the database removes a familiar piece of plumbing. An external engine such as Elasticsearch needs another cluster plus a path that copies changes out of Postgres. Tin queries participate in Postgres transactions, joins, backups and replication. PlanetScale's feature guide says searches see a consistent snapshot while writes continue, so a newly committed row can become searchable without an application-managed synchronization job.
PlanetScale pairs the speed pitch with features that core Postgres full-text search lacks. In its comparison, the company lists BM25 top-k retrieval, exact index-backed counts, fuzzy matching, span queries and configurable highlighting. It says only ParadeDB completed every workload; built-in GIN and Tiger Data's pg_textsearch could not run some of the tested query shapes. That is PlanetScale's account, and the source of Tin itself is unavailable for inspection.
The benchmark is fast and vendor-run
PlanetScale tested Tin 1.0.2 on an 85GB Stack Exchange export containing 150 million documents. The company generated 1,719 synthetic queries by sampling two-to-15-term substrings and interpreting each as a conjunction, disjunction or phrase. Every engine ran in a Postgres 18.6 container with eight virtual CPUs and 32GB of RAM on an AWS i7i.8xlarge machine with local NVMe storage.
Tin built a 50.7GB index in 8 minutes 10 seconds. ParadeDB took 19 minutes 20 seconds for a 52.1GB index, pg_textsearch took 26 minutes 49 seconds for 41.5GB, and Postgres GIN took 2 hours 9 minutes for 28GB. There is an important footnote in PlanetScale's published results: the other three engines exceeded the 32GB build limit, so the company raised their build memory to 64GB or 128GB before returning every container to 32GB for queries.
In the full benchmark table, Tin recorded 199 queries per second and 256ms p99 latency for mixed top-10 searches without writes. ParadeDB recorded 7.9 queries per second and 6,765ms p99. Under the corresponding write workload, Tin reported 172 queries per second and 284ms p99, against ParadeDB's 6 queries per second and 7,990ms. On an 8GB Wikipedia index that fit in memory, Tin reported 10,260 count queries per second at 2ms p99.
Those are large gaps, but they remain vendor measurements over synthetic traffic. PlanetScale published its fork of the benchmark harness, including changes for prewarming and measuring bytes read and WAL written. An outside team can inspect the driver and rerun the public competitors. It cannot rerun Tin on its own Postgres server, which leaves the most interesting side of the comparison dependent on access to PlanetScale.
Tin uses Postgres row locations as document IDs
PlanetScale's design notes explain a choice behind the headline ratios. Search indexes normally assign compact sequential IDs to documents inside each segment. Postgres extensions eventually have to convert those IDs back to ctid values, the physical locations Postgres uses to find row versions. PlanetScale says ParadeDB and pg_textsearch keep another structure for that mapping. Tin skips it by storing the 48-bit ctid as the posting from the start.
A raw set of scattered 48-bit values would compress poorly, so Tin splits each location into a page number and an offset within that page. Its page-level bitmap is 256 bits, small enough for one AVX2 register. Offset bitmaps fit in one AVX-512 register or two AVX2 registers. Intersections and unions then become vector AND and OR operations, while POPCNT handles counts. For a query such as the AND rareword, Tin can reject a page before decoding its offsets.
The same representation helps with row visibility. Updates create new ctid values, and Postgres keeps old row versions until vacuum removes them. Tin intersects its page bitmaps with Postgres visibility data, then uses a liveness bitmap for deleted tuples. Because the posting already identifies a heap location, matched rows arrive in heap order rather than through a second ID lookup. PlanetScale attributes lower I/O and cheaper segment merges to that decision.
The open code is a compatibility layer
PlanetScale released Lead one day after Tin and published its code under the AGPL. Lead provides the same extension name, operators, tokenizer, scoring functions and TINQL parser for Postgres 17 and 18. That lets an application run its search SQL in development, CI and staging without connecting every test to PlanetScale. Search results and transaction visibility are meant to match Tin.
Lead stores no search data. Every index scan marks all heap pages as candidates, and Postgres rechecks visible rows one by one. PlanetScale measured one query over its 8GB Wikipedia corpus at four minutes in Lead, versus its claimed 2ms p99 for Tin. The repository even exposes a TIN_PRIVATE_REPO variable for maintainers who have access to the fuller private regression suite. The fast engine and the code that tests it most deeply remain on the company side of the line.
That boundary differs from two products in the benchmark. Tiger Data's pg_textsearch is available under the PostgreSQL License, with source builds and packages for Postgres 17 and 18. ParadeDB Community publishes its pg_search extension under AGPL-3.0 and supports self-hosted Postgres beginning with version 15. Neither is a drop-in replacement because the operators, query features and performance profiles differ. They do give teams a path to run the production index on infrastructure they control.
Fast search still has an operations bill
Tin inherits some ordinary database chores and adds a few of its own. Its documented limitations say index files grow to a high-water mark and shrink only after REINDEX. Scores on partitioned tables are calculated within each partition, so one ordered result can mix values derived from different corpus statistics. Read replicas need hot_standby_feedback enabled, and queries can still require retries during heavy replay.
Index construction is memory hungry. PlanetScale's operations guide recommends roughly 1GB of maintenance_work_mem for each build worker. Too little memory produces a more fragmented index that stays slower until a better-funded rebuild. On update-heavy tables, dead entries remain until vacuum reports them; exact count performance can fall between vacuum runs. The guide suggests lowering the per-table autovacuum trigger from Postgres's 20 percent default to about 1 percent plus 1,000 changed rows for large searched tables.
Tin deserves testing because its architecture addresses a real cost in Postgres search, and the reported numbers are wide enough to investigate. The next evidence should come from an application's own documents and query trace: index build memory, p99 latency during writes, vacuum behavior and the work required to leave the service. The 191-point thread shows the appetite for search that stays in Postgres. The more useful number is the p99 from the data you will actually serve.