Skip to content

fix(storage): address misc db/sql/common findings - #2409

Merged
Effi-S merged 1 commit into
mainfrom
2042
Sep 30, 2026
Merged

Effi-S merged 1 commit into
mainfrom
2042

Conversation

@AkramBitar

Copy link
Copy Markdown
Contributor

Fixes a batch of medium/low findings from a bug-hunting review of the shared SQL
storage layer (token/services/storage/db/sql/common).

Correctness

  • keystore.Put() reported success on a real value conflict — it wrapped an
    already-nil error, and wrapping nil yields nil.
  • Foreign-key violations are detected from the driver's typed error (Postgres
    23503, SQLITE_CONSTRAINT_FOREIGNKEY) instead of the message text, whose
    wording is not part of any driver's contract.
  • IdentityExists returns (bool, error), so a failed lookup no longer reads as
    "not found".
  • tokenlock.Lock returned nil for any error that wasn't a unique-key
    violation, telling the caller it held a lock it did not.

Performance

  • SetSpendableBySupportedTokenFormats no longer rewrites the whole Tokens
    table on every reconciliation; both updates are predicated on the flag actually
    having to change. This also fixes a latent bug: with no supported format, every
    token was marked spendable.

Robustness

  • The prepared-statement cache evicts a statement that fails at execute time, so
    the next call re-prepares instead of degrading to the unprepared path forever.

Cleanup

  • Use the FSC errors package instead of fmt.Errorf, wrap or log previously raw
    or discarded errors, and delete the callerless legacy query builder in
    query.go.

Also adds a cond.Not combinator, regression tests for each behavioural fix,
real-driver dbtest cases for the keystore conflict and the foreign-key mapping,
and a "Store Contract Notes" section in docs/development/storage.md.

Two findings from the issue needed no change here: the StorePublicParams
check-then-insert race was already closed by ON CONFLICT DO NOTHING, and
tablename.go's fmt.Errorf calls were already converted, both on main.

Fixes #2042

@AkramBitar AkramBitar added this to the Q3/26 milestone Sep 22, 2026
@AkramBitar AkramBitar self-assigned this Sep 22, 2026
@AkramBitar
AkramBitar marked this pull request as draft September 22, 2026 21:26
@Effi-S
Effi-S self-requested a review September 23, 2026 08:35

@Effi-S Effi-S left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Comment thread token/services/storage/db/sql/query/cond/not.go Outdated
@Effi-S
Effi-S marked this pull request as ready for review September 23, 2026 09:28
@AkramBitar
AkramBitar force-pushed the 2042 branch 3 times, most recently from 0346703 to 89a0635 Compare September 23, 2026 12:19
@Effi-S
Effi-S self-requested a review September 23, 2026 12:28
AkramBitar added a commit that referenced this pull request Sep 23, 2026
Adopt the WalletStoreService.IdentityExists signature introduced in #2409,
so a failed lookup is no longer indistinguishable from "the binding does
not exist".

Both implementations are updated. The KVS store previously swallowed the
composite-key error and answered false; it now wraps it. Only key
construction can be reported that way, because this backend's Exists is
FSC's GetExisting, which drops the underlying store error - the same
caveat already documented on GetWalletID.

Registry.ContainsIdentity cannot grow an error return: it backs
driver.Wallet.Contains, which returns a plain bool. It therefore logs the
failure before reporting false, so a storage problem is at least visible.

This duplicates the IdentityExists slice of #2409 verbatim so the two
branches converge without conflict; whichever lands second drops the
hunks as already applied.

Signed-off-by: AkramBitar <akram@il.ibm.com>
Comment thread token/services/storage/db/sql/common/transactions.go
Comment thread token/services/storage/db/sql/common/sqlerrors.go
Comment thread token/services/storage/db/sql/common/prepared_stmt_holder.go
Comment thread token/services/storage/db/sql/common/sqlerrors.go
Fixes a batch of medium/low findings from a bug-hunting review of the shared
SQL storage layer (token/services/storage/db/sql/common).

Correctness
- keystore.Put() reported success on a real value conflict: it wrapped an
  already-nil error, and wrapping nil yields nil.
- Foreign-key violations are detected from the code the driver reports through
  its own typed error (Postgres 23503, SQLITE_CONSTRAINT_FOREIGNKEY) rather
  than from the message text, whose wording is not part of any driver's
  contract and has changed across releases. A driver exposing neither accessor
  is deliberately left unclassified, which is the conservative branch in every
  caller.
- IdentityExists returns (bool, error), so a failed lookup no longer reads as
  "not found".
- tokenlock.Lock returned nil for any error that was not a unique-key
  violation, telling the caller it held a lock it did not.

Performance
- SetSpendableBySupportedTokenFormats no longer rewrites the whole Tokens table
  on every reconciliation; both updates are predicated on the flag actually
  having to change. This also fixes a latent bug: with no supported format,
  every token was marked spendable.

Robustness
- The prepared-statement cache evicts a statement that fails at execute time,
  so the next call re-prepares instead of degrading to the unprepared path
  forever. Eviction is narrow on purpose: an ordinary failure such as a
  constraint violation leaves a perfectly valid statement, and a permanent
  failure unrelated to validity would add a DEALLOCATE to every call. Only the
  two PostgreSQL states that mean the statement itself is gone qualify;
  SQLITE_SCHEMA is left out because modernc.org/sqlite prepares through
  sqlite3_prepare_v2 and SQLite re-prepares transparently instead of returning
  the code, so no real backend can reach such a branch.

Cleanup
- Use the FSC errors package instead of fmt.Errorf, wrap or log previously raw
  or discarded errors, and delete the callerless legacy query builder in
  query.go.

Also adds a cond.Not combinator, regression tests for each behavioural fix,
real-driver dbtest cases for the keystore conflict and the foreign-key
mapping, and a "Store Contract Notes" section in docs/development/storage.md.

The foreign-key code table is local because FSC's per-driver ErrorMapper, which
these stores already inject to recognise UniqueKeyViolation, has no
ForeignKeyViolation sentinel to map onto; when it gains one, the predicate
should give way to the injected SQLErrorWrapper rather than a second table.

Fixes #2042

Signed-off-by: AkramBitar <akram@il.ibm.com>
@Effi-S
Effi-S merged commit 3b59639 into main Sep 30, 2026
215 checks passed
@Effi-S
Effi-S deleted the 2042 branch September 30, 2026 15:15
Effi-S pushed a commit that referenced this pull request Oct 1, 2026
Adopt the WalletStoreService.IdentityExists signature introduced in #2409,
so a failed lookup is no longer indistinguishable from "the binding does
not exist".

Both implementations are updated. The KVS store previously swallowed the
composite-key error and answered false; it now wraps it. Only key
construction can be reported that way, because this backend's Exists is
FSC's GetExisting, which drops the underlying store error - the same
caveat already documented on GetWalletID.

Registry.ContainsIdentity cannot grow an error return: it backs
driver.Wallet.Contains, which returns a plain bool. It therefore logs the
failure before reporting false, so a storage problem is at least visible.

This duplicates the IdentityExists slice of #2409 verbatim so the two
branches converge without conflict; whichever lands second drops the
hunks as already applied.

Signed-off-by: AkramBitar <akram@il.ibm.com>

Signed-off-by: Effi-S <effi.szt@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

db/sql/common: misc medium/low findings (keystore Put silent success, FK detection, perf)

2 participants