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

16 KiB

Optimização local da fila administrativa de associações

Continuação posterior: projecção de referências, ensaio de 100 linhas e índices isolados. As medições abaixo pertencem à fase anterior à projecção; a continuação reduziu a memória de documentos largos e preservou o contrato da fila. A capacidade de implantação continua pendente.

02/11/2026. Incremento autorizado após a avaliação sintética de carga. Fonte anterior: checkpoint técnico 340f3bb. O incremento altera apenas a descrição da página na API; não altera a confirmação individual, direitos, contratos de prévia, navegação ou interface React. Sem publicação, activação de Compras ou índice no destino.

Implementação

ModuleAssociationReview::listing() conserva filtros, pesquisa literal, ordenação por ID e paginação no servidor. A página continua limitada a 100, com 25 na interface. Os registos dessa página são a raiz de um percurso das suas referências explícitas; não se consulta todo o cadastro para construir o grafo.

As dependências são deduplicadas por área/ID, consultadas em grupos de até 500 IDs e limitadas ao tenant/empresa e à unidade activa quando aplicável. O direito original de consulta de cada área é conferido antes da leitura. Uma dependência inacessível ou ausente continua a marcar a linha como restrita e a ocultar toda a lista de dependências dessa linha, sem divulgar os identificadores inacessíveis. Não há propagação de associações.

A auditoria é lida por área e grupos de até 500 IDs da página e da sua cadeia de dependências. O SQL agrega a revisão máxima e o último evento de associação/retirada por módulo. O PHP recebe estados agregados por registo, sem receber o universo de eventos. module.record.reviewed avança a revisão, mas não cria uma associação activa nem altera a classificação «sem eventos de associação» dos filtros.

As estruturas de registos, referências, permissões e estados são locais à chamada. O percurso final usa um método recursivo, sem uma closure que se capture a si própria e retenha o grafo até à recolha de ciclos do PHP. Não há cache entre pedidos nem alteração da cache cuja limpeza foi bloqueada.

preview(), snapshot(), graph() e confirm() mantêm a leitura actual, as permissões, o contexto, versões, hashes, bloqueios, auditoria e idempotência anteriores. A confirmação não reutiliza a descrição da listagem. Tokens anteriores continuam sujeitos às suas condições originais; a optimização não inventa uma nova autorização ou decisão.

Protocolo de comparação

A comparação final usa as mesmas fixtures imutáveis por escala, nos mesmos processos/ligação/kernel e na mesma base isolada. São 1 000, 5 000 e 10 000 registos em cada uma das quatro áreas: clientes, serviços, prestações e rascunhos de facturação. Nesta comparação, os serviços e rascunhos crescem também; por isso os resultados não devem ser confundidos com as cardinalidades menores dessas áreas no primeiro levantamento de carga.

Variantes:

  • A — anterior, sem índices experimentais: serviço recuperado de 340f3bb para ficheiro privado de prova, alterando apenas o nome da classe. A API usa um controlador de ensaio com o mesmo kernel, middleware, resposta e serviço anterior. Nenhum percurso de produção carrega essa cópia.
  • B — leitura conjunta, sem índices experimentais: código funcional desta entrega, na mesma fixture. A ordem A/B alterna entre as três repetições para reduzir viés de ordem/cache.
  • C — leitura conjunta com índices experimentais: mesma fixture e código B, depois de criar dois índices na transacção da base sintética. O ganho B→C é apresentado separadamente do ganho A→B.

As respostas completas são comparadas por SHA-256 dos bytes JSON, incluindo dependências, versões, associações e revisão. Os IDs/totais/estados esperados também são verificados contra a fixture, com ruído de outro tenant e unidade não seleccionada. Quando A não conclui, não se afirma equivalência dos seus bytes nesse caso: compara-se B/C e verifica-se o resultado esperado da fixture.

PHP 8.4.27, Node fixado 24.22.1 e PostgreSQL no cluster sgf_modular_regression_2576427d1939, porta 55439, base sgf_tempo_test, role sgf_test_runner. A guarda e identidade são conferidas antes das fixtures/DDL. Não houve ligação à instalação local ou à base persistente 8039. A preparação inteira é sintética e desfeita por rollback.

Há aquecimento, recolha de ciclos antes de cada amostra e três amostras por caso completo. Limites de diagnóstico iguais entre variantes: 3 s por SQL, 15 s por pedido observado entre consultas e 2 500 consultas. Uma amostra limitada é explicitamente marcada; não se extrapola uma mediana, um SLA ou uma latência exacta para pedidos incompletos. Medições incluem kernel/instrumentação; não incluem login, transporte HTTP externo, browser ou concorrência multiutilizador.

Medições finais comparáveis

Em cada célula: A / B / C. Tempos em ms; consultas por pedido; memória incremental máxima usada pelo PHP em KiB. lim. significa que não se completaram três amostras nesse perfil; não é uma mediana nem uma medição completa de memória. Os detalhes de cada tentativa, incluindo os pedidos limitados, estão no JSON.

Registos por área Área Página Tempo A / B / C (ms) Consultas A / B / C Memória A / B / C (KiB)
1000 Clientes 25 1986,5 / 36,8 / 10,7 185 / 11 / 11 86,5 / 100,3 / 100,1
1000 Clientes 100 7407,4 / 29,8 / 12,9 710 / 11 / 11 158,6 / 254,4 / 254,2
1000 Serviços 25 1924,4 / 30,1 / 10,8 185 / 11 / 11 86,6 / 99,6 / 99,4
1000 Serviços 100 7778,4 / 32,0 / 11,9 710 / 11 / 11 158,7 / 253,6 / 253,4
1000 Prestações 25 3905,9 / 77,2 / 16,6 435 / 19 / 19 117,0 / 221,4 / 221,2
1000 Prestações 100 lim. / 77,0 / 22,2 lim. / 19 / 19 lim. / 744,6 / 744,5
1000 Facturação 25 5014,7 / 96,2 / 19,6 560 / 23 / 23 131,7 / 294,4 / 294,3
1000 Facturação 100 lim. / 100,6 / 28,9 lim. / 23 / 23 lim. / 1037,2 / 1037,1
5000 Clientes 25 6295,4 / 97,2 / 18,5 185 / 11 / 11 86,8 / 100,3 / 100,1
5000 Clientes 100 lim. / 87,0 / 19,8 lim. / 11 / 11 lim. / 254,6 / 254,5
5000 Serviços 25 5238,4 / 92,2 / 17,1 185 / 11 / 11 86,8 / 100,4 / 100,2
5000 Serviços 100 lim. / 78,0 / 18,1 lim. / 11 / 11 lim. / 254,7 / 254,6
5000 Prestações 25 10553,1 / 216,0 / 37,0 435 / 19 / 19 117,2 / 221,6 / 221,5
5000 Prestações 100 lim. / 232,9 / 44,4 lim. / 19 / 19 lim. / 744,9 / 744,8
5000 Facturação 25 13947,3 / 377,2 / 33,7 560 / 23 / 23 132,3 / 294,4 / 294,3
5000 Facturação 100 lim. / 322,1 / 65,7 lim. / 23 / 23 lim. / 1037,2 / 1037,1
10000 Clientes 25 9683,0 / 156,3 / 13,9 185 / 11 / 11 86,8 / 100,3 / 100,1
10000 Clientes 100 lim. / 203,1 / 14,5 lim. / 11 / 11 lim. / 254,6 / 254,5
10000 Serviços 25 9385,2 / 117,2 / 11,7 185 / 11 / 11 86,6 / 99,6 / 99,4
10000 Serviços 100 lim. / 109,8 / 14,9 lim. / 11 / 11 lim. / 253,6 / 253,4
10000 Prestações 25 lim. / 333,8 / 20,3 lim. / 19 / 19 lim. / 221,0 / 220,9
10000 Prestações 100 lim. / 399,9 / 26,9 lim. / 19 / 19 lim. / 744,3 / 744,1
10000 Facturação 25 lim. / 592,8 / 28,7 lim. / 23 / 23 lim. / 294,0 / 293,9
10000 Facturação 100 lim. / 451,1 / 35,7 lim. / 23 / 23 lim. / 1036,8 / 1036,7

A memória incremental da leitura conjunta é maior que a da leitura individual nos casos anteriores completos: é a troca por reter e reutilizar a página/dependências. Em compensação, para o mesmo tamanho de página, mantém-se aproximadamente estável entre 1 000 e 10 000 registos por área: cerca de 100 KiB para 25 clientes, 255 KiB para 100 clientes e 1 037 KiB para 100 rascunhos de facturação. Não se usa a memória total do processo — que inclui todas as fixtures — como se fosse custo exclusivo do pedido.

Na escala de 10 000, os filtros de clientes sem eventos/associados/retirados ficaram em 2 553 / 522 / 1 291 ms sem índices, e 114 / 255 / 293 ms com os índices experimentais. Portanto, a queda nas consultas por linha não elimina todos os custos dos filtros e das contagens.

Amostras completas e métricas · resumo, hashes e verificações.

Foram comparados 33 cenários por variante: quatro áreas, páginas de 25/100 e três filtros adicionais de clientes, em três escalas. B e C concluíram as três amostras em todos os cenários. A concluiu 20 cenários; 13 atingiram o orçamento de 15 s, sem timeout SQL. No total: 258 pedidos medidos completos e 13 tentativas limitadas. Os casos completos devolvem bytes JSON iguais entre as variantes comparáveis; todos os casos B/C são iguais entre si e aos resultados esperados da fixture.

As escalas contêm, respectivamente, 22 670 / 113 339 / 226 662 eventos. A comparação final passou 1 teste / 25 521 asserções. O resumo inclui os hashes dos JUnit, do serviço anterior e do serviço optimizado; o hash da fonte entregue corresponde ao da medição. A parte da fonte desde preview() até confirm() foi também comparada com 340f3bb e permanece igual, desconsiderando apenas a normalização de fins de linha.

Índices experimentais

Somente na base sintética, dentro de savepoint:

CREATE INDEX sgf_qa_assoc_links ON identity_audit
 (tenant_id,company_id,(details->>'area'),(details->>'record_id'),
  (details->>'module_id'),id DESC)
 WHERE action IN ('module.record.associated','module.record.disassociated');

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');

O primeiro foi desenhado para as consultas de associação/retirada; o segundo inclui as revisões sem mudança e a leitura conjunta das revisões. O par é medido em conjunto; não se atribui a cada índice isoladamente a totalidade do ganho. Nos quatro planos capturados após os índices, observou-se o uso de sgf_qa_assoc_revision; não se demonstrou aí uma contribuição isolada de sgf_qa_assoc_links. O catálogo e os dados anteriores são comparados antes/depois do rollback, e é verificada a ausência destes índices experimentais. Nenhum destes comandos foi executado no destino, nem foi criada migração ou concessão para o aplicar.

Os planos na base sintética comparam a leitura conjunta antes/depois dos índices numa fixture de 10 000 registos por área. Não exportam bindings ou valores de filtros de documentos. Limites de plano, quando atingidos, são registados no artefacto e não contados como planos completos.

Testes afectados

20 testes / 312 asserções, aprovados na versão funcional final: os 17 casos da fila/navegação e três casos novos. Estes verificam constância das consultas ao aumentar a página com dependências partilhadas, ausência de leitura de cadastros alheios ao grafo, revisões sem associação, múltiplos módulos/retiradas, revogação de consulta entre pedidos, versões novas nas dependências e conflito na confirmação de uma prévia antiga.

Mantêm-se as provas afectadas de isolamento/recusas, geração de contexto, versão desactualizada, recuperação após falha de auditoria e confirmações realmente concorrentes. Não se repetiram as suites de módulos financeiros ou a regressão geral. Não houve alteração React, pelo que o build e controlos de apresentação aceites foram reutilizados; não se declara nova revisão visual.

O ensaio dirigido de planos também passou: 1 teste / 11 asserções. A primeira comparação completa foi preliminar: depois da revisão de memória, substituiu-se o percurso por closure recursiva pelo método normal e executou-se a comparação novamente sobre a versão final. Só a execução final deve ser usada para as medições desta entrega.

Uma prova directa de paridade de acessos passou 1 teste / 238 asserções, antes/depois dos índices sintéticos. O serviço anterior e o novo devolveram as mesmas recusas: 403 para consulta sem administração ou sem consulta própria e unidade não seleccionada, 422 para tamanho inválido e 409 para geração antiga. Depois de revogar a consulta de uma dependência, ambos ocultaram a cadeia inteira e devolveram exactamente a mesma resposta. As concessões desta prova são apenas fixtures transaccionais e foram desfeitas; não são direitos públicos ou configurações reais.

O catálogo de índices e as impressões digitais das oito tabelas anteriores da base de regressão foram conferidos antes das fixtures e após o rollback de cada escala, sem alterações. Não se repuseram sequências nem se apagou auditoria: são fixtures novas dentro de transacções, com comparação do estado anteriormente gravado. A prova de paridade e a prova de planos confirmaram também o rollback dos índices.

Limitações preservadas

A memória de produto cresce com a página e as dependências explicitamente referenciadas, não com o cadastro inteiro. O ensaio usa uma linha por rascunho; não valida o maior fan-out possível de 100 linhas por documento, payloads documentais grandes ou todo o histórico multi-módulo. O PHP ainda conserva os conteúdos dos registos seleccionados durante a descrição; os picos do processo de teste incluem fixtures/kernel. A memória incremental do pedido é apresentada separadamente da memória total.

Os filtros de estado e as contagens continuam no SQL anterior; o ganho da leitura conjunta não significa que todos os seus planos estejam optimizados. A variante C é posterior a A/B e usa buffers quentes; não constitui uma experiência de arranque frio ou prova estatística da contribuição individual de cada índice. As escalas usam a mesma base sucessivamente, com rollback/ANALYZE e sem reconstrução física entre escalas; páginas físicas/cache podem influenciar os valores.

Não há percentis de cauda, throughput, saturação ou capacidade com vários utilizadores validada. Custos de escrita, criação concorrente/recuperação e manutenção de índices exigem decisão/plano próprios antes de implantação. O histórico apresentado continua limitado a 20 eventos recentes, com auditoria integral preservada. Os tokens mantêm validade de 15 minutos.

Os seis registos protegidos, a cache bloqueada e o trabalho alheio permanecem intactos. Não houve decisão real, nova concessão pública, reinício de demonstração, publicação, activação de Compras ou checkpoint neste incremento. Revisão visual e validação da capacidade de implantação continuam separadas.

Repetição deliberada

Usar as ferramentas fixadas e a cópia de regressão existente. scripts/modular-regression-run.mjs sync copia a fonte para essa aplicação, preservando a demonstração persistente. Não executar prepare, reset ou PHPUnit na base de 8039/instalação local.

Com C:/laragon/tools/sgf-tempo/node-v24.22.1-win-arm64/node.exe:

scripts/module-association-compare.mjs
scripts/module-association-compare.mjs plans
scripts/module-association-compare.mjs access

O launcher fixa PHP 8.4.27, confere o destino, recupera a baseline do Git para ficheiro privado e guarda relatório/JUnit fora do repositório. Os testes afectados usam o runner existente com focused --filter "ModuleAssociationReviewTest|ModuleNavigationTest". Não há instrução de aplicar os índices ao destino; essa aplicação exige procedimento e evidência próprios. Esta entrega local não presume aceitação do incremento ou checkpoint.