Solution/mritunjay kumar - #119
Open
mritunjaysingh22 wants to merge 3 commits into
Open
Conversation
There was a problem hiding this comment.
Pull request overview
Implements a Go/PostgreSQL wallet-to-wallet transfer service with transactional balance updates, idempotency, and integration tests, exposing the operation via an HTTP endpoint.
Changes:
- Adds transactional transfer workflow (row locks, balance updates, ledger entries, retry on serialization/deadlock).
- Introduces PostgreSQL schema + sqlc queries/models, plus Makefile targets for local workflows.
- Adds integration tests covering success, validation, idempotency, concurrency, ledger correctness, and failure scenarios.
Reviewed changes
Copilot reviewed 21 out of 29 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/transfer_service_test.go | Integration tests for basic transfer flows and validations |
| tests/ledger_test.go | Integration tests validating debit/credit ledger entry behavior |
| tests/idempotency_test.go | Integration tests for idempotent retries and distinct keys |
| tests/helper_test.go | Test harness: DB pool setup and helper functions |
| tests/failure_test.go | Integration tests for failure/validation scenarios |
| tests/concurrency_test.go | Integration tests for concurrent transfer behaviors |
| sqlc.yaml | sqlc configuration for generating Go db layer from SQL |
| sql/queries/wallet.sql | Wallet queries (including FOR UPDATE read + balance update) |
| sql/queries/transfer.sql | Transfer queries (idempotency lookup + create + status update) |
| sql/queries/ledger.sql | Ledger entry insert and query-by-transfer |
| sql/migrations/20260721105133_init_schema.sql | Initial database schema (wallets, transfers, ledger_entries) |
| Makefile | Local developer targets (build/run/migrate/test/coverage/etc.) |
| internal/service/transfer_service.go | Core transfer workflow (tx, locks, idempotency, ledger, retries) |
| internal/routes/routes.go | HTTP routing (health + POST /api/v1/transfers) |
| internal/logger/logger.go | Placeholder logger package |
| internal/handler/transfer_handler.go | HTTP handler for transfer creation and error mapping |
| internal/db/wallet.sql.go | sqlc-generated wallet query bindings |
| internal/db/transfer.sql.go | sqlc-generated transfer query bindings |
| internal/db/querier.go | sqlc-generated Querier interface |
| internal/db/postgres.go | Postgres pool creation + connectivity verification |
| internal/db/models.go | sqlc-generated models/enums |
| internal/db/ledger.sql.go | sqlc-generated ledger query bindings |
| internal/db/db.go | sqlc DBTX abstraction + WithTx support |
| internal/config/config.go | App config loading (env/.env) |
| cmd/server/main.go | Application wiring: config, db, service, handler, router, server |
| .gitignore | Ignore patterns for env/IDE/build artifacts |
| go.mod | Module definition and dependencies |
| go.sum | Dependency checksums |
| coverage.out | Coverage profile artifact committed into the repo |
Files not reviewed (6)
- internal/db/db.go: Generated file
- internal/db/ledger.sql.go: Generated file
- internal/db/models.go: Generated file
- internal/db/querier.go: Generated file
- internal/db/transfer.sql.go: Generated file
- internal/db/wallet.sql.go: Generated file
|
|
||
| DB_URL=postgres://postgres:postgres@localhost:5432/wallet_transfer?sslmode=disable | ||
|
|
||
| GOOSE_DIR=sql/schema |
Comment on lines
+243
to
+251
| req := service.TransferRequest{ | ||
| IdempotencyKey: uuid.NewString(), | ||
| FromWalletID: from, | ||
| ToWalletID: to, | ||
| Amount: amount, | ||
| } | ||
|
|
||
| _, _ = transferService.CreateTransfer(ctx, req) | ||
|
|
Comment on lines
+71
to
+73
| _, _ = testPool.Exec(ctx, "DELETE FROM ledger_entries") | ||
| _, _ = testPool.Exec(ctx, "DELETE FROM transfers") | ||
| _, _ = testPool.Exec(ctx, "DELETE FROM wallets") |
Comment on lines
+148
to
+159
| // defer func() { | ||
| // if p := recover(); p != nil { | ||
| // _ = tx.Rollback(ctx) | ||
| // panic(p) | ||
| // } | ||
|
|
||
| // // Safe to call even after Commit() | ||
| // if err != nil { | ||
| // _ = tx.Rollback(ctx) | ||
| // } | ||
| // }() | ||
|
|
Comment on lines
+231
to
+234
| existing, err := s.queries.GetTransferByIdempotencyKey( | ||
| ctx, | ||
| req.IdempotencyKey, | ||
| ) |
Comment on lines
+43
to
+66
| case errors.Is(err, service.ErrInvalidAmount): | ||
| writeError( | ||
| w, | ||
| http.StatusBadRequest, | ||
| err.Error(), | ||
| ) | ||
| return | ||
|
|
||
| case errors.Is(err, service.ErrSameWallet): | ||
| writeError( | ||
| w, | ||
| http.StatusBadRequest, | ||
| err.Error(), | ||
| ) | ||
| return | ||
|
|
||
| case errors.Is(err, service.ErrInsufficientBalance): | ||
| writeError( | ||
| w, | ||
| http.StatusConflict, | ||
| err.Error(), | ||
| ) | ||
| return | ||
|
|
Comment on lines
+21
to
+30
| CREATE TABLE transfers ( | ||
| id UUID PRIMARY KEY, | ||
| idempotency_key VARCHAR(255) NOT NULL UNIQUE, | ||
| from_wallet_id UUID NOT NULL REFERENCES wallets(id), | ||
| to_wallet_id UUID NOT NULL REFERENCES wallets(id), | ||
| amount BIGINT NOT NULL CHECK (amount > 0), | ||
| status transfer_status NOT NULL, | ||
| created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), | ||
| updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() | ||
| ); |
Comment on lines
+44
to
+46
| CREATE INDEX idx_transfer_idempotency | ||
| ON transfers(idempotency_key); | ||
|
|
Comment on lines
+1
to
+2
| mode: set | ||
| github.com/mritunjaysingh22/wallet-transfer-assignment/internal/service/transfer_service.go:51.20,57.2 1 1 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implemented a wallet transfer service with a strong focus on correctness, transactional integrity, idempotency, and concurrency safety using PostgreSQL and Go.
The solution provides:
AI Disclosure
1. AI tools used
I used ChatGPT (GPT-5.5) to assist with the implementation and review of the assignment.
2. How I used the tool
The AI was used as an engineering assistant throughout development. Specifically, I used it to:
All design decisions, implementation, debugging, and final verification were completed and validated manually.
3. Prompts used
The AI was used with prompts covering topics such as:
Schema Design
The following tables were introduced:
wallets
Stores the current balance for each wallet.
Columns include:
idbalancetransfers
Stores every transfer request.
Important constraints:
ididempotency_keyStatus values:
PENDINGPROCESSEDledger_entries
Stores immutable accounting records.
Each successful transfer creates:
Foreign key relationships ensure ledger entries always belong to a valid transfer and wallet.
Idempotency Strategy
Every transfer request requires an Idempotency-Key.
Processing flow:
idempotency_key.This guarantees clients can safely retry requests without creating duplicate transfers.
Concurrency Strategy
The implementation prevents race conditions and double spending using PostgreSQL transactions and row-level locks.
Key techniques include:
SELECT ... FOR UPDATE.Additionally, retry logic is implemented for PostgreSQL serialization failures (
40001) and deadlock errors (40P01).How to Run
Or:
(if using the provided Makefile)
How to Test
Run all tests:
go test ./testsRun with race detection:
go test -race ./testsGenerate service coverage:
go test ./tests \ -coverpkg=./internal/service \ -coverprofile=coverage.out go tool cover -func=coverage.outTradeoffs / Assumptions
walletstable while ledger entries provide an immutable audit trail.Checklist