← Back to blog
Solution 4 min read 炬鲸团队

Financial Observability in Practice: Root-Causing a Payment-Link Latency Spike

A real payment-link timeout incident at a city commercial bank, reconstructed to show how metrics, traces, and logs combine to isolate root cause in 20 minutes — plus observability advice for financial environments.

Background: a real payment timeout

On a Monday at 10:00 AM, a city commercial bank's payment gateway started timing out in batches. Transaction success rate dropped from 99.95% to 96%, and support began taking user complaints. Regulation requires this class of incident to be located and reported within a fixed window. The team isolated root cause in 20 minutes — not luck, but a three-layer observability setup wired together in advance.

Layer one: metrics narrow the scope

The first alert wasn't from the payment gateway itself but from the downstream core ledger system's response time. Opening the metrics dashboard showed the ledger's P99 latency spiking from 80ms to 4 seconds, affecting only the "balance query" interface — everything else stayed normal. That shrank the search from "the whole payment chain" to "the ledger's balance query."

The key point: metrics dashboards must be split by interface, not stored as one generic "system latency." Without that split you'd only see "the ledger got slower," never "only balance query got slower."

Layer two: tracing pins down the node

Filtering service.name="core-ledger" AND operation="queryBalance" around the alert time surfaced the slow request traces. Ninety percent of the slow spans stopped at the same place: the balance query's internal call to an "account cache service," with latency clustered at 3.9 seconds.

Looking at the cache service's spans revealed many get operations returning a cache-miss marker — the cache wasn't hitting, so everything fell through to the database. Root cause direction was clear: cache failure saturating the DB.

Layer three: logs confirm the root cause

Logs sealed it. Searching the cache service's logs from 10:02 to 10:05 for level=error showed a burst of Connection pool exhausted and redis connection reset. Scrolling back to 10:01 found a cache key evicted: BAL:* record — a batch job had mistakenly flushed the entire balance cache.

A three-minute run of the cache warm-up script restored success rate above 99.9%. Alert to root cause: 20 minutes.

Advice for financial environments

  1. Metrics, traces, and logs must share the same trace_id / request ID, or each layer gets searched separately and correlation becomes manual.
  2. Middleware — caches, databases, message queues — must be instrumented too. Failures usually live there, not in application code.
  3. Use full trace sampling for critical transaction paths; cold paths can be downsampled to control cost.
  4. Set alert thresholds per interface and tier them: latency alerts, error-rate alerts, and traffic-drop alerts separately, so they don't drown each other out in an alert storm.