fix: token/trader 24h rollups never decay; stale global_stats_24h freezes Charts volume #577

Closed
opened 2026-08-19 11:53:03 +00:00 by PlasticDigits · 16 comments
PlasticDigits commented 2026-08-19 11:53:03 +00:00 (Migrated from gitlab.com)

Summary

Charts 24h Volume (USD) is supposed to be a trailing 24h sum from global_stats_24h. Pair list volume already zeros idle pairs. Two other rollups do not decay, and /overview will keep serving a frozen non-zero 24h total if the ~5 min aggregator stops.

Retail report: 24h volume “never goes back to 0 / only goes up.” Copy/UX is a linked issue. This issue owns rollup decay + freshness so token, trader, pair, and global windows actually fall when swap_events leave the cutoff.

Current codebase

What already decays

refresh_global_stats rebuilds global_stats_24h from swap_events with FILTER (WHERE block_timestamp >= $1) for 24h (and 7d/30d). A successful refresh can drop volume to 0.

refresh_pair_volumes INSERTs pairs with 24h swaps, then:

UPDATE pair_volume_24h pv
SET volume_quote = 0, updated_at = NOW()
WHERE NOT EXISTS (
  SELECT 1 FROM swap_events se
  WHERE se.pair_id = pv.pair_id AND se.block_timestamp >= $1
)

Idle pairs go to 0. There is no integration test that a pair with only 48h-old swaps is zero after refresh.

Pair stats (get_24h_stats_for_pair) are a live now − 24h query — they decay without a rollup.

What does not decay

1. token_volume_stats — refresh_token_volumes

INSERT … SELECT … WHERE block_timestamp >= $cutoff GROUP BY offer_asset_id ON CONFLICT DO UPDATE.

If an asset has no swaps in that window, the SELECT emits no row, so the previous 24h / 7d / 30d row is left forever. GET /api/v1/tokens/{addr} returns that stale volume_stats (tokens.rs). No tests call refresh_token_volumes.

Offer-side only (GROUP BY offer_asset_id) is existing semantics — this issue must not silently start summing both legs.

2. traders.volume_24h / volume_7d / volume_30d — refresh_rolling_volumes

UPDATE traders … FROM (SELECT sender … FROM swap_events WHERE block_timestamp >= $30d GROUP BY sender) sub WHERE t.address = sub.sender.

Traders inside the 30d subquery get correct CASE zeros (24h can be 0 while 30d > 0). Traders whose last swap is older than 30d are not in the subquery, so volume_30d (and any leftover 24h/7d if a refresh was skipped) sticks. Leaderboard sort=volume_30d / volume_24h can rank ghosts (get_leaderboard). Charts Volume tab uses lifetime total_volume_usd (#553) — still fix the rolling columns for API / QA.

Do not zero total_volume / total_volume_usd (lifetime).

3. Stale global_stats_24h on /overview

get_global_stats reads the rollup and ignores updated_at. Live fallback only when total_trades == 0 and recent swaps exist (uninitialized seed). If the aggregator dies after a non-zero refresh, /overview keeps the last 24h USD/trades forever (plus 60s response cache). That matches “only goes up / never comes back.”

run_volume_refresh_loop sleeps 300s first. poller.rs does an initial refresh_pair_volumes + refresh_global_stats only — not token volumes or trader rolling windows.

Existing test global_stats_rollup_excludes_swaps_older_than_24h inserts a 48h swap alongside fresh seed swaps and asserts the count stays 5. It does not age a previously counted swap out and assert the total decreases.

Why this is needed

  1. Token and trader window APIs can look cumulative even when global/pair math is rolling — same user-visible class as “24h never resets.”
  2. A dead aggregator freezes Charts/Protocol 24h USD at the last refresh. There is no freshness check, no metric on global_stats_24h.updated_at, no live fallback for a stale non-zero row.
  3. Asymmetric hygiene: pair rollup already zeros idle rows; token/trader do not. Tests do not prove decay.

Constraints / guardrails

  1. Do not live-SUM(swap_events) on every /overview GET (#281 / #333 V5). Stale handling must not become a DoS. Prefer: keep serving rollup; log + metric on updated_at age; optional one-shot refresh in the background loop (not on the request path); live fallback only behind the existing env flag or a tight stale threshold with a timeout/statement_timeout.
  2. Trailing Utc::now() − window. No calendar-day reset.
  3. Do not change USD ingest (volume_usd_for_swap / P522-Q). Decay uses stored volume_usd / raw amounts as today.
  4. Do not zero lifetime fields (traders.total_volume, total_volume_usd, total_trades).
  5. Keep token stats offer-side unless a follow-up issue owns both-leg volume.
  6. Keep LEAST numeric caps on sums (overflow / NUMERIC(38) / USD 38,18).
  7. No extra full-table scans on the request path. Zero-out SQL must be keyed (pair_id / asset_id / address), not DELETE FROM swap_events.
  8. Block timestamps stay V3 (resolve_block_time — never Utc::now() at ingest). Decay tests must use stored block_timestamp, not wall-clock sleeps of 24h.
  9. Out of scope: Charts copy (linked UX issue); CG/CMC live 24h queries; changing #548 $0 vs — JSON contract.

Relevant files

Area Path
Token / pair / global refresh indexer/src/db/queries/volume.rs
Trader rolling indexer/src/db/queries/traders.rs
Loop + startup indexer/src/indexer/volume_aggregator.rs, indexer/src/indexer/poller.rs
Overview read indexer/src/api/overview.rs
Token API indexer/src/api/tokens.rs
Tests indexer/tests/indexer_overview_global_stats.rs, indexer/tests/indexer_pair_volume_pagination.rs, indexer/tests/api_traders.rs, indexer/tests/common/mod.rs
Docs / skill docs/indexer-invariants.md, docs/runbooks/overview-global-stats-brin.md, skills/AGENTS_INDEXER_VOLUME_PAGINATION.md
  1. Token windows: after each window INSERT, UPDATE token_volume_stats SET volume=0, volume_usd=0, trade_count=0, unique_traders=0 for that "window" when no swap_events for offer_asset_id in cutoff (same pattern as pair_volume_24h). Bind window/cutoff — no string-concat SQL.
  2. Traders: second UPDATE setting volume_24h=volume_7d=volume_30d=0 for addresses not in the 30d sender set (or NOT EXISTS recent swaps). Do not touch lifetime columns.
  3. Startup: poller initial refresh also runs refresh_token_volumes + refresh_rolling_volumes (failures = warn, same as pair/global).
  4. Freshness: read global_stats_24h.updated_at in get_global_stats. If older than a documented bound (e.g. 15 min): tracing warning + Prometheus/histogram if the indexer already has metrics; do not default to unbounded live 30d scan. Optional JSON stats_updated_at is integrator-only — not a retail Charts box.
  5. Tests that mutate block_timestamp into the past (25h / 8d / 31d), call refresh, assert windows drop (including to 0). Add pair idle-zero test (behavior already implemented).
  6. make verify-issue-<iid> + invariants/skill row.

Acceptance criteria

  • D1 Asset with only swaps older than 24h → token_volume_stats window 24h is 0 after refresh (7d/30d analogously).
  • D2 Trader whose last swap is older than 30d → volume_24h, volume_7d, volume_30d are 0; total_volume / total_volume_usd unchanged.
  • D3 Pair with only 48h-old swaps → pair_volume_24h.volume_quote = 0 after refresh (tested).
  • D4 Aging a previously counted 24h swap to 25h then refreshing decreases global_stats_24h volume/trades (not only “extra old row ignored”).
  • D5 Indexer restart refreshes token + trader windows without waiting 5 min; pair/global initial refresh unchanged.
  • D6 Stale global_stats_24h.updated_at is observable (log/metric and/or documented operator check). /overview GET must not grow into a production live 30d swap_events scan.
  • D7 Docs/skill + make verify-issue-<iid>.

Test plan (all paths)

ID Case Expect
I1 Seed 24h token volume; set those swaps to now−25h; refresh_token_volumes 24h row zeros; 7d still counts if within 7d
I2 Same for 8d / 31d cutoffs 7d / 30d rows zero when past cutoff
I3 Asset never traded No crash; no invented USD
I4 Trader last swap 25h ago volume_24h=0, volume_7d>0
I5 Trader last swap 31d ago all three rolling columns 0; lifetime intact
I6 Trader with no swap_events rolling columns 0 after refresh
I7 Pair only 48h swaps pair_volume_24h.volume_quote=0; list sort=volume_24h does not rank it as live
I8 Global: 5 seed swaps; move all to 25h; refresh total_trades_24h=0, USD 0 / JSON "0" when idle
I9 Global: mix of 1h and 25h swaps; refresh only 1h swaps in 24h totals
I10 OVERVIEW_GLOBAL_STATS_LIVE=1 still matches live; no decay regression
I11 Uninitialized rollup zeros + recent swaps existing live fallback still works
I12 Empty DB refresh zeros, no error
I13 Concurrent ingest + refresh no double-count; unique (asset_id, window) / pair PK hold
I14 Startup path token + trader refresh invoked (unit or poller hook test)

Test plan (attack, hack, abuse)

ID Vector Expect
A1 Future block_timestamp (clock skew / hostile LCD) Window can stay inflated until V3 timestamps; do not clamp with Utc::now() at ingest. Document; decay tests use explicit past timestamps
A2 window SQL injection (24h bind) Parameterized only; reject concatenating user strings
A3 NUMERIC overflow on SUM before zero-out Keep LEAST(…, POWER(10,38)-1) / USD cap; zero-out uses 0 not NULL unless column allows
A4 /overview stale → live 30d scan on every GET Forbidden (DoS). Metric/log only, or bounded refresh off the request path
A5 Zeroing total_volume_usd “to fix 24h” Forbidden — lifetime #553
A6 Leaderboard sort=volume_24h with stale ghosts After D2, idle 30d+ traders do not occupy top ranks via leftover volume_30d
A7 Refresh lock / table bloat Zero-out UPDATE must use indexes (pair_id, (asset_id, window), traders.address); EXPLAIN on refresh statements in tests optional
A8 Fake offer_asset_id / spoofed sender Decay still keyed by stored swap rows; no new trust of LCD on GET
A9 Statement timeout during zero-out leaving half-updated windows Prefer one transaction per refresh function; on error log and retry next loop (do not serve torn token rows if possible)

Verification criteria

  1. make verify-issue-<iid>: cd indexer && cargo test --test indexer_overview_global_stats --test indexer_pair_volume_pagination --test api_traders --test api_tokens -- --test-threads=1 (plus any new decay test file) after make setup-indexer-postgres if needed.
  2. SQL: after refresh, a fixture with no swaps in 24h shows overview 24h trades 0 and token 24h volume 0.
  3. Operator: SELECT updated_at FROM global_stats_24h WHERE id = 1 advances on a running indexer (document in the BRIN/overview runbook).
  4. Charts copy issue remains separate — this issue may still leave retail $2.7K on a live trailing window; it must not leave frozen or never-zeroed token/trader windows.
## Summary Charts **24h Volume (USD)** is supposed to be a **trailing 24h** sum from `global_stats_24h`. Pair list volume already **zeros** idle pairs. Two other rollups **do not decay**, and `/overview` will **keep serving a frozen non-zero 24h total** if the ~5 min aggregator stops. Retail report: 24h volume “never goes back to 0 / only goes up.” Copy/UX is a linked issue. This issue owns **rollup decay + freshness** so token, trader, pair, and global windows actually fall when `swap_events` leave the cutoff. ## Current codebase ### What already decays [`refresh_global_stats`](indexer/src/db/queries/volume.rs) rebuilds `global_stats_24h` from `swap_events` with `FILTER (WHERE block_timestamp >= $1)` for 24h (and 7d/30d). A successful refresh **can** drop volume to 0. [`refresh_pair_volumes`](indexer/src/db/queries/volume.rs) INSERTs pairs with 24h swaps, then: ```sql UPDATE pair_volume_24h pv SET volume_quote = 0, updated_at = NOW() WHERE NOT EXISTS ( SELECT 1 FROM swap_events se WHERE se.pair_id = pv.pair_id AND se.block_timestamp >= $1 ) ``` Idle pairs go to **0**. There is **no integration test** that a pair with only 48h-old swaps is zero after refresh. Pair **stats** (`get_24h_stats_for_pair`) are a **live** `now − 24h` query — they decay without a rollup. ### What does not decay **1. `token_volume_stats`** — [`refresh_token_volumes`](indexer/src/db/queries/volume.rs) `INSERT … SELECT … WHERE block_timestamp >= $cutoff GROUP BY offer_asset_id ON CONFLICT DO UPDATE`. If an asset has **no** swaps in that window, the SELECT emits **no row**, so the previous `24h` / `7d` / `30d` row is left forever. `GET /api/v1/tokens/{addr}` returns that stale `volume_stats` ([`tokens.rs`](indexer/src/api/tokens.rs)). **No tests** call `refresh_token_volumes`. Offer-side only (`GROUP BY offer_asset_id`) is existing semantics — this issue must not silently start summing both legs. **2. `traders.volume_24h` / `volume_7d` / `volume_30d`** — [`refresh_rolling_volumes`](indexer/src/db/queries/traders.rs) `UPDATE traders … FROM (SELECT sender … FROM swap_events WHERE block_timestamp >= $30d GROUP BY sender) sub WHERE t.address = sub.sender`. Traders **inside** the 30d subquery get correct CASE zeros (24h can be 0 while 30d > 0). Traders whose last swap is **older than 30d** are **not in the subquery**, so `volume_30d` (and any leftover 24h/7d if a refresh was skipped) **sticks**. Leaderboard `sort=volume_30d` / `volume_24h` can rank ghosts ([`get_leaderboard`](indexer/src/db/queries/traders.rs)). Charts Volume tab uses **lifetime** `total_volume_usd` ([#553](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/553)) — still fix the rolling columns for API / QA. Do **not** zero `total_volume` / `total_volume_usd` (lifetime). **3. Stale `global_stats_24h` on `/overview`** [`get_global_stats`](indexer/src/db/queries/volume.rs) reads the rollup and **ignores `updated_at`**. Live fallback only when `total_trades == 0` **and** recent swaps exist (uninitialized seed). If the aggregator dies after a non-zero refresh, `/overview` keeps the last 24h USD/trades **forever** (plus 60s response cache). That matches “only goes up / never comes back.” [`run_volume_refresh_loop`](indexer/src/indexer/volume_aggregator.rs) **sleeps 300s first**. [`poller.rs`](indexer/src/indexer/poller.rs) does an initial `refresh_pair_volumes` + `refresh_global_stats` only — **not** token volumes or trader rolling windows. Existing test `global_stats_rollup_excludes_swaps_older_than_24h` inserts a **48h** swap alongside **fresh** seed swaps and asserts the count stays 5. It does **not** age a previously counted swap out and assert the total **decreases**. ## Why this is needed 1. **Token and trader window APIs can look cumulative** even when global/pair math is rolling — same user-visible class as “24h never resets.” 2. **A dead aggregator freezes Charts/Protocol 24h USD** at the last refresh. There is no freshness check, no metric on `global_stats_24h.updated_at`, no live fallback for a **stale non-zero** row. 3. **Asymmetric hygiene:** pair rollup already zeros idle rows; token/trader do not. Tests do not prove decay. ## Constraints / guardrails 1. **Do not live-`SUM(swap_events)` on every `/overview` GET** ([#281](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/281) / [#333](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/333) **V5**). Stale handling must not become a DoS. Prefer: keep serving rollup; **log + metric** on `updated_at` age; optional **one-shot refresh** in the background loop (not on the request path); live fallback only behind the existing env flag or a **tight** stale threshold with a timeout/statement_timeout. 2. **Trailing `Utc::now() − window`.** No calendar-day reset. 3. **Do not change USD ingest** (`volume_usd_for_swap` / P522-Q). Decay uses stored `volume_usd` / raw amounts as today. 4. **Do not zero lifetime fields** (`traders.total_volume`, `total_volume_usd`, `total_trades`). 5. **Keep token stats offer-side** unless a follow-up issue owns both-leg volume. 6. **Keep `LEAST` numeric caps** on sums (overflow / NUMERIC(38) / USD 38,18). 7. **No extra full-table scans on the request path.** Zero-out SQL must be keyed (pair_id / asset_id / address), not `DELETE FROM swap_events`. 8. **Block timestamps stay V3** (`resolve_block_time` — never `Utc::now()` at ingest). Decay tests must use stored `block_timestamp`, not wall-clock sleeps of 24h. 9. **Out of scope:** Charts copy (linked UX issue); CG/CMC live 24h queries; changing `#548` `$0` vs `—` JSON contract. ## Relevant files | Area | Path | |------|------| | Token / pair / global refresh | `indexer/src/db/queries/volume.rs` | | Trader rolling | `indexer/src/db/queries/traders.rs` | | Loop + startup | `indexer/src/indexer/volume_aggregator.rs`, `indexer/src/indexer/poller.rs` | | Overview read | `indexer/src/api/overview.rs` | | Token API | `indexer/src/api/tokens.rs` | | Tests | `indexer/tests/indexer_overview_global_stats.rs`, `indexer/tests/indexer_pair_volume_pagination.rs`, `indexer/tests/api_traders.rs`, `indexer/tests/common/mod.rs` | | Docs / skill | `docs/indexer-invariants.md`, `docs/runbooks/overview-global-stats-brin.md`, `skills/AGENTS_INDEXER_VOLUME_PAGINATION.md` | ## Recommended direction 1. **Token windows:** after each window INSERT, `UPDATE token_volume_stats SET volume=0, volume_usd=0, trade_count=0, unique_traders=0` for that `"window"` when no `swap_events` for `offer_asset_id` in cutoff (same pattern as `pair_volume_24h`). Bind window/cutoff — no string-concat SQL. 2. **Traders:** second `UPDATE` setting `volume_24h=volume_7d=volume_30d=0` for addresses **not** in the 30d sender set (or `NOT EXISTS` recent swaps). Do not touch lifetime columns. 3. **Startup:** `poller` initial refresh also runs `refresh_token_volumes` + `refresh_rolling_volumes` (failures = warn, same as pair/global). 4. **Freshness:** read `global_stats_24h.updated_at` in `get_global_stats`. If older than a documented bound (e.g. 15 min): **tracing warning** + Prometheus/histogram if the indexer already has metrics; **do not** default to unbounded live 30d scan. Optional JSON `stats_updated_at` is integrator-only — **not** a retail Charts box. 5. **Tests** that **mutate `block_timestamp` into the past** (25h / 8d / 31d), call refresh, assert windows drop (including to 0). Add pair idle-zero test (behavior already implemented). 6. **`make verify-issue-<iid>`** + invariants/skill row. ## Acceptance criteria - [ ] **D1** Asset with only swaps older than 24h → `token_volume_stats` window `24h` is **0** after refresh (7d/30d analogously). - [ ] **D2** Trader whose last swap is older than 30d → `volume_24h`, `volume_7d`, `volume_30d` are **0**; `total_volume` / `total_volume_usd` unchanged. - [ ] **D3** Pair with only 48h-old swaps → `pair_volume_24h.volume_quote = 0` after refresh (tested). - [ ] **D4** Aging a previously counted 24h swap to 25h then refreshing **decreases** `global_stats_24h` volume/trades (not only “extra old row ignored”). - [ ] **D5** Indexer restart refreshes token + trader windows without waiting 5 min; pair/global initial refresh unchanged. - [ ] **D6** Stale `global_stats_24h.updated_at` is observable (log/metric and/or documented operator check). `/overview` GET must **not** grow into a production live 30d `swap_events` scan. - [ ] **D7** Docs/skill + `make verify-issue-<iid>`. ## Test plan (all paths) | ID | Case | Expect | |----|------|--------| | I1 | Seed 24h token volume; set those swaps to `now−25h`; `refresh_token_volumes` | `24h` row zeros; `7d` still counts if within 7d | | I2 | Same for 8d / 31d cutoffs | `7d` / `30d` rows zero when past cutoff | | I3 | Asset never traded | No crash; no invented USD | | I4 | Trader last swap 25h ago | `volume_24h=0`, `volume_7d>0` | | I5 | Trader last swap 31d ago | all three rolling columns 0; lifetime intact | | I6 | Trader with no `swap_events` | rolling columns 0 after refresh | | I7 | Pair only 48h swaps | `pair_volume_24h.volume_quote=0`; list `sort=volume_24h` does not rank it as live | | I8 | Global: 5 seed swaps; move all to 25h; refresh | `total_trades_24h=0`, USD 0 / JSON `"0"` when idle | | I9 | Global: mix of 1h and 25h swaps; refresh | only 1h swaps in 24h totals | | I10 | `OVERVIEW_GLOBAL_STATS_LIVE=1` | still matches live; no decay regression | | I11 | Uninitialized rollup zeros + recent swaps | existing live fallback still works | | I12 | Empty DB refresh | zeros, no error | | I13 | Concurrent ingest + refresh | no double-count; unique `(asset_id, window)` / pair PK hold | | I14 | Startup path | token + trader refresh invoked (unit or poller hook test) | ## Test plan (attack, hack, abuse) | ID | Vector | Expect | |----|--------|--------| | A1 | Future `block_timestamp` (clock skew / hostile LCD) | Window can stay inflated until V3 timestamps; do **not** clamp with `Utc::now()` at ingest. Document; decay tests use explicit past timestamps | | A2 | `window` SQL injection (`24h` bind) | Parameterized only; reject concatenating user strings | | A3 | NUMERIC overflow on SUM before zero-out | Keep `LEAST(…, POWER(10,38)-1)` / USD cap; zero-out uses `0` not NULL unless column allows | | A4 | `/overview` stale → live 30d scan on every GET | **Forbidden** (DoS). Metric/log only, or bounded refresh off the request path | | A5 | Zeroing `total_volume_usd` “to fix 24h” | **Forbidden** — lifetime [#553](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/553) | | A6 | Leaderboard `sort=volume_24h` with stale ghosts | After D2, idle 30d+ traders do not occupy top ranks via leftover `volume_30d` | | A7 | Refresh lock / table bloat | Zero-out `UPDATE` must use indexes (`pair_id`, `(asset_id, window)`, `traders.address`); EXPLAIN on refresh statements in tests optional | | A8 | Fake offer_asset_id / spoofed sender | Decay still keyed by stored swap rows; no new trust of LCD on GET | | A9 | Statement timeout during zero-out leaving half-updated windows | Prefer one transaction per refresh function; on error log and retry next loop (do not serve torn token rows if possible) | ## Verification criteria 1. `make verify-issue-<iid>`: `cd indexer && cargo test --test indexer_overview_global_stats --test indexer_pair_volume_pagination --test api_traders --test api_tokens -- --test-threads=1` (plus any new decay test file) after `make setup-indexer-postgres` if needed. 2. SQL: after refresh, a fixture with **no** swaps in 24h shows overview 24h trades **0** and token `24h` volume **0**. 3. Operator: `SELECT updated_at FROM global_stats_24h WHERE id = 1` advances on a running indexer (document in the BRIN/overview runbook). 4. Charts copy issue remains separate — this issue may still leave retail `$2.7K` on a **live** trailing window; it must not leave **frozen** or **never-zeroed token/trader** windows.
PlasticDigits commented 2026-08-19 11:53:04 +00:00 (Migrated from gitlab.com)

marked as related to #576

marked as related to #576
PlasticDigits commented 2026-08-20 01:53:01 +00:00 (Migrated from gitlab.com)

mentioned in merge request !1099

mentioned in merge request !1099
PlasticDigits commented 2026-08-20 03:20:37 +00:00 (Migrated from gitlab.com)

mentioned in commit 39c6cb382a

mentioned in commit 39c6cb382a050b39e446b5926cacf61b7fbbfed0
PlasticDigits commented 2026-08-20 03:20:44 +00:00 (Migrated from gitlab.com)

mentioned in commit 704e5b9793

mentioned in commit 704e5b97938a7fad8f9e8fb96d087b2c744c16a8
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-08-20 03:20:44 +00:00
PlasticDigits commented 2026-08-20 03:54:13 +00:00 (Migrated from gitlab.com)

mentioned in issue #576

mentioned in issue #576
PlasticDigits commented 2026-08-20 03:54:21 +00:00 (Migrated from gitlab.com)

Merged via !1099.

make verify-issue-577 passed (docs, stale-bound unit tests, integration decay + overview + pair + traders + tokens). Remaining operator checks on a running indexer:

  • SELECT updated_at FROM global_stats_24h WHERE id = 1 advances; age > 15 min logs the stale-rollup warning without a live 30d scan
  • Restart refreshes token/trader windows immediately (not after the 5 min loop sleep)
Merged via !1099. `make verify-issue-577` passed (docs, stale-bound unit tests, integration decay + overview + pair + traders + tokens). Remaining operator checks on a running indexer: - `SELECT updated_at FROM global_stats_24h WHERE id = 1` advances; age > 15 min logs the stale-rollup warning without a live 30d scan - Restart refreshes token/trader windows immediately (not after the 5 min loop sleep)
PlasticDigits commented 2026-08-20 03:54:52 +00:00 (Migrated from gitlab.com)

mentioned in issue #583

mentioned in issue #583
PlasticDigits commented 2026-08-20 03:54:59 +00:00 (Migrated from gitlab.com)

marked as related to #583

marked as related to #583
PlasticDigits commented 2026-08-21 00:21:03 +00:00 (Migrated from gitlab.com)

mentioned in issue #586

mentioned in issue #586
PlasticDigits commented 2026-08-22 03:10:05 +00:00 (Migrated from gitlab.com)

mentioned in issue #589

mentioned in issue #589
PlasticDigits commented 2026-08-24 00:30:13 +00:00 (Migrated from gitlab.com)

mentioned in issue #613

mentioned in issue #613
PlasticDigits commented 2026-08-26 01:10:54 +00:00 (Migrated from gitlab.com)

mentioned in issue #652

mentioned in issue #652
PlasticDigits commented 2026-08-26 03:06:34 +00:00 (Migrated from gitlab.com)

mentioned in issue #655

mentioned in issue #655
PlasticDigits commented 2026-08-27 01:00:19 +00:00 (Migrated from gitlab.com)

mentioned in issue #682

mentioned in issue #682
PlasticDigits commented 2026-08-27 01:00:29 +00:00 (Migrated from gitlab.com)

mentioned in issue #683

mentioned in issue #683
PlasticDigits commented 2026-08-28 05:22:08 +00:00 (Migrated from gitlab.com)

mentioned in issue #692

mentioned in issue #692
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
code/cl8y-dex-terraclassic#577
No description provided.