Meteor Performance - a TODO list

Hi folks,

As everybody knows, we’ve been working hard to improve Meteor’s performance. Recently, @nachocodoner implemented SWC and has also been advancing our RSPack integration.

On my side, I’m working on an upcoming change that will significantly reduce memory usage by replacing the oplog with a Change Streams–based implementation, along with a new EventEmitter-based DDP transport (WIP) that promises replace the pooling. I’m also setting up full OpenTelemetry instrumentation to measure and validate these improvements (more news coming soon!).

Beyond that, we’ve had some interesting discussions around replacing EJSON with CBOR and a PR about a BSON-to-JavaScript transformation approach to in the oplog(maybe in pooling as well, didnt check it)

A few additional points:

  1. There are many opportunities to optimize Meteor’s JavaScript for V8. If you run TOOL_NODE_FLAGS="--trace-deopt" meteor, you’ll see a long list of unoptimized functions we could improve.
  2. I started cleaning up unnecessary Promise overhead (article/ PR)

So I’d like to open this topic by inviting everyone to share ideas on how we can keep improving Meteor’s performance and reducing resource usage.

20 Likes

DDP session resumption is an easy performance win:

1 Like

Maybe check and revive this PR

Integration with uWebSockets could be very interesting.

1 Like

Could the network performance be improved by using Pro instead of sending json data?

would be nice to have a benchmark:

  • socketjs(current package used in meteor) vs Uwebsocket
  • Protobuff over ws vs json over ws

[uWebSockets]
A good point about uWebSockets is the fact the sockjs was updated 4y ago vs 3 weeks ago from uWebSockets.

@ignl do you mind to do a small POC?


[Proto]
One advantage of using Protobuf in Meteor is that we’re full-stack, so developers don’t need to worry about Protobuf-specific knowledge. They only need to work with JS/TS types, and we can handle everything behind the scenes.

1 Like

I was looking with chatgpt into this some time ago and conclusion was that it’s a big undertaking so stopped right there. uWebsockets are very very very performant, but it’s complete reimplementation with different api, so it’s not really compatible with nodejs ws or socksjs and would require deep refactoring in meteor code. But I don’t really know much about meteor.js internals, maybe it’s not as bad as chatgpt says. I just put this idea out as potentially interesting.
There were also some discussions about maybe moving to bun (and bun I think uses uWebsockets underneath) so if refactor is big anyway maybe that could be a move. Of course this is just brainstorming for now.

Protobuf needs to be de-serialized, I am not sure it would be more performant than simple json. I would really research and test this before investing into implementation. Maybe there are some new browser api’s that could do deserialization natively idk, but if done in js it probably won’t give any big wins and introduce complexity.

For those who want to work on it, understand DDP pkg could be a good point of start, here and here you can find a guide

@ignl but what do you think to do an benchmark comparing ws, sockjs and uws and talk about the diffenreces between them, it will be a good content for the dev community and for who want to do a poc like this. It will a required knowledge for who want to work on it

I’ve been working on the benchmarking side of things and built a framework on top of the existing meteor/performance repo that could help with some of the questions raised here (measuring the impact of change streams, etc.).

Live dashboard: https://meteor-benchmark-dashboard.sandbox.galaxycloud.app/

Fork: GitHub - dupontbertrand/performance at feature/benchmark-framework · GitHub

What it does

  • A bench.js CLI that runs Artillery+Playwright scenarios against any Meteor checkout, collects CPU/RAM/GC metrics, compares two branches and detects regressions

  • GC tracking via Node.js perf_hooks (zero overhead when disabled)

  • A Blaze dashboard on Galaxy to visualize runs, compare branches, and track trends over time

  • GitHub Actions workflows for automated PR and nightly benchmarks

There’s already data in the dashboard for release-3.2 through release-3.5 and devel. For example, devel vs release-3.5 shows nearly identical performance on the light scenario, with devel having slightly better GC behavior (−31% max pause, −34% major GC time)

This is still very much a draft/experimental setup — benchmarks are running on shared GitHub Actions runners (no dedicated VM), so the numbers have some variance. But the framework itself is functional end-to-end: run, compare, push to dashboard, CI automation. Happy to iterate on it if the team finds it useful :partying_face: :man_dancing:

6 Likes

Give this man a baguette!! :clap: :clap: :clap:

2 Likes

This was exactly the longer-term vision I expected for meteor/performance: making it more dynamic and allowing easy checks between branches, different app examples and setups, multiple metrics, and so on.

Still, the app there is really basic and does not cover many real-world scenario behaviors (I will continue and intent here - I will try with Claude, I invoke you! :grin:). But we can scale the performance app overtime, and with that the benchmark suite UI will be a good asset to compare changes and ensure we do not go backwards.

This dashboard opens also the possiblity to test the bundler scenario with meteor profile and check over versions we don’t go backwards in those terms. :rocket:

I completely support the direction of your changes.

benchmarks are running on shared GitHub Actions runners (no dedicated VM), so the numbers have some variance.

Don’t worry we can improve that later with dedicated machines to get more stability on numbers, the idea is the direction to go.

3 Likes

Nice! This is something I was trying to do with OpenTelemetry, but you’re following a better path than I was. I really loved the live dashboard; I can definitely see it in our release pipeline one day.

I have a few questions:

  1. Is the server running in the same environment as Artillery? I’m asking because a few users reported a CPU decrease in production from 3.4 to 3.5 (check here).
  2. Do you have plans to test only the backend, like this artillery setup does?

Such a great tool. Squash the “meteor doesn’t scale” rhetoric.

4 Likes

Quick update on the benchmark work: the benchmark framework is now merged into dupontbertrand/performance on main , and the dashboard is live here: https://meteor-benchmark-dashboard.sandbox.galaxycloud.app :smiling_imp:

It currently covers:

  • browser-based reactive scenarios
  • DDP-only scenarios
  • reactive fanout
  • cold start
  • bundle size

I also added CI workflows for nightly runs, branch vs baseline comparisons, and transport comparisons like SockJS vs UWS :wink:

The dashboard has evolved quite a bit too. It now has a proper Release Health view, compare pages, trends, run detail pages, scenario detail pages, and more context around what each metric means.

Current tags/data include:

  • release-3.2
  • release-3.3
  • release-3.4
  • release-3.5
  • devel
  • release-3.5-sockjs
  • release-3.5-uws

@italojs To answer your questions directly:

1. Is the server running in the same environment as Artillery?
Yes, for now the Meteor app and the load generator run on the same machine, either on the same GitHub Actions runner in CI or on the same laptop locally.

That works well enough for relative comparisons on the same setup , but not for production-like absolute numbers, since the load generator also competes with the app for CPU and RAM. So for the 3.4 → 3.5 CPU differences some users reported, a split setup would definitely be cleaner: Meteor on one machine, load generation on another.

2. Do I have plans to test only the backend?
Yes, and that part is already in place :+1:

I added backend-only scenarios:

  • ddp-reactive-light / ddp-non-reactive-light
    SimpleDDP clients over raw WebSocket, no browser, to isolate DDP throughput
  • fanout-light / fanout-heavy
    50 or 200 subscribers + 1 writer, measuring reactive propagation latency (p50 , p95 , p99 , max )

I also added CLI-only benchmarks for:

  • cold-start
  • bundle-size

So the current setup now covers browser/full-stack, backend-only, reactive fanout, and startup/build benchmarks.

The main next step now is improving signal quality:

  • more datapoints
  • less noise
  • ideally a dedicated machine instead of shared runners

So there’s still a lot to refine, but it’s starting to feel like something genuinely useful rather than just a pile of benchmark scripts :smile:

3 Likes

This is so cool. Fantastic job on this!

Edit: Something that may be of interest for developers who have not migrated to 3.x yet is a baseline comparison of 2.16?

1 Like

Are all these tests using oplog tailing? Should 3.5 also have change streams support to measure 3.4 oplog tailing vs 3.5 change streams?

https://meteor-benchmark-dashboard.sandbox.galaxycloud.app/scenario/reactive-light

Measuring 2.x vs 3.x is turning out to be a bit more complicated than expected, but I’ll get there :smile:

And regarding oplog vs. change streams, yes, I can run both tests just like I did for SockJS vs. uWS :+1:

But until we have a dedicated machine, the metrics are completely skewed :grimacing:

2 Likes

IMO we should focus in make benchmark toolkit stable for 3.x first, as I can see still have so much to do to mensure 3.x, metrics, scenarios, pkgs…

1 Like

I’m in favor of totally dropping 2.x. It’s a piece of history now. So don’t waste your time focusing on it.

2 Likes

yes, agree 100% with @harry97