Metrics System Architecture Diagrams
Version: 2.0
Last Updated: February 2, 2026
1. High-Level System Architecture
┌─────────────────────────────────────────────────────────────────────────┐
│ eXate METRICS SYSTEM │
│ Installation-Wide Billing Pipeline │
└─────────────────────────────────────────────────────────────────────────┘
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ ds_micro_ │ │ com.exate. │ │ com.exate. │
│ APIgator │◄────►│ core │◄────►│ aggregator │
│ │ │ │ │ (Insight) │
└──────────────┘ └──────────────┘ └──────────────┘
│ │ │
│ • Collection │ • Models │ • Persistence
│ • Pre-Aggregation │ • Configuration │ • IBM Submission
│ • Leader Election │ • Redis Cache │ • Report Storage
│ • Reporting APIs │ • Window Logic │
│ • IBM Integration │ │
└─────────────────────┴─────────────────────┴──────────────┘
2. Request Flow Architecture
┌──────────────┐
│ Client │
│ Application │
└───────┬──────┘
│
POST /protect/mask (with data)
│
▼
┌─────────────────────────┐
│ MetricsCollection │
│ Interceptor │
│ • Captures metadata │
│ • In-memory cache │
│ • Per-tenant tracking │
└───────────┬─────────────┘
│
60s flush timer
│
▼
┌─────────────────────────┐
│ Redis │
│ • Raw metrics per │
│ tenant/window │
│ • Key: {tenant}: │
│ {window}:{metric} │
└───────────┬─────────────┘
│
┌───────────┴──────────────┐
│ Pre-Aggregation Service │
│ (Hourly, Leader Only) │
│ │
│ • Read raw metrics │
│ • Aggregate per tenant │
│ • Assign UUID │
│ • Store snapshot │
└───────────┬──────────────┘
│
▼
┌─────────────────────────┐
│ Redis │
│ • Pre-computed snapshots│
│ • UUID → JSON │
│ • Ready for reporting │
└───────────┬─────────────┘
│
┌─────────────┴─────────────┐
│ │
▼ ▼
┌────────────────┐ ┌──────────────────┐
│ Preview API │ │ Report/Send APIs │
│ (read-only) │ │ (with persist) │
└────────────────┘ └─────────┬────────┘
│
▼
┌──────────────────────┐
│ Insight API │
│ (Batch Persistence) │
│ │
│ • UUID idempotency │
│ • DB storage │
│ • IBM submission │
└──────────────────────┘
3. Installation-Wide Reporting Flow
┌─────────────────────────────────────────────────────────────────────┐
│ POST /api/metrics/send │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌────────────────────────┐
│ 1. Acquire Lock │
│ TTL: 10 minutes │
└────────┬───────────────┘
│
┌───────┴───────┐
│ Lock Success? │
└───────┬───────┘
│
┌────────┴────────┐
│ No │ Yes
▼ ▼
┌─────────────┐ ┌──────────────────────┐
│ Return 409 │ │ 2. Get Closed Windows│
│ Conflict │ │ (isClosed = true) │
└─────────────┘ └──────────┬───────────┘
│
▼
┌──────────────────────────┐
│ 3. Get All Tenants │
│ (from closed windows) │
└──────────┬───────────────┘
│
▼
┌──────────────────────────┐
│ 4. For Each Tenant: │
│ • Get persisted UUIDs │
│ • Get snapshots │
│ • Filter unpersisted │
└──────────┬───────────────┘
│
┌────────┴────────┐
│ Pending exists? │
└────────┬────────┘
│
┌────────┴────────┐
│ No │ Yes
▼ ▼
┌──────────────┐ ┌──────────────────┐
│ Return 204 │ │ 5. Batch Persist │
│ No Content │ │ (Single HTTP) │
└──────────────┘ └────────┬─────────┘
│
▼
┌──────────────────────┐
│ 6. Mark UUIDs as │
│ Persisted │
│ (Redis SET - PERM) │
└────────┬─────────────┘
│
▼
┌──────────────────────┐
│ 7. Update last_report│
│ (per tenant) │
└────────┬─────────────┘
│
▼
┌──────────────────────┐
│ 8. Release Lock │
└────────┬─────────────┘
│
▼
┌──────────────────────┐
│ Return 200 OK │
│ with DB UUIDs │
└──────────────────────┘
4. UUID Idempotency Architecture
┌─────────────────────────────────────────────────────────────────────┐
│ UUID LIFECYCLE │
└─────────────────────────────────────────────────────────────────────┘
┌────────────────────┐
│ Pre-Aggregation │ ──► UUID = UUID.randomUUID()
│ (Hourly) │ "abc-123-def-456"
└─────────┬──────────┘
│
▼
┌──────────────────────────────────────────────────┐
│ Redis: Pre-Computed Snapshot │
│ { │
│ "uuid": "abc-123-def-456", │
│ "tenantId": 123, │
│ "windowId": "202601", │
│ "metrics": {...} │
│ } │
└─────────┬────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────┐
│ Send API - Idempotency Check │
│ │
│ persistedUuids = Redis.SMEMBERS( │
│ "tenant:123:persisted" │
│ ) │
│ │
│ IF "abc-123-def-456" IN persistedUuids: │
│ SKIP (already sent) │
│ ELSE: │
│ PERSIST to Insight API │
└─────────┬────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────┐
│ Insight API Returns DB UUID │
│ "db-uuid-999-from-postgres" │
└─────────┬────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────┐
│ Mark as Persisted │
│ │
│ Redis.SADD( │
│ "tenant:123:persisted", │
│ "abc-123-def-456" <-- Original UUID │
│ ) │
│ │
│ ✅ NEVER CLEARED (Permanent Audit Trail) │
└──────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ Retry Scenario │
│ │
│ User calls /send again: │
│ 1. Check: "abc-123-def-456" in persisted? ✅ │
│ 2. Skip: Already sent │
│ 3. Return: 0 reports generated │
│ │
│ ✅ NO DUPLICATE BILLING │
└─────────────────────────────────────────────────┘
5. Closed Window Logic
┌─────────────────────────────────────────────────────────────────────┐
│ WINDOW LIFECYCLE │
└─────────────────────────────────────────────────────────────────────┘
Granularity: MONTH
Timeline:
│
│ Jan 1 Jan 31 Feb 1 Feb 28
│ 00:00:00 23:59:59 00:00:00 23:59:59
├─────────────────────────┼────────────────────────────────────┼
│ Window: 202601 │ Window: 202602 │
│ (January 2026) │ (February 2026) │
└─────────────────────────┴────────────────────────────────────┴
Current Time: Feb 15, 2026
Check Window Status:
┌──────────┬────────────┬─────────────┬────────────────────┐
│ Window │ End Time │ Now > End? │ isClosed │
├──────────┼────────────┼─────────────┼────────────────────┤
│ 202601 │ Jan 31 EOD │ ✅ Yes │ ✅ true (INCLUDE) │
│ 202602 │ Feb 28 EOD │ ❌ No │ ❌ false (EXCLUDE) │
└──────────┴────────────┴─────────────┴────────────────────┘
Report Generation Result:
✅ January 2026 data INCLUDED (complete)
❌ February 2026 data EXCLUDED (in progress)
User sees:
{
"reportsGenerated": 1,
"reports": [
{ "windowId": "202601", ... } // ✅ January only
]
}
6. IBM Integration Flow
┌─────────────────────────────────────────────────────────────────────┐
│ IBM MODE SCHEDULER │
└─────────────────────────────────────────────────────────────────────┘
Cron: */15 * * * * (Every 15 minutes)
┌──────────────────┐
│ Scheduler Fires │
└────────┬─────────┘
│
▼
┌────────────────────┐
│ IS_IBM_MODE? │
└────────┬───────────┘
│
┌────────┴────────┐
│ No │ Yes
▼ ▼
┌─────────────┐ ┌──────────────────────┐
│ Skip │ │ sendReports( │
└─────────────┘ │ submitToIbm=true │
│ ) │
└──────────┬───────────┘
│
┌─────────────┴─────────────┐
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ 1. Persist to │ │ 2. Submit to IBM │
│ Insight API │ │ Metering API │
│ (batch) │ │ (per-tenant) │
└────────┬─────────┘ └────────┬─────────┘
│ │
└───────────┬───────────────┘
│
▼
┌──────────────────────┐
│ 3. Update IBM Status │
│ • SUBMITTED ✅ │
│ • FAILED ❌ │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ 4. Auto Cleanup │
│ clearReportedWindows()│
└──────────────────────┘
IBM API Payload per Tenant:
{
"start": 1738368000000,
"end": 1738368899999,
"region": "us-south",
"plan_id": "...",
"measured_usage": [
{
"measure": "ATTRIBUTE_COUNT",
"quantity": 450
},
...
]
}
7. Redis Key Hierarchy
EX8:PROD:COMMON:USAGE_METRICS
│
├── {tenantId}:{windowId}:jobtype [HASH] Raw metrics
├── {tenantId}:{windowId}:navigationtokens... [HASH] Raw metrics
│
├── window:{windowId}:tenants [SET] Tenant IDs
├── window:{windowId}:granularity [STRING] "MONTH"
│
├── snapshots:{windowId} [HASH] tenantId→JSON
├── precomputed [ZSET] windowId by time
├── active [ZSET] windowId by time
│
├── tenant:{tenantId}:persisted [SET] UUIDs (PERMANENT)
├── tenant:{tenantId}:last_report [STRING] timestamp
│
├── installation:lock [STRING] "1" (TTL: 600s)
├── granularity [STRING] "MONTH"
└── leader [STRING] "pod-abc-123"
8. Data Model Relationships
┌──────────────────────────────────────────────────────────────────┐
│ MODEL RELATIONSHIPS │
└──────────────────────────────────────────────────────────────────┘
PreComputedSnapshot (Redis) MetricsSnapshot (DB)
┌─────────────────────────┐ ┌──────────────────────┐
│ uuid: "abc-123" │──────┐ │ uuid: "db-uuid-999" │
│ tenantId: 123 │ │ │ tenantId: UUID(123) │
│ windowId: "202601" │ │ │ windowId: "202601" │
│ metrics: Map[...] │ │ │ totalManifests: 120 │
│ manifests: 120 │ │ │ totalAttributes: 450 │
│ attributes: 450 │ │ │ status: "PERSISTED" │
│ ... │ │ │ ibmStatus: "SUBMITTED"│
└─────────────────────────┘ │ └──────────────────────┘
│
toUsageMetrics()
│
▼
┌────────────────────────┐
│ UsageMetrics (Payload) │
│ │
│ uuid: "abc-123" │
│ tenantId: UUID(123) │
│ windowId: "202601" │
│ hostname: "pod-1" │
│ environment: "PROD" │
│ ... │
└────────────────────────┘
Persisted UUIDs (Redis SET)
┌──────────────────────────────┐
│ tenant:123:persisted │
│ • "abc-123" ◄─────────────┐ │
│ • "def-456" │ │
│ • "ghi-789" │ │
│ │ │
│ ✅ PERMANENT AUDIT TRAIL │ │
│ ❌ NEVER CLEARED │ │
└───────────────────────────┼──┘
│
Used for idempotency
check before persist
9. Service Dependencies
┌─────────────────────────────────────────────────────────────────────┐
│ SERVICE ARCHITECTURE │
└─────────────────────────────────────────────────────────────────────┘
┌───────────────────────────────────────────────────────────────────┐
│ ds_micro_APIgator │
├───────────────────────────────────────────────────────────────────┤
│ │
│ MetricsController │
│ │ │
│ ├──► MetricsPersistenceService │
│ │ │ │
│ │ ├──► MetricsPreAggregationService │
│ │ │ └──► RedisCache │
│ │ │ │
│ │ ├──► IBMMeteringClient │
│ │ │ └──► IBM Cloud Metering API │
│ │ │ │
│ │ └──► RestTemplate (Insight API) │
│ │ │ │
│ │ └──► com.exate.aggregator/api/reports/log │
│ │ │
│ └──► MetricsLeaderElection │
│ └──► RedisCache │
│ │
└───────────────────────────────────────────────────────────────────┘
┌───────────────────────────────────────────────────────────────────┐
│ com.exate.aggregator (Insight) │
├───────────────────────────────────────────────────────────────────┤
│ │
│ ReportController │
│ │ │
│ └──► ReportService │
│ │ │
│ └──► MetricsService │
│ │ │
│ ├──► getMetricsSnapshotByUUID() (idempotency)│
│ └──► persistUsageMetrics() (DB insert) │
│ │
└───────────────────────────────────────────────────────────────────┘
┌───────────────────────────────────────────────────────────────────┐
│ com.exate.core │
├───────────────────────────────────────────────────────────────────┤
│ │
│ • Models (MetricsSnapshot, PreComputedSnapshot, etc.) │
│ • Configuration (MetricsConfig) │
│ • RedisCache (WindowedMetricsCache) │
│ • MetricsWindow (boundary calculations) │
│ │
└───────────────────────────────────────────────────────────────────┘
10. Error Handling Flow
┌─────────────────────────────────────────────────────────────────────┐
│ ERROR HANDLING STRATEGY │
└─────────────────────────────────────────────────────────────────────┘
Send API Error Scenarios:
1. Lock Already Held
┌──────────────────┐
│ Try Acquire Lock │
└────────┬─────────┘
│ Fail
▼
┌──────────────────┐
│ HTTP 409 Conflict│
│ "Operation in │
│ progress" │
└──────────────────┘
2. No Closed Windows
┌──────────────────┐
│ Get Closed │
│ Windows │
└────────┬─────────┘
│ Empty
▼
┌──────────────────┐
│ HTTP 204 No │
│ Content │
└──────────────────┘
3. All UUIDs Already Persisted
┌──────────────────┐
│ Filter Unpersisted│
└────────┬─────────┘
│ Empty
▼
┌──────────────────┐
│ HTTP 204 No │
│ Content │
└──────────────────┘
4. Insight API Failure
┌──────────────────┐
│ Call Insight API │
└────────┬─────────┘
│ HTTP 5xx
▼
┌──────────────────┐
│ Release Lock │
│ Throw │
│ DownstreamService│
│ Exception │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ HTTP 500 Error │
│ UUIDs NOT marked │
│ as persisted │
│ ✅ Safe to retry │
└──────────────────┘
5. IBM Submission Failure
┌──────────────────┐
│ Submit to IBM │
└────────┬─────────┘
│ Fail
▼
┌──────────────────┐
│ Update ibmStatus │
│ = "FAILED" │
│ │
│ But DB persist │
│ succeeds ✅ │
│ │
│ (Can retry IBM │
│ separately) │
└──────────────────┘
For complete documentation, see METRICS_SYSTEM.md