Scaling Up without Breaking the Bank › Monitoring Usage and Catching Bottlenecks

Michael BrenndoerferAugust 28, 20253 min read
AI Agent HandbookAll chapters
Part 15 of 15Performance and Cost Optimization - Doing More with Less
Chapter 44Scaling Up without Breaking the Bank › Monitoring Usage and Catching Bottlenecks
Reading section 5 of 13 · Monitoring Usage and Catching Bottlenecks · 3 minSection 5 of 13 · 3 min

Book contents

Section 5 of 13 · 3 min read

Scaling Up without Breaking the Bank › Monitoring Usage and Catching BottlenecksOne section · Sections in this chapter5 / 13 ↓

Scaling isn't just about adding capacity. It's about knowing when and where to add it. This requires monitoring.

What should you monitor? Three key metrics:

Response time: How long does it take to handle a request? If this starts increasing, you're approaching capacity limits.

Request rate: How many requests per minute are you handling? This tells you if usage is growing and helps you plan capacity.

Error rate: How many requests fail? A spike in errors often indicates you've exceeded capacity or hit rate limits.

Here's a simple monitoring setup:

In[2]:
Code
## Using Claude Sonnet 4.5 for agent operations
import time
from collections import deque
from datetime import datetime, timedelta


class AgentMonitor:
    def __init__(self, window_minutes=5):
        self.window = timedelta(minutes=window_minutes)
        self.requests = deque()
        self.errors = deque()
        self.response_times = deque()

    def record_request(self, success=True, response_time=None):
        """Record a request and its outcome"""
        now = datetime.now()

        self.requests.append(now)
        if not success:
            self.errors.append(now)
        if response_time:
            self.response_times.append((now, response_time))

        # Clean old data outside the window
        self._clean_old_data(now)

    def _clean_old_data(self, now):
        """Remove data older than the monitoring window"""
        cutoff = now - self.window

        while self.requests and self.requests[0] < cutoff:
            self.requests.popleft()
        while self.errors and self.errors[0] < cutoff:
            self.errors.popleft()
        while self.response_times and self.response_times[0][0] < cutoff:
            self.response_times.popleft()

    def get_stats(self):
        """Get current statistics"""
        if not self.requests:
            return {
                "requests_per_minute": 0,
                "error_rate": 0,
                "avg_response_time": 0,
            }

        window_minutes = self.window.total_seconds() / 60
        requests_per_minute = len(self.requests) / window_minutes
        error_rate = (
            len(self.errors) / len(self.requests) if self.requests else 0
        )

        if self.response_times:
            avg_time = sum(t for _, t in self.response_times) / len(
                self.response_times
            )
        else:
            avg_time = 0

        return {
            "requests_per_minute": requests_per_minute,
            "error_rate": error_rate,
            "avg_response_time": avg_time,
        }


## Use the monitor
monitor = AgentMonitor(window_minutes=5)

## Process requests and record metrics
for query in queries:
    start = time.time()
    try:
        result = process_request(query)
        elapsed = time.time() - start
        monitor.record_request(success=True, response_time=elapsed)
    except Exception as e:
        elapsed = time.time() - start
        monitor.record_request(success=False, response_time=elapsed)
        print(f"Error: {e}")

## Check stats
stats = monitor.get_stats()
print(f"Requests/min: {stats['requests_per_minute']:.1f}")
print(f"Error rate: {stats['error_rate']:.1%}")
print(f"Avg response time: {stats['avg_response_time']:.2f}s")

This monitor tracks requests over a rolling time window. You can check statistics at any time to see how your agent is performing.

When should you scale up? Watch for these warning signs:

Response times increase: If your average response time starts climbing from 2 seconds to 4 seconds to 6 seconds, you're running out of capacity. Time to add more workers or instances.

Error rates spike: If errors jump from 1% to 5% or 10%, you're likely hitting rate limits or overwhelming your servers.

Request queues grow: If you're using a queue to buffer requests, watch its length. A growing queue means requests are arriving faster than you can process them.

In production systems, you'd typically send these metrics to a monitoring service like Datadog, Prometheus, or CloudWatch. These tools can alert you when metrics cross thresholds, so you can respond before users notice problems.