Skip to content

Otimizar cálculo de saldo com snapshots #21

@wallanpsantos

Description

@wallanpsantos

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 requestperformanceMelhoria de performance

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions