DEV Community

YukiKobayashi880
YukiKobayashi880

Posted on

Hosted Metrics Query API: React Dashboard Cards with Rollback-Safe Retention

TL;DR: For a fintech admin panel that must alert when scheduled imports stop producing results, choose a hosted metrics query API only after modeling four separate bill drivers: ingested samples, retained resolution, query work, and response egress. Put a small backend-for-frontend between the browser and that API, cache aligned time windows, and retain just enough raw evidence to roll back an alert-rule change. The least complex option is a stable range-query contract plus one freshness series per import, not a browser that speaks a provider's query language.

Consider an illustrative workload, not a benchmark: 200 scheduled import runs per day, eight result series per run, and 30 days of raw retention produce 48,000 stored points if each series emits once per run. Five dashboard cards, three queries per card, four operators, a 60-second refresh, and an eight-hour workday produce 28,800 query requests per day before caching. In that shape, query repetition is the dominant operation count. Moving from arbitrary browser windows to aligned, cached windows changes that term immediately; trimming a few labels does not.

Public pricing pages make the categories visible, but they do not make one provider universally cheaper. A cost review must use the startup's own cardinality, resolution, retention, read frequency, and egress assumptions. Price is a constraint here, not the selection thesis.

What should a hosted metrics query API return to a React dashboard?

The browser needs a boring contract. It should ask for a metric identifier, an import identity from an allowlist, a start time, an end time, and a step. The backend should translate that request into the hosted service's query language, enforce maximum ranges, and return provider-neutral points with explicit timestamps. Keep credentials and query construction out of React.

A useful response shape carries both data and interpretation boundaries: the effective step, the server's evaluation time, and whether the series is complete through that time. Without those fields, a card can render a smooth line while quietly comparing a partially closed bucket with completed buckets. That is a visual lie, even if every returned number is correct.

Gaps are evidence.

The core series for this job are small: last successful result time, completed result count, failed run count, and scheduled-run heartbeat. Do not attach account IDs, file names, transaction IDs, or unbounded error text as labels. Those belong in logs or durable audit records. A metrics label that grows with customer activity turns an operational question into a cardinality problem.

Here is a Python reference for the provider-neutral boundary. It deliberately rejects loose windows and excessive points before any remote query is made.

from dataclasses import dataclass
from datetime import datetime, timezone

MAX_POINTS = 720
ALLOWED_STEPS = {60, 300, 900, 3600}

@dataclass(frozen=True)
class RangeRequest:
    metric: str
    import_key: str
    start: datetime
    end: datetime
    step_seconds: int

def validate(request: RangeRequest, allowed_imports: set[str]) -> None:
    if request.start.tzinfo is None or request.end.tzinfo is None:
        raise ValueError("timestamps must include a timezone")
    if request.start >= request.end:
        raise ValueError("start must precede end")
    if request.import_key not in allowed_imports:
        raise ValueError("unknown import")
    if request.step_seconds not in ALLOWED_STEPS:
        raise ValueError("unsupported step")

    seconds = (request.end - request.start).total_seconds()
    if seconds / request.step_seconds > MAX_POINTS:
        raise ValueError("range exceeds point budget")

def utc_now() -> datetime:
    return datetime.now(timezone.utc)
Enter fullscreen mode Exit fullscreen mode

A 720-point ceiling is an application choice in this example, not a service limit. It makes the contract testable and bounds card payloads. The right ceiling depends on pixel width, aggregation semantics, and how much evidence operators need during an incident.

Retention is a rollback decision

Rollback safety changes the retention discussion. If an alert threshold or freshness calculation is deployed at noon and proves wrong at 14:00, the team needs enough unchanged evidence to restore the previous rule and replay both versions over the same interval. Keeping only the currently rendered aggregate cannot provide that comparison. Keeping every raw observation forever is equally hard to justify.

I would define three retention tiers in the design review, without pretending their durations are universal. Raw scheduled-run observations survive for the maximum rule rollback window. Downsampled operational trends survive longer for capacity and seasonality checks. Durable import audit records follow the fintech system's separate compliance policy; metrics are not the system of record. The important boundary is ownership: deleting a metrics series must never delete the evidence required to reconcile money movement.

Data retained Operational purpose Rollback value Main cost driver
Raw run observations Re-evaluate freshness and result rules Highest inside the rollback window Ingested samples and raw retention
Fixed-window aggregates Dashboard trends and broad incident context Cannot recover within-window ordering Retained aggregate points
Query cache entries Suppress repeated card reads None after expiry Cache memory and invalidation
Durable import audit records Reconciliation and compliance evidence Independent of alert rollback Storage, indexing, and governance

This is where I am deliberately strict: stop keeping raw metrics after the documented rollback and investigation window, provided the required audit evidence exists elsewhere. The cost of that choice appears during an old incident. You can inspect aggregates and audit events, but you cannot reconstruct the exact sequence of raw metric samples or test a newly invented rule against them. Write that loss into the retention decision; do not hide it under a generic "30 days" default.

Silence has more than one cause

An absent result is not automatically a failed import. It may mean the scheduler did not start the job, the worker started but could not reach the source, the import completed with zero valid rows, telemetry delivery failed, a label changed, or the query evaluated an interval that has not closed. A single "no data" alarm merges these failure modes and makes rollback dangerous because nobody can tell whether the new rule or the pipeline changed.

Do not erase that distinction.

Use two clocks. The scheduler heartbeat establishes that work was expected. The last successful result timestamp establishes that useful output appeared. Alert only after a grace period derived from the schedule and a measured completion allowance, and show both timestamps in the admin panel. A zero result count must remain a value; absence must remain absence. Converting both to zero destroys information.

The alert evaluator also needs a stable identity for each scheduled import across deploys. If a release renames a label, dual-publish the old and new identities for one rollback window, update queries, verify both paths, and then retire the old identity. Do not rewrite historical labels in place. Immutable history is less convenient, but it preserves the evidence needed to explain why an alert changed.

Failure testing should cover the boundaries rather than merely the happy response: delay telemetry beyond one evaluation interval, omit a heartbeat, return an empty result set, shift a timestamp across a daylight-saving transition, duplicate a completion observation, and force the remote query to time out. The backend should return a typed unavailable state for transport failure, not an empty series.

Make reads cheap without weakening evidence

Align requested windows to the selected step and cache by metric, import identity, aligned start, aligned end, and step. Historical buckets can have a long cache lifetime because they no longer change; the newest bucket needs a short lifetime or no cache until it closes. This removes repeated remote reads while preserving raw retention for rollback.

Downsampling is the next lever, but aggregation must match meaning. Counts can be summed across compatible windows. A last-success timestamp should use the maximum. Percentages should be recomputed from numerator and denominator, not averaged from already rounded percentages. These choices belong in code and tests because a generic "average everything" policy can make a stopped importer look healthy.

The earlier illustrative workload shows the effect. If the four operators share aligned cache keys, most of their identical card reads become local hits. If the cards refresh together, one backend refresh can serve all four sessions. No savings percentage is promised: the hit rate depends on navigation, refresh jitter, time ranges, and cache lifetime. Measure remote query count, returned points, response bytes, and cache hit rate before changing retention.

A rollback-safe selection exercise

A short proof of concept should use recorded, synthetic import events and the same acceptance tests for every candidate. It should not start with screenshots. Test the query API's range semantics, timeout behavior, pagination or point limits, timestamp precision, missing-series representation, authentication scope, export path, and the ability to retrieve raw observations throughout the proposed rollback window. Then inject one change at a time.

The release sequence is intentionally conservative:

  1. Record the old rule, query, label schema, and dashboard contract as a versioned bundle.
  2. Deploy dual evaluation so old and new rules read the same observations without paging twice.
  3. Compare decisions over the full rollback window and investigate every disagreement.
  4. Switch notification ownership to the new rule while preserving the old evaluator.
  5. Remove the old path only after the rollback deadline and an explicit evidence check.

A provider passes when the team can repeat that exercise through its documented API without browser-held secrets, ambiguous gaps, or an irreversible schema migration. It fails for this system if a routine rule change prevents replay over retained raw observations, even when its charts are attractive.

This approach has limits. It is unsuitable when the panel needs sub-second streaming updates, when operators must perform unrestricted ad hoc analysis in the browser, or when the metrics store is expected to serve as the regulated financial ledger. For those cases, choose a purpose-built streaming path, an authenticated exploration interface, or a durable audit datastore respectively; forcing a cached range-query API to cover all three creates weaker boundaries and a harder rollback. It is also a poor fit for a tiny internal panel with one fixed daily card and no paging duty, because the extra translation layer may cost more operational attention than it saves. The trade-off is deliberate: a narrow backend contract gives up direct access to every provider feature in exchange for controlled queries, bounded reads, and a reversible schema boundary.

Rollback first.

The final scorecard should weight semantic correctness and rollback evidence above interface polish. Include operational limits and cost dimensions, but use measured workload counts rather than a headline price. The right hosted service is the one whose boundaries the team can state, test, monitor, and reverse.

Further reading

Top comments (0)