LAT-04
Behaviour Under Congestion: What Actually Changes
Congestion is described as the network being slow, which is not what an operator experiences. What changes is which constraint binds first, and the four candidates behave differently enough that the same correction can be decisive against one and completely inert against another.
Fault card
- Symptom
- Landing rate falls sharply and recovers without any change on your side
- Mechanism
- A capacity constraint became binding: block, account, connection or leader
- Instrument
- Slot versus block height drift, per-account fee samples, landing rate by venue
- Correction
- Identify which constraint binds before spending anything on it
When Solana is busy, four different capacity constraints can bind, and they produce similar symptoms with different fixes. Block compute capacity, per-account write capacity, connection capacity toward the leader, and the leader schedule itself all fail in ways that look like slow. Identifying which one is binding is the whole job.
What congestion is not
It is not the network running slower. Slot time is a target the cluster paces itself against, and a busy period does not stretch it. What happens is that more transactions want space than there is space, and the mechanisms that resolve that competition become visible.
It is also not a single event. A period where one token is being traded heavily is a per-account condition affecting a narrow set of transactions. A period where block production is degraded is a network condition affecting everything. Both get reported as congestion by people whose transactions did not land, and they need opposite responses.
Finally, it is not a state your sender can measure directly. There is no congestion figure to read. What is available is your own landing rate, the drift between slot number and block height, and the fee levels being paid on the accounts you care about. A diagnosis has to be built out of those three, and it is a better diagnosis than any single number would be.
Four constraints, four behaviours
| Constraint | What binds | Who it affects | Responds to fee |
|---|---|---|---|
| Block compute capacity | Total units available in one block | Everyone, briefly | Yes, as ordering |
| Per-account write capacity | Units spent writing one account in one block | Only transactions touching that account | Only up to the ceiling |
| Connection capacity | Forwarding bandwidth toward the current leader | Traffic without a well-staked path | No |
| Leader schedule | Whether a block is produced at all | Everyone aimed at those slots | No |
The two rows that do not respond to fee are the ones that cause the most wasted money. An operator meeting a connection-level drop or a run of skipped slots will see landing fall, reach for the fee lever, see landing recover when the underlying condition passes, and conclude that the fee fixed it. That inference is wrong and it becomes policy.
The leader schedule and skipped slots
Leaders are assigned in advance for an epoch, which runs for 432,000 slots, and each leader holds four consecutive slots before the next takes over. That structure is public and queryable, which is why a sender can know which validator is expected to produce the next block.
A slot is skipped when its leader produces no block. Slot numbers keep advancing because slots are a clock; block height does not, because it counts blocks. The gap between them widening is the cleanest observable signal that production rather than demand is the problem, and it requires only two ordinary RPC calls to watch.
The four-slot grouping matters more than it first appears. Because a leader holds four consecutive slots, a leader having a bad time takes four slots with it rather than one, and transactions in flight across that stretch all wait together. That is why landing rate during these periods tends to fall in blocks rather than degrading smoothly, and a smooth-looking average across a minute will hide it entirely.
Connection capacity and stake weighting
Transactions reach a leader through connections, and connection capacity is finite. Validators allocate a share of that capacity in proportion to stake, so traffic arriving through a well-staked path competes for a larger allocation than traffic arriving through an unstaked one. Under heavy load this is where transactions disappear without producing an error anywhere.
This mechanism explains an otherwise confusing observation: two operators sending equivalent transactions at the same moment can see very different landing rates, with no difference in fee, size or venue. The difference is the path, not the transaction. It also explains why endpoint choice is a real variable rather than a commodity decision.
The corrections available are structural. Using an endpoint whose forwarding path is better provisioned, or spreading submission across more than one provider, addresses this constraint directly. Neither does anything at all about block or account capacity, which is why applying them to the wrong diagnosis produces cost without improvement.
The rule that keeps this affordable: identify the binding constraint before spending. Fee escalation, endpoint multiplication and concurrency reduction each address one of these constraints and are inert against the others. Applying all three at once guarantees an improvement and guarantees you will not know which change caused it.
The hot account problem
A block enforces a ceiling on the compute that may be spent writing any single account. That ceiling is well below the block's total capacity, which is deliberate: it stops one popular account from consuming an entire block and starving everything else.
For a trader this is the most common form of congestion and the least like the popular description. The network is fine. The block has room. The specific pool being traded has reached its per-block write allowance, and every transaction that needs to write it, including yours, waits for the next block regardless of what it paid. Fee competition still orders the queue for the next slot, but it cannot create capacity in the current one.
The available responses are limited and each has a price. Reducing your own concurrency on that account removes self-competition and costs throughput. Spreading activity across venues writes different accounts and costs whatever the alternative venue's liquidity costs you. Waiting costs the opportunity. There is no fourth option, and a sender that only knows how to escalate will keep paying more for the same outcome.
Symptom to mechanism
| What you see | Most likely mechanism | Confirm it by | Correction with a price |
|---|---|---|---|
| Landing falls on one token only | Per-account write capacity | Comparing landing by venue in the same window | Lower concurrency on that account; costs throughput |
| Landing falls across everything at once | Block capacity or production | Slot minus block height drift | Pace and wait; costs time, nothing else works |
| Landing falls in four-slot chunks | Leader schedule | Landed slots clustering away from certain leaders | Nothing available to a sender; ride it out |
| Landing falls for you but not for others | Connection capacity | Same build through a second provider | Better-provisioned path; costs a provider relationship |
| Fees paid rise, landing unchanged | You are outbidding yourself | Fee samples scoped to your own accounts | Reduce concurrency before raising price; costs throughput |
| Expired attempts rise, no other change | Window consumed before delivery | Block height at first send versus deadline | Move the blockhash fetch later; costs a round trip |
Pacing arithmetic
Pacing is the correction that costs the least and is used the least, so it is worth doing the arithmetic. The numbers below are illustrative and chosen to show the shape of the trade rather than to describe any observed run.
Suppose a sender issues 12 transactions per second against one pool and, during a busy window, 4 of every 12 land. That is a landing rate of 33 percent, and the 8 that miss have each paid nothing but have consumed window, requests and attention. Now suppose the same sender paces to 5 transactions per second and 4 of every 5 land. The landing rate is 80 percent and the absolute throughput is unchanged at 4 per second.
| Setting | Sent per second | Landed per second | Landing rate | Wasted attempts per second |
|---|---|---|---|---|
| Unpaced | 12 | 4 | 33 percent | 8 |
| Paced | 5 | 4 | 80 percent | 1 |
The paced configuration achieves the same result with less than half the requests and a rate that is actually diagnosable. That is the honest argument for pacing: not that it lands more, but that it stops the sender from being a large part of its own competition, and it makes every subsequent measurement cleaner.
The cost is real and should be stated. Pacing gives up the ability to seize a brief window with a burst, which for some strategies is the entire point. This is a trade between throughput and predictability, and either side can be correct depending on what the run is for.
Early signals worth watching
Every constraint above announces itself before landing rate collapses, and the announcements are cheap to collect. A sender that watches four signals continuously will usually know which mechanism is arriving while there is still time to change something, rather than reconstructing it afterwards from a bad hour.
- Track slot minus block height. One extra call each and a subtraction. Widening drift means blocks are not being produced, which is the one condition no sender-side setting addresses.
- Sample recent fees scoped to the accounts you write. Costs one call per account set. A rising sample is the earliest warning that ordering, rather than delivery, is about to become the constraint.
- Watch time to land at the upper percentiles, not the median. Costs nothing extra if the attempt log already stores landed slots. The tail moves first; the median moves after the problem is obvious.
- Compare landing rate per venue in the same window. Costs a group-by. A divergence between venues is an account-level constraint; a uniform fall is a network-level one.
The value of watching all four is that they disambiguate each other. Rising fee samples with stable block production point at ordering. Falling landings with stable fee samples and widening drift point at production. A venue-specific fall with everything else flat points at the write ceiling on one account. No single signal separates those cases, and together they do it in seconds.
None of these requires infrastructure beyond what the send path already touches. That is the argument for collecting them by default rather than adding them during an incident, when attention is the scarcest resource and the window for a useful measurement has usually already closed.
Corrections that do nothing
- Shortening the confirmation timeout. It abandons transactions that were still live and cannot create block space. It makes the reported rate worse and the real outcome worse.
- Rebuilding faster on timeout. It creates duplicate artefacts and inflates the denominator, and none of the new artefacts is more schedulable than the first.
- Widening slippage tolerance. It changes what happens after inclusion. During a capacity event, inclusion is the problem.
- Raising the requested compute limit. It makes the transaction larger to schedule and more expensive at the same unit price, which is the wrong direction on both counts.
- Resending more often to the same endpoint. Under a connection constraint this adds load to the constrained resource itself.
- Switching to a durable nonce. It removes expiry, which was a symptom, and does nothing about why the transaction is not being included.
All six are reached for regularly, and all six are reasonable-sounding responses to the wrong model of what congestion is. They share an assumption that the transaction is being rejected and needs to try harder, when in most capacity events it is not being reached at all.
Venues do not congest together
Because the binding constraint is often per-account, congestion is a local phenomenon. A launch pulling heavy traffic to one bonding curve does not make an unrelated pool harder to trade, and a sender running across several venues will see that clearly if it reports landing rate per venue rather than in aggregate.
This is a practical argument for measuring by venue, and it is also an argument about tool design. An engine that routes across several venues has a lever a single-venue sender does not: it can move activity toward the venue that is not currently constrained. Whether that is desirable depends on the goal, since a different venue means different liquidity and a different fill.
Migration events make the point sharply. When a token graduates from a bonding curve to an automated market maker pool, the accounts being written change entirely, and so does the contention profile. A run tuned against the curve can meet quite different behaviour the same afternoon against the new pool, which is why operators using a Raydium volume bot after a migration should re-measure rather than assume the previous settings carry over.
The same reasoning applies to any tool that spans venues. A multi-DEX Solana volume bot is only worth its complexity if it reports per venue, because that is the granularity at which the constraint actually operates. An aggregate figure across venues averages a constrained pool with unconstrained ones and hides the only thing worth acting on.
None of this makes congestion manageable. It makes it legible, which is a smaller claim and a more useful one. The window will pass, the constraint will release, and the operator who spent the window identifying which of four things was binding will know what to do next time, while the one who raised every dial at once will have a policy built on a coincidence.
Questions this entry keeps getting
Does the blockhash window get shorter when Solana is congested?
No. The window is defined in blocks, not seconds, so a period with skipped slots actually gives a transaction more wall-clock time inside the same 150 blocks. Expiry failures rise during congestion because delivery and scheduling consume the window without producing an inclusion, not because the window shrank.
What is a skipped slot?
A slot whose scheduled leader produced no block. The slot number advances, block height does not, and any transaction hoping to be included in that slot waits for the next block. A run of skipped slots is the clearest available signal that block production, rather than your send path, is the thing that changed.
Why do my transactions fail on one token but not on others?
Because contention is largely per account. A block enforces a ceiling on compute spent writing any single account, and a heavily traded pool can reach that ceiling while the rest of the block has room. Transactions aimed at a quiet pool at the same moment will land normally, which is why an overall network diagnosis is often wrong.
Does sending through more endpoints improve landing during congestion?
It can help against connection-level drops, because forwarding capacity toward the leader is finite and allocated by stake weight. It does nothing against a block or account capacity ceiling, since the constraint there is space in the block rather than reaching it. Multiplying endpoints without knowing which constraint binds multiplies cost instead.
Should retries be faster when the network is busy?
Faster retries increase request volume and do not increase block space. If the transaction is not being scheduled, resending it more often adds load without changing the reason it was not picked. Widening the interval and lowering concurrency is usually the better response, and it is free.
How long does a congestion event last?
This log does not publish a figure for that, because any honest answer depends on the cause and the window, and a number quoted without both would be an invention. What can be said is that the recovery signal is the same as the onset signal, read in reverse: slot and block height converge and per-account fee samples fall back.
Filed under Latency. Arithmetic on this page is either a documented protocol constant or an illustrative example built from numbers you supply. If a figure here is wrong or has moved, send the desk a correction and the entry gets amended rather than quietly rewritten.