Skip to content

fix(orderbook): re-fetch stale fulfilled cache entries missing executedFee - #123

Merged
yvesfracari merged 2 commits into
mainfrom
fix/stale-executed-fee-cache
Jul 23, 2026
Merged

fix(orderbook): re-fetch stale fulfilled cache entries missing executedFee#123
yvesfracari merged 2 commits into
mainfrom
fix/stale-executed-fee-cache

Conversation

@yvesfracari

Copy link
Copy Markdown
Contributor

Stacked on #122.

Problem

Fulfilled discrete orders on the deployed indexer have executedFee: null even though the CoW orderbook API returns a fee for them (checked: GET /orders/{uid} returns executedFee: 277555925419363854 for an order our DB stores with a null fee). As a result the TWAP fee totals in additionalData sum to zero.

Root cause

executed_fee was added to cow_cache.order_uid_cache and cow_cache.composable_order via ADD COLUMN IF NOT EXISTS (setup.ts), so every row cached by earlier deployments has a null fee. Terminal entries are cached indefinitely and never re-fetched, so the null can never heal — every reindex re-serves it from cache.

Fix

  • fetchOrderStatusByUids: a fulfilled cache hit with a null executedFee is treated as a miss and re-fetched once (the fresh result overwrites the cache row, so it's one extra fetch per stale UID, ever). If the UID has aged out of /by_uids, the cached data is returned as a fallback instead of omitting the UID — callers interpret a missing UID as "not on API yet".
  • reconcileOpenCachedRows: fulfilled durable-cache rows with a null fee join the re-fetch set, so owner drains heal them too.
  • Expired/cancelled entries with a null fee are deliberately left alone: nothing was executed, so re-fetching them would be a mass of pointless API calls.
  • Coalesce in the remaining conflict sets (upsertDiscreteOrders, OrderStatusTracker, CandidateConfirmer): executed columns now coalesce(excluded.x, existing.x) — a cached null can never erase amounts written by an earlier fresh fetch. uidPrecompute already did this; these paths didn't. The drain's no-op filter now compares post-coalesce effective values so unchanged rows don't bump updatedAtBlock.

Verification

  • New regression suite: stale fulfilled entry → re-fetched; aged-out stale entry → answered from cache; complete entry → no network call.
  • pnpm typecheck, pnpm lint, pnpm test green (159 tests).

…res all three fields

Ponder's context.db.sql maps raw-SQL result rows positionally via
Object.values(row). The three unaliased coalesce(sum(...)) columns all
came back named "coalesce", collapsed into one key in the row object,
and the values shifted: the executedFee sum was stored under
executedSellAmount and the other two fields were dropped from the JSON
entirely (9,830 of 9,834 TWAP parents on the deployed DB show the
one-key shape, and the stored value matches the children's fee sum
exactly).

Unique .as() aliases keep the column names distinct so the positional
mapping is stable. Unit tests can't catch this (they mock the DB and
vanilla drizzle drivers use array row mode, which is immune), hence the
warning comment.
…edFee

cow_cache rows written before the executed_fee column existed carry a
null fee, and terminal entries are never re-fetched — so fulfilled
orders served from those rows keep executedFee null forever (and the
TWAP fee totals stay zero). The API does return the fee for these
orders; the indexer just never asks again.

- fetchOrderStatusByUids: a fulfilled cache hit with a null executedFee
  is now treated as a miss and re-fetched once; if the UID has aged out
  of /by_uids, the cached data is returned as a fallback instead of
  omitting the UID (which callers read as "not on API yet").
- reconcileOpenCachedRows: fulfilled durable-cache rows with a null fee
  join the re-fetch set, so owner drains heal them too. Expired and
  cancelled rows are left alone — nothing was executed.
- upsertDiscreteOrders / OrderStatusTracker / CandidateConfirmer:
  executed columns in the conflict sets now coalesce with the existing
  value, so a cached null can never erase amounts written by an earlier
  fresh fetch. The drain's no-op filter compares post-coalesce values
  so unchanged rows don't bump updatedAtBlock.
Base automatically changed from fix/twap-additional-data-aliases to main July 23, 2026 14:45
@yvesfracari
yvesfracari merged commit 93b2ed8 into main Jul 23, 2026
2 checks passed
@yvesfracari
yvesfracari deleted the fix/stale-executed-fee-cache branch July 23, 2026 14:45
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