Today's posts are about systems that are right almost every time: a cache, an AI grader, a sharded sequence, GPU fleets, and a costly query.
Picked from 31 new articles across 23 sources · arrow keys for other days
Today in 30 seconds
30s left
Cloudflare found that two nodes shared one cache but wrote MySQL results in different wire formats, so 0.001% of responses came back empty. story 1 ↓
Netflix keeps its AI grader honest by tuning it on why humans reject outputs. Tuning on labels alone made one rubric worse. story 2 ↓
PlanetScale's sharded Postgres, Neki, runs nextval() once per row on the central router, which keeps sequence numbers unique across shards. story 3 ↓
Netflix's layered GPU health checks, automatic repair and checkpointing raised job success from about 98% to above 99.5%. story 4 ↓
GitHub's new star-history endpoint cut react's 2,511 API calls to 24 by filtering on date before counting, not after. story 5 ↓
1/5Cloudflare· Pranoy Dutta· Oct 14
Cloudflare traces a rare MySQL caching bug to a shared cache
Cloudflare's Hyperdrive, which pools database connections for its serverless platform, sometimes returned empty MySQL results. Two of its parts shared a cache but wrote results in different formats. Storing one fixed format fixed it.
Why it matters · A cache is only correct if every writer stores the same bytes for the same answer, and code ported from one protocol can quietly break that rule.
Know this first
Context from the editor, not the article · 43 sec
1 · Why pool database connections
Opening a SQL connection takes several network round trips, and each open connection uses memory on the database server. Serverless functions run briefly and in huge numbers, so connecting on every request is slow and can overload the database. A pooler keeps a few connections open and lends them out to many requests.
2 · Wire protocols and capabilities
Databases talk to clients in a binary wire protocol made of packets. In MySQL, the client and server swap capability flags when they connect. These are optional features, such as compression or newer packet formats, that both sides agree to use. If one side uses a feature the other didn't agree to, the two fall out of sync.
3 · Cache keys and values
2/5Netflix Tech Blog· Netflix Technology Blog· Oct 14
How Netflix keeps an AI grader of AI-written text trustworthy over time
Netflix uses one AI model to grade the short "why we recommended this" blurbs another model writes. It ties the grader to human raters' written reasons, checks it every week, and has humans approve every change.
Why it matters · Using one model to grade another is now the default way to evaluate AI output at scale, and this is a detailed account of keeping that grader honest in production.
Know this first
Context from the editor, not the article · 32 sec
1 · The evaluation problem
When a large language model (LLM) generates hundreds of thousands of texts a week, nobody can read them all. Teams need an automatic way to tell good outputs from bad ones. Otherwise poor or misleading text reaches users without anyone noticing.
2 · LLM-as-a-Judge
The usual fix is a second LLM that scores the first model's output, standing in for human raters. It follows a written rubric (a list of criteria), and it is cheap and fast. But it can be wrong, or slowly drift, and nothing downstream would flag it.
3 · Prompts, not retraining
Here the judge model's weights never change. Its behavior is set entirely by the rubric text in its prompt. So "tuning" the judge means rewriting that text, which a person can read and review.
3/5PlanetScale· Mats Rydberg· Oct 14
How PlanetScale's sharded Postgres hands out sequence numbers without duplicates
PlanetScale's Neki splits Postgres tables across many servers called shards. To keep sequence numbers unique, it generates them on one central router, one per result row, as rows come back from the shards.
Why it matters · Anyone sharding a SQL database has to decide where each function runs. This post shows how Postgres's volatility labels on functions drive that choice, and how getting it wrong breaks correctness.
Know this first
Context from the editor, not the article · 38 sec
1 · Sharding and the router
A sharded database splits one table's rows across many servers, called shards, usually by hashing a column such as id. A router sits in front of them. It takes each client query, sends pieces of it to the right shards, and combines what comes back. No single shard holds all the data.
2 · Postgres sequences
A sequence is a counter object in PostgreSQL. Each call to nextval('seq') returns a number that has never been handed out before. Sequences supply auto-incrementing IDs (serial, GENERATED AS IDENTITY). The numbers are unique but can have gaps: a rolled-back transaction does not give its numbers back.
3 · Function volatility classes
4/5Netflix Tech Blog· Netflix Technology Blog· Oct 14
How Netflix finds failing GPUs and repairs them without a human
Netflix built layered GPU health checks that assume hardware and diagnostic tools can lie, automatic repair matched to each fault, and resumable training jobs. GPU nodes now stay over 99.95% available, and job success rose to over 99.5%.
Why it matters · Anyone running distributed training or GPU inference hits these failures: the job that slows to a crawl without crashing, and health checks that report green while the hardware is broken.
Know this first
Context from the editor, not the article · 44 sec
1 · Training runs as a gang
Large models train across many GPUs on many machines. Each worker process, called a rank, swaps results with the others every step, much like rowers keeping one stroke. So the whole job moves at the pace of its slowest rank and stops if any rank dies. One bad GPU anywhere can stall hundreds of good ones.
2 · Kubernetes nodes and taints
Kubernetes is a system that schedules software across a fleet of machines, which it calls nodes. A node can carry "conditions" (status flags such as NotReady) and "taints" (marks that keep new work off it). Controllers are programs that watch these flags and act on them, for example by draining a node's work and replacing the machine.
3 · Collective communication
5/5GitHub Engineering· Taylor Cuilty· Oct 14
GitHub's new star-history endpoint cuts react's 2,511 API calls to 24
GitHub replaced the scrape-the-stargazer-list approach to star history with an endpoint that returns 30 weeks of daily counts per call. It narrows the date range before aggregating, so each star is read once.
Why it matters · The bug is a common one: a smaller response doesn't make a query cheaper if the database still scans and sorts every row before LIMIT applies.
Know this first
Context from the editor, not the article · 41 sec
1 · GitHub stars and stargazers
Anyone can "star" a GitHub repository to bookmark it or show interest. The people who have starred it are called stargazers. Tools like star-history.com draw a repository's star count over time, and maintainers use the curve to see whether a release or launch drew new attention.
2 · Paginated APIs and rate limits
APIs split long lists into pages, often up to 100 items each, and the client asks for page 1, page 2 and so on. GitHub also caps how many requests a client can make per hour (a rate limit), so a job that needs thousands of pages can hit that cap before it finishes.
A cache stores a value, here the raw bytes of a query result, under a key, here a hash of the query. Anyone who reads the cache trusts that the stored bytes mean what they expect. If two writers store different formats under the same key, a reader can't tell which format it got.
Hyperdrive's Client and Endpoint nodes each use their own data center's cache, which is shared when both are co-located.1 / 4
Cloudflare has made MySQL support in 1 generally available. Hyperdrive is its database 2 for 3, Cloudflare's serverless functions. Until now it supported only Postgres. Pranoy Dutta writes that getting MySQL out of beta took about 20 bug fixes. The post covers one in detail: a caching bug that made roughly 0.001% of queries return empty results or hang.
What Hyperdrive does
Opening a SQL connection takes at least five network round trips and uses server resources, which is too costly to repeat on every serverless request. Hyperdrive keeps warm connections open and shares them across many requests. It can also cache query results in Cloudflare's 4, the storage each data center normally uses for web content.
The clue
The biggest MySQL beta customer kept seeing a small, steady rate of empty results from queries that should always return rows. Telemetry and traces showed nothing, because failures were so rare. The one clue was that the bug only happened when caching was on.
Two nodes, one cache
Each Hyperdrive instance has a Client node, which runs next to the user's Worker, and an Endpoint node, which holds the connection pool near the customer's database. Each node uses its own data center's cache. If both are in the same data center, they share it. Results are cached as raw bytes under a hash of the query, so both nodes must store identical bytes for the same answer.
Where the bytes diverged
When a MySQL connection opens, both sides agree on 5, which are optional protocol features. One flag, CLIENT_DEPRECATE_EOF, changes how a result set ends: an OK packet instead of EOF packets. Hyperdrive uses this flag when talking to the database. The popular Node.js driver 6 doesn't support it, so the Client rewrites each response into the format the Worker's driver expects.
The Client-side caching code came from Postgres, where it was safe to cache exactly what was sent to the Worker. In MySQL, that means caching already-translated bytes. Two failures followed:
The Endpoint cached and the Client read. The Client sent Hyperdrive-format bytes to the Worker without translating them.
The Client cached and the Endpoint read. The Endpoint passed already-translated bytes to another Client, which translated them a second time. It sent an early EOF, which looked like an empty result, or it hung.
Fix and result
Now both nodes write one canonical form to the cache, encoded with Hyperdrive's own capabilities. Translation happens only on output. The empty results stopped, and the Client connection success rate reached the 99.99% target.
The post also describes two encoding fixes. The first was switching from utf8 to utf8mb4 so emoji survive. The second was setting the encoding again after COM_RESET_CONNECTION, which clears session state but resets the encoding to the server's default.
Cloudflare has made MySQL support in 1 generally available. Hyperdrive is its database 2 for 3, Cloudflare's serverless functions that run in data centers around the world. Until now Hyperdrive supported only Postgres, and MySQL had been in beta for about a year. Pranoy Dutta's post jokes that going from beta to general availability (GA) took "20 bug fixes in a trenchcoat." Most of the post covers one of those fixes: a caching bug that made about 0.001% of queries return empty results or hang.
What Hyperdrive does
Connecting to a SQL database takes at least five network round trips. Every open connection also costs the database server resources. MySQL's cost is lower than Postgres's because it doesn't run a whole operating-system process per connection, but large numbers of connections still cause trouble. Serverless code that opens a fresh connection on every request pays the latency each time and can flood the database. Hyperdrive keeps warm connections open on Cloudflare's network and shares them across many Worker requests. It can also cache the results of common queries in Cloudflare's 4, the per-data-center storage Cloudflare normally uses to serve web content.
Two smaller bugs first
Both were about character encoding. Hyperdrive asked MySQL for utf8 encoding when it connected. In MySQL, utf8 is a deprecated alias for utf8mb3, which allows at most 3 bytes per character. Real UTF-8 can use 4 bytes, and emoji need all 4, so emoji came back corrupted. Switching to utf8mb4 fixed that.
Then Hyperdrive started cleaning each connection between uses with COM_RESET_CONNECTION, a MySQL command that clears session state so one user's settings don't leak into the next user's requests. That command doesn't restore the encoding agreed at connection time. It restores the server's default encoding, which might be latin1. The fix was to set utf8mb4 again after every reset.
The hard bug
The highest-volume MySQL beta customer saw a small, steady rate of empty results from queries that should always return rows. Telemetry, user logic, network checks and a raised trace rate all turned up nothing. At a 0.001% rate, a full end-to-end trace of a failing request was hard to capture. The one clue was that the bug only appeared when caching was turned on.
How Hyperdrive is laid out
Each Hyperdrive instance has two nodes:
Client: runs on the same machine as the user's Worker, handles local caching, and forwards queries when needed.
Endpoint: holds the connection pool and runs in the data center nearest the origin database (the customer's actual MySQL server).
Each node uses the CDN cache of the data center it runs in. When both nodes sit in the same data center, they share one cache. That helps performance, because the Client can serve a result the Endpoint just cached. The cache key is a normalized hash of the query's wire messages, and the value is the raw result bytes. The scheme only works if a given response is stored as the same bytes no matter which node stored it.
The protocol detail that broke it
MySQL connections start with a Connection Phase, where the two sides agree on authentication and 5. Then comes the Command Phase, where the client sends statements and the server replies. Hyperdrive sits in the middle of two such conversations. To the Worker's MySQL driver it acts as a server. To the origin database it acts as a client. It uses a fixed set of capabilities toward the origin, and it translates every response into whatever capabilities the Worker's driver negotiated.
One flag mattered here: CLIENT_DEPRECATE_EOF. Without it, a result set looks like this: column definitions, an EOF packet, the rows, then another EOF packet. With it, the first EOF is dropped and the final one becomes an OK packet. MySQL 5.7.5 and later support the flag, and Hyperdrive uses it. But 6, a popular Node.js MySQL driver, doesn't support it. So for mysql2 users, Hyperdrive rewrites results to put the EOFs back and turn OK packets into EOFs.
The flaw came from the Postgres code. When Client-side caching was built for Postgres, it assumed the Client could cache exactly the bytes it sent back to the Worker. That holds for Postgres. For MySQL it fails, because the Client's output has already been translated into the Worker's format. The Endpoint always works in Hyperdrive's own format.
Two ways it failed
Both cases need the Client and Endpoint to be in the same data center, sharing a cache.
The Endpoint caches and the Client reads. The Endpoint gets response R for query Q from the origin and caches it in Hyperdrive's format (call it R_hyperdrive). A Client then gets Q, finds R_hyperdrive in the cache and sends it to the Worker untranslated. A mysql2 driver expecting EOF packets gets OK-style packets instead.
The Client caches and the Endpoint reads. A Client finishes serving Q and caches its translated bytes, R_worker. The Endpoint, serving Q for some other Client, finds R_worker and forwards it. That other Client then translates bytes that were already translated. It either ends the result early with a premature EOF, which shows up as an empty result, or it hangs.
An illustrative analogy: two clerks file the same document in one cabinet under the same label. One files it in English and the other in French. Each clerk assumes the cabinet holds their own language, and the label doesn't say which one it is.
The fix and results
The cache now always holds one canonical version of the result, encoded with Hyperdrive's capabilities, whichever node writes it. Translation into the Worker's format happens only on the way out. After the change, the empty results stopped. Client connection success rate reached 99.99%, the availability target Cloudflare set for the beta.
What's left open
The post doesn't say how the team confirmed the diagnosis. It doesn't mention a reproduction, a test, or how many hours of traffic were checked after the fix. It also doesn't explain why the rate was as low as 0.001%. Presumably it depended on how often Client and Endpoint ended up in the same data center and on cache timing, but the article doesn't measure this. The other ~18 fixes aren't described. The wider lesson, which the post implies more than states, is that any cache shared by components speaking different dialects needs one agreed encoding at rest.
Remember
A shared cache needs one canonical encoding at rest; translate into each consumer's format only on output.
An assumption that held for Postgres's wire protocol broke silently once MySQL's per-connection capability negotiation was involved.
In MySQL, 'utf8' allows at most 3 bytes per character, and COM_RESET_CONNECTION resets the encoding to the server's default.
The four lifecycle phases (benchmark, training, deployment, monitoring) and the data passed between them.1 / 4
Netflix has described how it keeps an 1 trustworthy over time. An LLM judge is a large language model that grades another model's output in place of human raters. The output being graded is recommendation explanations: one-line blurbs that tie a recommended title to one you've watched ("much like 'My Secret Santa'"). In month-long app tests, these were generated by the hundreds of thousands each week, far too many for people to read.
The problem
A judge solves the volume problem but raises a new one: who checks the checker? A judge can quietly 2, or give the right verdict for the wrong reason. That second failure looks fine on a dashboard. But the judge's reasons are fed back to the generator as feedback, so wrong reasons steer rewrites the wrong way.
Build and align
Experts write the quality criteria and tricky test cases. Raters label examples PASS/FAIL, and every FAIL comes with a written reason. Netflix runs one judge per criterion. The model itself is never retrained; only the 3 text in its prompt changes.
The tuning loop, 4 (Reasoning-Aligned Rubric Tuning), uses two other LLMs. A 5 rewrites the rubric based on the judge's mistakes. A 6 checks whether the judge's reason matches the human's. A "mistake" is either a wrong label, or a correct FAIL given for a different reason than the human's.
Netflix compared RART with label-only tuning. On two of three criteria, both ended the same, because the starting rubric was already near its ceiling. On the third, label-only tuning made the rubric steadily worse and ended below its starting point. RART stayed stable and improved.
Deploy
The judge does two jobs. As a gate, it blocks failing explanations. As a critic, its reason goes back to the generator for a retry. Tests on 1,000 explanations with four generator models showed most of the gain comes in the first few revisions, and strong generators benefit far more than weak ones. Production used 3 retries. Anything still failing was dropped, not shown. In an 7, showing explanations measurably increased viewing of titles members hadn't watched before.
Monitor
Each week about 300 outputs went to a panel of at least three raters. The judge has to score no worse than the raters' average minus two standard deviations of their own disagreement. Falling below that triggers re-tuning, and a human approves every new rubric. Reviewers also caught things the metrics couldn't, such as vague adjectives that technically pass, or generic "word soup."
Most results appear only as charts. The post gives few exact numbers, and the full figures are in Netflix's accompanying paper.
Netflix has published how it builds, aligns and monitors an 1, which is a large language model that grades another model's output in place of human raters. The test case is recommendation explanations: one-line blurbs on a title's detail page that tie a recommended title to one the member has already watched. An example: "A funny, heartfelt holiday romance about love and new beginnings, much like 'My Secret Santa.'" During online experiments, including a month-long test on the mobile app, an LLM generated hundreds of thousands of these a week. Humans couldn't review them all, so a second LLM did.
The team's central claim is that a judge is "not a static artifact." It has a lifecycle with four phases, and humans own each one.
Why a judge is hard to trust
The judge solves the volume problem and raises a new one: who checks the checker? If it slowly starts approving bad text, nothing downstream notices. There is also a quieter failure. A judge can agree with humans on every verdict and still be getting there for the wrong reasons. That looks fine on a dashboard. But the judge's explanations steer the generator's rewrites, so wrong reasons mean misleading feedback. And reasoning that happens to produce the right labels is unlikely to keep doing so on new data.
The cast
Four automated parts:
Generator: writes the explanation.
Judge: returns PASS/FAIL plus a written rationale. Netflix runs one judge per quality criterion.
2: an LLM that checks whether the judge's rationale matches a human rater's rationale.
3: an LLM that rewrites the judge's 4 when the judge makes mistakes.
Around them are human writing experts, who define the criteria, and trained raters, who supply labels and rationales.
Phase I: a benchmark with reasons attached
Public judge benchmarks test general text quality. That doesn't fit short, title-specific blurbs held to Netflix's own editorial bar, so the team built its own benchmark from three sources:
adversarial cases written by experts, each with a known failure;
LLM-made examples deliberately pushed close to the pass/fail line, then rated by humans;
real samples from the generator.
Every FAIL label comes with a free-text rationale. The labeling guidelines double as the judge's starting rubric. The benchmark is never frozen: monitoring adds new human-rated examples every week.
Phase II: Reasoning-Aligned Rubric Tuning
Tuning never touches the model's weights. The judge is a fixed prompt template, and only the rubric text inside it changes. Netflix calls its loop Reasoning-Aligned Rubric Tuning (5). Each iteration:
Scores the training data with the current rubric.
Measures alignment on a held-out validation set and stops once every metric clears its threshold.
Otherwise, collects a "focus set" of mistakes and hands it to the Reflector, which proposes a new rubric.
What's new is how a mistake is defined. The focus set includes ordinary label disagreements, plus a second kind: cases where judge and human both said FAIL but the Meta-Judge finds their reasons don't match.
The worked example: a member watched a musical, a family fantasy with a young heroine and a well-known ensemble cast. The system recommends an adventure film that is also a family fantasy with a young heroine, but has no songs and no ensemble cast. Judge and human both fail the explanation. Side by side, though, the judge's reason turns out to be incomplete. A label-only check counts this as agreement. RART counts it as an error and makes the next rubric revision account for it. The Meta-Judge was checked first: on a sample of agreed-fail pairs, it matched trained human raters on "nearly all." The article gives no exact figure.
Each criterion is scored on three metrics:
6 (fail-recall): how often bad explanations get rejected. This one matters most.
pass-recall: how often good explanations get through.
reasoning agreement: on shared rejections, how often the judge and human give the same reason.
The ablation. "Vanilla" tuning is the same loop with no Meta-Judge and no human rationales. On two of the three must-have criteria, both methods ended in the same place, because the starting rubric was already near its ceiling. On the third, vanilla tuning rewrote the rubric into "degenerate forms." Specificity and reasoning agreement fell every iteration and ended below the starting point. RART stayed stable on that criterion, improved both metrics, and gained a little pass-recall. The post shows these results as a chart only, with no exact figures in the text.
Phase III: gate and critic
The aligned judge does two jobs. As a gate, it rejects any explanation that fails any must-have criterion. As a critic, its rejection reason is added to the generator's prompt for a retry. That makes a generate → judge → revise loop.
Netflix ran 1,000 explanations through up to 12 revisions with four different generator models. The share that passed rose with every round. Most of the gain came early, and the gap between generators was large. The authors' summary: revision "amplifies a capable generator rather than rescuing a weak one." In production they allowed 3 retries with the strongest generator. Anything still failing was dropped, not shown. A bad explanation damages trust, while a missing one only loses an opportunity.
Passing the judge proves an explanation meets Netflix's standard, not that it helps anyone. A month-long 7 (explanations versus none) found members shifted viewing toward titles they hadn't watched before, and more browse sessions ended in a meaningful play. Both effects were statistically significant. The post doesn't give effect sizes. Its point is that agreeing with raters offline was necessary, but only the live test showed the explanations were actually useful.
Phase IV: 8 monitoring
Each week, about 300 explanations were sampled across served, revised and dropped outcomes, weighted toward newer titles. A panel of at least three raters reviewed each one, and the majority label counted as ground truth. The bar for the judge is set by how much the humans disagree with each other. For each metric, the judge must score at least the raters' average minus two standard deviations. On a hard week, raters disagree more and the band widens. If the judge falls below the band on any metric for any criterion, that is a drift event and triggers re-tuning on the enlarged benchmark. No new rubric goes live without a person signing off, and the previous version is kept for instant rollback.
Reviewers also caught problems the metrics couldn't:
accurate attributes that overstate how similar two titles are;
vague adjectives like "fun" that technically pass;
reference titles a member wouldn't see as comparable;
stand-up specials with matching tags but different tone;
generic "word soup."
Some fixes went into the guidelines, which update the raters' instructions and the judges' rubric together. Others became product rules outside the judge, such as blocking certain title types from being used as references.
Caveats
Most results appear only as charts. The Meta-Judge's own accuracy is described as "nearly all," with no number. The reasoning-alignment gain shows up on only one of three criteria. The full numbers are in the accompanying research paper, which the post summarizes.
Remember
Collect a written reason for every human FAIL label; it lets you tune the judge's reasoning, not just its verdicts.
Netflix sets the judge's bar from how much its human raters disagree, not from a fixed accuracy threshold.
Feeding the judge's critique back to the generator helps strong generators much more than weak ones.
Postgres labels every function and expression immutable (same inputs always give the same output), stable (same output within one statement, like now()), or volatile (can change on every call, like nextval() or random()). The planner uses these labels to decide what it may cache, reorder or skip.
Query 3 highlights now(), which Neki must evaluate on the router, not the shards.1 / 4
PlanetScale is building Neki, a version of PostgreSQL that splits tables across many servers, called 1. Mats Rydberg, who works on Neki's 2 (the component that decides how to run each query), explains how Neki keeps Postgres sequences unique once the data is spread out.
Router and shards
Each shard is a full Postgres database holding a slice of the data. A 3 in front splits up incoming queries and combines the results. Neki prefers to push work down to the shards, but some expressions can only run on the router. now(), for example, must return the same transaction start time on every row. If each shard computed it, clock drift would give different rows different times. So the router computes it once and attaches that value to every row.
Three kinds of expressions
Postgres labels every expression:
Immutable: same inputs, same output, always (age + 1).
Stable: fixed within one statement but dependent on session settings. to_char() prints "Wednesday" or "Onsdag" depending on the lc_time setting, so Neki forwards such settings to shards before pushing it down.
Volatile: can change on every call. nextval(), which returns the next unique value from a sequence, is the key example.
In Neki, sequences exist on every shard but stay empty. If shards counted independently they would hand out duplicates. So all sequence calls run on the router, which takes values from one authoritative shard.
Once per row, and which rows count
A volatile expression must run exactly once per row of the 4, meaning the rows left after , and . In Rydberg's example, joining to gives three rows because one animal has two feedings. Filtering to morning feedings leaves one. must run three times or once to match. An optimizer may skip recomputing repeated values like , but it can't skip .
When both tables are sharded on the join column, Neki runs the join on each shard. The router computes now() once and calls nextval() for each row as it streams back. Values can't be fetched in advance, because the router doesn't know how many rows will arrive.
UPDATE needs two phases
For UPDATE animals SET tag_id = nextval('tag_seq'), the router doesn't know how many rows exist or where they live. Neki first fetches just the id column from every shard and locks those rows with FOR UPDATE. Then, for each id, it calls nextval() and sends a single-row update to the shard that holds it.
That costs one round trip per row. The post gives no performance numbers and says other designs are possible, without comparing them.
PlanetScale is building Neki, a version of PostgreSQL that splits tables across many servers, called 1. In a new post, Mats Rydberg, who works on Neki's 2 (the component that decides how to run each query), explains how Neki makes Postgres sequences behave exactly as they do on a single server. The answer depends on two things: where each expression gets evaluated, and how many times.
The setup: 3 plus shards
Each Neki shard is a complete Postgres database with its own planner, but it holds only a slice of the data. A router in front receives each query. Rydberg says the ideal is to pass the whole query through to one shard. Usually that isn't possible, so in the general case shards supply raw rows and the router does whatever they can't.
Take SELECT age + 1, lower(name) FROM animals. Either side could compute age + 1, and choosing well is a real optimization problem: which side has spare capacity, which is faster, and whether shipping raw columns costs more than shipping computed values. For the post, Rydberg assumes a simple rule: push work down to the shards whenever that's allowed.
Router-only expressions
Some expressions must never be pushed down. now() returns the wall-clock time at the start of the transaction. If each shard computed it, clock drift between machines and different request arrival times would give different rows different timestamps. That breaks what now() promises. So Neki computes it on the router and attaches the same value to every row.
The router doesn't know in advance how many rows will arrive, so repeated evaluations must return the same answer. Rydberg describes two ways to do that: compute the value once into a one-row, one-column table and join it to the incoming rows, or cache it for the length of the statement. Neki's general model follows from this. Work out which parts of a query can't run on shards, plan for those parts to run on the router, and push everything else down.
Immutable, stable, volatile
During semantic analysis, the planning phase that works out what each part of a query means, Postgres labels every expression with one of three classes:
Why sequences are router-only
Sequences are created with CREATE SEQUENCE, with identity columns, or with DEFAULT nextval('seq'). In Neki the sequence objects exist on every shard but stay empty. Any query can reference any sequence, so letting shards count independently would produce duplicate values. Instead, all sequence operations run on the router. The router gets fresh values from a single authoritative shard, which physically hosts every sequence, and hands them out.
That leaves 5, meaning the number of rows. A volatile expression must run exactly once per row, so Neki has to know which rows count.
Table expressions decide "once per row"
The FROM … JOIN … WHERE part of a query (plus GROUP BY, HAVING and so on) forms a 6. Its rows are what the SELECT list gets evaluated against. Rydberg's example uses animals with ids 0 (BISMUTH, age 8) and 1 (DOGE, age 13), and feedings with rows (0, morning), (1, midday), (1, evening) and (2, morning).
The inner join on id gives three rows. DOGE appears twice, once for each of its feedings.
Adding WHERE feedings.time = 'morning' leaves one row, so nextval('tag_seq') runs once and uses up one value.
Without the WHERE, there are three rows. A smart optimizer might notice that DOGE's age + 1 and lower(name) repeat and compute them only once. It must not do that for nextval(), which has to produce a fresh value on each of the three rows.
Planners may also reorder joins and filters, for example filtering feedings before joining. Neki and the shard's own Postgres planner may not pick the same order. Rydberg notes that if a volatile expression appears inside a WHERE or JOIN … ON condition, the results can vary from run to run, but they are still correct.
Distributed joins
Network round trips between router and shards are Neki's biggest cost. A cross-shard join normally means pulling both tables to the router and joining there. If both tables are sharded on the column being joined (here id), Neki can join locally on each shard and simply concatenate the results. That is correct because matching rows always live on the same shard.
With that in place, Neki handles SELECT age + 1, lower(name), nextval('tag_seq'), now() FROM animals JOIN feedings ON … like this: the join and the two immutable columns run on the shards. now() is computed once on the router. nextval() is computed on the router for each row as that row streams in. Values can't be fetched ahead of time, because the router doesn't know how many rows each shard will return.
The hard case: UPDATE
UPDATE animals SET tag_id = nextval('tag_seq') modifies every row, but the router doesn't know how many rows exist or how they're spread across shards. Neki handles it in two phases:
It first sends each shard a query that selects only the columns it needs, here id, which is both the primary key and the shard key, using FOR UPDATE to lock those rows.
As each id reaches the router, Neki calls nextval() and sends a parameterized single-row update to the right shard. The id picks the shard and the WHERE clause picks the row.
The post shows the exact queries as images. Above they're described in words rather than quoted.
Open questions
Rydberg says other designs are possible but doesn't compare them. The two-phase update costs one network round trip per row, and the post gives no performance numbers for it. Fetching sequence values in batches, a common workaround, isn't discussed. Sequence resets and cycling are explicitly out of scope. How a stable expression like now() would work in that UPDATE is left as an exercise. It's simpler, because it doesn't depend on the row count.
Remember
Volatility labels set the boundary of where code can run: stable expressions can be cached, volatile ones must run once per row.
Join reordering and deduplication optimizations must leave volatile expressions alone, or they consume the wrong number of sequence values.
Sharding on the join column lets the join run on each shard, cutting traffic through the router.
GPUs in a training job exchange data through "collective operations", such as summing gradients across all ranks. NVIDIA's NCCL library does this over fast links inside a machine and over special network adapters between machines. If a link is degraded, these operations still finish, only slower. The failure looks like a slowdown, not an error.
Ways a machine passes basic checks while faulty, and the safeguard that closes each gap.1 / 4
Netflix's compute platform team has described how it keeps its GPU fleet healthy for training and serving machine-learning models, including the recommendation models behind each member's home screen. Since the work landed, GPU nodes stay above 99.95% available and job success rose from about 98% to above 99.5%.
Why simple checks fall short
A big training job runs for weeks on hundreds of GPUs. Its worker processes, called 1, exchange data every step, so the job moves at the speed of its slowest rank and stops if any rank dies. Cloud checks confirm the machine is up. A "driver responds" check confirms the GPU is visible. Neither catches the costly failures: memory faults that appear only under load, a network adapter missing links (the job doesn't crash, it crawls), or a health check that hangs or quietly stops running after an upgrade.
Detection that assumes tools break
An agent built on 2, an open-source 3 component, runs every 30 seconds on each machine. (Kubernetes is the system that schedules work across the fleet.) The agent records each result on the machine as a Kubernetes "condition". GPU checks use NVIDIA's 4 diagnostics. Network checks count the machine's 5, AWS's fast links between machines. The checks follow four rules:
A check that times out marks the machine unhealthy.
If the tool that reads 6 codes (NVIDIA driver error codes) breaks, the agent reads them from the kernel log.
Netflix keeps its own list of which codes are fatal on each GPU generation.
A missing dependency fails the check. An incident taught this one: machines missing adapters entered service for days.
Graded, automatic repair
A "problem" condition only blocks new work. If it persists, it becomes a "failure" and the machine is drained and replaced. This builds on 7, an open-source tool that adds and removes Kubernetes machines. Netflix gave each condition its own tolerance window and capped how much of a cluster can be disrupted at once. The fix matches the fault: kill the job, reset one GPU, reboot, or replace the machine. Each failed GPU's permanent hardware ID goes on a blocklist so it can't return. New machines must pass diagnostics and two check cycles before taking work.
Surviving the rest
Jobs checkpoint and resume after losing a machine. Failures are retried only when classified as the platform's fault. Some timeouts in 8, NVIDIA's GPU-to-GPU communication library, looked like network faults but were data-loading stalls. Retrying them on new hardware would have fixed nothing.
Caveats
The post's results come down to the two headline numbers. It gives no earlier availability figure and no repair times. Next steps include fencing off single bad GPUs instead of whole machines, and flagging GPUs that run 10–15°C hotter than their peers.
Netflix's compute platform team has described how it keeps a growing GPU fleet healthy for model training and live inference. These GPUs train and serve the recommendation models that build each member's home screen, along with newer generative-AI work. Netflix reports two results since the system landed. GPU nodes now stay above 99.95% available. The share of jobs that succeed rose from about 98% to above 99.5%.
Why ordinary health checks miss the costly failures
A large training job can run for weeks on hundreds of GPUs spread across many machines. It runs as a gang: every 1 (worker process) exchanges data with the others each step. A single faulty machine can therefore slow or stop the entire job.
Netflix starts with the cloud provider's checks. These confirm that the virtual machine's host has power and network, that the operating system is reachable, and that attached storage works. They also announce planned maintenance. Most teams then add a GPU check that confirms the driver responds and the GPUs are visible. That catches a dead GPU, but Netflix says it misses the failures that eat the most time:
Faults that appear under load. A GPU can answer a status query and still have a fault in its on-chip memory or firmware. The fault only shows up once a job allocates memory or runs sustained compute.
Degraded networking. A high-speed network adapter can be present but show fewer links than expected. Collective operations still work, at a fraction of the bandwidth. The job doesn't crash. It crawls.
Broken checks. The diagnostic tool can hang when the driver hangs, and a check that waits on it hangs with it. A driver upgrade can delete a binary a check relies on, so the check stops running and reports nothing.
The team's rule is that "the hardware can lie and the tools that inspect it can break."
Four layers
Netflix arranges reliability in four layers, each catching what the one below misses:
Node and hardware health: detect bad GPUs, adapters, interconnects, memory errors and overheating, then taint or recycle the machine.
Scheduler and platform: retry tasks and jobs, validate machines before they take work, and drain and recycle them safely.
Training loop: checkpoints and elastic workers, so a job survives losing a machine.
Observability: metrics, service-level objectives and attribution of each failure to a cause.
How detection works
An agent built on 2, an open-source 3 component, runs on every GPU machine every 30 seconds. It writes each check result onto the node as a Kubernetes condition, the same mechanism that marks a node NotReady. The GPU checks use NVIDIA's 4 (Data Center GPU Manager), which reports driver error codes, uncorrectable memory errors, interconnect integrity and temperature. The network checks count the machine's 5 (EFA) interfaces, AWS's high-bandwidth adapters for traffic between machines, and check each link's state.
Four design rules address the "broken checks" problem:
Fail closed. If the driver or diagnostic tool doesn't answer, the check times out and marks the machine unhealthy instead of passing it.
Graded, automatic repair
Faults escalate in two levels. A problem condition only blocks new work, because evicting a running job is disruptive and many problems clear on their own. If the problem lasts past a tolerance window, it becomes a failure. The machine is then evacuated and drained, overriding normal disruption limits, then terminated and replaced. This runs on 7, an open-source tool that adds and removes Kubernetes machines, using its node auto-repair feature. Netflix added a policy that sets a tolerance time per condition. Fatal errors get acted on within minutes, while flaky signals get longer windows. A cap limits how much of a cluster can be disrupted at once, so a buggy rollout of the checks themselves can't tear a cluster down. Netflix plans to contribute this to Karpenter.
The repair matches the fault. Killing the job clears a software wedge. Resetting one GPU clears many transient faults. A reboot clears firmware state that has drifted over a long uptime. Replacement is kept for hardware that fails a trusted test. In that case Netflix records the GPU's permanent hardware ID, reports it to the cloud provider and keeps its own blocklist, so the same device can't come back under a new instance.
A new machine has to prove itself before it gets work. It passes system checks, confirms its GPUs and their layout, and runs GPU diagnostics. Then it waits for two health-check cycles, because slower checks such as the adapter count need more than one cycle to report. Only then is the startup taint removed. Netflix also found a race: machines were handed to the scheduler while their checks were still settling. It now holds capacity until the checks settle.
Surviving loss, and retrying only the right failures
Jobs save checkpoints and resume from the last good one after losing a machine. Workers are elastic, and the scheduler automatically retries failures caused by the platform. The key word is platform. Each failure is classified as platform or application before any retry, because blindly retrying would hide real bugs and burn GPU hours. Netflix gives an example. Timeouts in 8, NVIDIA's library for moving data between GPUs, looked like network faults. They were actually data-loading stalls at high worker counts. Treating them as hardware faults would have cycled healthy machines and fixed nothing.
Measuring it
Three service-level objectives track the system: capacity available to schedule, jobs starting promptly, and jobs finishing without a platform-caused failure. Each is split by cause: hardware, driver, platform or user. The guiding metric is 9, meaning useful work completed. A job held back by one slow GPU looks fine on an uptime chart while getting little done. Before admitting a job, Netflix runs a short collective-communication test and pairwise network tests, so machines that would fail in the first minute fail before the job starts.
What's missing and what's next
The results come down to two numbers. The post gives no availability figure from before the change, no repair times, and no failure rates per fault class.
Planned work includes:
Moving to Kubernetes 10, a newer way of handing individual devices to jobs. It would let Netflix fence off one bad GPU instead of losing the whole machine, and place GPUs next to their matching network adapters and CPUs.
Using 11 (Multi-Instance GPU), NVIDIA's feature for splitting one GPU into isolated slices, which it already uses for small inference jobs.
A custom Linux CPU scheduler built on , which lets custom scheduling policies be loaded into the Linux kernel, to give priority to collective-communication and data-loading processes.
Remember
A health check that can silently stop running is worse than none. Make checks fail closed and fail if a dependency is missing.
Match the repair to the fault: kill the job, reset the GPU or reboot before replacing hardware, and blocklist bad devices by hardware ID.
Classify each failure as platform or application before retrying. Some apparent network timeouts were really data-loading stalls.
In SQL, LIMIT sets how many result rows to return and OFFSET sets how many to skip first. Both apply at the very end, after the database has filtered, grouped and sorted everything. An index works like a sorted lookup table that lets the database jump straight to rows in a range, such as one date window.
Image: GitHub Engineering
GitHub has released an API endpoint that returns a repository's star history as daily star counts, up to 30 weeks per request, with no usernames attached. For react/react, which has about 251,000 stars, rebuilding the full history takes about 24 requests instead of about 2,511.
Why there was a problem
Maintainers use star history to see whether a release or launch changed a project's trajectory. Until now, tools built the curve from the 1 endpoint, which lists the people who currently star a repository, 100 per page, each with a timestamp. Tools fetched every page, kept the dates and dropped the names.
This went wrong in two ways. Scrapers harvested the identities for spam and phishing. And a 2 bug made every page request count all of the repository's stars to work out how many pages existed. In one week, nearly 37,000 of those count queries ran slowly enough to be logged, averaging almost 3 seconds each, and safeguards killed more than 1,000 of them.
GitHub first limited stargazer lists to repository admins and collaborators. Total time the database spent counting fell about 80%, but tools like Star History broke. When GitHub asked their maintainers, it learned they needed the curve, not the identities.
Why "just return counts" wasn't enough
The obvious design was to group stars into daily buckets in SQL, the database query language, and page through the buckets. But SQL's LIMIT and OFFSET (how many rows to return, and how many to skip) run last. The database would still read, group and sort every star before keeping one page. The response gets smaller; the work doesn't. Reading react's full history that way would mean about 6 million rows, because each of the 24 requests would reprocess all its stars.
What shipped
The new endpoint turns the page number into a date range of calendar weeks. An 3, a sorted lookup structure, lets the database fetch only stars created in those weeks, and it counts just those by day. Each star is read about once across the whole history, about 251,000 rows for react/react. The response has the same shape as GitHub's weekly commit activity endpoint.
Along the way GitHub found the GitHub.com stargazers page counting stars on demand. It switched that page to a cached count, and its slow queries went from hundreds a week to zero.
Caveats
The design limits each request to a period of time, not to a number of stars, so a week with a huge spike can still produce a slow query. Un-starring deletes the record, so the curve shows only people who still star the project. The author's lesson: a smaller response doesn't mean a cheaper query. Look at the order in which the work happens.
GitHub has shipped an API endpoint that returns a repository's star history as daily star counts, up to 30 weeks per request, with no user identities attached. For react/react, a repository with about 251,000 stars, rebuilding the full history now takes about 24 requests instead of about 2,511. The more interesting part of the write-up by Taylor Cuilty is why the obvious fix, returning counts instead of people, would not have made the database's job any lighter.
The old way and its two problems
Maintainers look at star history to answer one practical question: did a launch, release or event change the project's trajectory? GitHub had no endpoint for this. Tools rebuilt the curve from GET /repos/{owner}/{repo}/stargazers, which lists the people who currently star a repository. With a custom media type, a request header that asks for an extended response format, each entry also included the time of the star. Tools fetched every page, kept the timestamps and threw away the usernames.
That caused trouble in two places. First, because identities were easy to collect in bulk, bad actors scraped stargazer lists to target spam and phishing. Second, the database paid heavily. Each page held at most 100 people, so the work grew with popularity, and tools often hit 1 or sampled pages instead of reading them all. A 2 bug made this worse: every request counted all of the repository's stars to work out how many pages existed, even though it returned only 100. In the week before GitHub restricted access, nearly 37,000 of these count queries were slow enough to be logged. They averaged almost 3 seconds each, and database safeguards killed more than 1,000 of them, which contributed to request timeouts.
The data model added a quieter limit. Stars are stored as the current set of people starring a repository. They are not an event log, so an un-star deletes the record. Any history rebuilt from them only reflects people who still star the project.
Stop the bleeding, then ask what people need
GitHub first limited stargazer and watcher lists to repository administrators and collaborators. Other callers now got an empty response or a 3. Afterwards the database served about 60% fewer stargazer requests, and total time spent on the count query fell by about 80%.
This also broke tools such as Star History. GitHub talked to maintainers and tool builders and found they wanted the curve, not the names. Identity had looked like part of the contract only because every timestamp used to arrive attached to a user.
Two designs that didn't go far enough
Timestamps without identities. The first prototype kept the one-record-per-star endpoint but removed the user for callers without collaborator access. That closed the scraping path with a small change, and existing tools would keep working. But clients would still page through thousands of records, and a trends use case would stay tied to an endpoint built for listing people.
Aggregate, then paginate. The next idea was to group stars into daily or weekly buckets in SQL and page through the much smaller set of buckets. The problem is the order in which a database runs that query:
all matching stars → bucket every row → sort every bucket → OFFSET/LIMIT → response
LIMIT and OFFSET run last. Asking for page 3 of the buckets still makes the database read, group and sort every star in the repository, then throw most of the result away. The JSON response shrinks, but the work does not. For react/react, all 24 pages would each reprocess all ~251,000 stars, about 6 million rows read to get the history once.
The shipped design: filter by date first
The endpoint GitHub shipped pages by calendar week. It turns page and per_page into a created_at date range that an 4 can serve, fetches only the stars in those weeks, and groups just those into daily counts:
indexed date range → group rows in that range → daily buckets → response
As an illustration (not GitHub's actual numbers), suppose page 1 covers the most recent 30 weeks. The index lets the database jump straight to stars created in that window, perhaps a few thousand, and count them by day. Page 2 covers the 30 weeks before that, and so on back through history. Each star is read about once over the whole history, about 251,000 rows for react/react instead of 6 million. A single request covers up to 30 weeks however popular the repository is. The response has the same shape as GitHub's existing weekly commit activity endpoint (GET /repos/{owner}/{repo}/stats/commit_activity).
Weekly paging also fits the way people use the data: they usually look at growth across weeks or around a known release date.
Side effects
Comparing the old and new paths turned up other places where GitHub counted stars on demand. The 5 page on GitHub.com was one, and it was switched to the repository's 6. Slow queries from that page went from hundreds a week to zero. GitHub says it recently made a similar fix to other stargazer API endpoints.
What remains open
The design limits each request to a period of time, not to a number of stars. A week with a huge spike, such as a viral launch, can still produce a slow query. GitHub calls dense weeks a remaining scaling limit and does not say how it will handle them. Weeks follow calendar boundaries, which affects how data lines up. Because un-stars delete records, the curve shows today's stargazers by the date they starred, not a true record of past totals. GitHub says these details are part of the API contract.
The author's main lesson: when a query is expensive, look at the order of operations, not just the size of the output. Two more: start from what users are trying to do, and return only what that job needs, since any extra data is data someone else can misuse.
Remember
LIMIT and OFFSET shrink the output, not the work: the database still groups and sorts every row first.
Turning page numbers into indexed date ranges means each star is read once across a full history.
GitHub's star data isn't an event log, so un-stars vanish and the curve shows only people who still star a project.
Immutable: the same inputs always give the same output, in any statement. age + 1 is an example. Most expressions fall here.
Stable: fixed within a single statement, but possibly dependent on session settings. now() depends on the TimeZone setting (Postgres calls these settings 4, short for Grand Unified Configuration). to_char(date, 'TMDay') gives "Wednesday", "Onsdag" or "星期三" depending on lc_time. Neki can push to_char() down to shards, but first it has to forward any SET lc_time or set_config() from the session, or different rows could come back in different languages.
Volatile: may return a new value on every call. nextval() is the main example.
A fallback path. If the tool that reads 6 and SXid codes (NVIDIA's driver error codes for GPUs and for the switches that link them) breaks, the agent reads the codes straight from the kernel log, dmesg.
An in-house fatal-error map. NVIDIA's error catalogs lag behind new GPU generations, so Netflix keeps its own list of which codes are fatal on which GPU family. A code that means "reset the GPU" on one generation can be harmless on another. One generation emits a burst of normally fatal codes on every healthy boot. Netflix learned to ignore it rather than drain new machines.
No silent no-ops. A check now fails if a dependency is missing. This rule comes from an incident: an upgrade removed a binary the adapter check needed, and for days machines with the wrong number of adapters entered service until slow jobs were traced back to them.
12
Treating a GPU that runs 10–15°C hotter than its peers as a fault, even within normal limits, because it sets the pace for the whole gang.