Skip to main content

Performance Optimization

Learn how to use SkySignal to identify and fix performance bottlenecks.

Finding Slow Methods

Using the Methods Tab

  1. Navigate to your site's Methods tab
  2. Sort by Avg Response Time (descending)
  3. Look for methods with high P95/P99 values

Key Metrics to Watch

MetricWarning ThresholdCritical Threshold
Avg Response Time> 200ms> 500ms
P95 Response Time> 500ms> 1000ms
Error Rate> 1%> 5%

Common Performance Issues

1. N+1 Query Problems

Symptoms:

  • High method response times
  • Many database queries per request

Solution:

// ❌ Bad: N+1 queries
const posts = Posts.find().fetch();
posts.forEach(post => {
post.author = Users.findOne(post.authorId);
});

// ✅ Good: Batch query
const posts = Posts.find().fetch();
const authorIds = [...new Set(posts.map(p => p.authorId))];
const authors = Users.find({ _id: { $in: authorIds } }).fetch();
const authorsMap = Object.fromEntries(authors.map(a => [a._id, a]));
posts.forEach(post => {
post.author = authorsMap[post.authorId];
});

2. Missing Database Indexes

Symptoms:

  • Slow queries on large collections
  • High CPU usage on database

Solution:

// Add indexes for frequently queried fields
Posts.createIndex({ authorId: 1 });
Posts.createIndex({ createdAt: -1 });
Posts.createIndex({ status: 1, createdAt: -1 });

3. Blocking Operations

Symptoms:

  • Event loop lag spikes
  • Slow response times across all methods

Solution:

// ❌ Bad: Blocking the event loop
const result = heavyComputation(data);

// ✅ Good: Use this.unblock() in methods
Meteor.methods({
'posts.process'() {
this.unblock(); // Allow other methods to run
return heavyComputation(data);
}
});

4. Memory Leaks

Symptoms:

  • Memory usage continuously increasing
  • Periodic slowdowns or crashes

Common Causes:

  • Unreleased subscriptions
  • Growing global caches
  • Event listeners not cleaned up

Optimization Strategies

Method Optimization

Meteor.methods({
'posts.list'(options = {}) {
this.unblock();

const { limit = 20, skip = 0 } = options;

// Use projection to limit returned fields
return Posts.find(
{ status: 'published' },
{
fields: { title: 1, summary: 1, createdAt: 1 },
sort: { createdAt: -1 },
limit,
skip
}
).fetch();
}
});

Publication Optimization

Meteor.publish('posts.recent', function(limit = 10) {
// Limit fields sent to client
return Posts.find(
{ status: 'published' },
{
fields: { title: 1, summary: 1, authorId: 1 },
sort: { createdAt: -1 },
limit
}
);
});

Caching Strategies

import { Meteor } from 'meteor/meteor';

// Simple in-memory cache
const cache = new Map();
const CACHE_TTL = 60000; // 1 minute

function getCachedOrFetch(key, fetchFn) {
const cached = cache.get(key);
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
return cached.data;
}

const data = fetchFn();
cache.set(key, { data, timestamp: Date.now() });
return data;
}

Meteor.methods({
'stats.dashboard'() {
return getCachedOrFetch('dashboard-stats', () => {
return computeExpensiveStats();
});
}
});

Setting Up Alerts

Create alerts for performance degradation:

  1. Go to SettingsAlerts
  2. Add alert for Method Response Time
  3. Set threshold (e.g., P95 > 500ms)
  4. Configure notification channel

Performance Budgets

Define performance budgets to track over time:

MetricBudget
Method P95< 300ms
Subscription Ready Time< 500ms
Error Rate< 1%
Memory Usage< 512MB

Next Steps