-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Labels
enhancementNew feature or requestNew feature or requestperformanceMelhoria de performanceMelhoria de performance
Description
Título: Optimize balance calculation with snapshots and deltas
Labels: critical, performance, balance-calculation
Prioridade: ALTA
Problema Atual (Ponto 5 do Feedback):
- Cálculo em memória somando muitas transações
- Timeout/performance em contas com alto volume
Solução Proposta:
-- Tabela de snapshots
CREATE TABLE balance_snapshots (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
wallet_id UUID NOT NULL REFERENCES wallets(id),
balance DECIMAL(19,4) NOT NULL,
currency CHAR(3) NOT NULL,
snapshot_date DATE NOT NULL,
last_transaction_id UUID NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
UNIQUE(wallet_id, snapshot_date),
INDEX idx_wallet_snapshot_date (wallet_id, snapshot_date DESC)
);// DataProvider - Balance Calculator Service
@Service
@Transactional(readOnly = true)
public class BalanceCalculatorService {
@Transactional(readOnly = true)
public Money calculateBalanceAt(String walletId, LocalDateTime pointInTime) {
LocalDate targetDate = pointInTime.toLocalDate();
// 1. Buscar último snapshot antes da data
Optional<BalanceSnapshot> snapshot =
snapshotRepository.findLatestSnapshotBefore(walletId, targetDate);
Money balance;
LocalDateTime fromDate;
if (snapshot.isPresent()) {
// Começar do snapshot
balance = Money.of(snapshot.get().getBalance(), snapshot.get().getCurrency());
fromDate = snapshot.get().getSnapshotDate().atStartOfDay();
} else {
// Começar do zero
balance = Money.of(BigDecimal.ZERO, getCurrencyForWallet(walletId));
fromDate = getWalletCreationDate(walletId);
}
// 2. Aplicar transações desde o snapshot até pointInTime
List<WalletTransaction> deltaTransactions =
transactionRepository.findByWalletIdAndTimestampBetween(
walletId, fromDate, pointInTime);
for (WalletTransaction tx : deltaTransactions) {
balance = applyTransaction(balance, tx);
}
return balance;
}
// Job para criar snapshots periodicamente
@Scheduled(cron = "0 0 2 * * *") // Todo dia às 2h
@Transactional
public void createDailySnapshots() {
LocalDate yesterday = LocalDate.now().minusDays(1);
List<String> activeWallets = walletRepository.findActiveWalletIds();
for (String walletId : activeWallets) {
if (!snapshotRepository.existsByWalletIdAndDate(walletId, yesterday)) {
createSnapshot(walletId, yesterday);
}
}
}
private void createSnapshot(String walletId, LocalDate date) {
Money balance = calculateBalanceAt(walletId, date.atTime(23, 59, 59));
// Encontrar a última transação do dia
WalletTransaction lastTransaction =
transactionRepository.findLastTransactionForDate(walletId, date);
BalanceSnapshot snapshot = BalanceSnapshot.builder()
.walletId(walletId)
.balance(balance.getAmount())
.currency(balance.getCurrency())
.snapshotDate(date)
.lastTransactionId(lastTransaction.getId())
.build();
snapshotRepository.save(snapshot);
}
}Tarefas:
-
Criar tabela balance_snapshots
-
Implementar BalanceCalculatorService
-
Criar job para snapshots diários
-
Indexar por (wallet_id, timestamp)
-
Implementar cache para consultas frequentes
@Cacheable(value = "currentBalances", key = "#walletId") public Money getCurrentBalance(String walletId) { return calculateBalanceAt(walletId, LocalDateTime.now()); }
-
Testes de performance com alto volume
-
Métricas de tempo de resposta
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or requestperformanceMelhoria de performanceMelhoria de performance