Marketplace
usage-metering
Usage tracking, metering infrastructure, aggregation patterns, and consumption-based billing support for SaaS applications
allowed_tools: Read, Glob, Grep, Task, mcp__perplexity__search, mcp__microsoft-learn__microsoft_docs_search, mcp__context7__query-docs
$ 安裝
git clone https://github.com/melodic-software/claude-code-plugins /tmp/claude-code-plugins && cp -r /tmp/claude-code-plugins/plugins/saas-patterns/skills/usage-metering ~/.claude/skills/claude-code-plugins// tip: Run this command in your terminal to install the skill
SKILL.md
name: usage-metering description: Usage tracking, metering infrastructure, aggregation patterns, and consumption-based billing support for SaaS applications allowed-tools: Read, Glob, Grep, Task, mcp__perplexity__search, mcp__microsoft-learn__microsoft_docs_search, mcp__context7__query-docs
Usage Metering Skill
Guidance for implementing usage tracking and metering infrastructure in SaaS applications.
MANDATORY: Documentation-First Approach
Before implementing usage metering:
- Invoke
docs-managementskill for billing and metering patterns - Verify patterns via MCP servers (perplexity for current best practices, microsoft-learn for Azure patterns)
- Base all guidance on official documentation and current industry standards
When to Use
- Implementing consumption-based pricing
- Tracking API calls, storage, compute, or feature usage
- Building usage dashboards and alerts
- Integrating with billing systems for usage-based charges
Core Concepts
Usage Event Types
Usage Categories:
┌─────────────────────────────────────────────────────────────┐
│ Category │ Examples │ Billing Unit │
├─────────────────┼────────────────────────────┼──────────────┤
│ API Calls │ Requests, queries, ops │ Per 1K calls │
│ Compute │ CPU seconds, GPU hours │ Per hour │
│ Storage │ GB stored, objects │ Per GB/month │
│ Data Transfer │ Egress, ingress bytes │ Per GB │
│ Seats/Users │ Active users, MAU │ Per user │
│ Features │ Reports generated, exports │ Per action │
│ Resources │ Projects, environments │ Per resource │
└─────────────────┴────────────────────────────┴──────────────┘
Metering Patterns
Pattern Selection:
┌──────────────────────────────────────────────────────────────┐
│ Pattern │ Use When │ Trade-offs │
├──────────────────┼───────────────────────────┼──────────────┤
│ Real-time │ Need instant visibility │ Higher cost │
│ Near-real-time │ 5-15 min latency OK │ Balanced │
│ Batch │ Daily/hourly aggregation │ Lower cost │
│ Sampling │ High volume, estimates OK │ Approximate │
│ Hybrid │ Mix of above │ Complex │
└──────────────────┴───────────────────────────┴──────────────┘
Implementation Patterns
Usage Event Model
// Core usage event structure
public sealed record UsageEvent
{
public required Guid EventId { get; init; } = Guid.NewGuid();
public required Guid TenantId { get; init; }
public required string MetricName { get; init; } // e.g., "api.requests"
public required decimal Quantity { get; init; }
public required string Unit { get; init; } // e.g., "count", "bytes"
public required DateTimeOffset Timestamp { get; init; }
public required string Source { get; init; } // e.g., "api-gateway"
public string? UserId { get; init; }
public string? ResourceId { get; init; }
public Dictionary<string, string> Dimensions { get; init; } = [];
public string? IdempotencyKey { get; init; }
}
// Aggregated usage for billing periods
public sealed record UsageAggregate
{
public required Guid TenantId { get; init; }
public required string MetricName { get; init; }
public required DateOnly PeriodStart { get; init; }
public required DateOnly PeriodEnd { get; init; }
public required decimal TotalQuantity { get; init; }
public required string Unit { get; init; }
public required int EventCount { get; init; }
public DateTimeOffset LastUpdated { get; init; }
}
Metering Service Interface
public interface IUsageMeteringService
{
// Record single usage event
Task RecordUsageAsync(
Guid tenantId,
string metricName,
decimal quantity,
string unit,
Dictionary<string, string>? dimensions = null,
string? idempotencyKey = null,
CancellationToken ct = default);
// Record batch of events
Task RecordBatchAsync(
IReadOnlyList<UsageEvent> events,
CancellationToken ct = default);
// Get current period usage
Task<UsageAggregate> GetCurrentUsageAsync(
Guid tenantId,
string metricName,
CancellationToken ct = default);
// Get historical usage
Task<IReadOnlyList<UsageAggregate>> GetUsageHistoryAsync(
Guid tenantId,
string metricName,
DateOnly startDate,
DateOnly endDate,
CancellationToken ct = default);
}
High-Volume Ingestion Pattern
// Buffered writer for high-throughput scenarios
public sealed class BufferedUsageWriter : IAsyncDisposable
{
private readonly Channel<UsageEvent> _channel;
private readonly IUsageEventStore _store;
private readonly Task _processorTask;
public BufferedUsageWriter(
IUsageEventStore store,
int batchSize = 100,
TimeSpan? flushInterval = null)
{
_store = store;
_channel = Channel.CreateBounded<UsageEvent>(
new BoundedChannelOptions(10_000)
{
FullMode = BoundedChannelFullMode.Wait
});
flushInterval ??= TimeSpan.FromSeconds(5);
_processorTask = ProcessEventsAsync(batchSize, flushInterval.Value);
}
public ValueTask RecordAsync(UsageEvent evt, CancellationToken ct = default)
=> _channel.Writer.WriteAsync(evt, ct);
private async Task ProcessEventsAsync(int batchSize, TimeSpan flushInterval)
{
var batch = new List<UsageEvent>(batchSize);
var timer = new PeriodicTimer(flushInterval);
while (await _channel.Reader.WaitToReadAsync())
{
// Drain available events up to batch size
while (batch.Count < batchSize &&
_channel.Reader.TryRead(out var evt))
{
batch.Add(evt);
}
if (batch.Count >= batchSize || await ShouldFlushAsync(timer))
{
await _store.WriteBatchAsync(batch);
batch.Clear();
}
}
// Flush remaining on shutdown
if (batch.Count > 0)
await _store.WriteBatchAsync(batch);
}
}
Architecture Options
Event Streaming Architecture
High-Volume Metering:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ API Gateway │────▶│ Event Hub/ │────▶│ Stream │
│ (emit) │ │ Kafka │ │ Processor │
└─────────────┘ └─────────────┘ └──────┬──────┘
│
┌──────────────────────────┼──────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Time-Series │ │ Aggregates │ │ Alerts │
│ Store │ │ (Redis/SQL) │ │ Engine │
└─────────────┘ └─────────────┘ └─────────────┘
Simple Database Pattern
Low-Volume Metering:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Application │────▶│ Usage Table │────▶│ Background │
│ (record) │ │ (append) │ │ Aggregator │
└─────────────┘ └─────────────┘ └──────┬──────┘
│
▼
┌─────────────┐
│ Aggregates │
│ Table │
└─────────────┘
Quota Enforcement
Quota Check Pattern
public sealed class QuotaEnforcementService(
IUsageMeteringService metering,
IEntitlementService entitlements,
ILogger<QuotaEnforcementService> logger)
{
public async Task<QuotaCheckResult> CheckQuotaAsync(
Guid tenantId,
string metricName,
decimal requestedQuantity,
CancellationToken ct = default)
{
// Get current usage
var currentUsage = await metering.GetCurrentUsageAsync(
tenantId, metricName, ct);
// Get entitlement limit
var limit = await entitlements.GetLimitAsync(
tenantId, metricName, ct);
if (limit is null)
{
return QuotaCheckResult.Unlimited();
}
var projectedUsage = currentUsage.TotalQuantity + requestedQuantity;
if (projectedUsage > limit.HardLimit)
{
logger.LogWarning(
"Quota exceeded for tenant {TenantId}, metric {Metric}",
tenantId, metricName);
return QuotaCheckResult.Denied(
current: currentUsage.TotalQuantity,
limit: limit.HardLimit,
requested: requestedQuantity);
}
if (projectedUsage > limit.SoftLimit)
{
return QuotaCheckResult.Warning(
current: currentUsage.TotalQuantity,
softLimit: limit.SoftLimit,
hardLimit: limit.HardLimit);
}
return QuotaCheckResult.Allowed(
remaining: limit.HardLimit - projectedUsage);
}
}
Rate Limiting vs Quota
Comparison:
┌─────────────────────────────────────────────────────────────┐
│ Aspect │ Rate Limiting │ Quota/Metering │
├─────────────────┼─────────────────────┼────────────────────┤
│ Time Window │ Seconds/minutes │ Hours/days/months │
│ Purpose │ Protect system │ Enforce billing │
│ Enforcement │ Hard block │ Soft/hard limits │
│ Response │ 429 + retry-after │ 402/403 + upgrade │
│ Tracking │ In-memory/Redis │ Durable storage │
│ Precision │ Approximate OK │ Exact required │
└─────────────────┴─────────────────────┴────────────────────┘
Use BOTH:
- Rate limiting: System protection (per-second/minute)
- Quota: Business limits (per-month billing cycles)
References
Load for detailed implementation:
references/event-ingestion.md- High-throughput event capture patternsreferences/aggregation-strategies.md- Time-series aggregation and rollups
For billing integration patterns, see the billing-integration skill.
Related Skills
billing-integration- Payment processing and invoicingentitlements-management- Feature gating and limitssubscription-models- Pricing tier definitions
Last Updated: 2025-12-29
Repository

melodic-software
Author
melodic-software/claude-code-plugins/plugins/saas-patterns/skills/usage-metering
3
Stars
0
Forks
Updated1d ago
Added6d ago