Skip to content

Audit-token cache fast-fail fallback for recently-audited outputs #2176

Description

@Effi-S

Problem

Auditor-side transaction validation (AuditorCheck
common.RetrieveAuditTokens, token/core/common/auditor.go) can spuriously
fail when a transaction references — as inputs — outputs of a very
recently-audited transaction. The SQL-backed token store returns a hard
"token not found for key [...]" the moment a requested output row is
missing, and those rows are only written asynchronously by the finality
listener (token/services/ttx/finality/listener.go) once the network reports
the referenced transaction as final. A closely-following transaction can
therefore reach audit validation before those rows are persisted.

See parent issue #2105 for the full analysis of the race.

Scope of this issue

This tracks only the cache fast-fail solution (Suggestion 2 in #2105,
refined in the review on #2140), not the retry/backoff mitigation that
landed in #2140.

The idea: tokens.Service already maintains an in-memory RequestsCache,
populated at audit-approval time (AuditApproveView.Call,
token/services/ttx/auditor.go, via CacheRequest) and removed in
AppendValid (token/services/tokens/tokens.go). Its lifetime lines up
exactly with the race window, and the cached value is byte-identical to what
the DB read returns (owner_raw, token_type, quantity). Consulting this
cache on a DB miss lets audit validation resolve the in-flight token
immediately — fast — instead of relying on a multi-second backoff window
that is unbounded under load and only shrinks (never removes) the failure
probability.

Proposed shape:

  • DB read first; consult the tokens.Service cache only on a miss.
  • Replicate the auditor = true filter (Flags.Auditor) so a cached entry
    is only used where the DB read would have qualified.
  • token/core/common cannot import token/services/tokens directly, so this
    needs a consumer-side interface plus an adapter wired in the SDK, and
    tokens.Service (or a narrow view of it) plumbed into AuditorService
    construction for both the fabtoken and zkatdlog drivers — a dependency that
    does not exist in either construction chain today.

Known limits

The cache is in-process and best-effort (Ristretto's Set result is
discarded), so a process restart or a second auditor replica falls straight
through it. This is why the cache is a complement to the backoff, not a
replacement: the backoff remains the outer net for whatever the cache does
not cover.

Impact

Removes the multi-second latency the backoff adds to the common case (a
just-audited output referenced on the same node), turning a slow retry into
an immediate cache hit, while further reducing the spurious-failure rate for
quickly-chained transactions.

Metadata

Metadata

Assignees

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions