Skip to content

Measure public cache effectiveness and invalidation causes - #76

Merged
abhi1693 merged 2 commits into
masterfrom
fix/public-cache-observability
Oct 4, 2026
Merged

abhi1693 merged 2 commits into
masterfrom
fix/public-cache-observability

Conversation

@abhi1693

@abhi1693 abhi1693 commented Oct 4, 2026 •

Copy link
Copy Markdown
Owner

What changes and why?

Production public response caching lacks route labels and invalidation measurements. A read-only Prometheus snapshot at 2026-10-04 00:52 UTC found 33.2% hits over 24 hours (about 76,108 hits / 152,842 misses) and 35.7% over one hour. Cache-unavailable bypasses were about 0.024% of 24-hour decisions. Hit ratio uses hits / (hits + misses), with bypasses tracked separately. These aggregates do not establish why entries miss or prove broad invalidation is responsible.

This PR adds bounded route templates to the final HIT/MISS/BYPASS decision and records generation invalidation attempts separately from their contributing causes. A transaction counts once, even when it touches several public tables. ORM and bulk writes report table causes after the outer commit; profile, avatar, search-index, and manual clears report explicit causes. Outcomes distinguish successful generation changes, Redis unavailability, and disabled caching. Cache keys, query strings, IDs, and supplied values never become labels.

Writer delivery matters to the measurement: RQ children reuse pod identities but reset counters, and CLI writers previously did not initialize telemetry. Short-lived writer counters now export OTLP deltas with a final shutdown flush; CLI commands that can change public data own and close their runtime. Worker/scheduler entrypoints retain their existing runtime ownership, and parent scrapes remain cumulative. The live production collector was confirmed to have stream-affine routing and deltatocumulative enabled.

The observability guide includes route hit ratio, traffic/bypass, and writer-cause queries. Writer totals use OTLP-only production series: filtering by Kubernetes namespace would omit work-horse data, while summing scrapes and OTLP would duplicate long-lived services.

Validation

Updated against master after #75 and the separate reader touch-fixture prerequisite #87. Hosted CI passes on 4fdc5a8efbf468202758013caff5f07327370377: CI required, all CodeQL analyses, SonarQube and SonarCloud Code Analysis. Auto-merge remains disabled.

  • 2,642 backend unit tests and 1,136 integration tests passed, with no skips; populated upgrades and recovery checks passed.
  • 801 web and 648 admin unit tests, frontend production builds, lint/types, actual website/Chrome/Edge reader parity, admin and real-API browser journeys passed.
  • Security checks, ARM64 images and the API performance gate passed. The benchmark does not establish a production improvement.
  • Sonar's quality gate passes with 97.5% new-code coverage, zero new duplication and no new issues. No threshold or coverage-exclusion changes were needed.
  • Local focused checks: 97 unit and 44 PostgreSQL 18 / Redis 8 integration tests, including real RQ fork, route/cause cardinality, commit/rollback, disabled/unavailable caching, writer runtime ownership and OTLP wire deltas. An RQ fork deprecation warning remains.
  • Commit hooks, Python checks, formatting and whitespace checks passed. Previously validated PromQL expressions remain documented; new production series require deployment.
  • No visible UI changes or API contract changes; review screenshots are not applicable. No production writes or deployment were performed.
  • GitHub's separate managed AI scan remains unavailable because its monthly quota is exhausted (HTTP 402); repository CodeQL checks passed.

Rollout notes

Cache domains, keys, TTLs, invalidation triggers, and generation fencing stay unchanged. No migration or cache clear is required. Roll out the API, user/admin API, scheduler, indexer, workers, and CLI together to measure every writer. Short-lived writer counter exports require an OTLP collector that accumulates deltas; production already has this configuration. Other environments must provide equivalent handling. Collector outages or unconfigured CLI export can still lose observations.

After deployment, verify writer coverage and delivery, then compare a full post-rollout window of route traffic, hit ratio, bypasses, successful invalidations, latency, and database pressure. Causes are overlapping transaction categories and must not be summed as invalidation counts. Individual misses cannot be attributed to TTL expiry or invalidation with these aggregate counters. Whether broad invalidation actually explains the hit rate remains open until that evidence is collected. Cache boundary changes belong in a follow-up PR supported by those measurements.

This branch is based directly on master and contains only this cache-observability task; earlier audit fixes remain in their separate PRs. No production deployment is included.

Ready for review

  • The description explains the change and the validation results, including any gaps.
  • Relevant documentation and regression tests are updated.

@github-actions

github-actions Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

PR reader API performance

Result: INCONCLUSIVE

8 inconclusive. Zero request failures across all six runs.

Base 25389e3450900196d7d83f76b85e896f75fb403e → PR 4fdc5a8efbf468202758013caff5f07327370377

Three independent runs per revision, each on a fresh GitHub-hosted ARM64 runner. 16 users, 60 seconds/run, 1,000 synthetic articles, cache off, five PgBouncer slots.

Latency comparison

All latency values are in milliseconds; lower is better. Values are medians of three runs.

Route Base p95 PR p95 Change Result
All requests 120 86 -34 (-28.3%) Inconclusive
Latest feed 150 110 -40 (-26.7%) Inconclusive
Feed pagination 120 82 -38 (-31.7%) Inconclusive
Article detail 91 59 -32 (-35.2%) Inconclusive
Source feed 120 67 -53 (-44.2%) Inconclusive
Topic feed 100 74 -26 (-26.0%) Inconclusive
Feed filters 94 67 -27 (-28.7%) Inconclusive
Sources 68 34 -34 (-50.0%) Inconclusive
Topics 70 45 -25 (-35.7%) Inconclusive
Additional latency and throughput metrics

Each cell shows base → PR. Latencies are in milliseconds.

Endpoint p50 p99 Requests/s
Aggregated 33 → 28 170 → 120 30.16 → 29.99
/v1/feed [latest] 51 → 41 210 → 160 8.81 → 9.05
/v1/feed [page] 30 → 27 150 → 100 3.48 → 3.88
/v1/articles/[id] 21 → 17 140 → 94 6.53 → 6.12
/v1/feed [source] 29 → 26 140 → 160 1.79 → 1.72
/v1/feed [topic] 33 → 24 140 → 110 1.89 → 1.84
/v1/feed/options 24 → 20 120 → 93 2.66 → 2.26
/v1/sources 13 → 11 110 → 67 2.66 → 2.26
/v1/topics 13 → 11 110 → 77 2.66 → 2.26
Individual runs
Run Requests Failures Exit code
Base 1 1699 0 0
Base 2 1772 0 0
Base 3 1654 0 0
PR 1 1772 0 0
PR 2 1685 0 0
PR 3 1651 0 0
How to interpret this report

Regressed / Improved: a p95 regression needs >20% and >20 ms increase in the medians and at least two head samples versus the base median. Improvements use the inverse threshold. Within threshold means the change did not meet both thresholds.

Inconclusive: a within-revision p95 range >35% of its median and >20 ms is too noisy for a confident latency verdict.

Inconclusive latency comparisons are non-blocking: the check passes without claiming an improvement. Confirmed regressions and test/report errors fail the PR gate. Throughput is descriptive: paced users do not measure maximum capacity. This uncached public API test does not establish production health, browser performance or worker capacity. Separate runners can differ in hardware or host load; repetitions and noise checks reduce but cannot eliminate that uncertainty.

Full HTML/CSV reports and logs · attempt 1

@gitar-bot

gitar-bot Bot commented Oct 4, 2026

Copy link
Copy Markdown
Code Review ✅ Approved

🟡 Medium risk · Adds route and writer invalidation telemetry plus CLI runtime lifecycle changes

Adds bounded route templates and invalidation cause tracking to public cache observability, enabling per-route hit ratio and traffic analysis. Production data shows 33.2% hits over 24 hours; this PR instruments why entries miss and whether broad invalidation is responsible. Writer delivery is carefully measured across long-lived services, short-lived workers, and CLI commands with proper counter lifecycle and OTLP delta exports. Comprehensive regression testing covers cache decisions, invalidation causes, commit/rollback behavior, Redis failures, and route separation. No issues found.

Review coverage

📋 Rules No rules evaluated

🧪 Functional validation Not enabled · Set up

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Counting what did not apply, without listing it.

Comment with these commands to change the behavior for this request:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@sonarqubecloud

sonarqubecloud Bot commented Oct 4, 2026

Copy link
Copy Markdown

@abhi1693
abhi1693 merged commit be90896 into master Oct 4, 2026
56 checks passed
@abhi1693
abhi1693 deleted the fix/public-cache-observability branch October 4, 2026 13:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant