operations table: Store the operation_xdr as BYTEA#497
operations table: Store the operation_xdr as BYTEA#497aditya1702 merged 26 commits intohash-byteafrom
operations table: Store the operation_xdr as BYTEA#497Conversation
Part of the plan to store XDR data as raw bytes for efficiency.
Similar pattern to HashBytea but uses base64 encoding (standard for XDR) and supports variable-length data.
Changed OperationXDR field from string to XDRBytea for automatic base64/BYTEA conversion.
Cast the base64 XDR string to XDRBytea for proper database storage.
- Change UNNEST cast from text[] to bytea[] - Convert XDRBytea to raw bytes in BatchInsert - Update BatchCopy to pass raw bytes instead of pgtype.Text
Forces GraphQL to use a resolver for operationXdr to convert XDRBytea to base64 string for API consumers.
- Update test_utils.go to use types.XDRBytea - Regenerate GraphQL code with OperationXdr resolver
Returns the XDRBytea as a base64-encoded string for API consumers.
- Use parameterized queries with XDRBytea for SQL inserts - Update assertions to use .String() for comparison - Fix type casting in test data creation
- Use base64-encoded test XDR data in test_utils.go - Add testOpXDR helper functions in test files - Update all assertions to use .String() method
- Update createTestOperation to use proper base64 XDR - Update generateTestOperations to encode test data as base64 - Update operations_test.go with base64 encoding for all test XDR data - Fix assertions to compare against the stored XDRBytea values
This simplifies the XDR storage flow by storing raw bytes directly instead of encoding to base64 and then decoding. The String() method now handles base64 encoding for external representation.
Skip the intermediate base64 encoding step by using MarshalBinary() instead of MarshalBase64(). The raw bytes are now stored directly in XDRBytea.
Remove unnecessary Value() calls since XDRBytea is now []byte. Access raw bytes directly via type conversion.
Decode expected base64 XDR string to raw bytes for comparison since XDRBytea now uses []byte underlying type.
Use raw bytes directly instead of base64-encoded strings when creating test data for XDRBytea fields.
Use raw bytes directly for test XDR data instead of base64-encoding. The String() method will handle base64 encoding for assertions.
Use raw bytes directly instead of pre-encoded base64 string.
Use parameterized queries instead of raw SQL string literals for BYTEA operation_xdr column. Fix .String() assertion to compare base64 values via opXdr1.String().
Use parameterized queries instead of raw SQL string literals for BYTEA operation_xdr column in BatchGetByOperationIDs and BatchGetByStateChangeIDs tests.
Use parameterized queries instead of raw SQL string literals for BYTEA operation_xdr column in BatchGetByOperationIDs test.
Copy the byte slice from the database driver instead of referencing it directly. The pgx driver reuses its internal buffer across rows, so without copying, all scanned XDRBytea values end up pointing to the same (overwritten) buffer.
operations table: Store the operation_xdr as BYTEA
There was a problem hiding this comment.
Pull request overview
Updates the operations.operation_xdr storage to use PostgreSQL BYTEA (raw XDR bytes) while preserving the GraphQL API contract by continuing to expose operationXdr as a base64-encoded String.
Changes:
- Change
operations.operation_xdrfromTEXTtoBYTEAin the schema/migration and update DB write paths accordingly. - Introduce
types.XDRByteato scan/store raw bytes and encode to base64 when presented as a string. - Update GraphQL schema/resolvers and tests to reflect the new storage/encoding behavior.
Reviewed changes
Copilot reviewed 15 out of 16 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| internal/services/ingest_test.go | Updates ingest test fixtures to use types.XDRBytea. |
| internal/serve/graphql/schema/operation.graphqls | Forces resolver for operationXdr so GraphQL continues returning a base64 string. |
| internal/serve/graphql/resolvers/transaction_resolvers_test.go | Updates expectations for base64-encoded XDR output. |
| internal/serve/graphql/resolvers/test_utils.go | Stores operation XDR as raw bytes in test DB setup. |
| internal/serve/graphql/resolvers/queries_resolvers_test.go | Updates query resolver tests to compare base64 encoding of raw bytes. |
| internal/serve/graphql/resolvers/operation.resolvers.go | Adds OperationXdr field resolver to base64-encode XDRBytea. |
| internal/serve/graphql/resolvers/account_resolvers_test.go | Updates account resolver tests for base64-encoded XDR output. |
| internal/serve/graphql/generated/generated.go | Regenerates gqlgen output to wire the forced resolver. |
| internal/indexer/types/types.go | Adds XDRBytea type and updates types.Operation.OperationXDR to use it. |
| internal/indexer/processors/utils_test.go | Updates ConvertOperation test to compare raw bytes vs base64 string. |
| internal/indexer/processors/utils.go | Stores raw XDR bytes via MarshalBinary() instead of base64 strings. |
| internal/db/migrations/2025-06-10.3-operations.sql | Changes operation_xdr column type to BYTEA (but see migration concern). |
| internal/data/transactions_test.go | Updates test inserts to pass XDRBytea for operation_xdr. |
| internal/data/operations_test.go | Updates operation model tests for bytea storage and base64 string comparisons. |
| internal/data/operations.go | Updates batch insert/copy paths to send operation_xdr as bytea. |
| internal/data/accounts_test.go | Updates account model tests inserting operations to use XDRBytea. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Code Review — Unsafe type assertions on
|
Code Review — Thresholds and SignerWeights resolvers silently map NULL to 0Severity: Medium (incorrect data for GraphQL clients) The wallet-backend/internal/serve/graphql/resolvers/statechange.resolvers.go Lines 275 to 280 in b84442d When a threshold is first set (no prior state), The wallet-backend/internal/serve/graphql/resolvers/statechange.resolvers.go Lines 238 to 247 in b84442d The correct pattern exists in wallet-backend/internal/serve/graphql/resolvers/statechange.resolvers.go Lines 349 to 364 in b84442d |
Code Review — Getter methods return internal maps without cloning (data race risk)Severity: Medium (latent data race)
wallet-backend/internal/indexer/indexer_buffer.go Lines 169 to 175 in b84442d wallet-backend/internal/indexer/indexer_buffer.go Lines 349 to 355 in b84442d The safe pattern is already used by wallet-backend/internal/indexer/indexer_buffer.go Lines 590 to 597 in b84442d |
Code Review — XDRBytea.Value() returns aliased slice (latent correctness hazard)Severity: Low (defensive programming)
wallet-backend/internal/indexer/types/types.go Lines 190 to 196 in b84442d The old string-based For comparison, the fixed wallet-backend/internal/indexer/types/types.go Lines 175 to 188 in b84442d |
Code Review — Cursor values interpolated directly into SQL instead of parameterizedSeverity: Low (code consistency)
wallet-backend/internal/data/statechanges.go Lines 521 to 530 in b84442d Compare with the parameterized approach in wallet-backend/internal/data/statechanges.go Lines 69 to 75 in b84442d |
What
Updates the operations.operation_xdr storage to use PostgreSQL BYTEA (raw XDR bytes) while preserving the GraphQL API contract by continuing to expose operationXdr as a base64-encoded String.
Changes:
Change operations.operation_xdr from TEXT to BYTEA in the schema/migration and update DB write paths accordingly.
Introduce types.XDRBytea to scan/store raw bytes and encode to base64 when presented as a string.
Update GraphQL schema/resolvers and tests to reflect the new storage/encoding behavior.
Why
Storage optimization for our DB
Known limitations
N/A
Issue that this PR addresses
Closes #492