Distinguir o ponto de espera
O timeout descreve um prazo ultrapassado, mas não identifica sozinho a etapa que consumiu esse prazo. O laboratório cria uma tabela sintética em SQLite e três ligações locais, cedidas através de asyncio.Queue. Cada operação adquire uma ligação, lê um valor conhecido e devolve a cedência. Na versão defeituosa, uma exceção de aplicação após a consulta evita a devolução. Depois de três falhas, nenhuma ligação está livre e a tentativa seguinte expira antes de chamar o driver. A evidência relevante é a sequência entre aquisição, consulta e saída, juntamente com recursos livres e cedidos. CPU baixa e ausência de SQL ativo são compatíveis com esta espera. Não demonstram serviço saudável nem identificam um bloqueio de tabela.
Saídas normais não cobrem cancelamento
Um teste que apenas conclui leituras pode passar mesmo quando o código perde recursos em saídas anormais. O guião usa um evento para confirmar aquisição e outro para manter a tarefa suspensa. Só depois cancela e aguarda a conclusão, observando CancelledError e o estado do pool. Na versão corrigida, finally devolve a ligação e o cancelamento continua visível para o chamador. Capturar uma exceção e devolver sucesso mudaria o contrato da operação. Existe ainda uma variante com finally demasiado tarde: a tarefa adquire, aguarda fora do try e só depois entra na região protegida. Cancelar nessa espera deixa uma cedência retida. A revisão precisa de verificar a posição da proteção, não apenas a presença da palavra finally.
Posse, concorrência e limites do ensaio
Cancelar uma tarefa que ainda espera por queue.get não lhe dá uma ligação para devolver. O guião mantém três cedências noutras tarefas, cancela o consumidor em espera e confirma que a contagem não muda indevidamente. Noutro grupo, oito tarefas partilham três ligações: três ficam cedidas enquanto um evento impede conclusão, e as restantes aguardam. Depois de libertar o evento, todas terminam e a capacidade regressa. Isto observa concorrência limitada e reutilização neste código. Não mede oito consultas fisicamente paralelas: sqlite3 é síncrono e as tarefas usam um único event loop. O timeout curto existe para terminar um teste negativo; não é um SLA nem medição de desempenho. Não há WebSphere, JDBC ou base remota nesta execução.
Transação, cedência e objeto utilizável
A contabilidade é necessária mas não suficiente. Um slot livre pode conter uma ligação já fechada e falhar na consulta seguinte. O exercício reproduz esse caso e substitui o recurso inutilizável antes de demonstrar nova leitura. Também mostra que sair de with connection em sqlite3 não fecha a ligação: esse context manager trata transações, enquanto close tem outra responsabilidade. Não transfiras esta semântica sem ler o contrato do driver de destino. Uma biblioteca pode devolver um proxy ao pool quando o cliente chama close; este pool pedagógico usa uma função explícita de devolução. No fim, o guião termina todas as tarefas, fecha as ligações que criou e remove apenas a sua pasta temporária. Essa limpeza não constitui aceitação de um pool empresarial.
python3 content/labs/problem-runtime/run.py --output /tmp/dr-problem-pool.json
# Teaching pattern: synchronous return with no intervening await.
# lease = await pool.acquire()
# try:
# return await body(lease)
# finally:
# pool.release(lease)Três saídas anormais retêm três ligações; a tentativa seguinte espera no pool e não executa uma nova consulta.
Armadilhas comuns
Confundir espera por recurso com SQL lento, proteger só o caminho normal ou iniciar finally depois de uma espera cancelável.
Tópicos relacionados: Investigação e evidência · Workarounds e aceitação de correções
Uma tarefa que adquiriu um recurso precisa de o devolver nos percursos aplicáveis; uma tarefa ainda em espera não possui essa cedência.
Referência: Effective Troubleshooting · Problem management practices 2026-09; ServiceNow Brazil examples with scoped plugins and properties