DNATech :: Quando múltiplas threads mascaram problemas de arquitetura: uma refatoração real no TOTVS Protheus

## Quando múltiplas threads mascaram problemas de arquitetura: uma refatoração real no TOTVS Protheus

Em muitos projetos no TOTVS Microsiga Protheus, principalmente em rotinas legadas de faturamento, precificação e atualização massiva de tabelas como `DA1`, `SB1` e `SB2`, existe um padrão muito comum:

> O processo está lento?
> Então “joga thread”.

Na prática, isso normalmente significa criar múltiplas `threads`, `StartJob`, processos paralelos ou dividir cargas artificialmente para tentar compensar gargalos estruturais do código.

O problema é que, na maioria dos casos, isso não resolve a causa raiz. Apenas mascara.

---

## O cenário clássico

Encontramos recentemente uma rotina extremamente lenta de carga e gravação de preços.

O comportamento era o seguinte:

* Loop registro a registro.
* `dbSeek` para cada item.
* `RecLock` individual.
* `dbAppend` repetitivo.
* Atualizações linha a linha.
* Busca SQL dentro de loops.
* Reprocessamento desnecessário.
* Concorrência artificial via múltiplas threads.

O resultado?

* Alto consumo de CPU.
* Excesso de roundtrip entre AppServer e banco.
* Locking excessivo.
* Timeout no DBAccess.
* Crescimento exponencial do tempo de processamento.
* Necessidade “aparente” de paralelismo.

Ou seja:

O sistema não precisava de mais threads.
Precisava de menos loops.

---

## O grande problema das múltiplas threads em customizações Protheus

Muita gente trata thread como solução de performance.

Mas em ambientes Microsoft SQL Server + DBAccess + AppServer, múltiplas threads podem:

* aumentar contenção de lock;
* gerar disputa de recursos;
* elevar uso de TEMPDB;
* piorar deadlocks;
* aumentar timeout;
* elevar custo de sincronização;
* mascarar queries ruins;
* esconder arquitetura ineficiente.

Na prática:

> Você paraleliza o problema ao invés de resolver o problema.

---

## O verdadeiro gargalo: processamento registro a registro

O maior vilão normalmente não é o banco.

É o padrão:

```advpl
dbSeek()
RecLock()
campo := valor
MsUnlock()
```

Executado milhares de vezes.

Ou pior:

```advpl
While !EOF()
    U_SeekSQL(...)
    DBSkip()
EndDo
```

O famoso anti-pattern:

* N+1 Query
* Row-by-row processing
* Registro a registro

Extremamente comum em customizações antigas do Protheus.

---

## A refatoração: mudar o paradigma

Ao invés de “acelerar o loop”, a ideia foi:

### Eliminar o loop

A solução aplicada foi baseada em:

* `UPDATE FROM`
* `INSERT INTO SELECT`
* operações em lote;
* uso correto de índices;
* tratamento manual de `R_E_C_N_O_`;
* cálculo sequencial de `???_ITEM`;
* `LEFT JOIN` correto;
* redução de roundtrips;
* minimização de locks.

---

## O impacto real

O mais interessante foi:

### O novo modelo conseguiu atender a regra de negócio inteira usando apenas uma thread.

Sem necessidade de paralelismo artificial.

Ou seja:

* menos consumo;
* menos lock;
* menos timeout;
* menos stress no DBAccess;
* menos disputa de recursos;
* mais previsibilidade;
* mais estabilidade;
* mais velocidade.

---

## O principal aprendizado

Threads não substituem arquitetura.

Muitas vezes elas apenas escondem:

* query mal escrita;
* acesso excessivo ao banco;
* processamento redundante;
* falta de operações em lote;
* design procedural excessivo;
* ausência de pensamento orientado a dados.

---

## O que mudou tecnicamente

### Antes

* `RecLock()` em massa.
* `dbAppend()` em loop.
* múltiplos `dbSeek`.
* atualização individual.
* SQL dentro de loops.
* múltiplas threads para “aguentar”.

### Depois

* SQL set-based.
* processamento vetorizado.
* atualização em lote.
* inserção em lote.
* exclusão lógica em lote.
* uma única thread.

---

## Resultado prático no Protheus

Customizações mais performáticas significam:

* menor tempo de faturamento;
* menor impacto no AppServer;
* menor carga no DBAccess;
* menos timeout;
* menos deadlock;
* menor janela de lock;
* menor necessidade de infraestrutura;
* mais escalabilidade.

E principalmente:

> Menos “gambiarras arquiteturais” para compensar problemas de modelagem.

---

## Um detalhe importante sobre o Protheus

O próprio framework do Protheus induz muitos desenvolvedores ao paradigma procedural linha a linha.

Mas quando falamos de processamento massivo:

* SQL set-based quase sempre vence;
* menos tráfego quase sempre vence;
* menos lock quase sempre vence;
* menos thread quase sempre vence.

---

## Conclusão

Performance real não nasce de multiplicar threads.

Ela nasce de:

* reduzir operações;
* minimizar roundtrips;
* processar em lote;
* pensar orientado a dados;
* remover gargalos estruturais.

No fim das contas:

> A melhor otimização não foi criar mais threads.
> Foi fazer o sistema parar de precisar delas.

---

#DNATech, #NaldoDJ, #Protheus, #TOTVS, #ADVPL, #TLPP, #SQLServer, #DBAccess, #Performance, #ERP, #Desenvolvimento, #ArquiteturaDeSoftware, #CleanCode, #Refactoring, #Microsiga, #Backend, #TechLead, #Programacao

Comentários

Postagens mais visitadas