The deposit arm of the transaction search, as a view.
Assembled inline, the 12-arm transaction UNION serialises to ~124KB. RDS
Proxy refuses to parse a statement over 16KB and reacts by pinning the client
session to its database connection for the remainder of that session,
permanently costing the proxy a multiplexable connection — prod logged 471
such pins in three hours, with pinned share ratcheting 5% -> 38%. Behind a
view the runtime statement is ~90 bytes.
The caller-supplied filters live in transaction-services/queries/deposit.ts
as predicates on this view's own projected columns, because a view takes no
parameters. They are plain contract-column comparisons emitted by the shared
transactionScalarConditions, so what matters here is what each filtered
column projects:
customer_id — t.customer_id on the credit and fee-debit sub-arms,
fee_customer.external_id on the fee-credit sub-arm.
signature — t.signature on the credit and fee-debit sub-arms (a fee
debit has no receipt of its own, so it carries the parent deposit's),
t.fee_sweep_signature on the fee-credit sub-arm.
bank_id — the coalesce() over the deposit's own and its bank
transaction's bank.
Changing what those three project silently changes the filters. The column
list must also stay identical to the sibling arms, or the UNION breaks.
dbmate owns the DDL: the body below is mirrored by
20260728235500_deposit_transaction_view.sql, exactly as the tables here
mirror their own migrations.
securityInvoker is required, not cosmetic — every underlying table has RLS
enabled, and without it the view would run with the owner's row visibility
and silently bypass the querying role's policies.
The deposit arm of the transaction search, as a view.
Assembled inline, the 12-arm transaction UNION serialises to ~124KB. RDS Proxy refuses to parse a statement over 16KB and reacts by pinning the client session to its database connection for the remainder of that session, permanently costing the proxy a multiplexable connection — prod logged 471 such pins in three hours, with pinned share ratcheting 5% -> 38%. Behind a view the runtime statement is ~90 bytes.
The caller-supplied filters live in
transaction-services/queries/deposit.tsas predicates on this view's own projected columns, because a view takes no parameters. They are plain contract-column comparisons emitted by the sharedtransactionScalarConditions, so what matters here is what each filtered column projects:customer_id—t.customer_idon the credit and fee-debit sub-arms,fee_customer.external_idon the fee-credit sub-arm.signature—t.signatureon the credit and fee-debit sub-arms (a fee debit has no receipt of its own, so it carries the parent deposit's),t.fee_sweep_signatureon the fee-credit sub-arm.bank_id— the coalesce() over the deposit's own and its bank transaction's bank.Changing what those three project silently changes the filters. The column list must also stay identical to the sibling arms, or the UNION breaks.
dbmate owns the DDL: the body below is mirrored by
20260728235500_deposit_transaction_view.sql, exactly as the tables here mirror their own migrations.securityInvokeris required, not cosmetic — every underlying table has RLS enabled, and without it the view would run with the owner's row visibility and silently bypass the querying role's policies.