Skip to content

Commit aad90e8

Browse files
authored
fix: make balance hydration best-effort in RetrieveCurrentAccount (#1441)
When Position Keeping service is unavailable, RetrieveCurrentAccount now returns the account with empty balance instead of failing with an internal error. This aligns with the existing best-effort pattern used in ExecuteDeposit and ExecuteWithdrawal. Co-authored-by: Ben Coombs <bjcoombs@users.noreply.github.com>
1 parent 755a90f commit aad90e8

2 files changed

Lines changed: 20 additions & 19 deletions

File tree

services/current-account/service/grpc_account_endpoints.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -417,13 +417,14 @@ func (s *Service) RetrieveCurrentAccount(ctx context.Context, req *pb.RetrieveCu
417417

418418
// Hydrate account with balance from Position Keeping service.
419419
// Balance is no longer persisted locally - Position Keeping is the source of truth.
420-
account, err = s.hydrateAccountWithBalance(ctx, account)
420+
// Best-effort: return account without balance if Position Keeping is unavailable.
421+
hydratedAccount, err := s.hydrateAccountWithBalance(ctx, account)
421422
if err != nil {
422-
operationStatus = opStatusRetrieveFailed
423-
s.logger.Error("failed to retrieve balance from Position Keeping",
423+
s.logger.Warn("failed to retrieve balance from Position Keeping, returning account without balance",
424424
"account_id", req.AccountId,
425425
"error", err)
426-
return nil, status.Errorf(codes.Internal, "failed to retrieve account balance: %v", err)
426+
} else {
427+
account = hydratedAccount
427428
}
428429

429430
return &pb.RetrieveCurrentAccountResponse{

services/current-account/service/grpc_service_integration_test.go

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1458,14 +1458,14 @@ func TestPositionKeeping_MultiAssetAPI_InstrumentCodeMismatch(t *testing.T) {
14581458
logger: testLogger(),
14591459
}
14601460

1461-
// Retrieve account should fail due to instrument mismatch
1462-
_, err := svc.RetrieveCurrentAccount(ctx, &pb.RetrieveCurrentAccountRequest{
1461+
// Retrieve account should succeed with degraded response (best-effort)
1462+
resp, err := svc.RetrieveCurrentAccount(ctx, &pb.RetrieveCurrentAccountRequest{
14631463
AccountId: "ACC-MULTI-002",
14641464
})
14651465

1466-
require.Error(t, err, "Retrieve should fail due to instrument mismatch")
1467-
assert.Contains(t, err.Error(), "instrument code mismatch",
1468-
"Error should indicate instrument code mismatch")
1466+
require.NoError(t, err, "Retrieve should succeed even with instrument mismatch (best-effort)")
1467+
assert.NotNil(t, resp.Facility, "Account facility should be returned")
1468+
assert.Equal(t, int64(0), resp.Facility.CurrentBalance.CurrentBalance.Amount.Units, "Balance should be zero when hydration fails")
14691469
}
14701470

14711471
// Circuit Breaker Tests for Position Keeping Balance Queries
@@ -1526,8 +1526,9 @@ func (m *mockPositionKeepingClientWithGetBalanceFailure) GetAccountBalances(_ co
15261526
}, nil
15271527
}
15281528

1529-
// TestPositionKeeping_CircuitBreaker_GetBalanceFailure verifies error handling
1530-
// when GetAccountBalance fails.
1529+
// TestPositionKeeping_CircuitBreaker_GetBalanceFailure verifies that
1530+
// RetrieveCurrentAccount returns the account without balance when
1531+
// GetAccountBalance fails (best-effort degradation).
15311532
func TestPositionKeeping_CircuitBreaker_GetBalanceFailure(t *testing.T) {
15321533
db, ctx, cleanup := setupIntegrationTestDB(t)
15331534
defer cleanup()
@@ -1547,17 +1548,16 @@ func TestPositionKeeping_CircuitBreaker_GetBalanceFailure(t *testing.T) {
15471548
logger: testLogger(),
15481549
}
15491550

1550-
// Retrieve account - should fail because Position Keeping is unavailable
1551-
_, err := svc.RetrieveCurrentAccount(ctx, &pb.RetrieveCurrentAccountRequest{
1551+
// Retrieve account - should succeed with empty balance (best-effort)
1552+
resp, err := svc.RetrieveCurrentAccount(ctx, &pb.RetrieveCurrentAccountRequest{
15521553
AccountId: "ACC-CB-001",
15531554
})
15541555

1555-
// Verify error
1556-
require.Error(t, err, "Retrieve should fail when Position Keeping is unavailable")
1557-
st, ok := status.FromError(err)
1558-
require.True(t, ok, "Error should be gRPC status error")
1559-
assert.Equal(t, codes.Internal, st.Code(), "Should return Internal error")
1560-
assert.Contains(t, st.Message(), "Position Keeping", "Error should mention Position Keeping")
1556+
// Verify success with degraded response (zero balance, not hydrated)
1557+
require.NoError(t, err, "Retrieve should succeed even when Position Keeping is unavailable")
1558+
assert.NotNil(t, resp.Facility, "Account facility should be returned")
1559+
assert.Equal(t, int64(0), resp.Facility.CurrentBalance.CurrentBalance.Amount.Units, "Balance should be zero when Position Keeping is unavailable")
1560+
assert.Equal(t, int32(0), resp.Facility.CurrentBalance.CurrentBalance.Amount.Nanos, "Balance nanos should be zero when Position Keeping is unavailable")
15611561

15621562
// Verify Position Keeping was called
15631563
assert.Equal(t, 1, mockPosKeeping.getBalanceCalls, "GetAccountBalance should have been called once")

0 commit comments

Comments
 (0)