npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bernierllc/retry-metrics

v0.2.0

Published

Atomic retry metrics collection and analysis utilities

Readme

@bernierllc/retry-metrics

Atomic retry metrics and analytics utilities for the BernierLLC package suite.

Overview

@bernierllc/retry-metrics provides comprehensive metrics collection, analytics, and performance monitoring for retry operations. It's designed to be a lightweight, atomic package that can be used independently or as part of larger retry systems.

Features

  • Real-time Metrics Collection: Track retry attempts, success rates, and performance metrics
  • Performance Scoring: Calculate performance scores with customizable weights and grades
  • Analytics & Reporting: Generate comprehensive reports with distributions and trends
  • Alert System: Configurable alerts for performance thresholds
  • Storage Adapters: In-memory and Redis storage options
  • Event System: Subscribe to metrics events for real-time monitoring
  • Time-based Aggregation: Aggregate metrics by minute, hour, day, week, or month
  • Custom Metrics: Add application-specific metrics alongside standard ones

Installation

npm install @bernierllc/retry-metrics

Quick Start

import { MetricsCollector, createMetricsStorage } from '@bernierllc/retry-metrics';

// Create a metrics collector
const collector = new MetricsCollector({
  enabled: true,
  maxStoredAttempts: 1000
});

// Record retry attempts
collector.recordAttemptStart('operation-1', 1);
// ... perform retry operation ...
collector.recordAttemptEnd('operation-1', 1, true, 150);

// Get current metrics
const metrics = collector.getMetrics();
console.log(`Success rate: ${(metrics.successfulRetries / metrics.totalRetries * 100).toFixed(1)}%`);

// Calculate performance score
const score = collector.calculatePerformanceScore();
console.log(`Performance grade: ${score.grade} (${(score.score * 100).toFixed(1)}%)`);

API Reference

MetricsCollector

The main class for collecting and managing retry metrics.

Constructor

new MetricsCollector(config: MetricsCollectorConfig)

Configuration Options:

  • enabled: Whether metrics collection is enabled (default: true)
  • storage: Optional storage adapter for persistence
  • performanceWeights: Custom weights for performance scoring
  • maxStoredAttempts: Maximum attempts to store in memory (default: 1000)
  • cleanupInterval: Auto-cleanup interval in milliseconds (default: 60000)

Methods

recordAttemptStart(id: string, attempt: number, metadata?: Record<string, unknown>): void

Record the start of a retry attempt.

collector.recordAttemptStart('api-call-1', 1, { endpoint: '/users' });
recordAttemptEnd(id: string, attempt: number, success: boolean, duration: number, error?: string): void

Record the completion of a retry attempt.

collector.recordAttemptEnd('api-call-1', 1, true, 150);
// or for failed attempts
collector.recordAttemptEnd('api-call-1', 1, false, 200, 'Network timeout');
getMetrics(): RetryMetrics

Get current metrics snapshot.

const metrics = collector.getMetrics();
console.log(`Total retries: ${metrics.totalRetries}`);
console.log(`Success rate: ${(metrics.successfulRetries / metrics.totalRetries * 100).toFixed(1)}%`);
calculatePerformanceScore(weights?: Partial<PerformanceWeights>): PerformanceScore

Calculate overall performance score.

const score = collector.calculatePerformanceScore();
console.log(`Grade: ${score.grade}, Score: ${(score.score * 100).toFixed(1)}%`);
console.log('Recommendations:', score.recommendations);
on(eventType: string, listener: MetricsEventListener): void

Subscribe to metrics events.

collector.on('attempt_completed', (event) => {
  console.log(`Attempt ${event.data.id} completed: ${event.data.success}`);
});

collector.on('metrics_updated', (event) => {
  console.log('Metrics updated:', event.data.metrics);
});
updateMemoryUsage(usage: number): void

Update current memory usage.

collector.updateMemoryUsage(process.memoryUsage().heapUsed);
addCustomMetric(key: string, value: number): void

Add custom application-specific metrics.

collector.addCustomMetric('api_latency_p95', 250);
collector.addCustomMetric('cache_hit_rate', 0.85);

Storage Adapters

Memory Storage

import { MemoryMetricsStorage } from '@bernierllc/retry-metrics';

const storage = new MemoryMetricsStorage(1000); // Max 1000 attempts

Redis Storage

import { RedisMetricsStorage } from '@bernierllc/retry-metrics';
import Redis from 'ioredis';

const redis = new Redis();
const storage = new RedisMetricsStorage(redis, 'retry:metrics:', 86400);

Factory Function

import { createMetricsStorage } from '@bernierllc/retry-metrics';

// Memory storage
const memoryStorage = createMetricsStorage('memory', { maxAttempts: 1000 });

// Redis storage
const redisStorage = createMetricsStorage('redis', { 
  redisClient: redis,
  prefix: 'retry:metrics:',
  ttl: 86400
});

Analytics Utilities

Basic Calculations

import { 
  calculateSuccessRate, 
  calculateFailureRate,
  calculateThroughput,
  calculateMedianRetryTime,
  calculate95thPercentileRetryTime
} from '@bernierllc/retry-metrics';

const successRate = calculateSuccessRate(metrics);
const failureRate = calculateFailureRate(metrics);
const throughput = calculateThroughput(attempts, 60000); // ops per minute
const medianTime = calculateMedianRetryTime(attempts);
const p95Time = calculate95thPercentileRetryTime(attempts);

Distributions

import { 
  calculateErrorDistribution,
  calculateHourlyDistribution,
  calculateDailyDistribution
} from '@bernierllc/retry-metrics';

const errorDist = calculateErrorDistribution(attempts);
const hourlyDist = calculateHourlyDistribution(attempts);
const dailyDist = calculateDailyDistribution(attempts);

Aggregation

import { aggregateMetrics } from '@bernierllc/retry-metrics';

const hourlyMetrics = await aggregateMetrics(
  attempts,
  startTime,
  endTime,
  'hour'
);

Performance Scoring

import { calculatePerformanceScore } from '@bernierllc/retry-metrics';

const score = calculatePerformanceScore(metrics, {
  successRate: 0.4,
  averageTime: 0.3,
  memoryUsage: 0.2,
  throughput: 0.1
});

Report Generation

import { generateMetricsReport } from '@bernierllc/retry-metrics';

const report = generateMetricsReport(metrics, attempts, {
  start: new Date('2025-01-01T00:00:00Z'),
  end: new Date('2025-01-01T23:59:59Z')
});

console.log('Summary:', report.summary);
console.log('Performance:', report.performance);
console.log('Distributions:', report.distribution);
console.log('Trends:', report.trends);

Types

RetryMetrics

interface RetryMetrics {
  totalRetries: number;
  successfulRetries: number;
  failedRetries: number;
  averageRetryTime: number;
  memoryUsage: number;
  lastUpdated: Date;
  customMetrics?: Record<string, number>;
}

RetryAttempt

interface RetryAttempt {
  id: string;
  attempt: number;
  success: boolean;
  duration: number;
  error?: string;
  startTime: Date;
  endTime: Date;
  metadata?: Record<string, unknown>;
}

PerformanceScore

interface PerformanceScore {
  score: number; // 0-1
  components: {
    successRate: number;
    averageTime: number;
    memoryUsage: number;
    throughput: number;
  };
  grade: 'A' | 'B' | 'C' | 'D' | 'F';
  recommendations: string[];
}

Alert

interface Alert {
  type: 'success_rate' | 'average_time' | 'memory_usage' | 'performance_score';
  severity: 'warning' | 'critical';
  message: string;
  currentValue: number;
  thresholdValue: number;
  timestamp: Date;
  context?: Record<string, unknown>;
}

Examples

Basic Usage with Alerts

import { MetricsCollector } from '@bernierllc/retry-metrics';

const collector = new MetricsCollector({
  enabled: true,
  onAlert: (alert) => {
    console.log(`[${alert.severity.toUpperCase()}] ${alert.message}`);
    // Send to monitoring system, Slack, etc.
  }
});

// Configure alert thresholds
collector.configureAlerts({
  enabled: true,
  thresholds: {
    minSuccessRate: 0.8,
    maxAverageTime: 5000,
    maxMemoryUsage: 100 * 1024 * 1024, // 100MB
    minPerformanceScore: 0.7
  }
});

Integration with Retry System

import { MetricsCollector } from '@bernierllc/retry-metrics';

class RetryManager {
  private metrics: MetricsCollector;

  constructor() {
    this.metrics = new MetricsCollector({
      enabled: true,
      maxStoredAttempts: 10000
    });
  }

  async executeWithRetry<T>(id: string, fn: () => Promise<T>, maxRetries: number = 3): Promise<T> {
    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      const startTime = Date.now();
      
      this.metrics.recordAttemptStart(id, attempt);
      
      try {
        const result = await fn();
        const duration = Date.now() - startTime;
        
        this.metrics.recordAttemptEnd(id, attempt, true, duration);
        return result;
      } catch (error) {
        const duration = Date.now() - startTime;
        
        this.metrics.recordAttemptEnd(id, attempt, false, duration, error.message);
        
        if (attempt === maxRetries) {
          throw error;
        }
        
        // Wait before retry
        await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000));
      }
    }
  }

  getMetrics() {
    return this.metrics.getMetrics();
  }

  getPerformanceScore() {
    return this.metrics.calculatePerformanceScore();
  }
}

Redis Storage with Persistence

import { MetricsCollector, createMetricsStorage } from '@bernierllc/retry-metrics';
import Redis from 'ioredis';

const redis = new Redis();
const storage = createMetricsStorage('redis', { 
  redisClient: redis,
  prefix: 'myapp:retry:',
  ttl: 86400 * 7 // 7 days
});

const collector = new MetricsCollector({
  enabled: true,
  storage,
  maxStoredAttempts: 5000
});

// Metrics will be automatically persisted to Redis
collector.recordAttemptStart('api-call', 1);
collector.recordAttemptEnd('api-call', 1, true, 150);

// Retrieve historical metrics
const historicalMetrics = await storage.getMetrics(
  new Date('2025-01-01T00:00:00Z'),
  new Date('2025-01-01T23:59:59Z')
);

Custom Metrics and Monitoring

import { MetricsCollector } from '@bernierllc/retry-metrics';

const collector = new MetricsCollector({
  enabled: true
});

// Add custom metrics
collector.addCustomMetric('api_latency_p95', 250);
collector.addCustomMetric('cache_hit_rate', 0.85);
collector.addCustomMetric('database_connections', 12);

// Subscribe to events for monitoring
collector.on('metrics_updated', (event) => {
  const metrics = event.data.metrics;
  
  // Send to monitoring system
  monitoringSystem.record('retry.success_rate', metrics.successfulRetries / metrics.totalRetries);
  monitoringSystem.record('retry.average_time', metrics.averageRetryTime);
  monitoringSystem.record('retry.memory_usage', metrics.memoryUsage);
  
  // Log custom metrics
  Object.entries(metrics.customMetrics || {}).forEach(([key, value]) => {
    monitoringSystem.record(`retry.custom.${key}`, value);
  });
});

collector.on('performance_score_calculated', (event) => {
  const score = event.data.performanceScore;
  
  if (score.grade === 'F') {
    // Send critical alert
    alertSystem.sendCritical('Retry performance is critically low', score);
  }
});

Performance Considerations

  • Memory Usage: The in-memory storage keeps attempts in memory. Use maxStoredAttempts to limit memory usage.
  • Redis Performance: Redis storage uses sorted sets for time-based queries. Consider Redis memory limits and TTL settings.
  • Event Listeners: Remove event listeners when no longer needed to prevent memory leaks.
  • Cleanup: Use clearOldMetrics() to remove old data and free memory.

Testing

import { MetricsCollector } from '@bernierllc/retry-metrics';

describe('Retry Metrics', () => {
  let collector: MetricsCollector;

  beforeEach(() => {
    collector = new MetricsCollector({ enabled: true });
  });

  afterEach(() => {
    collector.destroy();
  });

  it('should track retry attempts', () => {
    collector.recordAttemptStart('test-1', 1);
    collector.recordAttemptEnd('test-1', 1, true, 100);

    const metrics = collector.getMetrics();
    expect(metrics.totalRetries).toBe(1);
    expect(metrics.successfulRetries).toBe(1);
  });
});

License

MIT License - see LICENSE file for details.

Contributing

This package is part of the BernierLLC tools suite. Please refer to the main project documentation for contribution guidelines.