Features
Docs
CLI
Benchmarks
Examples

© 2024 MoroJs

Object Pooling

Boost performance with MoroJS's built-in object pooling system. Reduce garbage collection pressure and improve throughput by reusing objects instead of creating new ones.

Performance That Scales

Object pooling reduces garbage collection overhead and memory allocation time by reusing objects instead of creating and destroying them repeatedly.

Performance Impact

Without Pooling

  • • Requests/sec: 15,000
  • • Avg latency: 6.5ms
  • • GC time: 15% of CPU
  • • Memory: 250MB

With Pooling

  • • Requests/sec: 45,000 (+200%)
  • • Avg latency: 2.2ms (-66%)
  • • GC time: 3% of CPU (-80%)
  • • Memory: 180MB (-28%)

Quick Example

Object Pooling in Action

typescript

1import { createApp, ObjectPoolManager } from '@morojs/moro';
2
3const app = createApp();
4const poolManager = ObjectPoolManager.getInstance();
5
6// Pre-warm pools for better performance
7poolManager.preWarm({
8  params: 500,
9  query: 500,
10  headers: 200,
11  buffers: { 1024: 100, 4096: 50 }
12});
13
14app.get('/api/search', async (req, res) => {
15  // Query params are automatically pooled
16  const { q, page = 1, limit = 10 } = req.query;
17
18  // Use buffer pool for response
19  const buffer = poolManager.acquireBuffer(4096);
20
21  try {
22    const results = await search(q, page, limit);
23    const json = JSON.stringify(results);
24    buffer.write(json);
25
26    res.setHeader('Content-Type', 'application/json');
27    res.end(buffer.slice(0, json.length));
28  } finally {
29    poolManager.releaseBuffer(buffer, 4096);
30  }
31});
32
33await app.listen(3000);

Built-in Pools

Automatic Pooling

MoroJS automatically manages several object pools for common use cases:

Parameter Object Pool

Reuses objects for route parameters (req.params).

// Automatically pooled
app.get('/users/:userId/posts/:postId', (req, res) => {
  // req.params is from the pool
  const { userId, postId } = req.params;
  res.json({ userId, postId });
});

Query Object Pool

Reuses objects for query string parameters (req.query).

// Automatically pooled
app.get('/search', (req, res) => {
  // req.query is from the pool
  const { q, page, limit } = req.query;
  res.json({ results: search(q, page, limit) });
});

Buffer Pools

Reuses buffers for various sizes (64, 256, 1024, 4096, 16384 bytes).

const poolManager = ObjectPoolManager.getInstance();

// Acquire a buffer
const buffer = poolManager.acquireBuffer(1024);

// Use buffer
buffer.write('Hello World');

// Release back to pool
poolManager.releaseBuffer(buffer, 1024);

Route & Response Cache

Caches route lookups and complete responses with LRU eviction.

// Route lookups are automatically cached
app.get('/api/v1/users/:id', handler1);
app.get('/api/v1/posts/:id', handler2);

// Subsequent requests hit the cache
// GET /api/v1/users/123 - cache hit
// GET /api/v1/users/456 - cache hit

Custom Pools

Creating Custom Object Pools

typescript

1import { ObjectPool } from '@morojs/moro';
2
3// Create a custom object pool
4class RequestContext {
5  userId?: string;
6  requestId?: string;
7  timestamp?: number;
8}
9
10const contextPool = new ObjectPool<RequestContext>(
11  // Factory function
12  () => new RequestContext(),
13  // Max pool size
14  200,
15  // Reset function
16  (ctx) => {
17    ctx.userId = undefined;
18    ctx.requestId = undefined;
19    ctx.timestamp = undefined;
20  }
21);
22
23// Use the pool
24const ctx = contextPool.acquire();
25ctx.userId = req.user.id;
26ctx.requestId = req.id;
27ctx.timestamp = Date.now();
28
29// ... use context ...
30
31contextPool.release(ctx); // Returns to pool

LRU Cache

typescript

1import { LRUCache } from '@morojs/moro';
2
3// Create LRU cache
4const cache = new LRUCache<string, any>(1000); // Max 1000 items
5
6// Set values
7cache.set('user:123', { name: 'John', email: 'john@example.com' });
8
9// Get values
10const user = cache.get('user:123');
11
12// Check existence
13if (cache.has('user:123')) {
14  // User is cached
15}
16
17// Cache statistics
18const stats = cache.stats;
19console.log({
20  size: stats.size,
21  maxSize: stats.maxSize,
22  hits: stats.hits,
23  misses: stats.misses,
24  hitRate: stats.hitRate
25});

Monitoring and Statistics

Global Statistics

typescript

1const poolManager = ObjectPoolManager.getInstance();
2
3// Get comprehensive statistics
4const stats = poolManager.getStats();
5
6console.log('Object Pooling Statistics:', {
7  // Parameter pool
8  params: stats.paramPool,
9
10  // Query pool
11  query: stats.queryPool,
12
13  // Header pool
14  headers: stats.headerPool,
15
16  // Buffer pools
17  buffers: stats.bufferPools,
18
19  // Caches
20  routeCache: stats.routeCache,
21  responseCache: stats.responseCache,
22
23  // Performance
24  performance: stats.performance
25});

Monitoring Endpoint

typescript

1app.get('/metrics/pools', (req, res) => {
2  const poolManager = ObjectPoolManager.getInstance();
3  const stats = poolManager.getStats();
4
5  res.json({
6    timestamp: Date.now(),
7    pools: {
8      params: {
9        size: stats.paramPool.poolSize,
10        utilization: `${(stats.paramPool.utilization * 100).toFixed(1)}%`,
11        acquisitions: stats.paramPool.acquireCount,
12        releases: stats.paramPool.releaseCount
13      },
14      query: {
15        size: stats.queryPool.poolSize,
16        utilization: `${(stats.queryPool.utilization * 100).toFixed(1)}%`,
17        acquisitions: stats.queryPool.acquireCount,
18        releases: stats.queryPool.releaseCount
19      },
20      routeCache: {
21        size: stats.routeCache.size,
22        hitRate: `${(stats.routeCache.hitRate * 100).toFixed(1)}%`,
23        hits: stats.routeCache.hits,
24        misses: stats.routeCache.misses
25      }
26    }
27  });
28});

Adaptive Pool Sizing

typescript

1// Enable adaptive mode (enabled by default)
2poolManager.setAdaptiveMode(true);
3
4// Get adaptive sizing recommendations
5const recommendations = poolManager.getAdaptiveSizingRecommendations();
6
7console.log('Pool Size Recommendations:', recommendations);
8// {
9//   paramPool: { current: 100, recommended: 250 },
10//   queryPool: { current: 100, recommended: 150 },
11//   ...
12// }
13
14// Apply recommendations automatically
15poolManager.applyAdaptiveSizing();
16
17// MoroJS automatically adjusts pool sizes based on:
18// - Usage patterns over time
19// - Peak utilization
20// - Average acquisition rate
21// - Memory constraints

API Reference

ObjectPoolManager Methods

getInstance(): ObjectPoolManager
acquireParamObject(): Record<string, string>
releaseParamObject(obj): void
acquireQueryObject(): Record<string, string>
releaseQueryObject(obj): void
acquireBuffer(size: number): Buffer
releaseBuffer(buffer: Buffer, size: number): void
getRouteCache(key: string): any
setRouteCache(key: string, value: any): void
getResponseCache(key: string): any
setResponseCache(key: string, value: any): void
preWarm(config: PreWarmConfig): void
getStats(): PoolStats
setAdaptiveMode(enabled: boolean): void
getAdaptiveSizingRecommendations(): Recommendations
applyAdaptiveSizing(): void

ObjectPool<T> Methods

constructor(factory, maxSize, reset?)  // Create pool
acquire(): T                            // Get object from pool
release(obj: T): void                   // Return object to pool
clear(): void                           // Clear all objects
get size(): number                      // Current pool size
get stats()                             // Get pool statistics

LRUCache<K, V> Methods

constructor(maxSize: number)            // Create cache
get(key: K): V | undefined              // Get value
set(key: K, value: V): void             // Set value
has(key: K): boolean                    // Check existence
delete(key: K): boolean                 // Delete entry
clear(): void                           // Clear cache
get size(): number                      // Current size
get stats()                             // Get statistics

Best Practices

Always Release Objects

// Good: Always release
const params = poolManager.acquireParamObject();
try {
  await process(params);
} finally {
  poolManager.releaseParamObject(params);
}

Pre-warm Pools

// Pre-warm before high traffic
poolManager.preWarm({
  params: 1000,
  query: 500,
  buffers: {
    1024: 200,
    4096: 100
  }
});

await app.listen(3000);

Choose Appropriate Pool Sizes

// Size based on traffic
const isProduction = process.env.NODE_ENV === 'production';

poolManager.preWarm({
  params: isProduction ? 2000 : 100,
  query: isProduction ? 1000 : 50
});

Monitor Pool Utilization

setInterval(() => {
  const stats = poolManager.getStats();

  if (stats.paramPool.utilization > 0.9) {
    console.warn('Parameter pool nearly exhausted!');
  }

  if (stats.paramPool.utilization < 0.1) {
    console.info('Parameter pool underutilized');
  }
}, 60000); // Check every minute

Next Steps