# Fila de associações — consolidação de carga, concorrência e HTTP 02/11/2026. **A optimização está implementada localmente.** A listagem usa leitura conjunta da página e das suas dependências em `describePage/pageStates`, com lotes limitados, sem materializar o universo. A projecção posterior `pageColumns` selecciona metadados e referências em vez dos corpos completos. Prévia e confirmação continuam a obter e revalidar o estado completo actual. Código: [ModuleAssociationReview.php](../app/Modules/Identity/Services/ModuleAssociationReview.php). Esta passagem acrescenta provas e documentação; não altera esse código funcional. ## Cobertura conferida e medições em falta Consolidação das três entregas, com conferência dos hashes das provas e das fontes actuais do serviço e controlador. Existem **24 combinações obrigatórias**: 3 volumes × 4 áreas × páginas de 25/100, todas com resultados medidos para A/B/C, mais **9 cenários de filtros**. Não falta nenhuma combinação dessa comparação da leitura conjunta. **Não se abriu ligação a qualquer base, nem se executou nova medição ou suite nesta consolidação.** Foram reutilizadas as provas válidas. Há **13 tentativas da baseline limitadas** pelo orçamento de 15 s; constituem resultados censurados, não medições completas nem combinações nunca executadas. A tabela mantém esses limites explícitos, sem inventar medianas ou rácios de ganho. Prolongar apenas a baseline mudaria as condições; não se repetiram as experiências válidas para obter números completos da versão antiga. A comparação nos três volumes mede o incremento de leitura conjunta **anterior à projecção**, com a fonte B/C identificada abaixo. A versão actual com projecção tem prova própria de 10 000 registos por área, documentos de 100 linhas e duas dimensões de descrição; **não foi medida numa nova matriz completa de 1 000/5 000/10 000 contra a baseline original**. Não se fundiram essas experiências nem se apresentam valores de B como sendo da fonte projectada actual. Essa matriz adicional não é necessária para demonstrar o ganho isolado da leitura conjunta e não foi repetida nesta consolidação. [Inventário verificável da cobertura, métricas por combinação e hashes](proofs/module-association-consolidation-summary-20261102.json). O verificador `scripts/consolidate-association-evidence.mjs` lê apenas fontes e provas locais, valida as 24 combinações e rejeita lacunas ou alterações de hash; não executa SQL. ## Espera da confirmação: causa e resultado após libertação A prova anterior atingiu `lock_timeout=1000ms` (SQLSTATE 55P03, 1 044,222 ms) porque a primeira transacção foi deliberadamente mantida aberta. A nova prova observou a consulta sobre **companies com FOR UPDATE**, o PID da primeira transacção em `pg_blocking_pids` e a espera **Lock / transactionid / ShareLock** em `pg_stat_activity/pg_locks`. É serialização da confirmação por empresa; o limite não surgiu da leitura conjunta da auditoria. Pode também serializar confirmações de registos diferentes da mesma empresa; esse volume de escritores não foi medido. Em quatro cenários novos, a primeira transacção foi libertada depois de observar a espera real e aguardar mais 200 ms. O segundo processo tinha limite de bloqueio de 8 s exclusivamente para esta prova. Os tempos abaixo incluem espera e execução, não representam latência normal nem tempo puro após desbloqueio. | Cenário | Libertação da primeira | Segunda confirmação | Tempo total ms | Versão do registo final | Eventos de decisão após repetição | | --- | --- | --- | ---: | ---: | ---: | | rollback_then_complete | rollback | 200 | 352,39 | 1 | 1 | | commit_then_stale_revision | commit | 409 | 338,39 | 1 | 1 | | commit_then_same_token | commit | 200 (repetição) | 333,37 | 1 | 1 | | commit_record_change_then_stale_version | commit | 409 | 335,55 | 2 | 0 | Após rollback, a segunda decisão gravou exactamente um evento. Após commit de outra decisão, o token preparado antes da espera recebeu **409 por revisão de associações desactualizada**, mesmo mantendo versão documental 1; nova prévia foi possível. O mesmo token da decisão vencedora recebeu sucesso idempotente e o mesmo identificador de evento. Quando a versão documental mudou para 2 durante a espera, o token da versão 1 recebeu **409**, sem decisão; uma prévia da versão 2 foi possível. As repetições não acrescentaram eventos. Os estados de sucesso/409 do segundo processo são resultados do serviço; as repetições e novas prévias foram também verificadas pela API em processo. A apresentação HTTP do limite foi posteriormente implementada e provada separadamente, conforme a secção seguinte. **Prova de libertação reutilizada: 1 teste / 79 asserções, sem falhas ou erros**, mais quatro trabalhadores concluídos. [Prova](proofs/module-association-release-20261102.json) e [resumo com hashes](proofs/module-association-release-summary-20261102.json). A primeira tentativa desta prova concluiu os quatro cenários mas falhou na comparação final da auditoria por exceder 65 535 parâmetros SQL; corrigiu-se apenas o auxiliar de preservação para IDs inteiros capturados. A execução acima repetiu esta prova afectada e passou; não se repetiram as matrizes ou suites válidas. ## Tratamento HTTP entregue e provado O controlador administrativo existente passou a traduzir exclusivamente SQLSTATE **55P03** em **423 / association_busy**, para prévia e confirmação, depois do rollback do serviço. A resposta contém apenas mensagem administrativa e código, com `Cache-Control: no-store`, sem SQL/bindings ou stack trace. Não foram alterados o prazo de espera, os direitos ou o serviço de decisão; não há repetição automática. Outras excepções mantêm o tratamento existente, e mudanças de estado continuam a produzir 409. | Pedido limitado | Tempo total com espera diagnóstica ms | Resposta | Libertação e recuperação posterior | | --- | ---: | --- | --- | | Confirmação | 1086,658 | 423 | Rollback da primeira; repetição manual 200 e repetição idempotente | | Confirmação | 1093,136 | 423 | Commit da primeira; token antigo 409 e nova prévia 200 | | Prévia | 1083,944 | 423 | Rollback da primeira; nova prévia 200, confirmação e repetição idempotente | Em cada caso foi observado o bloqueador real, configurando 1 s apenas na ligação do trabalhador de prova. A contagem após libertação confirmou ausência de decisão parcial do pedido limitado; após recuperação ficou exactamente **um evento por cliente**, sem duplicação. Estes tempos incluem a espera deliberada, não latência normal. **1 teste / 54 asserções**, mais três trabalhadores aprovados; **13 testes / 232 asserções** da fila afectados pela alteração do controlador também aprovados. Estes últimos foram repetidos na entrega HTTP por haver alteração funcional, não nesta consolidação. A prova anterior de navegação e as matrizes mantiveram-se válidas, sem repetição. Não somar asserções de suites sobrepostas como cobertura distinta. [Entrega HTTP](MODULE_ASSOCIATION_HTTP_LOCK_DELIVERY.md) · [Prova](proofs/module-association-http-lock-20261102.json) · [Testes e hashes](proofs/module-association-http-lock-summary-20261102.json). Controlador actual SHA-256 **6c16481f7e3f4355ba60ca22bc67385ec3b3113b4784e5d9f57025618e1735a9**. A apresentação foi provada pelo kernel HTTP em processo; navegador, servidor HTTP de implantação e rede permanecem fora da prova. ## Comparação A/B/C: leitura conjunta e índices separados Medições já válidas, reutilizadas sem nova execução. **A**: checkpoint 340f3bb, consultas por registo; **B**: leitura conjunta, sem índices experimentais, **antes da projecção posterior**; **C**: mesmo B com dois índices experimentais de auditoria. Portanto A→B mede a leitura conjunta e B→C mede separadamente os índices. Não atribuir ao código actual exactamente os tempos de B: a projecção tem outra medição e hash abaixo. Mesma fixture imutável em cada volume, 1 000/5 000/10 000 **por cada uma das quatro áreas**, dependências incluídas; três repetições, ordem A/B alternada, C sobre os mesmos dados, kernel e buffers aquecidos. Valores: mediana de tempo; consultas por pedido completo; máximo de memória PHP usada incremental acima do início do pedido, em **KiB**, sem incluir PostgreSQL ou memória total do processo. “limite” significa tentativa interrompida pelo orçamento diagnóstico de 15 s, sem mediana/consultas/memória comparáveis; não é um valor de 15 s nem um ganho calculável. Na prova completa foram 258 leituras medidas concluídas e 13 tentativas limitadas; respostas completas comparáveis idênticas. Cada célula contém **A / B / C**. A memória em B pode aumentar face a A, por conservar o conjunto da página; a redução de consultas não implica redução de memória. | Registos por área | Área / filtro | Página | Tempo ms A/B/C | Consultas A/B/C | Memória incremental KiB A/B/C | | ---: | --- | ---: | ---: | ---: | ---: | | 1000 | Clientes / all | 25 | 1986,50 / 36,77 / 10,66 | 185 / 11 / 11 | 86,50 / 100,29 / 100,13 | | 1000 | Clientes / all | 100 | 7407,37 / 29,77 / 12,85 | 710 / 11 / 11 | 158,61 / 254,37 / 254,21 | | 1000 | Serviços / all | 25 | 1924,42 / 30,09 / 10,81 | 185 / 11 / 11 | 86,55 / 99,55 / 99,39 | | 1000 | Serviços / all | 100 | 7778,43 / 31,96 / 11,90 | 710 / 11 / 11 | 158,70 / 253,59 / 253,45 | | 1000 | Rascunhos serviço / all | 25 | 3905,95 / 77,23 / 16,65 | 435 / 19 / 19 | 116,95 / 221,35 / 221,22 | | 1000 | Rascunhos serviço / all | 100 | limite / 77,02 / 22,16 | limite / 19 / 19 | limite / 744,63 / 744,50 | | 1000 | Rascunhos facturação / all | 25 | 5014,74 / 96,21 / 19,61 | 560 / 23 / 23 | 131,74 / 294,41 / 294,28 | | 1000 | Rascunhos facturação / all | 100 | limite / 100,60 / 28,94 | limite / 23 / 23 | limite / 1037,20 / 1037,06 | | 1000 | Clientes / pending | 25 | 1974,80 / 100,45 / 86,03 | 185 / 11 / 11 | 85,87 / 96,77 / 96,63 | | 1000 | Clientes / associated | 25 | 2034,10 / 107,24 / 34,23 | 185 / 11 / 11 | 92,58 / 112,45 / 112,33 | | 1000 | Clientes / removed | 25 | 2721,26 / 754,12 / 78,34 | 185 / 11 / 11 | 88,05 / 107,75 / 107,61 | | 5000 | Clientes / all | 25 | 6295,36 / 97,22 / 18,45 | 185 / 11 / 11 | 86,75 / 100,30 / 100,14 | | 5000 | Clientes / all | 100 | limite / 87,01 / 19,76 | limite / 11 / 11 | limite / 254,63 / 254,47 | | 5000 | Serviços / all | 25 | 5238,39 / 92,19 / 17,11 | 185 / 11 / 11 | 86,80 / 100,36 / 100,20 | | 5000 | Serviços / all | 100 | limite / 77,98 / 18,13 | limite / 11 / 11 | limite / 254,70 / 254,55 | | 5000 | Rascunhos serviço / all | 25 | 10553,09 / 216,02 / 36,97 | 435 / 19 / 19 | 117,20 / 221,60 / 221,47 | | 5000 | Rascunhos serviço / all | 100 | limite / 232,94 / 44,38 | limite / 19 / 19 | limite / 744,88 / 744,75 | | 5000 | Rascunhos facturação / all | 25 | 13947,25 / 377,18 / 33,66 | 560 / 23 / 23 | 132,33 / 294,41 / 294,28 | | 5000 | Rascunhos facturação / all | 100 | limite / 322,13 / 65,66 | limite / 23 / 23 | limite / 1037,20 / 1037,06 | | 5000 | Clientes / pending | 25 | 7236,64 / 578,25 / 104,08 | 185 / 11 / 11 | 85,87 / 96,77 / 96,63 | | 5000 | Clientes / associated | 25 | 7045,59 / 362,62 / 329,05 | 185 / 11 / 11 | 92,58 / 112,45 / 112,33 | | 5000 | Clientes / removed | 25 | 6394,64 / 679,37 / 409,55 | 185 / 11 / 11 | 88,05 / 107,75 / 107,61 | | 10000 | Clientes / all | 25 | 9683,04 / 156,32 / 13,88 | 185 / 11 / 11 | 86,75 / 100,30 / 100,14 | | 10000 | Clientes / all | 100 | limite / 203,09 / 14,52 | limite / 11 / 11 | limite / 254,63 / 254,47 | | 10000 | Serviços / all | 25 | 9385,15 / 117,18 / 11,66 | 185 / 11 / 11 | 86,55 / 99,55 / 99,39 | | 10000 | Serviços / all | 100 | limite / 109,77 / 14,87 | limite / 11 / 11 | limite / 253,59 / 253,45 | | 10000 | Rascunhos serviço / all | 25 | limite / 333,79 / 20,32 | limite / 19 / 19 | limite / 220,98 / 220,85 | | 10000 | Rascunhos serviço / all | 100 | limite / 399,87 / 26,89 | limite / 19 / 19 | limite / 744,27 / 744,13 | | 10000 | Rascunhos facturação / all | 25 | limite / 592,80 / 28,73 | limite / 23 / 23 | limite / 294,05 / 293,91 | | 10000 | Rascunhos facturação / all | 100 | limite / 451,13 / 35,65 | limite / 23 / 23 | limite / 1036,83 / 1036,70 | | 10000 | Clientes / pending | 25 | limite / 2553,13 / 113,84 | limite / 11 / 11 | limite / 96,77 / 96,63 | | 10000 | Clientes / associated | 25 | 9377,31 / 521,51 / 254,95 | 185 / 11 / 11 | 92,58 / 112,45 / 112,33 | | 10000 | Clientes / removed | 25 | 9973,28 / 1290,76 / 292,74 | 185 / 11 / 11 | 88,05 / 107,75 / 107,61 | Métricas brutas, incluindo picos PHP absolutos, tempos SQL, amostras e hashes: [comparação](proofs/module-association-optimization-comparison-20261102.json). [Método, planos, índices e recusas de acesso](MODULE_ASSOCIATION_OPTIMIZATION_DELIVERY.md). Os dois índices (ligações por módulo e revisão por registo) foram criados apenas no ensaio sintético e desfeitos por rollback/savepoint; nenhum índice no destino. ## Código actual: projecção e documentos com muitas dependências A fase posterior comparou B com a projecção actual, mantendo 10 000 registos por área e 100 linhas por rascunho, páginas 25/100, descrições de 128/1 000 bytes. Foram 132 leituras completas, 44 cenários, respostas equivalentes. A projecção reduziu a memória, mas não acelerou todas as leituras sem índice. Para descrições de 1 000 bytes: | Área / página | Tempo ms antes projecção / actual sem índice / actual só índice revisão / actual par | Memória incremental MiB antes / actual | | --- | ---: | ---: | | Serviço / 25 | 668,119 / 728,558 / 97,487 / 225,676 | 8,28 / 4,21 | | Serviço / 100 | 2150,980 / 2861,608 / 341,479 / 408,455 | 32,42 / 16,65 | | Facturação / 25 | 815,219 / 905,073 / 111,725 / 127,118 | 11,55 / 4,85 | | Facturação / 100 | 2720,541 / 2481,159 / 374,043 / 368,403 | 45,49 / 19,23 | Consultas: serviço 27/57 e facturação 31/61, páginas 25/100; o crescimento decorre dos lotes das dependências (até 10 000 serviços na página), não do carregamento de todo o universo. Índice de revisão: 13,73 MiB; par: 30,50 MiB. O segundo índice não mostrou benefício consistente; não foi recomendado/aplicado automaticamente. Experiências desfeitas apenas na base sintética. [Relatório e provas desta fase](MODULE_ASSOCIATION_PROJECTION_DELIVERY.md). Proveniência SHA-256 do serviço: A **44a2c75d6a2ecaddc151b3c92175324e15ca4e2664a87a4d50c093913adb4114**; B/C **6df1a85af8ea3112ff8e61741408158cd0444944cc3e0b407ad5bd4ad8043361**; projecção actual e nova prova de libertação **b901bccc73a59e80d98d32a56532e9300a55ba9a216e5ac5a0ce84d78f976397**. A confirmação/prévia não foram alteradas pela optimização. ## Concorrência de leitura e equivalência preservadas Mantêm-se válidas **440 leituras completas**, até quatro identidades/processos com sobreposição efectivamente observada: 420 da matriz e 20 durante confirmação sem commit. Página 25, 3 000 por área, rascunhos com 100 linhas de 1 000 bytes. Todas corresponderam aos hashes autorizados; os leitores viram o estado anterior enquanto a decisão não tinha commit. Não foram repetidas. Com quatro processos, clientes: P50/P95 amostral **37,990/60,810 ms sem índice**, **14,790/21,402 ms com revisão**; facturação: **429,457/495,430 ms sem índice**, **102,861/121,342 ms com revisão**. Consultas 11/31, memória incremental máxima aproximada 0,09/4,85 MiB por pedido. Os percentis descrevem amostras pequenas (15/30/60 pedidos); não constituem SLA. O índice desta prova entre processos foi gravado só na regressão para ser visível, depois removido explicitamente, com catálogo anterior igual. [Ondas, tempos e limites](MODULE_ASSOCIATION_CONCURRENCY_REVIEW.md). Preservam-se também as provas válidas afectadas: **21 testes / 322 asserções**, equivalência de filtros, ordenação, paginação, isolamento e recusas, versões/dependências, prévia, confirmação, falha e recuperação; ensaios separados de planos e acessos documentados na entrega. Não foram repetidos por não haver alteração funcional nesta continuação. ## Gargalos que persistem e correcção mínima proposta **Leitura SQL:** a leitura conjunta eliminou as consultas à auditoria por cada registo da página, mas os filtros de estado e as contagens conservam `EXISTS/NOT EXISTS` correlacionados, e a agregação de estados ainda tem de procurar/ordenar eventos da auditoria. Sem índice adequado ao âmbito/chaves JSON, continuam varrimentos e trabalho proporcional ao histórico. Em 10 000 clientes, o filtro de pendentes ainda demorou **2 553,134 ms** na variante B sem índice, contra **113,836 ms** com o par; essa melhoria não deve ser atribuída apenas ao batching. Os planos e a experiência posterior distinguindo revisão/par sustentam o candidato mais pequeno, não a criação automática de ambos. **Proposta mínima:** usar como candidato apenas o índice parcial de revisão já experimentado, sem nova tabela, cache de decisões ou reescrita do circuito. Definição reproduzida para revisão, **não executada nesta passagem e não aplicada no destino**: ```sql CREATE INDEX sgf_qa_assoc_revision ON identity_audit (tenant_id, company_id, (details->>'area'), (details->>'record_id'), id DESC) WHERE action IN ('module.record.associated', 'module.record.disassociated', 'module.record.reviewed'); ``` Este é o nome/DDL do candidato QA, não uma migração de produção. A prova isolada da projecção mediu cerca de **13,73 MiB** para esse índice; o par ocupou **30,50 MiB** e não mostrou ganho consistente. Antes de qualquer implantação, falta validar plano, custo de escrita/manutenção, criação/recuperação e condições do destino. Se os filtros ainda forem lentos com o índice, o incremento seguinte mínimo será calcular os estados por âmbito numa relação SQL comum reutilizada na contagem/página, preservando a semântica da última associação/retirada por módulo e separando-a da revisão. Essa reescrita **não está implementada nem validada**; os dados actuais não justificam fazê-la antes de avaliar o candidato menor. **Documentos com muitas dependências:** o custo residual de memória/consultas cresce com a página e o número de referências reais (até 10 000 serviços), mesmo após projectar o corpo documental. A projecção já reduziu o pior caso medido de **45,49 para 19,23 MiB**. Um índice não elimina esse custo em PHP; não declarar que o índice resolve memória ou renderização. A página de 25 já disponível é a opção menos pesada no ensaio; não se alterou o limite de 100 nem se truncaram dependências silenciosamente. Uma eventual consulta progressiva das dependências exigiria outro incremento e prova de equivalência. **Confirmações:** o `FOR UPDATE` da empresa serializa os escritores e foi mantido para preservar a revalidação de contexto/permissões. O 423 já entregue melhora a resposta a um limite, mas não remove o bloqueio nem aumenta o débito. Não remover ou estreitar esse lock sem provar corridas com a administração de acessos. O mínimo operacional pendente é definir um orçamento de espera coerente com o servidor HTTP e manter repetição manual/revalidação; não se escolheu ou aplicou um prazo real por consequência destes testes. ## Preservação e pendências separadas A execução de libertação anteriormente concluída verificou fisicamente **sgf_tempo_test / sgf_test_runner / porta 55439 / cluster sgf_modular_regression_2576427d1939**. Usou quatro clientes e âmbito novos, exclusivamente de regressão, gravados para visibilidade entre processos e retidos com manifesto privado. A primeira tentativa também deixou a sua pequena fixture sintética nesta base; não se apagou nem reinicializou a base. A execução válida verificou hashes dos registos QA anteriores e igualdade do catálogo de índices. Tokens, contexto e manifestos privados não integram estas provas públicas. Os seis registos protegidos, a demonstração 8039, o SGF local, a cache bloqueada, provas válidas e trabalho alheio foram preservados; não se abriu ligação ao destino nesta prova. Sem novos índices, direitos públicos, activação de Compras, publicação ou checkpoint. O auxiliar de preservação foi corrigido, não a aplicação. **Capacidade de implantação não validada.** Estes ensaios locais usam kernel HTTP em processo e trabalhadores PHP; não medem navegador, login/MFA, rede/TLS, servidor HTTP implantado, carga sustentada, vários escritores ou recursos completos do servidor. Faltam condições, orçamento de memória, índices/planos e política de espera/repetição próprios do destino. A revisão visual permanece separada e pendente, sem repetir a via de navegador bloqueada. Nesta consolidação não se alterou código da aplicação, não se executou SQL nem se repetiram testes. Conferiram-se as fontes e os hashes das provas anteriores e produziu-se o inventário de cobertura. **Revisão visual**: pendente, incluindo a apresentação de páginas com muitas dependências e mensagens ao utilizador. **Capacidade de implantação**: pendente, com as provas operacionais próprias enumeradas acima. Uma pendência não é pré-condição para documentar a outra nem prova de que esteja concluída. Os seis registos e a cache não foram acedidos ou alterados.