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​