RecordTransactionWithBalances atomically records a transaction and updates both source and destination balances within a single database transaction. This ensures that either all operations succeed together, or none of them are committed, preventing inconsistent ledger states. Parameters: - ctx: Co
(ctx context.Context, txn *model.Transaction, sourceBalance, destinationBalance *model.Balance)
| 209 | // Returns: |
| 210 | // - The recorded transaction if successful, or an error if any operation fails. |
| 211 | func (d Datasource) RecordTransactionWithBalances(ctx context.Context, txn *model.Transaction, sourceBalance, destinationBalance *model.Balance) (*model.Transaction, error) { |
| 212 | ctx, span := otel.Tracer("transaction.database").Start(ctx, "RecordTransactionWithBalances") |
| 213 | defer span.End() |
| 214 | |
| 215 | tx, err := d.Conn.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelDefault}) |
| 216 | if err != nil { |
| 217 | span.RecordError(err) |
| 218 | return nil, apierror.NewAPIError(apierror.ErrInternalServer, "Failed to begin transaction", err) |
| 219 | } |
| 220 | |
| 221 | defer func(tx *sql.Tx) { |
| 222 | _ = tx.Rollback() |
| 223 | }(tx) |
| 224 | |
| 225 | if err := updateBalance(ctx, tx, sourceBalance); err != nil { |
| 226 | span.RecordError(err) |
| 227 | return nil, err |
| 228 | } |
| 229 | |
| 230 | if err := updateBalance(ctx, tx, destinationBalance); err != nil { |
| 231 | span.RecordError(err) |
| 232 | return nil, err |
| 233 | } |
| 234 | |
| 235 | if err := recordTransactionInTx(ctx, tx, txn); err != nil { |
| 236 | span.RecordError(err) |
| 237 | return nil, err |
| 238 | } |
| 239 | |
| 240 | if err := tx.Commit(); err != nil { |
| 241 | span.RecordError(err) |
| 242 | return nil, apierror.NewAPIError(apierror.ErrInternalServer, "Failed to commit transaction", err) |
| 243 | } |
| 244 | |
| 245 | span.AddEvent("Transaction and balances recorded atomically", trace.WithAttributes( |
| 246 | attribute.String("transaction.id", txn.TransactionID), |
| 247 | attribute.String("source.balance_id", sourceBalance.BalanceID), |
| 248 | attribute.String("destination.balance_id", destinationBalance.BalanceID), |
| 249 | )) |
| 250 | |
| 251 | return txn, nil |
| 252 | } |
| 253 | |
| 254 | // RecordTransactionWithBalancesAndOutbox atomically records a transaction, updates balances, |
| 255 | // and optionally inserts a lineage outbox entry within a single database transaction. |