Files
SGF/docs/MODULE_ASSOCIATION_LOAD_CONSOLIDATION.md
2026-11-02 20:29:34 +02:00

146 lines
21 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.