Catalog · How the catalog works
conn4d
- ★ 2 on GitHub
Anyone may press Interesting, signed in or not. It helps others find this project.
README
Conn4D
Gerenciamento de conexões de banco de dados para Delphi — pool, coleta de lixo (GC) de conexões, segurança em múltiplas threads e abstração de engine (FireDAC, Zeos, UniDAC) sob Clean Architecture e SOLID.
Conn4D é uma biblioteca de infraestrutura (não de domínio): ela cuida do ciclo de vida das conexões — abrir, reaproveitar, vigiar e fechar — para que a sua aplicação não precise. Não executa SQL, não é um ORM e não tem query builder. O foco é uma única coisa, bem feita: entregar conexões saudáveis ao seu código, com segurança, sob qualquer carga de threads.
Índice
- Para quem é isto?
- O problema que o Conn4D resolve
- Conceitos fundamentais
- Arquitetura
- Recursos
- Engines e bancos suportados
- Instalação
- Início rápido
- Configuração
- Transações
- Monitoramento de conexões
- Estrutura do repositório
- Exemplo: ConnMonitor
- Testes
- Adicionando um novo engine
- Roadmap
- Segurança
- Contribuindo
- Licença
Para quem é isto?
Para quem desenvolve em Delphi aplicações que abrem conexões de banco com frequência — serviços REST (Horse, DataSnap, RAD Server), aplicações multithread, workers, jobs em background — e quer parar de gerenciar conexões "na mão".
Se você já escreveu algo como FDConnection1.Connected := True espalhado pelo código, esqueceu de fechar uma conexão e viu o banco recusar novos logins, ou teve race conditions ao compartilhar uma conexão entre threads — o Conn4D existe para resolver exatamente esses problemas.
O problema que o Conn4D resolve
Abrir uma conexão de banco é caro (handshake TCP, autenticação, alocação de recursos no servidor). Em uma aplicação que atende muitas requisições, abrir e fechar uma conexão por operação é lento e não escala. As armadilhas clássicas:
| Problema | Sem pool | Com Conn4D |
|---|---|---|
| Custo de abertura | Cada operação paga o handshake completo. | Conexões são reaproveitadas de um pool. |
| Vazamento de conexão | Esqueceu de fechar? A conexão fica presa até o servidor derrubá-la. | O GC detecta a conexão "zumbi" e a recupera automaticamente. |
| Conexões ociosas | Ficam abertas consumindo recursos do servidor à toa. | O GC encerra conexões idle após um tempo configurável. |
| Concorrência | Compartilhar uma conexão entre threads gera race condition / corrupção. | Thread-safety por design; cada thread recebe sua própria conexão emprestada. |
| Esgotamento (DoS acidental) | N threads abrem N conexões → o servidor recusa. | O pool limita o total; excedentes aguardam em fila com timeout. |
| Acoplamento ao driver | Trocar FireDAC por outro driver = reescrever a aplicação. | Você programa contra interfaces; trocar o engine não toca o seu código. |
Conceitos fundamentais
Quatro ideias sustentam a biblioteca:
1. Pool de conexões
Um pool é um conjunto de conexões já abertas, mantidas prontas para uso. Quando seu código pede uma conexão (Acquire), o pool entrega uma já existente em vez de criar do zero; quando você termina (Release/ReturnToPool), ela volta ao pool — não é destruída. O pool tem um tamanho máximo (MaxSize): pedidos além do limite entram em uma fila e aguardam até uma conexão liberar ou até estourar o AcquireTimeout (que lança EConn4DTimeoutException, falha controlada, nunca trava).
2. Garbage Collector (GC) de conexões
Diferente do GC de memória da linguagem, este coleta conexões. Uma thread separada acorda periodicamente (GCInterval, padrão 30s) e:
- Conexão ociosa (idle): disponível há mais tempo que
IdleTimeout→ é fechada para liberar recursos. - Conexão zumbi (zombie): emprestada há mais tempo que
MaxLeaseTime(sinal de vazamento — a thread morreu ou esqueceu de liberar) → fazRollbackde transação aberta, registra o ocorrido com thread id e horário, e destrói.
Isso torna a aplicação autocurável: vazamentos não derrubam o sistema; o GC os recolhe.
Created ??????init????????? Available ??????acquire????????? InUse ??????release????????? Available
??? ???
idle timeout lease timeout
??? ???
Idle Zombie
??? ???
GC.collect GC.forceKill
???????????????????????? Destroyed ???????????????3. Thread-safety por design
Todo o estado mutável vive em um único componente (TConnPoolStore), protegido por seção crítica. Os locks são adquiridos sempre na mesma ordem fixa (semáforo → pool → contexto), o que elimina deadlock estruturalmente — não existe ciclo de espera possível. O GC roda em thread própria e fecha sockets fora do lock, minimizando contenção. Cada thread só enxerga a conexão que pegou emprestada.
4. Abstração de engine (Ports & Adapters)
O engine é o driver que realmente fala com o banco (FireDAC, Zeos, UniDAC). No Conn4D, o engine é uma estratégia substituível: todo o núcleo conversa apenas com a interface IConnEngine. Trocar de driver é trocar um registro, não reescrever a aplicação. O seu código nunca nomeia o engine nem faz cast:
FDQuery.Connection := Conn.Connection; // sem cast, sem nomear a engineArquitetura
Conn4D segue Clean Architecture (Ports & Adapters). Como é uma biblioteca de infraestrutura — sem regras de negócio — não se aplica DDD. A regra de ouro é a Regra da Dependência: camadas externas dependem das internas, nunca o contrário.
Adapters ???????????? Core ???????????? Shared
??? ???
????????????????????????????????????????????????????????????????????????????????????
??? Shared : tipos, config, exceptions, logger. N??o conhece ningu??m.
??? Core : contratos (interfaces) + algoritmos agn??sticos de driver.
NUNCA d?? `uses` em FireDAC/Zeos/UniDAC.
??? Adapters: ??NICA camada que conhece engines concretos.Princípios não-negociáveis:
- Inversão de dependência total — todo contrato vive no
Core; oCorejamais referencia um driver concreto. - Engine substituível — trocar de engine = trocar um registro. Zero impacto fora do adapter.
- Thread-safety por design — estado centralizado, ordem de lock fixa, deadlock impossível por construção.
- Testabilidade de primeira classe — toda dependência é injetada por interface; o engine é 100% mockável (testes rodam sem banco real).
- SOLID aplicado de verdade — cada princípio com justificativa concreta (veja docs/conn4d-spec.md).
📚 A especificação técnica completa está em docs/conn4d-spec.md e o empacotamento/componente em docs/conn4d-packaging-spec.md.
Recursos
- ✅ Pool de conexões por instância com tamanho mínimo/máximo e fila de espera.
- ✅ GC de conexões — encerra ociosas e recupera zumbis (vazamentos) automaticamente.
- ✅ Thread-safe por design — sem race condition, sem deadlock (ordem de lock fixa).
- ✅ Agnóstico de engine — FireDAC incluso; Zeos e UniDAC opcionais por diretiva.
- ✅ Acesso à conexão nativa sem cast —
operator Implicitresolve o tipo pelo destino. - ✅ API fluente para configuração (
TConnConfig.New.Provider(...).Port(...)). - ✅ Componente de paleta
TConn4D(arraste no form) ou factory programático. - ✅ Transações com níveis de isolamento.
- ✅ Monitoramento em tempo real —
LiveConnectionsexpõe estado, idade e thread dona de cada conexão. - ✅ Observers — assine eventos do ciclo de vida (criada, adquirida, liberada, despejada...).
- ✅ Distribuição via Boss.
Engines e bancos suportados
| Engine | Status | Como habilitar |
|---|---|---|
| FireDAC | ✅ Padrão (sempre compila — vem com o Delphi) | Nenhuma ação |
| Zeos | 🧩 Opcional | {$DEFINE CONN4D_ZEOS} em src/Conn4D.inc |
| UniDAC | 🧩 Opcional | {$DEFINE CONN4D_UNIDAC} em src/Conn4D.inc |
Engines opcionais usam compilação condicional: com o define desligado, a unit compila vazia, então incluí-la num pacote nunca quebra a build mesmo sem a biblioteca instalada.
Provedores prontos no adapter FireDAC: Firebird, PostgreSQL, MySQL, MS SQL Server e SQLite.
Requisitos: Delphi 12 (Athens). O núcleo é Windows/cross-platform; o exemplo ConnMonitor é VCL (Windows).
Instalação
Via Boss (recomendado)
boss install github.com/gabrielcb08/conn4dO boss.json declara mainsrc (search path) e os pacotes. O postinstall compila os pacotes automaticamente.
Manual
- Clone o repositório.
- Em Project ▸ Options ▸ Delphi Compiler ▸ Search path, adicione as pastas de
src/(src,src\Shared,src\Core\Abstractions,src\Core\Services,src\Adapters,src\Adapters\FireDAC,src\Component). - (Opcional) Para usar o componente na paleta, abra e instale
packages/Delphi12/dclConn4D.dprojna IDE. - No seu código:
uses Conn4D;— essa unit única reexporta tudo o que o consumidor precisa.
Início rápido
A unit Conn4D é a fachada pública: um uses Conn4D; dá acesso ao factory/componente TConn4D, à config TConnConfig e às interfaces.
Como factory programático
uses Conn4D;
var
Conn: IConn4DNative; // interface AGNÓSTICA — não nomeia a engine
Qry: TFDQuery;
begin
// Sem engine => FireDAC por padrão. Configuração fluente entregue pronta:
Conn := TConn4D.Acquire.Configure(
TConnConfig.New
.Provider('Firebird')
.Host('127.0.0.1').Port(3050)
.Database('C:\dados\MEUBANCO.FDB')
.UserName('SYSDBA').Password('masterkey')
.MaxSize(10).AcquireTimeout(5000).IdleTimeout(120000)
.GCEnabled(True).GCInterval(30000));
Qry := TFDQuery.Create(nil);
try
Qry.Connection := Conn.Connection; // SEM cast → TFDCustomConnection
Qry.Open('SELECT * FROM PRODUTOS');
// ... use o dataset ...
finally
Qry.Free;
// NUNCA dê Free na conexão nativa: o pool e o GC cuidam disso.
end;
end;Como componente (paleta ORData)
Arraste um TConn4D no form/datamodule, preencha as propriedades no Object Inspector (Provider, Host, Port, Database, PoolMaxSize, GCEnabled...) e use:
FDQuery1.Connection := Conn4D1.Connection; // lazy: abre no primeiro usoLazy open: o
Acquirecria o gerenciador e o registra; a conexão física só abre no primeiro uso real (.Connection,.BeginTransactionou.Openexplícito). Isso permite encadear a configuração depois doAcquire.
Configuração
A configuração é uma interface (IConnConfig), construída por um builder fluente. É o único lugar com setters de config.
Cfg := TConnConfig.New
.Provider('Firebird').Host('127.0.0.1').Port(3050)
.Database('C:\dados\MEUBANCO.FDB').UserName('SYSDBA').Password('masterkey')
.MinSize(1).MaxSize(10)
.AcquireTimeout(5000) // ms para conseguir uma conexão antes de falhar
.MaxLeaseTime(60000) // ms até uma conexão emprestada virar "zumbi"
.IdleTimeout(120000) // ms até uma conexão disponível virar "ociosa"
.GCEnabled(True).GCInterval(30000);| Parâmetro | Padrão | Significado |
|---|---|---|
MinSize |
1 | Conexões mantidas no mínimo. |
MaxSize |
10 | Teto de conexões simultâneas (excedentes vão para a fila). |
AcquireTimeout |
5000 ms | Espera máxima por uma conexão antes de lançar timeout. |
MaxLeaseTime |
60000 ms | Tempo emprestada até ser considerada zumbi (vazamento). |
IdleTimeout |
120000 ms | Tempo disponível até ser considerada ociosa e fechada. |
GCEnabled |
True | Liga/desliga o coletor. |
GCInterval |
30000 ms | De quanto em quanto tempo o GC roda. |
Configuração por arquivo INI
O exemplo lê de um .ini. Por segurança, dados reais ficam em settings.ini (ignorado pelo git) e o repositório versiona apenas settings.example.ini com valores fictícios:
[Connection]
Provider=Firebird
Host=127.0.0.1
Port=3050
Database=C:\caminho\para\MEUBANCO.FDB
UserName=SYSDBA
Password=masterkey
[Pool]
MinSize=1
MaxSize=5
AcquireTimeout=5000
MaxLeaseTime=30000
IdleTimeout=120000
[GC]
Enabled=1
Interval=30000⚠️ Nunca comite credenciais reais. Copie
settings.example.iniparasettings.inie edite o local — veja SECURITY.md.
Transações
var Tx: IConnTransaction;
begin
Tx := Conn.BeginTransaction; // abre a conexão física, se ainda lazy
try
// ... operações via Conn.Connection ...
Tx.Commit;
except
Tx.Rollback;
raise;
end;
end;Níveis de isolamento são expostos por IConnTransaction. Se uma conexão vazar com transação aberta, o GC faz Rollback automático ao recolhê-la.
Monitoramento de conexões
O Core expõe uma projeção somente-leitura do estado do pool (snapshot sob lock curto, agnóstico de engine):
var Leases: TArray<TConnLeaseInfo>;
begin
Leases := Conn.LiveConnections;
for var L in Leases do
Writeln(Format('%s | estado=%d | thread=%d | idade=%dms | em uso=%dms',
[GUIDToString(L.Id), Ord(L.State), L.OwnerThreadId, L.AgeMs, L.InUseForMs]));
end;TConnLeaseInfo traz: Id, EngineID, State, OwnerThreadId, CreatedAtMs, AcquiredAtMs, LastReleasedAtMs, AgeMs, InUseForMs, IdleForMs. Veja o exemplo ConnMonitor abaixo, que renderiza isso em tempo real num grid.
Estrutura do repositório
Conn4D/
????????? src/
??? ????????? Conn4D.pas # fachada p??blica: uses Conn4D;
??? ????????? Conn4D.inc # defines dos engines opcionais
??? ????????? Shared/ # tipos, config, exceptions, logger (sem depend??ncias)
??? ????????? Core/
??? ??? ????????? Abstractions/ # interfaces puras (IConnEngine, IConnPool, IConnGC...)
??? ??? ????????? Services/ # PoolManager, GarbageCollector, PoolStore, Registry...
??? ????????? Adapters/ # ??nica camada que conhece drivers
??? ??? ????????? FireDAC/ # engine padr??o + provedores (FB, PG, MySQL, MSSQL, SQLite)
??? ??? ????????? Zeos/ ?? UniDAC/ # engines opcionais (compila????o condicional)
??? ????????? Component/ # TConn4D (componente de paleta + factory)
????????? packages/Delphi12/ # Conn4D.dpk (runtime) + dclConn4D.dpk (design)
????????? tests/ # DUnitX (engine 100% mockado)
????????? samples/ConnMonitor/ # app VCL de demonstra????o
????????? scripts/ # build/install/check-architecture (PowerShell)
????????? docs/ # especifica????es t??cnicas
????????? boss.json # manifesto BossExemplo: ConnMonitor
Em samples/ConnMonitor/, um app VCL demonstra o pool ao vivo: um TStringGrid + TTimer que a cada tique chama LiveConnections e mostra cada conexão, seu estado, a thread dona e há quanto tempo está aberta/ociosa. Botões Abrir 1, Abrir N (stress), Liberar e Shutdown permitem ver o pool, a fila e o GC trabalhando em tempo real.
Por padrão usa SQLite (roda sem servidor): se Database ficar vazio, o app cria um produtos_demo.sqlite ao lado do executável. Para Firebird/Postgres, ajuste o settings.ini (veja settings.example.ini). Os workers são robustos a falha de conexão — banco indisponível vira mensagem de status, não crash.
Testes
Testes em DUnitX, em tests/, com o engine 100% mockado (sem banco real):
Conn4D.Test.PoolManager— empréstimo/devolução, limites, fila.Conn4D.Test.GarbageCollector— despejo de idle/zombie com relógio mockado (IConnClock).Conn4D.Test.ThreadSafety— stress com N threads (N > poolSize): nunca excede o limite, nunca entrega a mesma conexão a duas threads, nunca trava.Conn4D.Test.Registry,Conn4D.Test.EngineSwap,Conn4D.Test.Config,Conn4D.Test.Component.
Abra tests/Conn4D.Tests.dproj e rode, ou use scripts/build.ps1.
Adicionando um novo engine
Graças ao OCP, adicionar um engine não toca Core nem Shared:
- Crie
src/Adapters/<Engine>/Conn4D.Engine.<Engine>.pasimplementandoIConnEngine. - Crie o adapter/fachada (
IXxxConn4D = interface(IConn4DNative)) e a factory. - Auto-registre na
initializationda unit (chave =EngineID). - Adicione o
{$DEFINE CONN4D_<ENGINE>}emConn4D.ince linke a unit na fachada.
O script scripts/check-architecture.ps1 valida que Core/Shared continuam sem uses de driver concreto.
Roadmap
- [x] Núcleo: pool, GC, thread-safety, registry, transações.
- [x] Adapter FireDAC (Firebird, PostgreSQL, MySQL, MSSQL, SQLite).
- [x] Componente de paleta + factory + monitoramento.
- [ ] Adapters Zeos e UniDAC estáveis (estrutura pronta, sob diretiva).
- [ ] Suporte cross-platform expandido nos exemplos.
Segurança
Antes de tornar o repositório público, leia SECURITY.md. Pontos-chave:
- Credenciais reais ficam em
settings.ini, ignorado pelo git. Sósettings.example.ini(fictício) é versionado. - O
.gitignoreexclui binários, caches do compilador, DLLs de terceiros e o cache do Delphi LSP (*.delphilsp.json), que vazaria o layout do seu disco. - As credenciais que aparecem na documentação (
SYSDBA/masterkey) são os valores de fábrica públicos do Firebird, usados apenas como exemplo — não são segredos reais.
Contribuindo
Contribuições são bem-vindas! Veja CONTRIBUTING.md para o fluxo de trabalho, padrão de código e as regras de arquitetura que todo PR deve respeitar.
Licença
Distribuído sob a licença MIT. Veja LICENSE.

