Change Streams for Meteor – Status Update, Performance Benchmarks & Call for Contributors
Hey everyone!
I wanted to give you an update on the progress of the Change Streams integration for Meteor, where we currently stand in terms of performance, challenges, and how you can help us push this forward.
Performance Benchmark: Change Streams vs Oplog
We’ve run stress tests comparing the new Change Streams-based reactivity engine against the traditional Oplog-based one, using the performance tools available in the Meteor Performance repo. Here’s a summary of the results:
Attachment: you can find the log files here
Command used:
METEOR_NO_DEPRECATION=true METEOR_CHECKOUT_PATH=<my-meteor-path>/meteor MONGO_URL="mongodb://localhost:27017,localhost:27018,localhost:27019/?replicaSet=rs0" METEOR_REACTIVITY='CHANGE_STREAMS' ./scripts/monitor.sh tasks-3.x reactive-stress.yml
Test scenario:
• Artillery config: 30s duration, 2 users/sec, max 100 users
• Same codebase, one with METEOR_REACTIVITY=‘CHANGE_STREAMS’ and another with the default Oplog
Performance Summary
Metric |
Change Streams |
Oplog |
Best |
FCP Mean |
1546ms |
2527.9ms |
CS |
FCP Median |
424.2ms |
295.9ms |
Oplog |
LCP Mean |
1724.7ms |
2706.9ms |
CS |
LCP Median |
441.5ms |
713.5ms |
CS |
TTFB Mean |
194.4ms |
196.5ms |
CS |
TTFB Median |
29.1ms |
7.6ms |
Oplog |
Resource Usage
Resource |
Change Streams |
Oplog |
Best |
App CPU (avg) |
21.43% |
31.05% |
CS |
App Memory (avg) |
164 MB |
179 MB |
CS |
DB CPU (avg) |
0.26% |
0.25% |
~ tie |
DB Memory (avg) |
39.1 MB |
41.3 MB |
CS |
Glossary of Performance Metrics
Acronym |
Full Term |
Description |
FCP |
First Contentful Paint |
Time when the first visible element is rendered on the screen. |
LCP |
Largest Contentful Paint |
Time when the largest visible element is rendered (typically main content). |
TTFB |
Time to First Byte |
Time from request initiation until the first byte is received from server. |
FID |
First Input Delay |
Time between a user’s first interaction and the browser’s response. |
Final Verdict
- Oplog wins in early response time (TTFB + initial paint).
- Change Streams is more stable, efficient, and consistent under load.
- Change Streams ~45% less CPU usage and 8% less memory** on average for the app.
- Change Streams is better for scaling and high-load scenarios* .
Current Challenges
While Change Streams is working and reactive, making it fully compatible with Oplog expectations is tough. Many of the core tests fail not due to major bugs but minor semantic differences:
Examples of Issues:
- _id formatting: Meteor sometimes expects a string, but Mongo returns ObjectId.
- Remove events: Change Streams gives the full removed doc; Oplog relies on client cache.
- Updates: Change Streams delivers pre/post update docs; Meteor expects a diff.
Known Broken Tests:
- accounts-2fa - Meteor.loginWithPasswordAnd2faCode() fails with invalid code
- accounts - verify setAdditionalFindUserOnExternalLogin hook can provide user
- most of password_tests.js file tests
- most of server_tests.js file
- livedata - methods with nested stubs
- livedata server - stopping a handle should preserve its context on callbacks (breaking for oplog and change streams)
- ejson - stringify
- mongo-livedata - fuzz test
- mongo-livedata - observe sorted, limited, big initial set
- email_tests.js
- tinytest - observeChanges - single id - basics added
- tinytest - observeChanges - callback isolation
- tinytest - observeChanges - single id - initial adds
- tinytest - observeChanges - unordered - initial adds
- tinytest - observeChanges - unordered - basics
- tinytest - observeChanges - unordered - specific fields
- tinytest - observeChanges - unordered - specific fields + selector on excluded fields
- tinytest - observeChanges - unordered - specific fields + modify on excluded fields
- tinytest - observeChanges - unordered - unset parent of observed field
- tinytest - observeChanges - unordered - enters and exits result set through change
- observeChanges - tailable
Plus, there’s a ~100ms delay before data reaches the client, causing many tests with tight time expectations to fail.
How to Run the Tests
To help us debug and fix failing tests:
git clone git@github.com:meteor/meteor.git
cd meteor
git checkout feat/change-streams
MONGO_URL="mongodb://localhost:27017,localhost:27018,localhost:27019/?replicaSet=rs0" METEOR_REACTIVITY='CHANGE_STREAMS' ./meteor test-packages
You might need to force METEOR_REACTIVITY=‘CHANGE_STREAMS’ in mongo_connection.js due to hot-reload not always preserving env vars:
const canUseChangeStreams = [
function () {
return true; // temporarily override reactivity detection
}
];
Call for Contributors
We’re getting really close — performance is solid, and the architecture is scalable. What we need now is help with:
- Test fixing and standardization
- Patch contributions to MiniMongo/LiveData to adapt to new event structure
- Reviewing existing test failures and proposing changes
If you’re interested in real-time Meteor internals , this is a great opportunity to get involved with the core!