Skip to main content

Implement XaaS Resource Metering

This guide explains how to implement resource metering and hierarchical quota enforcement in Framework M. This is essential for building XaaS (X-as-a-Service) platforms where you need to track usage (e.g., AI tokens, API calls, storage) and enforce billing limits across complex tenant hierarchies.


1. Recording Usage via MeteringProtocol

The MeteringProtocol is the core "Writer" for usage facts. In your service logic, you don't worry about where the data goes; you simply emit a UsageEvent.

Example: Metering an AI Completion

from framework_m_core.interfaces.metering import MeteringProtocol, UsageEvent
from datetime import datetime

class AIService:
def __init__(self, meter: MeteringProtocol):
self.meter = meter

async def generate_text(self, tenant_id: str, prompt: str):
# 1. Perform the work
response = await self.llm.call(prompt)
tokens_used = len(response.split())

# 2. Record the usage fact
await self.meter.record_usage(UsageEvent(
tenant_id=tenant_id,
feature_name="llm_tokens",
quantity=tokens_used,
timestamp=datetime.utcnow(),
metadata={"model": "gpt-4"}
))

return response

2. Enforcing Hierarchical Quotas

Quotas leverage the TenantContext.lineage to check limits at multiple levels of the hierarchy (e.g., a "Project" quota inside an "Organization" quota).

The "Zero-Trust" Quota Check

Framework M includes a QuotaPolicyHandler that automatically checks metering facts before critical actions.

  1. Define the Quota: Set a limit in your Control Plane or configuration.
  2. The Check: When a user tries to consume more resources:
    • The framework resolves the lineage (e.g., ["org-1", "project-a"]).
    • It calls get_usage() for both org-1 and project-a.
    • If either exceeds their respective quota, the action is blocked with a QuotaExceeded exception.

3. Scaling to Hyperscale (Lagos/ClickHouse)

For "Indie" or "Production" scale, the Postgres Metering Adapter stores usage in a standard DocType table. For Hyperscale, you bridge these events to a specialized sink like Lagos or ClickHouse.

Pattern: The Async Event Bridge

Instead of writing directly to ClickHouse from your request thread, use the Webhook DocType or a specialized adapter to push events out-of-process.

To enable a Hyperscale Sink (e.g. Lagos):

  1. Configure the Adapter: Swap the default Postgres adapter for a Lagos-aware one.
  2. Set the Sink URL: Configure the endpoint where your ClickHouse/Lagos ingestion service lives.
# framework_config.toml
[metering]
adapter = "framework_m_standard.adapters.metering.LagosMeteringAdapter"
sink_url = "https://ingest.lagos.internal"
batch_size = 100

4. Best Practices

Use "Zero-Cap" for Internal Tenants

In your PermissionProtocol, you can assign a "Zero-Cap" policy to internal or admin tenants. This ensures the MeteringProtocol still records the usage (for auditability) but the Quota Handler never blocks them.

Decouple Writing from Reading

Always use the MeteringProtocol to write facts. Never try to calculate quotas by querying your primary business tables (e.g., SELECT count(*) FROM orders). This keeps your Data Plane fast and your accounting logic isolated.

Leverage the Cache

When using get_usage for real-time enforcement, ensure your MeteringProtocol adapter is configured with a CacheProtocol (Redis) to avoid hitting the database on every check.


Next Steps