Skip to content

fix(agbossjs/agboss#771): the ledger's entries are pruned under a mark — migration 0023; core at a8fe1124 - #44

Merged
PavelRavvich merged 1 commit into
mainfrom
fix/771-ledger-entry-retention
Nov 5, 2026
Merged

PavelRavvich merged 1 commit into
mainfrom
fix/771-ledger-entry-retention

Conversation

@PavelRavvich

Copy link
Copy Markdown
Member

Refs agbossjs/agboss#771

What changed

  • Migration 0023_cost_entries_pruned adds:

    • agboss_cost_entries.made_at with an index. Rows written before 0023 get 'infinity', so they are never pruned and never refused; their pass's retry finds them held.
    • agboss_cost_entries_pruned, a one-row table holding the pruning mark, seeded at '-infinity'.
    • A COMMENT ON TABLE on agboss_cost_entries.

    0022 stays unchanged because migrations are checksummed. 0023's header corrects 0022's: a ledgerId is the pass's own id, and a step run again counts again. The README's row for 0022 now says the same.

  • saveAll is one statement. It works like this:

    • It reads the mark with FOR SHARE. If a prune's UPDATE of the mark is in progress, this read waits and then sees the new mark, because a locked row is read at its latest version.
    • An entry made before the mark is never inserted.
    • If an entry made before the mark is not held, the statement adds nothing at all: no entries, no counters. It answers the refused ids, and the store throws CostLedgerEntryExpiredError.
    • If the mark row is missing (deleted by hand), the save adds nothing and throws PostgresLedgerMarkMissingError (postgres.ledger-mark-missing). Without that check, a save would silently drop its entries.
  • deleteEntriesByMadeAtBefore is one statement: UPDATE … SET pruned_before = greatest(…) (the mark only goes up), then DELETE … WHERE made_at < mark.

  • The test reset (truncateAll) seeds the mark again, as the migration does.

How it was tested

  • npm run check and npm run test:faults green against PostgreSQL 16 under the lock, at 2a5b4641 with the core packed at a8fe1124.
  • The core's costLedgerContract now includes the 4 #771 pruning cases. They run here through storeConformance against the real database.
  • tests/stores/cost-ledger.test.ts:
    • 40 lost answers are retried on two instances while a third instance prunes 10 times with rising cutoffs. Every entry counts once ($5) and none is left.
    • Bite: with FOR SHARE removed, the same test counts $5.875 for $5, i.e. 7 entries counted twice. With the mark filter removed, it fails too.
    • With the mark row deleted, a save adds nothing (counters included) and names the row.
  • tests/migrations/cost-entries-migration.test.ts: 0023 run on a database at 0022. An entry held from before is dated 'infinity'; a prune at 2030 keeps it, and a save of it adds nothing.
  • Every migration test that lists the later migrations now includes 0023: versions 23, 24 for the "newer package" cases, and the CLI's lines.

Wiki to update after merge

None here. agbossjs/agboss Wiki → FinOps "Pruning the entries (#771)" and Your-own-store already describe this store (published with agbossjs/agboss#783).

Deviations from the spec

None

Not auto-verifiable

None

🤖 Generated with Claude Code

https://claude.ai/code/session_012Kh8JmysuhZyQRSpNyJdPg

…k — migration 0023; a save of a pruned entry refused, never counted twice; core at a8fe1124

The core (agbossjs/agboss#771) dates every ledger entry by its `madeAt`, prunes them with
`deleteEntriesByMadeAtBefore` and refuses a save of one made before the store's pruning mark that it
no longer holds.

- migration 0023_cost_entries_pruned: `agboss_cost_entries.made_at` (indexed; rows of before dated
  'infinity': never pruned, never refused) and `agboss_cost_entries_pruned`, the one-row mark,
  seeded at '-infinity'. Its header corrects 0022's (kept as is: migrations are checksummed), which
  described ids derived from the step, a design that was dropped.
- saveAll: one statement — the mark read FOR SHARE (a prune's UPDATE waits, or the save waits and
  reads the new mark), an entry made before it never inserted, one not held refusing the whole save
  (CostLedgerEntryExpiredError). A missing mark row fails the save with
  PostgresLedgerMarkMissingError and adds nothing.
- deleteEntriesByMadeAtBefore: the mark raised (greatest) and the entries deleted in one statement.
- tests: the contract's four pruning cases; 40 lost answers retried on two instances while a third
  prunes ten times count once each (red without FOR SHARE: $5.875 for $5); the mark row missing;
  0023 on a database at 0022.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Kh8JmysuhZyQRSpNyJdPg
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