Free Windows RDP? Here’s What You Need to K
“Free Windows RDP” is one of the most searched phra...




A flash sale does not fail because too much traffic arrives. It fails because too many operations are in flight at the same instant on a resource that can only perform one of them at a time.
That distinction sounds academic until you watch it decide an outage. Teams plan a peak with a traffic number: requests per second, sessions per minute, megabits per second. Every one of those is an average over an interval. The thing that actually breaks is a count - the number of operations simultaneously queued at the slowest shared resource - and that count is not proportional to the rate you planned for. It is the rate multiplied by how long each operation takes, and the second factor is the one that moves.
This article is about the arithmetic of that count, and about the four places a retail stack serializes work whether you designed them to or not. The market context is real and current: Adobe's 2026 UK forecast, reported on 9 November 2026, puts online spend at 28.52 billion pounds for 1 November to 31 December, up 6.2 percent year on year, with Black Friday alone at 1.24 billion pounds - the season's highest single day. Do the division and the peak day runs at roughly 2.65 times the season's average day. In the United States, Adobe's 2026 holiday forecast of 28 September 2026 puts Cyber Week at 47.5 billion dollars, or 17.3 percent of a 275.1 billion dollar season in five days, and the National Retail Federation's 15 October 2026 forecast has November and December retail sales growing 4.2 percent to 1.013 trillion dollars.
So a peak is genuinely a couple of times an ordinary day. A factor of 2.65 should not take a site down. Understanding why it does is the whole subject.
The relationship between the two was settled long before cloud computing and is not in dispute. Little's Law states that the average number of items in a system equals the average arrival rate multiplied by the average time each item spends in it:
L = lambda x W
Read it as a sentence: to get the number of things in flight, multiply how often things arrive by how long each one stays. That is it. The reason it matters is that the second term is not a constant you can read off a specification. It is a function of load, and it grows on its own.
Suppose a checkout endpoint handles 1,000 requests per second. On a quiet day each request takes 50 milliseconds, so the average number of checkouts in flight is 50. That is a comfortable system. Now suppose the same endpoint still handles 1,000 requests per second, but each request now takes a second because a write is waiting behind something else. Throughput is identical. The concurrency is 1,000 - twenty times higher.
| Requests per second | Average time per request | Average requests in flight |
|---|---|---|
| 1,000 | 50 ms | 50 |
| 1,000 | 200 ms | 200 |
| 1,000 | 1,000 ms | 1,000 |
This is why a load test that reports a clean pass at a target rate tells you less than it appears to. It measured arrivals. What your database, your connection pool, and your socket backlog experience is the third column - and the third column is what your test did not hold constant. This is the same trap that makes a cross-region transfer look bandwidth-limited when it is really limited by how many bytes can be in flight at once; the count, not the rate, is the binding quantity.
If latency grew in proportion to load, capacity planning would be simple arithmetic. It does not. For a single queue served by a single resource - the standard M/M/1 model, which is a reasonable first approximation for a row lock, a single database writer, or one serialized counter - the average time a request waits before being served, measured in multiples of its own service time, is utilisation divided by one minus utilisation.
The formula is deliberately unremarkable. The table it produces is not.
| Utilisation of the constraint | Average waiting time, in multiples of service time |
|---|---|
| 50% | 1.0x |
| 60% | 1.5x |
| 70% | 2.3x |
| 80% | 4.0x |
| 90% | 9.0x |
| 95% | 19.0x |
| 98% | 49.0x |
| 99% | 99.0x |
Now put that table next to the traffic numbers. To move a system from 50 percent utilisation to 90 percent utilisation takes 1.8 times the traffic - not 18 times, 1.8. And that 1.8x multiplies waiting time by nine. To reach 95 percent takes 1.9 times the traffic and multiplies waiting time by 19.
That is the entire answer to "we only had double the traffic." A system sitting at half load on an ordinary day is one traffic doubling away from spending most of its time in a queue, and the queue time is what your users experience as the site being down. The measurement that tells you which side of the cliff you are on is not utilisation, which looks survivable right up to the point where it does not, but waiting time: if latency is climbing while throughput is flat, you are on the vertical part of the curve and adding traffic is not the fix.
The same mechanism governs any resource that queues - which is why buffering more work, rather than less, is so often the wrong instinct. The queue gets longer and the completion time gets later, and neither the queue nor the delay is visible in a throughput number. This is closely related to the reasons a full connection tracking table drops packets while a link sits idle, and to the general principle that queue depth is a property of seconds, not of bytes.
The third piece of arithmetic is the one that explains why the first reaction - add servers - so often makes the incident worse.
Amdahl's Law says that the maximum speedup you can get from parallelism is limited by the fraction of the work that cannot be parallelised at all. If a fraction s of the work is serial, the ceiling on your speedup, no matter how many workers you add, is one divided by s.
| Share of the work that is serialised | Maximum possible speedup, however many servers you add |
|---|---|
| 1% | 100x |
| 5% | 20x |
| 10% | 10x |
| 20% | 5x |
A checkout has a serial fraction. Every order for the same limited item has to decrement the same counter. Every coupon has to increment the same redemption total exactly once. Every seat has to be marked taken. These operations cannot be done out of order, cannot be done twice, and cannot be done by two processes at once without one of them being wrong. If the serialised portion of your checkout is ten percent of the work, ten application servers is your ceiling - and you probably have more than ten, which means the extra ones add nothing but arrival rate.
That is the trap. Application servers are the tier that is easy to add, cheap to add, and horizontally scalable by construction. The hot row is none of those things. Scaling the easy tier does not shrink the serial fraction. It increases the number of requests arriving at the resource that cannot be parallelised, which is the resource that is already the bottleneck.
When a peak arrives, work queues at whichever of the following saturates first. They are usually thought of as separate concerns - application code, connection management, kernel networking, storage - which is exactly why the constraint is so often misidentified.
| Serialized resource | What queues, and why | How many can be in flight | What a full queue looks like |
|---|---|---|---|
| The hot inventory row | Every order for one limited item writes the same row. The database takes a row-level lock for each write, so writers wait for each other in turn. | One writer. Effectively a single-file line. | Latency rises, database CPU stays low because the database is waiting, not computing. |
| The connection pool | The pool is deliberately capped at a small number of connections, and each request needs one for the duration of its transaction. | A few dozen at most, and often around ten. | Requests wait on the pool rather than on the database; pool wait time becomes the dominant term. |
| The accept queue | The kernel holds completed connections waiting for the application to accept them. The queue length is capped, and the cap is applied silently. | The backlog, itself truncated to a system-wide maximum. | Connections refused or SYN packets quietly ignored, so clients time out and retry into the same wall. |
| The durable commit | A committed write has to be made durable, and the device that does that is one thing, not a fleet. | Bounded by the device's concurrency, not by your instance count. | Every write waits behind a flush; throughput plateaus and no amount of application parallelism moves it. |
The row lock is documented plainly. PostgreSQL's locking documentation states that row-level locks do not affect data querying - they block only writers and lockers of the same row - and that a second attempt to update a locked row "waits for the transaction that acquired the lock to complete." The same documentation adds the sentence that should be printed above every checkout service: as long as no deadlock is detected, a transaction seeking a lock will wait indefinitely for conflicting locks to be released, which is why it is a bad idea for applications to hold transactions open for long periods.
The connection pool is the counter-intuitive one, because the fix is usually to make it smaller. The standard starting formula, attributed to the PostgreSQL project and popularised by HikariCP, is connections equal to twice the core count plus the effective spindle count - which for a four-core machine is nine, conventionally rounded to ten. The same source reports that those ten connections can serve on the order of 3,000 front-end users at roughly 6,000 transactions per second, that a PostgreSQL benchmark flattens out around 50 connections, and that in a demonstration dropping a pool from 2,048 connections to 96 cut response times from about 100 milliseconds to about 2 - a fifty-fold improvement from removing concurrency rather than adding it. A pool of 10,000 for 10,000 simultaneous users is not generosity; it is a way to convert a queue at the database into a queue at the pool plus a queue at the database.
The accept queue is the one people do not know they have. The backwards-compatible description of the listen backlog is that it caps the queue of completely established sockets waiting to be accepted, and that if a connection request arrives when that queue is full the client may receive a connection-refused error, or the request may simply be ignored so that a later attempt succeeds. Two details are easy to miss. First, a backlog larger than the system-wide maximum is silently capped to it - the default is 4,096 on kernels since 5.4, and was 128 before that. Second, the request that gets ignored produces no log line on your server, because as far as your server is concerned nothing happened; the client experiences a timeout and, in the worst case, retries.
Put the four together and the failure mode writes itself. Arrivals rise at the peak. Latency rises because of the utilisation curve. Latency rising means each request occupies its resources longer. Little's Law then delivers more concurrent in-flight work, which pushes utilisation at the constraint higher, which raises latency again. The feedback is closed, and it closes on the smallest resource in the system.
Almost every instinct available to an on-call engineer makes it worse. Add application servers: arrival rate at the hot row rises. Increase the connection pool: more concurrent writers contend for the same row and each transaction now holds its lock while competing for CPU. Increase the transaction isolation or add retries: more work per unit of contention. Add a longer queue in front of the database: completion time goes up, and every request now has more time to acquire more resources.
There is a second-order effect worth knowing by name. If a single code path acquires more than one connection - a service that opens two connections inside a transaction, say - the pool can deadlock against itself. HikariCP documents the minimum pool size that makes this impossible as the maximum number of threads, multiplied by the maximum number of simultaneous connections a single thread holds minus one, plus one. For eight threads each needing three connections, that is 17. Exceeding it is a lockout that looks exactly like a database stall and is not one.
Nothing multiplies concurrency faster than a client that has given up on an answer.
A request that times out at two seconds has not stopped doing work at the server. It is still holding its connection, its transaction, and possibly its lock. The client, having decided the request failed, sends another one - which queues behind the first, at the same serialized resource, for the same item. If a timeout triggers at the load balancer, the application, and the payment provider independently, one user action can become three or four units of demand on the row that has one writer.
This is why retry storms concentrate rather than spread. Retries do not distribute load across keys; they multiply load on the key that was already contended, because that is the key whose request was slow enough to time out. The result is the classic shape of a peak failure: a graph that spikes and stays spiked long after the traffic that caused it has passed, because the retries are now self-sustaining.
The defence is not to remove retries, which you need. It is to make the retried operation safe to repeat - so that the second attempt is cheap or free rather than additive - and to cap the concurrency at the door so that the queue never grows to the point where timeouts start firing in the first place.
There is one number that tells you whether you are about to survive a peak: the count of operations waiting at the constraint. Practically, that is the average number of requests waiting on the connection pool, plus the number of active database transactions, plus the time spent waiting for locks. It is measurable, it is boring, and almost nobody alerts on it, because it is not a percentage and it does not fit the standard dashboard.
What people alert on instead is a set of five observations that all point at the wrong tier.
| What you observe | What it seems to mean | What it usually means |
|---|---|---|
| Application CPU is high | The application tier needs bigger instances | Every worker is blocked on the same row; high CPU is the overhead of waiting, not of working |
| Latency rises but throughput is flat | More bandwidth or more instances are needed | Utilisation has crossed into the vertical part of the curve; the extra capacity will be consumed by the queue |
| Connection refused and gateway errors | The load balancer is undersized | The accept queue is full, and the kernel is refusing or ignoring connections before any application code runs |
| Pool wait time is climbing, database CPU is low | The database is idle, so the database is not the problem | The database is blocked on a lock, not busy on a query; a blocked database looks idle by CPU |
| Traffic was only 2.6x normal | The system should have coped | 2.6x is enough to move utilisation from survivable to vertical if the baseline was already at half load |
Re-read the fourth row, because it is the one that costs the most time. A database that is waiting for a lock uses almost no CPU. A team watching CPU concludes the database is fine and goes looking at the network. The signal that would have pointed straight at the row lock - lock wait time - is not on the dashboard, because the dashboard was built to show capacity rather than contention.
Fixes for a concurrency collapse, ordered by how much they cost and how well they hold.
Is concurrency just requests per second times latency? Yes - that is Little's Law, and the useful part is that latency is itself a function of load. The two factors multiply each other, which is why the product moves so much faster than either input.
Does a read replica help a flash sale? Only for the read path. The hot item counter is a write, and a write replica does not exist. Read replicas relieve catalogue and search traffic, which is why they help, and they do nothing at all for the row everyone is competing to update.
Does caching fix it? Caching fixes reads. If your peak is a stampede of reads on an expired key, caching plus a coordinated refresh is the fix. If your peak is 50,000 people trying to buy 500 units, the contention is on a write that cannot be cached away - though the decision of whether a unit is still available can be.
What about auto-scaling? Auto-scaling is correct and insufficient. It adds parallel capacity, which raises the arrival rate at the serial fraction without shrinking it. Pair it with moving the hot operation off the synchronous path, or you are buying arrival rate for a queue.
How do I know which side of the cliff I am on? Watch waiting time, not utilisation. If latency is rising while throughput is flat, you are past it, and the only useful questions left are which resource is serializing and how fast you can bound the arrivals at it.
All of the arithmetic above is yours to run, and none of it requires a purchase. It ends at one boundary the arithmetic cannot cross: the peak lands on an instance, and what that instance does at the peak - whether its port speed is a burst ceiling or a monthly allowance, whether traffic is metered, whether the price moves when the load does - is a property of the machine, not of your code. That is the point at which a sizing exercise becomes a purchasing decision, and it is worth stating plainly rather than in a footnote.
SurferCloud's High Bandwidth VPS line is built for exactly this shape of load - the page describes it as designed to deliver high peak bandwidth and suited to burst traffic and bandwidth-intensive workloads. It runs at the Los Angeles data centre with 200 Mbps of bandwidth and, in the page's own words, no fixed traffic: the plans are unmetered, so a peak month costs the same as a quiet one. Three plans are listed, all single-instance and all renewing at the same price as the first term - $4.25 per month for 2 vCPU and 1 GB with a 30 GB NVMe disk, $5.25 for 2 vCPU and 2 GB with 40 GB, and $8.05 for 2 vCPU and 4 GB with 50 GB, each with a dedicated IPv4 address and a choice of Linux or licensed Windows. Verify those figures against the console before you buy, as you should with any vendor page, including this one.
If the plan is for a peak you have already measured, the same site publishes the full offer list for that node, and the sizing measurements behind the instance you actually need are set out in the five measurements to take before you buy. For the region itself, the Los Angeles node offer list covers everything purchasable at that location.
See the plans and current terms on the High Bandwidth VPS page. If you are still deciding how much machine the peak needs, start with the five measurements to take before you buy, then read why latency under load behaves like a division and why a transfer is window-limited rather than bandwidth-limited. The full connection tracking table is the clearest example of the count-not-rate principle in production, and the Los Angeles offer list shows what else is available at that node.
“Free Windows RDP” is one of the most searched phra...
GPU servers are revolutionizing the field of deep learn...
Choosing the right ASP.NET hosting provider is essentia...