On September 18, 2026, Cloudflare announced that it had cut worldwide memory usage by more than 100TB by overhauling the Pingora Backend Router (PBR), an internal service that routes requests to caches. This is separate from the DNS cache redesign we covered in August. This time, Cloudflare reduced the number of hash points that decide which server handles which cache by 90%, and also made the storage format of each point smaller. The points had been added to keep load skew low, but it turned out there were far more than needed. Still, changing where a request is routed can send it to a server that doesn't hold the data it needs. Making the reduction work in production required careful migration design as much as it required the math.

AD

The bloat was in the cache routing table

What was squeezing PBR's memory was the data structure of "pingora-ketama," the library responsible for consistent hashing. In some cases it reached 6GB. Where August's "Big Pineapple" reworked how DNS responses are stored, PBR shrank the table that decides which server a request is sent to.

Consistent hashing is a routing scheme that keeps reassignments local when servers are added or removed. Servers and keys such as URLs are converted to numbers and placed on a circular range. Following the ring in one direction from a request's position to the next point identifies the server responsible. By steering cacheable requests for the same URL to the same place, the system makes it easier to find the data it needs.

However, if each server has only one point, the gaps between neighboring points vary widely. Servers covering large stretches receive a disproportionate share of work, so in practice one server is represented by many points, and many small ranges are added together. This is what keeps skew down.

Pingora's previous default was 160 points per unit of weight. Cloudflare multiplied that by weights based on disk capacity, assigning more requests to larger servers. In addition, the set of usable servers changes depending on compliance requirements and which cache features are enabled, so a separate hash ring is kept for each combination of conditions. There are dozens of them.

Every time a single server's weight grows, points increase across multiple rings. The headroom meant to reduce skew became a large memory burden held on every server.

How much precision did 100,000 points actually buy?

For illustration, Cloudflare gives an example with a weight of 625. Multiplied by the old factor of 160, that yields 100,000 points per server. This is an example to make the calculation easy to follow; it does not mean every production server has the same weight.

The metric used to read the relationship between point count and skew is the coefficient of variation. Here it expresses how widely servers' assigned ranges scatter, as a ratio to the expected value. It is not a value that guarantees the maximum skew.

The author's published supplementary explainer, "Consistent Hashing Proofs", gives a formula for N servers of equal weight, each with k points.

coefficient of variation = √((N − 1) / (N × k + 1))

This formula assumes a continuous space in which hash points are independent and uniformly distributed. Substituting N = 100 lets us compare the effect of adding points under the same conditions.

Points per server (k) Coefficient of variation of assigned range (theoretical)
160 about 7.866%
10,000 about 0.995%
100,000 about 0.315%

Assuming 100 servers of equal weight, increasing the points per server from 10,000 to 100,000 lowers the coefficient of variation of the assigned range only from about 0.995% to about 0.315%, a difference of about 0.680 percentage points.

The table is calculated from the author's formula, published in September 2026, and rounded to three decimal places in percentage terms. The difference is computed from the unrounded values. Increasing the point count tenfold does not improve the result by the same proportion. This theoretical value does not include collisions in a 32-bit space, and it is not a measurement of actual request volume or CPU load. Problems such as traffic concentrating on popular URLs, or differences in processing cost per request, cannot be solved by evening out assigned ranges alone.

Moreover, the implementation has limits that differ from the theory. The hash values PBR uses are 32-bit, so different points can end up with the same value, which is a collision. The published code also includes a step that sorts points with identical hash values and then removes duplicates. Not every point generated adds an independent interval.

In Cloudflare's simulations, for a configuration of 2,048 servers, increasing the points per server from 10,000 to 100,000 actually showed an increase in distribution error. Based on the theory and simulations, the company concluded that it could cut the number of generated points by 90% without causing a non-negligible increase in skew.

This result does not mean that 10,000 points per server is always optimal. The supplementary explainer presents a different formula for cases where weights differ, using the total number of points and each server's point count. The author also notes that a formula covering collisions among multiple points has not been derived. What adopters need is an evaluation that reflects their own server count and weight distribution.

AD

From 8 bytes to 6 bytes per point in Rust

The other change was shrinking the storage width of a single hash point from 8 bytes to 6 bytes. Previously each point held a 32-bit hash value and a 32-bit server index. Judging that a 16-bit index is enough for the number of servers PBR handles in one ring, Cloudflare made the index smaller.

However, simply shrinking the index type would leave the struct at 8 bytes under Rust's normal memory layout, because padding is inserted for alignment, the boundary requirement for 32-bit values. The new implementation packs the values into a 6-byte array, PointV2([u8; 6]), and converts them back to integers when reading. This cuts the width needed to store one point by 25%.

It's worth separating what the 25% and 90% figures refer to. The 25% is the storage width of a single point, and the 90% is the reduction in the number of generated points. Neither is a figure saying PBR's overall memory usage fell by that percentage. The 100TB-plus figure Cloudflare reported as its production result compares PBR's usage after the old rings were removed against its usage a few weeks earlier.

The public implementation also shows the boundaries of this change. In the linked code, the new scheme sits behind an optional Cargo feature, v2, and the old V1 scheme remains the default. The ordinary Continuum::new also selects the old scheme.

To use the new scheme, you enable the feature and pass V2 { point_multiple } to new_with_version, specifying the number of points per unit of weight. It is not designed so that updating the library changes all existing routing at once. The ability to run old and new schemes side by side is what supports the next step, migration.

Swapping the routing table alone can chill the cache

Rebuilding a hash ring means the same URL may be routed to a different server. Even if the data remains on the previous server, it may not yet exist on the new one. Switching everywhere at once could stack up cache misses and cause a surge of traffic to origin servers, the sources the content is fetched from.

So PBR temporarily held both the old and new rings in memory. Which one each request used was decided by an existing migration mechanism, with the choice kept stable for a given request hash. If anything went wrong, traffic could be returned to routing on the old ring without redeploying PBR.

The rollout began at small verification sites and expanded in stages to larger groups of data centers. Cloudflare reportedly controlled the share of traffic sent to the new ring and the set of sites permitted to migrate separately. Simply applying the same percentage worldwide would change cache assignments everywhere at the same time. Segmenting by site made it possible to confirm behavior while limiting the impact.

In addition to records of routing destinations and usage of the old and new rings, Cloudflare tracked connection errors. It checked memory usage and startup time, and also monitored cache behavior and traffic to origins. Only after the migration rate reached 100% and the no-longer-needed old rings were removed did the large memory reduction appear.

In other words, the memory it wanted to cut had to be held in surplus during the migration period. The public library provides the means to build both old and new rings, but it does not automatically include the site-by-site rollout and monitoring that Cloudflare uses internally.

For those trying this in other environments, it is worth verifying separately the skew in assigned ranges when the point count is reduced and the cache misses at the moment of switchover. Cloudflare has not disclosed the acceptable threshold for origin traffic, nor by what percentage user response times improved. If the accuracy needed for load balancing is maintained and the period during which data locations change can be supported, the RAM spent on the routing table can be redirected to other workloads.