Hyperloop B: the coroutine engine behind Appwrite 2.0_
Hyperloop B is the Swoole coroutine engine behind Appwrite 2.0. Here is what it is, the concurrency problem it solves, and the 7x I/O throughput it buys.

Most of the time a backend spends on a request, it is not computing anything. It is waiting, for a reply from the database or for a cached document to come back from Redis. What a server does with that waiting time is most of what decides how much traffic it can carry.
For most of Appwrite's history, it did nothing with it. A worker process picked up a request and stayed blocked for the length of every I/O call that request made, unavailable to anyone else until the response went out. Adding capacity meant adding processes, and a process costs memory whether it is serving a request or waiting on one.
Appwrite 2.0 changes that. It runs on Hyperloop B, which serves many concurrent requests from a single process by yielding every time one of them waits. On an I/O bound benchmark that is 7x the requests per second of the model it replaces, at 6x less memory. Getting there took a new dependency injection layer and a careful audit of what the codebase had been quietly assuming while every process handled one request at a time.
What Hyperloop B is
Hyperloop B is the engine underneath Appwrite 2.0. Concretely, it is the Swoole coroutine scheduler mode of utopia-php/http, the HTTP layer our services are built on.
In that mode, a single process serves many concurrent requests by yielding whenever it waits on I/O, instead of dedicating one worker process to a request for as long as that request takes. utopia-php/http can run either way, and the two modes are what the names refer to. Hyperloop A is the worker model. Hyperloop B is the coroutine scheduler.
Every Appwrite product sits on top of it, which is why it shows up as a foundation of 2.0 rather than a feature in it. There is no Hyperloop API. Nothing to call, nothing to configure, and no SDK behavior that changes because of it.
Why we named it Hyperloop B
Hyperloop B is named in loving memory of Binyamin Yawitz (1991-2025), aka byawitz, who built the foundational work to bring Coroutines to Appwrite, but tragically passed before he could finish it.
Binyamin was a Platform Engineer on the team. The engine that Appwrite 2.0 runs on began with his work, and it carries his name in his memory.
The problem that needed solving
The path to Hyperloop B runs through two earlier answers to the same question: how should a PHP process answer a request?
The first releases of Appwrite used PHP-FPM. Under FPM you spin up a process to respond to each request. It is a simple model and an easy one to reason about, and it is wasteful in both CPU and memory, because nothing survives between requests. Every request pays the full startup cost again, and every request pays it from scratch.
Then we found Swoole, and Appwrite got much faster. Swoole keeps a pool of worker processes alive and hands each of them a request to answer. Because a worker outlives the request, memory can be reused across requests instead of being thrown away with the process.
That model has two consequences, and together they are the actual problem.
The first is a hard ceiling on concurrency. Requests are served by a fixed pool, so N workers answer N requests at a time and no more. Request N+1 waits for a worker to free up, no matter how little work it actually needs. Scaling concurrency means adding processes, and processes are expensive, so the ceiling is set by how much memory you are willing to spend rather than by how much work there is to do.
The second is that memory becomes your problem. A per-request leak under FPM was invisible, because the process died at the end of the request and took the leak with it. Under worker mode the same leak accumulates across every request that worker handles, until it exhausts its memory. Nothing about the code changed; the model stopped hiding it.
The ceiling is the part that hurts in production, because a pool sized for the work is mostly a pool sized for waiting.
The arithmetic is unforgiving. Suppose a pool of 64 workers, and suppose each request spends 200ms waiting on I/O. Each worker can retire at most five of those per second, so the pool tops out around 320 requests per second, and it does so with the CPU almost entirely idle. Adding cores does not help, because nothing is CPU bound. The only lever the worker model gives you is more processes, and each one costs memory whether it is working or waiting.
How we solved it
More recently, Swoole built a coroutine scheduler, which takes the model a step further. Instead of a pool of processes each serving requests sequentially, one process serves many concurrent requests by yielding whenever it waits on I/O. While one request waits on the database, the process picks up another. Concurrency stops being tied to process count, which makes it very cheap, and memory usage becomes an order of magnitude more efficient.
Swoole is a low level framework, though. It exposes a lot of tuning knobs without much documentation about which ones matter, and the defaults are not the ones you want in production. That is why we built utopia-php/http, which runs on Swoole and on other backends, and which carries an opinionated set of configuration defaults so nobody has to rediscover them. Those defaults are here.
Switching modes was the easy part. Appwrite is a huge codebase, and it was built almost entirely under worker model assumptions, where a process handles one request at a time and its memory is effectively private for the duration. A great deal of ordinary, correct PHP quietly depends on that.
Coroutines break the assumption. Concurrent requests inside a single process share memory, so state that used to be safe by construction becomes shared mutable state. A cached connection, a request-scoped singleton, anything holding the current user: under a worker each of those was private, and under coroutines each of them is a bug waiting for two requests to arrive at once. A new approach to building PHP services was necessary.
We wanted coroutine safety to be a property of the ecosystem rather than a rule everyone has to remember. That is utopia-php/di, a new PSR compliant dependency injection library. It stores dependencies in two conceptual buckets:
- Shared singletons: vetted, coroutine-safe code that can be reused freely across concurrent requests.
- Per-message data: request-scoped state, plus anything we know to be coroutine unsafe, kept isolated so it is never shared across coroutines.
The value is that the split is declared where a dependency is registered, not remembered at every call site. Getting it wrong is a wiring mistake in one place rather than a race that only shows up under load.
We also enabled PHP 8.4's JIT and tuned opcache to 128M, for a modest boost on CPU bound work on top of everything above.
Benchmarks
Both modes, measured on 4 cores with 200 virtual users, 20 seconds per run, across three synthetic workloads: a minimal endpoint, an I/O bound endpoint, and a CPU bound endpoint.
| Workload | Mode | Requests/sec | p95 |
|---|---|---|---|
| Minimal | Hyperloop A | 14,653 | 29.22ms |
| Minimal | Hyperloop B | 15,443 | 28.10ms |
| I/O bound | Hyperloop A | 462 | 854.45ms |
| I/O bound | Hyperloop B | 3,346 | 51.56ms |
| CPU bound | Hyperloop A | 3,285 | 169.19ms |
| CPU bound | Hyperloop B | 3,112 | 84.48ms |
The minimal row is nearly identical between the modes, and that is the expected result rather than a disappointing one. An endpoint that does no I/O gives the scheduler nothing to yield on, so a coroutine has no idle time to trade away and the two modes are doing substantially the same work. It is a useful control: it shows the coroutine machinery is not costing anything measurable when there is nothing for it to exploit.
That leaves the CPU row, which rewards a close read. Hyperloop B gives up about 5% of raw throughput there and halves p95 latency in exchange. That is the right trade for a service where the slowest requests are the ones users notice, but it is a trade, not a free win, and coroutines do not make CPU work faster.
The I/O row is the one that matters, because it is the one that looks like production. Hyperloop B serves 7x the requests per second at a p95 roughly 16x lower, and it does it while using 6x less memory.
Read those two numbers together. 7x the throughput on its own would be a good result that you pay for in RAM. Getting it while using 6x less memory means the concurrency ceiling and the memory bill moved in the same direction, which is what makes it worth rebuilding a codebase over.
Try Appwrite 2.0 on Hyperloop B
Nothing changes in your code. What changes is what the same hardware can absorb: more concurrent traffic, and sharply lower tail latency on the I/O heavy endpoints that make up most of a real application. If your app spends its time waiting on databases and caches, which nearly every app does, that is the row of the table you are running on.
Hyperloop B is live for every project on Appwrite Cloud, and Community Edition ships the complete Appwrite 2.0 release for self-hosting when Init week wraps up. Either way, the engine is the part you do not have to think about.





