Intro
I’ve been thinking about Meteor performance a lot recently and I have one crazy idea… Would any of you be up for a talk about it? I can write it down later, but it’d take me longer than that
The codename would be “DDP Router”.
@radekmie, 25th January, 2024
It’s a genius idea! I really liked it, and I think it would be extremely beneficial for Meteor.
@denyhs, 26th January, 2024
It is an insane idea and I loved it
@grubba, 26th January, 2024
By the end of the last year, I went on a longer-than-usual vacation. I had a couple of PhD topics to think through, but after a couple of days, I started wondering: where is Meteor actually lacking performance? Is it the MongoDB integration? Is it the DDP protocol? Is it the merge box? Or is it an inevitable result of using Node.js, or more general, garbage-collected language?
A few days later, I realized there’s not a single piece that is slow or bad – it’s just a result of how we want it to work. We want real-time communication and as little amount of work as possible on both the server and client. Ideally an infinitely scalable solution, right?
So… What if we would rework the publications entirely? But we have to do it in a backward-compatible way, as otherwise, it’ll lead to a huge community split (like Python 2 vs 3). It’s a tall order already, but there’s more to it – we’d need to make it worth the additional effort.
Here’s an idea: implement a specialized service to handle the publications. The application remains unchanged, the database load is the same, and the change is transparent to the users. But how do we do that? And where’s the gain?
- We let this service ask the server, “What would the publication do?” That makes it entirely backward-compatible and transparent for both the client and the server.
- So, every request goes to this new service; it asks the server and then observes the database itself. That means the server only has to start the publications, and then no server resources are needed. (The load is now on the new service.)
DDP Router
That’s a new service hosted separately from the Meteor server. When the client connects to the DDP Router, the DDP Router connects to the Meteor server and forwards all DDP messages both ways.
However, when the client subscribes to a publication, the DDP Router executes a method instead. The Meteor server responds with a serialized cursor (or a list of them). Then, the DDP Router starts listening to the database on its own. Yes, that means it reimplements the entire merge box logic, including all MongoDB operators, sorting, limit/skip, etc. Yes, that’s a lot of logic and tests
In our initial tests, we see CPU gains of 10-20% and RAM gains of 15-25%. (The latter, of course, includes the RAM used by the DDP Router itself.) The gains are there because the server is not tailing the oplog, and the DDP Router is highly optimized to do only that. And yes, it’s written in Rust
One more upside – it uses Change Streams instead of Oplog!
Also, it’s perfectly possible to extend it further, making it a true router. For example, we could route certain DDP methods to a different Meteor server, some GraphQL/gRPC/REST/whatever API, or even support non-MongoDB publications easily!
Who wants to give it a try?
Yes, you heard it right! We tested it with Simple Todos, Atmosphere, and aleno. We haven’t got a chance to go live with it yet, but we’d like to test it with more apps. If you’re interested, please do let us know here!