Blog

Migração de índice de busca sem janela: trocar o motor com consulta ligada

A reindexação começou numa quinta às nove da noite com a promessa de terminar em duas horas, e às seis da manhã ainda faltavam quarenta por cento do catálogo. O time apontou a busca para o índice novo mesmo assim, porque o tráfego ia subir às oito. Naquele dia a busca por "tênis branco" devolveu trezentos resultados em vez de onze mil, ninguém percebeu por três horas porque a página não deu erro nenhum, e a receita de busca caiu dezenove por cento antes que alguém ligasse uma coisa à outra. Este artigo mostra por que a troca de motor de busca falha de um jeito silencioso que a troca de banco não tem, como o índice paralelo mais a repetição de escrita eliminam a janela de manutenção, por que a verificação por contagem de documentos aprova índices quebrados e qual verificação a substitui, por que a relevância muda mesmo quando os dados estão corretos e como comparar duas listas ordenadas sem depender de opinião, qual sequência de sete etapas troca o motor com a consulta ligada e reverte em qualquer ponto, e quais indicadores autorizam desligar o índice antigo.

2026-09-19 / Arquitetura / 18 min

01

Por que a troca de busca falha em silêncio

Uma migração de banco relacional que dá errado costuma gritar: a consulta quebra, a transação estoura, a aplicação devolve quinhentos e o alerta dispara. Uma migração de índice de busca que dá errado devolve duzentos com uma lista curta. O contrato da busca é uma lista de resultados ordenada, e uma lista com menos itens, ou com os itens na ordem errada, é indistinguível de uma busca que simplesmente não tinha muito a oferecer.

Isso muda tudo no desenho da migração. Não existe um erro para monitorar, então a validação precisa ser comparativa por construção: o índice novo só pode ser julgado contra o antigo, consulta a consulta, e não contra um limiar absoluto. O painel de erros vai ficar verde durante um incidente de busca inteiro, e o primeiro sinal real costuma vir de fora da engenharia, em forma de queda de conversão, de aumento de busca sem clique ou de chamado de cliente dizendo que um produto sumiu.

O segundo motivo é que o índice não é uma cópia dos dados: é uma interpretação deles. Entre a linha do banco e o documento indexado existe um pipeline com tokenização, remoção de acento, radicalização, sinônimo, decomposição de palavra composta, campo copiado e peso por campo. Dois motores com o mesmo conteúdo produzem resultados diferentes porque interpretam o texto de forma diferente, e é por isso que a pergunta certa nunca é se os dados chegaram inteiros, e sim se as respostas continuam boas.

AspectoMigração de bancoMigração de índice de busca
Sinal de falhaErro explícito, exceção, transação recusadaResposta válida com a lista errada, sem erro
Fonte de verdadeO próprio banco, que é a origemOutro sistema: o índice é sempre derivado
Validação possívelContagem e soma de verificação por tabelaComparação de listas ordenadas por consulta real
Custo da reversãoAlto: o dado novo já foi escrito no destinoBaixo: basta reapontar a leitura, o índice é reconstruível
Tempo de reconstruçãoNão se aplica, o dado é a origemHoras a dias, e define o tamanho da janela de repetição
Quem percebe primeiroO alerta de erro da aplicaçãoO negócio, pela queda de conversão

A quarta linha é a boa notícia dessa migração e a razão pela qual ela pode ser feita sem janela. O índice é derivado, então manter dois ao mesmo tempo não cria duas fontes de verdade: cria duas interpretações da mesma fonte, e qualquer uma pode ser descartada e reconstruída sem perda. É a propriedade que torna a leitura sombra segura e a reversão barata, e é justamente ela que a janela de manutenção desperdiça.

02

Índice paralelo e repetição de escrita: eliminar a janela

O problema real da reindexação não é o volume, é que o catálogo continua mudando enquanto ela roda. Uma carga completa que leva oito horas é uma fotografia do instante inicial, e ao terminar já está oito horas desatualizada. Preencher esse buraco depois, com uma consulta por data de atualização, funciona mal: depende de relógio confiável, perde a exclusão de documento e ignora alteração feita por rotina que não toca o campo de data.

A solução é inverter a ordem. Primeiro liga a repetição de escrita no índice novo, depois começa a carga histórica. Toda alteração que acontece durante a carga já é aplicada nos dois índices, e a carga histórica preenche o passado por baixo. Como a escrita é idempotente por identificador de documento, a ordem entre a carga e a repetição não importa desde que a repetição use número de versão: um documento sobrescrito pela carga com uma versão mais antiga é descartado.

Fase 1 - so o indice antigo
  aplicacao --escrita--> [INDICE ANTIGO] <--consulta-- aplicacao

Fase 2 - escrita dupla ligada, ainda sem carga historica
  aplicacao --escrita--+--> [INDICE ANTIGO] <--consulta-- aplicacao
                       \--> [INDICE NOVO]   (so deltas, vazio no passado)
  verificacao: taxa de erro da escrita nova isolada, 0 impacto no caminho critico

Fase 3 - carga historica por baixo, com escrita dupla ligada
  carregador --lote--> [INDICE NOVO] <--delta-- aplicacao
  regra: escrita so aplica se versao maior que a ja indexada
  verificacao: progresso por faixa de chave, nao por contagem total

Fase 4 - leitura sombra (consulta vai nos dois, responde o antigo)
  aplicacao --consulta--+--> [INDICE ANTIGO] --> resposta ao usuario
                        \--> [INDICE NOVO]   --> so comparacao, descartado
  verificacao: sobreposicao das listas, latencia, ausencia de resultado vazio

Fase 5 - leitura percentual (1% -> 10% -> 50% -> 100%)
  aplicacao --1%--> [INDICE NOVO]   --> resposta ao usuario
            -99%--> [INDICE ANTIGO] --> resposta ao usuario
  verificacao: taxa de clique e busca sem clique por rota

Fase 6 - desligar
  aplicacao --escrita+consulta--> [INDICE NOVO]
  o indice antigo fica recebendo escrita, sem consulta, por um ciclo inteiro

A fase dois parece inútil e é a que mais salva a migração. Ela não move leitura nenhuma: serve para provar que a escrita no motor novo funciona sob tráfego real, com o formato real dos documentos, incluindo aquele campo que só um fluxo raro preenche. Descobrir um mapeamento incompatível com zero leitura dependendo dele custa uma tarde. Descobrir depois de já ter migrado metade da leitura custa um incidente.

A escrita dupla precisa de uma regra clara sobre falha: o índice novo nunca pode derrubar a escrita principal. Enquanto ele não é fonte de leitura, a falha ali é registrada numa fila de reparo e não propagada. Depois que ele vira fonte de leitura, os papéis se invertem e o antigo passa a ser o lado tolerante. A escrita que falha silenciosamente e não vai para fila nenhuma é o defeito que produz divergência permanente, e é o mais comum dos três.

// Escrita dupla com versao e fila de reparo. O indice secundario nunca
// derruba a escrita principal: a falha vira item de reparo, nao excecao.
const PAPEIS = { PRIMARIO: 'primario', SECUNDARIO: 'secundario' };

// Configuracao dinamica: a troca de papel precisa valer sem reimplantacao,
// senao a reversao deixa de ser imediata.
const papelDe = (motor) => configuracao.get(`busca.papel.${motor.nome}`, PAPEIS.SECUNDARIO);

async function indexarDocumento(documento) {
  // A versao vem da origem, nao do relogio local. Relogio entre processos
  // diverge e faz a carga historica sobrescrever delta mais novo.
  const payload = {
    id: documento.id,
    versao: documento.atualizadoEmSequencia,
    corpo: montarDocumento(documento),
  };

  const resultados = await Promise.allSettled(
    motoresAtivos().map((motor) => escreverComVersao(motor, payload)),
  );

  const falhas = resultados
    .map((resultado, indice) => ({ resultado, motor: motoresAtivos()[indice] }))
    .filter(({ resultado }) => resultado.status === 'rejected');

  for (const { motor, resultado } of falhas) {
    // Sempre enfileira para reparo, inclusive quando vai relancar:
    // o reparo e o que garante convergencia se a nova tentativa falhar.
    await filaDeReparo.enfileirar({
      motor: motor.nome,
      documentoId: documento.id,
      versao: payload.versao,
      erro: String(resultado.reason),
    });

    if (papelDe(motor) === PAPEIS.PRIMARIO) throw resultado.reason;
  }

  return { indexadoEm: motoresAtivos().length - falhas.length };
}

// escreverComVersao usa o controle de concorrencia do proprio motor:
// a escrita e recusada quando a versao enviada e menor que a indexada,
// e essa recusa e sucesso, nao erro.
async function escreverComVersao(motor, payload) {
  try {
    return await motor.index(payload, { ifVersionGreaterThan: payload.versao - 1 });
  } catch (erro) {
    if (erro.code === 'version_conflict') return { ignorado: true };
    throw erro;
  }
}

O tratamento do conflito de versão no fim é o detalhe que separa uma escrita dupla correta de uma que corrompe dados devagar. Durante a carga histórica, conflito de versão é o comportamento esperado e frequente: significa que um delta mais novo já chegou e a carga tentou sobrescrever com a foto antiga. Tratar isso como erro enche a fila de reparo de lixo e esconde as falhas reais. Tratar como sucesso ignorado é o que faz a convergência acontecer sozinha.

03

A verificação de contagem aprova índice quebrado

A checagem que quase todo time faz é comparar o número de documentos dos dois índices. Ela é necessária e insuficiente: pega o caso em que a carga parou no meio e não pega nenhum dos outros. Um índice com o número exato de documentos pode ter perdido o campo de descrição, ter analisador diferente no campo de título, ter perdido os sinônimos ou ter pesos diferentes entre campos, e a contagem vai bater em todos esses casos.

DefeitoContagem bate?O que realmente aconteceVerificação que pega
Carga interrompida no meioNãoFaltam documentos inteirosContagem por faixa de chave
Campo não mapeadoSimBusca pelo termo daquele campo devolve vazioConsulta por campo com resultado esperado
Analisador diferenteSimAcento, plural ou composto param de casarConjunto de consultas com variação ortográfica
Sinônimo não migradoSimTermo comercial deixa de encontrar o produtoConsultas do topo da cauda com sinônimo conhecido
Peso de campo diferenteSimOrdem muda, o item certo cai para a página doisSobreposição de lista nas dez primeiras posições
Filtro com tipo diferenteSimFaceta some ou filtra errado por texto e númeroConsulta com filtro comparada lado a lado

A verificação que substitui a contagem é a reprodução do tráfego real de consulta contra os dois índices. Não um conjunto inventado de vinte consultas bonitas: as consultas que os usuários realmente fizeram na última semana, com a distribuição real, incluindo a cauda longa de termos digitados errado, que é onde os analisadores divergem. Duas mil consultas amostradas da cauda encontram mais defeitos que duzentas consultas populares, porque as populares casam por qualquer caminho.

// Comparador de listas ordenadas. Mede sobreposicao no topo e deslocamento
// de posicao, que sao as duas formas de a relevancia piorar sem erro nenhum.
const K_TOPO = 10;

const sobreposicaoNoTopo = (listaA, listaB, k = K_TOPO) => {
  const topoA = listaA.slice(0, k).map((item) => item.id);
  const topoB = new Set(listaB.slice(0, k).map((item) => item.id));
  const comuns = topoA.filter((id) => topoB.has(id)).length;
  return comuns / k;
};

// Deslocamento medio: para cada item do topo antigo, quantas posicoes ele
// andou no novo. Item que sumiu conta como se tivesse ido para o fim.
const deslocamentoMedio = (listaA, listaB, k = K_TOPO) => {
  const posicaoEm = new Map(listaB.map((item, indice) => [item.id, indice]));
  const penalidade = listaB.length || k * 10;

  const desvios = listaA.slice(0, k).map((item, indice) => {
    const posicaoNova = posicaoEm.has(item.id) ? posicaoEm.get(item.id) : penalidade;
    return Math.abs(posicaoNova - indice);
  });

  return desvios.reduce((soma, valor) => soma + valor, 0) / (desvios.length || 1);
};

async function compararTrafego(consultasAmostradas) {
  const relatorio = [];

  for (const consulta of consultasAmostradas) {
    const [antigo, novo] = await Promise.all([
      motorAntigo.buscar(consulta),
      motorNovo.buscar(consulta),
    ]);

    relatorio.push({
      termo: consulta.termo,
      sobreposicao: sobreposicaoNoTopo(antigo.itens, novo.itens),
      deslocamento: deslocamentoMedio(antigo.itens, novo.itens),
      // Queda brusca de total e o sinal de campo faltando no mapeamento.
      razaoDeTotal: novo.total / Math.max(antigo.total, 1),
      vazioApenasNoNovo: antigo.total > 0 && novo.total === 0,
    });
  }

  // Ordena pelo pior caso: esses sao os defeitos, nao a media.
  return relatorio.sort((a, b) => a.sobreposicao - b.sobreposicao);
}

A última linha da função é a mais importante do método. A média de sobreposição vai parecer ótima mesmo com um defeito grave, porque noventa e cinco por cento das consultas atravessam qualquer mapeamento razoável. O que revela o problema é a ponta de baixo: as cinquenta consultas com pior sobreposição, lidas uma a uma. Quase sempre elas têm uma característica em comum, e essa característica é exatamente o defeito, seja o acento, o hífen, o número no meio do termo ou o campo que ninguém mapeou.

O campo vazioApenasNoNovo merece alerta próprio, com limiar em zero. Uma consulta que devolvia resultado no índice antigo e devolve lista vazia no novo é sempre defeito, nunca melhoria, e é a falha de maior impacto comercial porque produz a página de nenhum resultado para um usuário que estava pronto para comprar. Nenhuma migração deveria avançar de degrau com esse contador acima de zero.

04

Leitura sombra e a decisão que ela não resolve

A leitura sombra consulta os dois índices para cada busca real, responde ao usuário com o índice antigo e usa o resultado do novo apenas para comparação. Ela é a forma mais barata de validar com tráfego real, e tem uma limitação que precisa ser dita com clareza: ela mede concordância, não qualidade. Se o índice novo discorda do antigo, a sombra mostra que discorda, mas não diz qual dos dois está certo.

Isso importa porque quase sempre se troca de motor esperando relevância melhor, e relevância melhor é discordância por definição. Um índice novo com noventa e cinco por cento de sobreposição no topo não melhorou nada relevante. Um com setenta por cento pode ter melhorado ou piorado, e a sombra sozinha não decide. A separação prática é entre discordância explicável e discordância inexplicável: se você consegue apontar a mudança de configuração que causa a diferença e ela era intencional, é evolução. Se não consegue, é defeito até prova em contrário.

  1. Custo em latência. A sombra dobra a consulta e, se for feita em série no caminho da resposta, dobra o tempo percebido. A chamada ao índice novo precisa ser disparada sem espera, com limite de tempo agressivo e sem propagar erro, porque uma falha no caminho de comparação nunca pode aparecer para o usuário.
  2. Custo em amostragem. Nem toda consulta precisa de sombra. Cinco a dez por cento do tráfego, amostrado de forma estável por termo, já produz volume suficiente e mantém o custo de infraestrutura sob controle. Amostrar por usuário em vez de por consulta é pior, porque concentra a amostra nos termos de quem busca muito.
  3. Custo em interpretação. O relatório precisa de dono e de horário, senão vira painel que ninguém abre. A leitura das cinquenta piores consultas, feita por uma pessoa, uma vez por dia durante a migração, encontra mais defeito que qualquer limiar automático, porque a maioria dos defeitos tem um padrão visível a olho nu.
  4. Limite do método. A sombra não mede comportamento do usuário. Para saber se a relevância nova é melhor, é preciso tráfego real com resposta real, o que só acontece a partir da leitura percentual, medindo taxa de clique, posição do primeiro clique e busca sem clique por rota.

O terceiro item é o que decide entre uma migração de busca boa e uma medíocre, e é o menos automatizável dos quatro. As ferramentas encontram a discordância; a pessoa encontra o motivo. Meia hora por dia lendo as piores consultas, durante uma migração de duas semanas, custa cinco horas de trabalho e costuma evitar o incidente inteiro.

05

A sequência de sete etapas com a consulta ligada

A sequência abaixo vale tanto para troca de motor quanto para mudança grande de mapeamento dentro do mesmo motor, que tem exatamente os mesmos riscos. Cada etapa tem critério de saída objetivo, e nenhuma avança por data: todas avançam por indicador.

  1. Torne a indexação idempotente e versionada. Escrita por identificador de documento com número de versão vindo da origem, nunca do relógio local. Critério de saída: reindexar o mesmo documento cinco vezes, em ordens diferentes, produz sempre o mesmo documento final.
  2. Ligue a escrita dupla sem leitura nenhuma no índice novo. Fila de reparo ativa, alerta de atraso da fila configurado, zero impacto na latência de escrita do caminho principal. Critério de saída: vinte e quatro horas com fila de reparo drenando e escrita principal sem regressão de latência.
  3. Rode a carga histórica por faixa de chave, não em lote único. Faixas permitem retomar de onde parou, paralelizar com controle e medir progresso real. Critério de saída: contagem por faixa igual entre os dois índices, com tolerância explícita para documentos criados durante a carga.
  4. Ligue a leitura sombra em cinco por cento do tráfego. Comparação por consulta com sobreposição no topo, deslocamento e detecção de vazio exclusivo. Critério de saída: zero consultas com vazio exclusivo no novo, e as cinquenta piores lidas e explicadas uma a uma.
  5. Migre a leitura em degraus, começando em um por cento. Um por cento é baixo de propósito: a primeira exposição real encontra problemas de infraestrutura, como limite de conexões, cache frio e tempo de resposta sob concorrência, que a sombra não encontra. Critério de saída: latência no percentil noventa e cinco igual ou melhor, e taxa de clique dentro do intervalo do índice antigo.
  6. Avance para dez, cinquenta e cem por cento com pelo menos um ciclo diário completo em cada degrau. O tráfego de busca tem sazonalidade forte dentro do dia, e um degrau validado só no horário calmo não prova nada sobre o pico. Critério de saída: mesmo conjunto de indicadores estável durante o pico de cada degrau.
  7. Mantenha a escrita dupla e o índice antigo por um ciclo completo depois dos cem por cento. Ele é o caminho de volta imediato e o comparador para qualquer dúvida de relevância que aparecer depois. Critério de saída: uma semana em cem por cento sem regressão de negócio, e então desligue primeiro a escrita dupla, depois o índice.

A etapa cinco merece atenção porque é onde a maioria das surpresas aparece, e nenhuma delas é de relevância. O motor novo geralmente é validado em consulta isolada e falha em concorrência: o cache de filtro que estava quente no antigo está frio no novo, o agrupamento por faceta que custava dez milissegundos com cache custa duzentos sem, e o número de conexões simultâneas estoura no primeiro pico. Um por cento é o degrau que transforma essas descobertas em ajuste de configuração em vez de incidente.

A ordem de desligamento da etapa sete não é detalhe. Desligar o índice antigo antes da escrita dupla deixa a escrita falhando contra um destino que não existe, enchendo a fila de reparo de erro permanente e possivelmente degradando o caminho principal. Desligar a escrita dupla primeiro, esperar o ciclo e só então desprovisionar o índice mantém o caminho de volta disponível até o último momento em que ele ainda seria útil.

06

Os indicadores que autorizam desligar o índice antigo

O desligamento é a única etapa irreversível, porque reconstruir o índice antigo depois significa rodar a carga histórica de novo, contra um motor que já foi desprovisionado, com a configuração que ninguém mais mantém. Os indicadores abaixo transformam essa decisão em verificação.

IndicadorO que medeCritério para desligarO que ele pega
Busca sem cliqueFração de buscas sem nenhum clique no resultadoDentro do intervalo histórico por sete dias em cem por centoRelevância pior que não gera erro nenhum
Resultado vazio por termo conhecidoTermos com tráfego que passaram a devolver lista vaziaZero, medido sobre a cauda e não só sobre os popularesCampo não mapeado ou analisador divergente
Atraso da fila de reparoIdade do item mais antigo pendente de reindexaçãoEstável e abaixo do acordo de atualização do catálogoEscrita dupla falhando em silêncio para um subconjunto
Divergência por amostragemDocumentos comparados entre origem e índice, por amostraAbaixo do limiar por um ciclo completo de atualizaçãoDocumento que ficou preso numa versão antiga
Cobertura de fluxos de escritaFluxos distintos que indexaram no períodoTodos presentes, incluindo rotinas mensais e importaçõesFluxo raro que só escreve no índice antigo

O último indicador é o que mais evita incidente pós-desligamento, e é o mais esquecido. Catálogos reais têm fluxos de escrita que rodam uma vez por mês, como a importação de fornecedor, o ajuste de preço em lote ou a correção manual feita por uma tela administrativa antiga. Se esse fluxo escreve direto no índice antigo, sem passar pela camada de escrita dupla, ele funciona durante toda a migração e quebra no dia do desligamento. Medir fluxos distintos, e não volume de escrita, é o que revela o fluxo que escreve cem documentos por mês com o mesmo peso do que escreve um milhão por dia.

O primeiro indicador precisa de uma ressalva honesta: busca sem clique é um sinal ruidoso e influenciado por sazonalidade, campanha e mudança de catálogo. Ele não serve como gatilho automático, serve como condição de bloqueio. Se estiver fora do intervalo, não desligue e investigue. Se estiver dentro, ele não prova que a relevância melhorou, apenas que não piorou de forma detectável, e essa é exatamente a garantia que uma migração de infraestrutura precisa dar.

FAQ

Perguntas frequentes

Dá para pular a leitura sombra e ir direto para um por cento do tráfego real, já que a sombra não decide qual índice é melhor?

Dá, e em times pequenos com catálogo simples essa é uma escolha defensável, desde que fique claro o que se está trocando. A sombra encontra defeitos categóricos, do tipo que afeta uma classe inteira de consultas, e encontra antes de qualquer usuário ver. Pular a sombra significa que esses defeitos serão descobertos por usuários reais, e em um por cento do tráfego isso é aceitável em volume mas não em natureza: se o defeito for um campo não mapeado, o um por cento dos usuários que buscou por aquele termo recebeu página vazia, e você só vai saber pelo contador de resultado vazio depois do fato. O caminho intermediário que costuma valer mais a pena é rodar a sombra fora do caminho de resposta, em lote, sobre as consultas gravadas da semana anterior, em vez de em tempo real. Isso custa uma execução noturna, não altera latência nenhuma, não exige código no caminho crítico e encontra a mesma classe de defeitos. Perde-se a comparação sob concorrência real, mas essa nunca foi a força da sombra e sim do degrau de um por cento. Para catálogo com mais de alguns milhares de documentos, ou quando a busca é fonte relevante de receita, a comparação em lote é o mínimo que eu recomendaria antes de expor qualquer tráfego.

Como migrar sem escrita dupla quando o índice é alimentado por um pipeline de dados que eu não controlo?

A escrita dupla na aplicação é o caminho mais limpo, mas não é o único, e quando a indexação vem de um pipeline de terceiros ou de uma ferramenta de integração fechada existem duas alternativas boas. A primeira é a bifurcação no transporte: se o pipeline publica num tópico ou numa fila antes de indexar, basta adicionar um segundo consumidor que escreve no índice novo, e você ganha a escrita dupla sem tocar no pipeline. Essa é a opção preferida sempre que existe um ponto de mensageria no caminho, porque o novo consumidor é isolado, tem métrica própria e pode ser desligado sem afetar nada. A segunda é a captura de alteração na origem, lendo o registro de alterações do banco e alimentando o índice novo a partir dele. Custa mais para montar, mas tem uma vantagem real: não depende do pipeline estar correto, o que significa que ela também valida o pipeline antigo e às vezes revela que o índice atual já estava divergindo da origem havia meses. A opção que eu evitaria é a sincronização periódica por data de atualização, porque ela não captura exclusão, depende de relógio e perde qualquer alteração feita por rotina que não atualize o campo de data. Se ela for a única possível, compense com uma reconciliação completa por amostragem rodando em paralelo, e trate a divergência encontrada como medida do erro do método, não como exceção.

Quanto tempo devo manter o índice antigo ligado depois de chegar a cem por cento, e o que exatamente estou pagando por isso?

A regra prática que funciona é manter por um ciclo completo de negócio, e para a maioria dos catálogos isso significa um mês, não uma semana. O motivo é o fluxo de escrita raro: a importação mensal de fornecedor, o fechamento que reprecifica categorias inteiras, a campanha sazonal que cria atributo novo. Nenhum desses aparece numa janela de sete dias, e todos podem quebrar no índice novo de um jeito que só se percebe quando já não existe comparador. O custo real é menor do que parece. O índice antigo sem tráfego de consulta consome quase só armazenamento e a escrita dupla, e escrita costuma ser uma fração pequena do custo comparada à consulta, que é o que exige memória e processador. Na prática, manter o antigo por um mês adiciona algo entre dez e vinte por cento ao custo do subsistema de busca durante esse período, o que é barato perto de reconstruir tudo sob pressão. Existe um custo não financeiro que importa mais: enquanto os dois índices existem, toda investigação começa perguntando qual deles respondeu, e todo ajuste de relevância precisa ser feito em dois lugares ou explicitamente em um só. Por isso a decisão deve vir com data marcada desde o início. Índice antigo sem data de desligamento vira permanente, e três meses depois ninguém lembra se ele ainda recebe escrita.

Trocar o motor de busca é migrar relevância, não documentos

A migração de busca raramente falha por perda de dado: falha porque um analisador diferente quebrou uma classe de consultas, porque um campo não mapeado passou a devolver lista vazia, ou porque o índice antigo foi desligado antes da importação mensal provar que o fluxo dela foi migrado. Posso revisar a sua camada de busca e definir o desenho de escrita dupla com versão e fila de reparo, a carga histórica por faixa de chave que convive com o tráfego, a comparação de listas ordenadas sobre o tráfego real de consulta, a sequência de degraus reversível em qualquer ponto e os indicadores que autorizam desligar o índice antigo.