Skip to main content

Live Query Monitoring

SkySignal monitors Meteor's reactive query system -- the observers that keep your client-side data in sync with MongoDB. This is one of the most important things to keep an eye on in a Meteor app, because observer leaks and inefficient driver usage are common sources of memory and performance problems.

What Gets Tracked

For each observer in your application, SkySignal captures:

FieldDescription
observerTypeDriver type: changeStream, oplog, or polling
observerCountTotal number of active observers
documentsUpdatedDocument update rates per observer
reactiveEfficiencyPercentage of observers using efficient drivers
timestampWhen the snapshot was taken

The key metric here is reactive efficiency: the percentage of your observers that use an efficient real-time driver (change streams or oplog tailing) rather than polling.

How It Works

Observer Driver Detection

The agent inspects each observer's internal driver to determine how it receives updates from MongoDB. It does this by looking at:

handle._multiplexer._observeDriver.constructor.name

This gives the agent the driver class name, which maps to one of three observer types:

Observer TypeDriverMeteor VersionEfficiency
changeStreamMongoDB Change Streams3.5+Best
oplogOplog tailing3.0+Good
pollingPeriodic polling3.0+Least efficient

Meteor 3.5+ Change Streams

Starting with Meteor 3.5, Meteor can use MongoDB Change Streams as the reactive driver instead of oplog tailing. Change Streams are the most efficient method -- they use MongoDB's built-in change notification system rather than tailing the oplog directly.

The agent automatically detects change stream usage. If you see changeStream observers in your dashboard, your app is taking advantage of this.

Requirements for Change Streams:

  • Meteor 3.5 or later
  • MongoDB replica set (standalone MongoDB instances don't support Change Streams)

Oplog vs Polling Fallback

For apps running Meteor 3.0 through 3.4 (or 3.5+ without a replica set), the agent checks for the MONGO_OPLOG_URL environment variable to distinguish between oplog tailing and polling:

  • If MONGO_OPLOG_URL is set and the driver matches an oplog driver, the observer is classified as oplog
  • Otherwise, it falls back to polling

Configuration

Live query monitoring is controlled by a single setting:

{
"skysignal": {
"collectLiveQueries": true
}
}
OptionTypeDefaultDescription
collectLiveQueriesBooleantrueEnable or disable live query monitoring

There is no sampling option for live queries. The agent takes periodic snapshots of observer state, and the overhead is minimal since it is reading in-memory data structures rather than generating new events.

Dashboard

Navigate to your site's Live Queries tab to see the monitoring data.

Observer Overview

At the top you will see summary cards:

  • Total Observers - Current count of active observers across your app
  • Reactive Efficiency - The percentage of observers using efficient drivers
  • Observer Breakdown - Count by type (change stream, oplog, polling)

Driver Statistics

The Driver Statistics card shows per-driver metrics:

  • Observer count for each driver type
  • Document update rates per driver
  • Relative efficiency comparison

Trend Charts

The trend charts show observer counts over time. This is where you spot problems:

  • A steadily growing observer count without corresponding subscriber growth usually means you have an observer leak
  • A sudden spike in polling observers might mean your oplog connection dropped
  • A shift from oplog to change stream after a Meteor upgrade is expected and good

Reactive Efficiency

Reactive efficiency is calculated as:

reactiveEfficiency = (changeStreamObservers + oplogObservers) / totalObservers

This tells you what percentage of your observers are using real-time drivers. The rest are polling, which means they are periodically re-running the query to check for changes.

What to aim for:

EfficiencyAssessment
90%+Good. Most of your observers are real-time.
70-90%Acceptable, but worth investigating the polling observers.
Below 70%You likely have a configuration issue. Check your MongoDB setup.

A few things can cause low reactive efficiency:

  • No replica set - Oplog tailing and Change Streams both require a MongoDB replica set. If you are running a standalone MongoDB instance, all observers will fall back to polling.
  • Missing MONGO_OPLOG_URL - On Meteor versions before 3.5, you need this environment variable set for oplog tailing to work.
  • Unsupported query operators - Some MongoDB query operators are not compatible with oplog tailing. Queries using $where, $near, or certain aggregation features will fall back to polling.

Change Streams (Meteor 3.5+)

If your app runs Meteor 3.5 or later with a MongoDB replica set, you get Change Streams support automatically. This is the most efficient reactive driver available.

Change Streams advantages over oplog tailing:

  • Lower overhead - MongoDB pushes changes directly rather than the app tailing the oplog
  • Better filtering - Change Streams can filter at the database level, reducing the data your app processes
  • More reliable - Less sensitive to oplog size and retention settings

The agent detects change stream observers automatically. After upgrading to Meteor 3.5+, you should see your observer types shift from oplog to changeStream in the dashboard.

If you have upgraded to 3.5+ but still see mostly oplog observers, make sure:

  1. Your MongoDB is running as a replica set
  2. You are using a MongoDB version that supports Change Streams (4.0+)
  3. Your app has restarted after the Meteor upgrade

Best Practices

Aim for high reactive efficiency

Minimize the number of polling observers. If your reactive efficiency is below 90%, check your MongoDB setup first -- a missing replica set is the most common cause.

Watch for observer leaks

An observer leak happens when subscriptions are created but never cleaned up. Common causes:

  • Components that subscribe in useEffect but don't return a cleanup function
  • Subscriptions created in Tracker.autorun that are never stopped
  • Server-side observers started with observeChanges that are never stopped

The trend chart in the Live Queries tab is your best tool here. If observer count keeps climbing and never comes back down, you have a leak.

// Good: cleanup on unmount
useEffect(() => {
const handle = Meteor.subscribe('posts');
return () => handle.stop();
}, []);

// Bad: no cleanup
useEffect(() => {
Meteor.subscribe('posts'); // This observer leaks on unmount
}, []);

Upgrade to Meteor 3.5+ for Change Streams

If you are still on Meteor 3.0-3.4 and using oplog tailing, consider upgrading to 3.5+ to take advantage of Change Streams. The agent will automatically detect and report the improved driver usage.

Ensure your MongoDB is a replica set

Both oplog tailing and Change Streams require a replica set. If you are developing locally with a standalone MongoDB instance, all your observers will show as polling. This is expected in local dev, but make sure your staging and production environments use replica sets.

For local development, you can convert to a single-node replica set:

mongosh --eval "rs.initiate()"

Troubleshooting

All Observers Show as Polling

  1. Check if your MongoDB is running as a replica set (rs.status() in mongosh)
  2. Verify MONGO_OPLOG_URL is set (for Meteor versions before 3.5)
  3. Make sure the agent can access the oplog database

Observer Count Keeps Growing

This is an observer leak. Check your client-side code for subscriptions that are not being cleaned up. The most common culprit is useEffect hooks that call Meteor.subscribe without returning a stop function.

Reactive Efficiency Dropped Suddenly

A sudden drop usually means your oplog or change stream connection was lost. Check:

  1. MongoDB replica set health
  2. Network connectivity between your app and MongoDB
  3. Oplog size -- if the oplog is too small and wraps around, Meteor loses its place and falls back to polling

Live Queries Not Appearing

  1. Verify collectLiveQueries is true (or not set -- it defaults to true)
  2. Make sure your app actually has active subscriptions (no subscriptions means no observers)
  3. Check that the agent is initialized and connected

Next Steps