Lesson 35-Deno Performance Monitoring and Debugging

As a modern JavaScript/TypeScript runtime, Deno provides a robust set of tools for performance monitoring and debugging. This article comprehensively explores Deno’s performance optimization and diagnostic methods, covering performance analysis, memory management, network debugging, and integration with debugging tools.

Performance Monitoring System

Built-in Performance Metrics Collection

Key Performance Metrics API:

// Retrieve memory usage
const memoryUsage = Deno.memoryUsage();
/*
{
  rss: 1024 * 1024 * 10, // Resident Set Size (bytes)
  heapTotal: 1024 * 1024 * 5, // Total heap size
  heapUsed: 1024 * 1024 * 3, // Used heap memory
  external: 1024 * 1024 * 2 // External resource usage
}
*/

// CPU usage monitoring (requires system tools)
const start = performance.now();
// Execute code to measure...
const duration = performance.now() - start;
console.log(`Execution time: ${duration.toFixed(2)}ms`);

Resource Monitoring Example:

// Continuous system resource monitoring
setInterval(() => {
  const memory = Deno.memoryUsage();
  const resources = Deno.resources();

  console.log({
    timestamp: new Date().toISOString(),
    memory: {
      rss_mb: (memory.rss / 1024 / 1024).toFixed(2),
      heap_used_mb: (memory.heapUsed / 1024 / 1024).toFixed(2)
    },
    open_resources: Object.keys(resources).length
  });
}, 5000);

Performance Analysis Toolchain

Built-in Performance Analysis Commands:

# CPU performance profiling
deno run --cpu-prof app.ts

# Heap memory profiling
deno run --heap-prof app.ts

# Generate trace file
deno run --trace-ops app.ts

Analysis Tool Integration:

  1. Chrome DevTools:
    • Load generated .cpuprofile and .heapsnapshot files
    • Visualize CPU hotspots and memory allocation
  2. Speedscope:
    • Open-source performance analysis tool
    • Supports multiple performance data formats

Network Performance Monitoring

HTTP Request Timing Statistics:

// Network request performance monitoring
async function fetchWithMetrics(url: string) {
  const start = performance.now();
  try {
    const response = await fetch(url);
    const duration = performance.now() - start;

    console.log({
      url,
      status: response.status,
      duration_ms: duration.toFixed(2),
      size: response.headers.get("content-length") || "unknown"
    });

    return response;
  } catch (err) {
    const duration = performance.now() - start;
    console.error({
      url,
      error: err.message,
      duration_ms: duration.toFixed(2)
    });
    throw err;
  }
}

WebSocket Performance Metrics:

// WebSocket message latency monitoring
const ws = new WebSocket("ws://localhost:8080");
const messageLatencies: number[] = [];

ws.onmessage = (event) => {
  const receiveTime = performance.now();
  const sendTime = parseFloat(event.data); // Assume client sends timestamp
  const latency = receiveTime - sendTime;

  messageLatencies.push(latency);
  if (messageLatencies.length > 100) {
    messageLatencies.shift();
  }

  const avgLatency = messageLatencies.reduce((a, b) => a + b, 0) / messageLatencies.length;
  console.log(`Average latency: ${avgLatency.toFixed(2)}ms`);
};

Memory Debugging Techniques

Memory Leak Detection

Heap Snapshot Comparison Analysis:

# Generate initial heap snapshot
deno run --heap-prof-initial app.ts

# Generate second heap snapshot after operations
deno run --heap-prof app.ts

# Analyze with Chrome DevTools

Common Leak Pattern Detection:

// 1. Global variable leak detection
function detectGlobalLeaks() {
  const initialGlobals = new Set(Object.keys(window));

  // Execute suspicious code...

  const currentGlobals = new Set(Object.keys(window));
  const leaked = [...currentGlobals].filter(g => !initialGlobals.has(g));
  console.log("Possible leaked global variables:", leaked);
}

// 2. Closure reference detection
function detectClosureLeaks() {
  let leakedData;

  function createClosure() {
    const largeData = new Array(1000000).fill("data");
    leakedData = largeData; // Closure retains large object
  }

  createClosure();
  // Check if leakedData is released after execution
}

Memory Optimization Practices

Large Object Handling Strategies:

// Chunked processing of large datasets
async function processLargeData(data: bigint[]) {
  const CHUNK_SIZE = 10000;
  for (let i = 0; i < data.length; i += CHUNK_SIZE) {
    const chunk = data.slice(i, i + CHUNK_SIZE);
    await processChunk(chunk); // Process in chunks
    await new Promise(r => setTimeout(r, 0)); // Yield event loop
  }
}

// Use TypedArray instead of regular array
function createEfficientBuffer(size: number) {
  return new Uint8Array(size); // More efficient memory layout than regular arrays
}

Cache Optimization Example:

// LRU cache implementation
class EfficientLRU<K, V> {
  private cache = new Map<K, V>();
  private capacity: number;

  constructor(capacity: number) {
    this.capacity = capacity;
  }

  get(key: K): V | undefined {
    if (!this.cache.has(key)) return undefined;
    const value = this.cache.get(key)!;
    this.cache.delete(key);
    this.cache.set(key, value); // Update access order
    return value;
  }

  set(key: K, value: V) {
    if (this.cache.has(key)) {
      this.cache.delete(key);
    } else if (this.cache.size >= this.capacity) {
      const oldestKey = this.cache.keys().next().value;
      this.cache.delete(oldestKey);
    }
    this.cache.set(key, value);
  }
}

Network Debugging Techniques

HTTP Debugging Tools

Request/Response Interception:

// Custom HTTP interceptor
const originalFetch = globalThis.fetch;

globalThis.fetch = async (input: RequestInfo, init?: RequestInit) => {
  const start = performance.now();
  const request = new Request(input, init);

  console.log("Request initiated:", {
    url: request.url,
    method: request.method,
    headers: Object.fromEntries(request.headers.entries())
  });

  try {
    const response = await originalFetch(request);
    const duration = performance.now() - start;

    console.log("Response received:", {
      status: response.status,
      headers: Object.fromEntries(response.headers.entries()),
      duration_ms: duration.toFixed(2)
    });

    return response;
  } catch (err) {
    console.error("Request failed:", err);
    throw err;
  }
};

WebSocket Debugging Tool:

// WebSocket message logging
class DebugWebSocket extends WebSocket {
  constructor(url: string | URL, protocols?: string | string[]) {
    super(url, protocols);

    this.onmessage = (event) => {
      console.log("WS message received:", {
        data: event.data,
        timestamp: performance.now()
      });
      if (super.onmessage) super.onmessage(event);
    };

    this.onerror = (event) => {
      console.error("WS error:", event);
      if (super.onerror) super.onerror(event);
    };
  }
}

// Usage example
const ws = new DebugWebSocket("ws://localhost:8080");

Network Performance Optimization

Connection Reuse Strategy:

// HTTP connection pool implementation
class ConnectionPool {
  private pool: Map<string, Deno.HttpConn> = new Map();

  async getConnection(url: string): Promise<Deno.HttpConn> {
    const key = new URL(url).host;
    if (this.pool.has(key)) {
      return this.pool.get(key)!;
    }

    const conn = await Deno.connectTls({
      hostname: new URL(url).hostname,
      port: 443
    });

    this.pool.set(key, conn);
    return conn;
  }

  releaseConnection(url: string) {
    // Actual implementation requires more complex connection management
  }
}

Request Batching:

// Batch request processor
class BatchRequest {
  private queue: Array<{url: string; resolve: (value: any) => void; reject: (reason?: any) => void}> = [];
  private timer: number | null = null;

  constructor(private batchInterval: number = 50) {}

  addRequest(url: string): Promise<any> {
    return new Promise((resolve, reject) => {
      this.queue.push({url, resolve, reject});
      if (!this.timer) {
        this.timer = window.setInterval(() => this.processBatch(), this.batchInterval) as unknown as number;
      }
    });
  }

  private async processBatch() {
    if (this.queue.length === 0) {
      clearInterval(this.timer as number);
      this.timer = null;
      return;
    }

    const batch = [...this.queue];
    this.queue = [];

    try {
      const responses = await Promise.all(
        batch.map(item => fetch(item.url))
      );
      responses.forEach((res, i) => {
        if (res.ok) {
          item.resolve(res.json());
        } else {
          item.reject(new Error(`Request failed: ${res.status}`));
        }
      });
    } catch (err) {
      batch.forEach(item => item.reject(err));
    }
  }
}

Debugging Tool Integration

Chrome DevTools Integration

Debugging Configuration:

# Enable debugging port
deno run --inspect-brk=9229 app.ts

Debugging Workflow:

  1. Open chrome://inspect in Chrome
  2. Configure “Discover network targets”
  3. Click “inspect” to connect to the debugging session

Breakpoint Debugging Techniques:

// Conditional breakpoint example
function processData(data: any) {
  if (data.id === 123) { // Set conditional breakpoint here
    debugger; // Or use Chrome-set breakpoint
  }
  // ...
}

VS Code Debugging Configuration

launch.json Configuration:

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Deno Debug",
      "type": "node",
      "request": "launch",
      "runtimeExecutable": "deno",
      "runtimeArgs": ["run", "--inspect-brk", "--allow-all"],
      "program": "${workspaceFolder}/app.ts",
      "cwd": "${workspaceFolder}",
      "sourceMaps": true,
      "outFiles": ["${workspaceFolder}/**/*.js"]
    }
  ]
}

Advanced Debugging Features:

  1. Conditional Breakpoints: Pause execution on specific conditions
  2. Log Points: Output debug information without pausing
  3. Multi-thread Debugging: Debug main thread and Worker threads simultaneously

Performance Analysis Toolchain

Flamegraph Generation:

# Generate flamegraph with 0x
deno install -n 0x --allow-read --allow-run https://deno.land/x/0x/cli.ts
0x app.ts

Analysis Result Interpretation:

  1. Flamegraph: Identify CPU hotspot functions
  2. Allocation Timeline: Detect memory allocation anomalies
  3. Trace Events: Analyze asynchronous operation flow

Production Environment Monitoring Solutions

Metrics Collection System

Prometheus Integration Example:

// metrics_server.ts
import { serve } from "https://deno.land/std@0.140.0/http/server.ts";
import { Registry, Gauge } from "https://deno.land/x/prometheus/mod.ts";

const registry = new Registry();
const memoryGauge = new Gauge({
  name: "deno_memory_usage_bytes",
  help: "Memory usage in bytes",
  labelNames: ["type"],
  registry,
});

serve(async (req) => {
  if (req.url === "/metrics") {
    memoryGauge.set({ type: "rss" }, Deno.memoryUsage().rss);
    memoryGauge.set({ type: "heap_used" }, Deno.memoryUsage().heapUsed);
    return new Response(registry.metrics());
  }
  return new Response("Deno Metrics Server");
}, { port: 9090 });

Grafana Dashboard Configuration:

  1. Configure Prometheus data source
  2. Import Deno monitoring dashboard template
  3. Set up alert rules

Distributed Tracing

OpenTelemetry Integration:

// tracing.ts
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
import { SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
import { ConsoleSpanExporter } from "@opentelemetry/sdk-trace-console";

const provider = new NodeTracerProvider();
provider.addSpanProcessor(
  new SimpleSpanProcessor(new ConsoleSpanExporter())
);
provider.register();

// Usage in code
import { trace } from "@opentelemetry/api";
const tracer = trace.getTracer("deno-tracer");

tracer.startActiveSpan("operation", (span) => {
  // Business logic...
  span.end();
});

Trace Data Export:

// Export to Jaeger
import { JaegerExporter } from "@opentelemetry/exporter-jaeger";

const exporter = new JaegerExporter({
  endpoint: "http://jaeger:14268/api/traces",
});
provider.addSpanProcessor(new SimpleSpanProcessor(exporter));

Summary and Best Practices

Core Monitoring Metrics Checklist

Metric CategoryKey MetricsMonitoring Frequency
MemoryRSS, Heap Memory, External Memory5 seconds
CPUUser/Kernel Time10 seconds
NetworkRequest Latency, ThroughputReal-time
DiskI/O Wait Time30 seconds
GoroutinesActive Goroutine Count15 seconds
Share your love