Nós visitados: 11. Comparações: sem alteração.
Em testes locais de setembro de 2026, o protótipo de descoberta repetia essas contagens. Com 32 agentes e também com 512. A requisição se propagava no máximo por dois saltos próximos; os números pareciam corretos.
O tempo, porém, mudou. O percentil 95 da latência foi de 5.66 para 47.64 milissegundos. A rede cresceu 16 vezes e a consulta ficou mais de oito vezes mais lenta.
A contradição apontou para algo: havia trabalho fora da contagem de visitas.
Antes de partir, a requisição já tinha percorrido a rede
Cada agente do protótipo guarda uma sinalização de descoberta e se conecta a poucos vizinhos. Ao passar por um nó, a requisição precisa decidir se existem objetos próximos que valem explorar. O sistema monta um resumo do entorno com os sinais públicos do nó e dos vizinhos a um salto.
Pensando em um mapa urbano, a consulta vai percorrer poucas ruas. O resumo é a placa de orientação de uma esquina, ajudando a escolher a direção seguinte.
A implementação inicial escolheu um caminho conveniente: reconstruir as placas de todos os nós no começo de cada consulta e só depois partir.
# 早期请求路径的简化结构
adverts = build_all_neighborhood_summaries(network)
return walk_local_routes(request, adverts)
A segunda linha realmente percorre rotas locais. A primeira já percorreu a rede inteira.
Os contadores cobriam sobretudo propagação e comparações, por isso informavam corretamente “11 nós visitados”. Mediam a propagação. A reconstrução completa acontecia antes, consumia tempo e crescia com a rede, mas não estava nessa conta.
Sistemas de dados conhecem bem esse fenômeno. Um banco pode retornar poucas linhas depois de ler muitas; uma consulta com LIMIT pode pagar primeiro o custo de ordenar ou construir um índice. A documentação de EXPLAIN do PostgreSQL distingue linhas de saída, custo inicial e custo total. É preciso saber que trecho do trabalho cada medida cobre.
No protótipo, o problema já era concreto: o trabalho economizado nas visitas locais era feito antes de elas começarem.
Construir o resumo só quando necessário
Dividimos a construção em lote em construção por nó. Quando a consulta chega a uma borda e precisa olhar o entorno, monta aquele resumo. Se precisar dele de novo na mesma consulta síncrona, reutiliza o resultado.
# 修订后请求路径的简化结构
adverts = {}
def summary_for(edge_id):
if edge_id not in adverts:
adverts[edge_id] = build_one_neighborhood_summary(edge_id)
return adverts[edge_id]
return walk_local_routes(request, summary_for)
adverts dura apenas uma consulta. A próxima lê novamente nós e conexões atuais; atualizações, entradas e saídas geram resumos novos naturalmente. É uma simplicidade deliberada: o protótipo é síncrono e não altera a rede de forma concorrente durante uma descoberta. Reutilizar dentro da consulta captura o ganho principal e evita a complexidade de invalidar cache entre requisições.
Construir todos os resumos continua útil para diagnóstico ou exportação de uma visão da rede. Apenas deixou de ser passagem obrigatória da consulta comum.
Acrescentamos dois contadores de ações reais: quantos resumos foram construídos e quantos contribuidores desses resumos foram lidos. Mantivemos as visitas e tornamos a preparação observável também.
A mesma carga produziu uma curva mais plana
A comparação manteve máquina, topologia, consultas e configuração. Foram 12 requisições por tamanho em um anel bidirecional fixo, com vizinhos nos deslocamentos 1 e 3 e propagação máxima de dois saltos. Este é o registro histórico dos testes locais curtos com topologia construída.
| Agentes na rede | p95 antes | p95 depois |
|---|---|---|
| 32 | 5.66 ms | 4.28 ms |
| 128 | 13.21 ms | 4.91 ms |
| 512 | 47.64 ms | 5.15 ms |
Figura 1. Comparação histórica antes e depois com a mesma carga. O eixo horizontal organiza os três tamanhos testados; linhas orientam a leitura. Cada ponto é o p95 de 12 consultas.
Com 512 agentes, a vazão passou de cerca de 22 para cerca de 209 consultas por segundo. Cada consulta continuou visitando 11 agentes; agora construía só 10 resumos e examinava 50 contribuidores. As duas contagens foram iguais nos três tamanhos.
A análise pareada conferiu candidatos, transferências, visitas e contadores originais: permaneceram iguais. A explicação é específica: retiramos pré-processamento que a consulta não usava. A curva de tempo e os novos contadores se confirmam mutuamente.
O resultado vale para aquela máquina única, execução síncrona e topologia construída. Cada grupo tem apenas 12 requisições; o p95 usa interpolação linear em amostra curta. Em produção, concorrência, rede e mudanças dos dados também afetam a cauda de latência. O resultado mais valioso foi localizar e eliminar um trabalho anterior à requisição que crescia de fato com o conjunto total.
Um algoritmo local precisa de um caminho local
Um algoritmo pode ser econômico no laço central e varrer todos os dados na inicialização, serialização, manutenção de índices ou limpeza. Aqui, a omissão estava nos resumos; os novos contadores incluíram esse trabalho no custo da mesma requisição.
Em outro sistema, um experimento pequeno ajuda: manter fixos os objetos necessários, aumentar os irrelevantes e medir o tempo de ponta a ponta. Se as operações centrais não mudam, mas o tempo cresce, procure trabalho ainda não contabilizado no caminho da requisição.
Depois da correção, só se constrói o resumo utilizado: 10 resumos, 50 contribuidores. Uma consulta local finalmente não prepara placas para a rede inteira.
Materiais: teste local de capacidade e análise pareada do Discovery Front de 1º de setembro de 2026. Esta revisão conferiu arquivos originais, configuração e código atual dos resumos. O código no texto é uma simplificação explicativa. Localizações em SOURCE-NOTES.md do pacote editorial. A nota reescreve um experimento existente.