Recuperação de desastres (DR) avançada com slot de failover lógico

Nesta página, descrevemos como usar o slot de failover lógico para configurar a replicação lógica do Cloud SQL para PostgreSQL de modo que funcione perfeitamente com operações de recuperação de desastres (DR) avançada, especificamente a troca e o failover de réplica em instâncias com a edição Cloud SQL Enterprise Plus.

Os recursos avançados de recuperação de desastres (DR) do Cloud SQL oferecem recursos robustos de recuperação de desastres. Quando combinado com a replicação lógica do PostgreSQL, é crucial que o fluxo de replicação permaneça ininterrupto após uma troca ou um failover de réplica.

Usando a recuperação de desastres (DR) avançada com a replicação lógica do PostgreSQL, você garante que os assinantes lógicos não percam dados e possam se reconectar automaticamente à nova instância principal após um evento de recuperação de desastres, garantindo assim a continuidade dos negócios.

É possível usar essa funcionalidade em instâncias do Cloud SQL com a seguinte configuração:

  • PostgreSQL versão 17 ou mais recente
  • Edição do Cloud SQL Enterprise Plus
  • Acesso a serviços particulares

    Recomendamos usar o endpoint de gravação do serviço de nome de domínio (DNS) de acesso a serviços particulares para ativar reconexões automáticas de assinantes lógicos.

Antes de começar

Configurar a recuperação avançada de desastres (DR) com replicação lógica

O processo para configurar a recuperação de desastres (DR) avançada com a replicação lógica do PostgreSQL tem as seguintes etapas gerais:

  1. Configure as variáveis de ambiente e a VM bastion.
  2. Crie e configure a instância principal.
  3. Criar e designar uma réplica de DR.
  4. Crie e configure uma instância de assinante lógico.
  5. Crie uma assinatura de replicação lógica.
  6. Faça uma troca ou um failover de réplica.
  7. Valide a replicação.
  8. Limpe o slot de replicação órfão na nova réplica.
  9. Opcional: faça o switchback.

Configurar variáveis de ambiente e VM bastião

  1. Defina as seguintes variáveis de ambiente.

    # Project
    export PROJECT="PROJECT_ID"
    
    # Instance names
    export PRIMARY_INSTANCE_NAME="PRIMARY_INSTANCE"
    export DR_REPLICA_NAME="DR_REPLICA"
    export SUBSCRIBER_INSTANCE_NAME="SUBSCRIBER_INSTANCE"
    export BASTION_VM_NAME="BASTION_VM"
    
    # Regions and zones
    export PRIMARY_REGION="PRIMARY_REGION"
    export REPLICA_REGION="REPLICA_REGION"
    export SUBSCRIBER_REGION="SUBSCRIBER_REGION"
    export VM_ZONE="VM_ZONE"
    
    # Network
    export NETWORK_NAME="NETWORK"
    
    # Credentials
    export POSTGRES_PASSWORD="PASSWORD"
    
    # Set gcloud project
    gcloud config set project PROJECT_ID
    

    Substitua:

    • PROJECT_ID: ID do projeto.
    • PRIMARY_INSTANCE: o nome da instância principal do Cloud SQL.
    • DR_REPLICA: o nome da réplica.
    • SUBSCRIBER_INSTANCE: o nome da instância do assinante.
    • BASTION_VM: o nome da VM bastion.
    • PRIMARY_REGION: a região em que a instância principal está localizada.
    • REPLICA_REGION: a região em que a réplica está localizada. A réplica precisa estar em uma região diferente da instância principal.
    • SUBSCRIBER_REGION: a região em que o assinante está localizado.
    • VM_ZONE: a zona em que a VM bastião está localizada.
    • NETWORK: o nome da sua rede VPC.
    • PASSWORD: a senha do usuário postgres.
  2. Crie uma VM bastion do Compute Engine.

    As instâncias do Cloud SQL usam IP particular. Portanto, crie uma VM de Bastion Host do Compute Engine na sua rede VPC.

    gcloud compute instances create $BASTION_VM_NAME \
      --zone=$VM_ZONE \
      --machine-type=e2-small \
      --network=projects/$PROJECT_ID/global/networks/$NETWORK_NAME \
      --image-project=debian-cloud \
      --image-family=debian-11 \
      --project=$PROJECT
    
  3. Conecte-se à VM bastion.

    gcloud compute ssh $BASTION_VM_NAME \
      --zone=$VM_ZONE \
      --project=$PROJECT
    
  4. Na VM bastion, instale o cliente PostgreSQL.

    sudo apt-get update
    sudo apt-get install -y postgresql-client
    exit
    

Os comandos do PostgreSQL nas etapas subsequentes precisam ser executados na VM bastion.

Criar e configurar a instância principal

  1. Crie a instância principal do Cloud SQL.

    gcloud sql instances create $PRIMARY_INSTANCE_NAME \
      --database-version=POSTGRES_17 \
      --edition=ENTERPRISE_PLUS \
      --region=$PRIMARY_REGION \
      --tier=db-perf-optimized-N-2 \
      --no-assign-ip \
      --network=projects/$PROJECT/global/networks/$NETWORK_NAME \
      --project=$PROJECT
    
  2. Ative a decodificação lógica.

    gcloud sql instances patch $PRIMARY_INSTANCE_NAME \
      --database-flags=cloudsql.logical_decoding=on \
      --project=$PROJECT
    
  3. Defina a senha do usuário postgres no servidor principal.

    gcloud sql users set-password postgres \
      --instance=$PRIMARY_INSTANCE_NAME \
      --password="$POSTGRES_PASSWORD" \
      --project=$PROJECT
    
  4. Conecte-se à instância principal pela VM bastion.

    1. Recupere o endereço IP privado da instância principal.

      gcloud sql instances describe $PRIMARY_INSTANCE_NAME \
        --format="value(ipAddresses[0].ipAddress)" \
        --project=$PROJECT
      

      Copie e mantenha o endereço IP privado da instância principal.

    2. Conecte-se por SSH à VM bastion.

      gcloud compute ssh $BASTION_VM_NAME --zone=$VM_ZONE --project=$PROJECT
      
    3. Na VM bastion, conecte-se à instância principal.

      psql -h PRIMARY_PRIVATE_IP -U postgres
      

      Substitua PRIMARY_PRIVATE_IP pelo IP particular da instância principal que você recuperou na Etapa 4.a deste procedimento.

    4. Quando uma senha for solicitada, insira a variável $POSTGRES_PASSWORD.

      A VM bastion agora está conectada à instância principal pelo PostgreSQL.

  5. Conceda permissões e crie a publicação.

    1. Conceda o privilégio REPLICATION ao usuário postgres.

      ALTER USER postgres WITH REPLICATION;
      
    2. Conceda os privilégios necessários no esquema e nas tabelas públicos.

      GRANT SELECT ON ALL TABLES IN SCHEMA public TO postgres;
      
    3. Crie a publicação para todas as tabelas.

      CREATE PUBLICATION my_publication FOR ALL TABLES;
      
    4. Digite exit para sair do PostgreSQL e exit novamente para fechar a sessão SSH da VM bastion.

Criar e designar uma réplica de recuperação de desastres (réplica de DR)

  1. Crie uma réplica de DR.

    gcloud sql instances create $DR_REPLICA_NAME \
      --master-instance-name=$PRIMARY_INSTANCE_NAME \
      --edition=ENTERPRISE_PLUS \
      --tier=db-perf-optimized-N-2 \
      --region=$REPLICA_REGION \
      --no-assign-ip \
      --network=projects/$PROJECT/global/networks/$NETWORK_NAME \
      --project=$PROJECT
    
  2. Designar essa réplica como a réplica de DR.

    gcloud sql instances patch $PRIMARY_INSTANCE_NAME \
      --failover-dr-replica-name=$DR_REPLICA_NAME \
      --project=$PROJECT
    
  3. Configure a réplica de DR para sincronização de slot lógico.

    gcloud sql instances patch $DR_REPLICA_NAME \
      --database-flags="^:^cloudsql.logical_decoding=on:hot_standby_feedback=on:sync_replication_slots=on:cloudsql.logical_slot_sync_dbname=postgres" \
      --project=$PROJECT
    
  4. Configure a replicação síncrona entre a instância principal e a réplica de DR.

    Para evitar a perda de dados no assinante lógico em caso de uma interrupção repentina da instância principal e um failover de réplica subsequente, recomendamos que você configure a replicação síncrona entre a instância principal e a réplica de DR.

    Definir cloudsql.synchronized_standby_replicas na instância principal força o remetente do registro de gravação antecipada (WAL) da replicação lógica da instância principal a aguardar até que a réplica de DR receba e limpe o WAL de uma determinada transação antes de enviar essa transação ao assinante lógico. Isso garante que o estado da réplica de DR esteja sempre à frente ou igual ao estado do assinante lógico.

    gcloud sql instances patch $PRIMARY_INSTANCE_NAME \
      --database-flags="^:^cloudsql.logical_decoding=on:cloudsql.synchronized_standby_replicas=$DR_REPLICA_NAME" \
      --project=$PROJECT
    

Criar e configurar uma instância de assinante lógico

  1. Crie uma instância de assinante.

    gcloud sql instances create $SUBSCRIBER_INSTANCE_NAME \
      --database-version=POSTGRES_17 \
      --tier=db-perf-optimized-N-2 \
      --region=$SUBSCRIBER_REGION \
      --no-assign-ip \
      --network=projects/$PROJECT/global/networks/$NETWORK_NAME \
      --project=$PROJECT
    
  2. Ative a decodificação lógica no assinante.

    gcloud sql instances patch $SUBSCRIBER_INSTANCE_NAME \
      --database-flags=cloudsql.logical_decoding=on \
      --project=$PROJECT
    

Criar uma assinatura de replicação lógica

  1. Recupere o endpoint de gravação de acesso a serviços particulares da instância principal.

    gcloud sql instances describe $PRIMARY_INSTANCE_NAME \
      --format="value(replicationCluster.psaWriteEndpoint)" \
      --project=$PROJECT
    

    Copie e mantenha o endpoint de gravação.

  2. Conecte-se à instância do assinante.

    1. Atualize a senha do usuário postgres da instância assinante.

      gcloud sql users set-password postgres \
        --instance=$SUBSCRIBER_INSTANCE_NAME \
        --password="$POSTGRES_PASSWORD" \
        --project=$PROJECT
      
    2. Recupere o endereço IP privado da instância do assinante.

      gcloud sql instances describe $SUBSCRIBER_INSTANCE_NAME \
        --format="value(ipAddresses[0].ipAddress)" \
        --project=$PROJECT
      

      Copie e guarde o endereço IP particular.

    3. Conecte-se por SSH à VM bastion.

      gcloud compute ssh $BASTION_VM_NAME \
        --zone=$VM_ZONE \
        --project=$PROJECT
      
    4. Na VM bastion, conecte-se à instância do assinante pelo PostgreSQL.

      psql -h SUBSCRIBER_PRIVATE_IP -U postgres
      

      Substitua SUBSCRIBER_PRIVATE_IP pelo IP particular da instância do assinante que você copiou na Etapa 2.b deste procedimento.

    5. Quando uma senha for solicitada, insira a variável $POSTGRES_PASSWORD.

  3. Crie uma assinatura.

    CREATE SUBSCRIPTION my_subscription
    CONNECTION 'host=DR_CLUSTER_PSA_DNS_WRITE_ENDPOINT port=5432 dbname=postgres user=postgres password=PASSWORD'
    PUBLICATION my_publication
    WITH (failover = true);
    

    Substitua:

    • DR_CLUSTER_PSA_DNS_WRITE_ENDPOINT: o endpoint de gravação de acesso a serviços particulares que você copiou na Etapa 1 deste procedimento.
    • PASSWORD: o valor da variável ${POSTGRES_PASSWORD}.
  4. Saia do PostgreSQL e da sessão SSH da VM bastion.

  5. Opcional. Verifique a persistência do slot na réplica de DR.

    Essa transição para persistente (temporary = false) geralmente ocorre rapidamente, muitas vezes em segundos, se o primário tiver pouca atividade. Com uma carga de gravação pesada no servidor principal, esse processo pode levar mais tempo, geralmente cerca de um minuto. O slot vai permanecer após a conclusão desses comandos manuais.

    1. Consiga o endereço IP particular da réplica de DR.

      gcloud sql instances describe $DR_REPLICA_NAME \
        --format="value(ipAddresses[0].ipAddress)" \
        --project=$PROJECT
      

      Copie e mantenha o endereço IP particular da réplica de DR.

    2. Conecte-se por SSH à VM bastion.

      gcloud compute ssh $BASTION_VM_NAME \
        --zone=$VM_ZONE \
        --project=$PROJECT
      
    3. Na VM bastion, conecte-se à réplica de DR.

      psql -h DR_REPLICA_PRIVATE_IP -U postgres
      

      Substitua DR_REPLICA_PRIVATE_IP pelo endereço IP particular da réplica de DR que você recuperou na Etapa 5.a deste procedimento.

    4. Quando uma senha for solicitada, insira a variável $POSTGRES_PASSWORD.

    5. Verifique o status do slot.

      SELECT slot_name, slot_type, temporary, failover, synced
      FROM pg_replication_slots
      WHERE slot_type = 'logical' AND failover = true;
      

      Aguarde até que a coluna temporary se torne f. Isso geralmente leva menos de um minuto.

    6. Saia do PostgreSQL e da sessão SSH da VM bastion.

Realizar uma troca ou um failover de réplica

Escolha a operação que você quer realizar com base no seu cenário:

  • Alternância (inversão de função planejada): escolha essa opção para manutenção planejada, teste de recuperação de desastres ou para alternar funções quando a instância principal estiver on-line e íntegra. Essa operação garante zero perda de dados para a replicação física.

    gcloud sql instances switchover $DR_REPLICA_NAME \
      --project=$PROJECT
    
  • Failover de réplica (recuperação de desastres): escolha essa opção quando a instância principal estiver indisponível ou sem resposta. Essa operação promove a réplica de DR para principal. Para minimizar o risco de perda de dados para o assinante lógico, verifique se o cloudsql.synchronized_standby_replicas foi definido na instância principal conforme recomendado em Criar e designar uma réplica de recuperação de desastres (réplica de DR).

    gcloud sql instances promote-replica $DR_REPLICA_NAME \
      --failover \
      --project=$PROJECT
    

    A promoção do $DR_REPLICA_NAME acontece rapidamente. No entanto, a instância principal original ($PRIMARY_INSTANCE_NAME) só é reconfigurada como uma réplica da nova principal quando volta a ficar on-line. Para acompanhar isso, procure a conclusão da operação RECONFIGURE_OLD_PRIMARY em $PRIMARY_INSTANCE_NAME no registro de operações. Execute este comando:

    gcloud sql operations list --instance=$PRIMARY_INSTANCE_NAME --project=$PROJECT --limit=10`
    

    A configuração de recuperação de desastres só é totalmente restaurada após a conclusão dessa fase.

Depois de qualquer uma das operações, o assinante se reconecta automaticamente ao novo $DR_REPLICA_NAME principal pelo endpoint de gravação de acesso a serviços particulares.

Gerenciamento de flags

Os fluxos de trabalho do Cloud SQL gerenciam automaticamente as flags de banco de dados necessárias nas duas instâncias do cluster de recuperação de desastres durante e após as operações de failover de réplica e de troca, incluindo:

  • As flags de sincronização de slot lógico (cloudsql.logical_decoding, hot_standby_feedback, sync_replication_slots, cloudsql.logical_slot_sync_dbname) são corretas na instância que se torna a nova réplica.
  • A flag cloudsql.synchronized_standby_replicas na instância que se torna a nova principal é atualizada automaticamente para apontar para o nome da nova réplica de DR.

Não é necessário reaplicar ou mudar manualmente essas flags após uma troca ou operação de failover de réplica. O Cloud SQL mantém a configuração correta para as funções de principal e réplica.

Validar replicação

  1. Verifique o status do assinante.

    1. Na VM bastion, execute o seguinte comando.

      psql -h SUBSCRIBER_PRIVATE_IP -U postgres
      

      Substitua SUBSCRIBER_PRIVATE_IP pelo endereço IP privado da instância do assinante.

    2. Na instância do assinante, execute o seguinte comando:

      SELECT subname, pid IS NOT NULL AS is_active
      FROM pg_stat_subscription;
      

      O status precisa ser streaming.

  2. Verifique o status do slot de replicação da nova instância principal. O novo primário é a antiga réplica de DR ($DR_REPLICA_NAME).

    1. Receba o endereço IP privado do novo primário.

      gcloud sql instances describe $DR_REPLICA_NAME \
        --project=$PROJECT --format="value(ipAddresses[0].ipAddress)"
      

      Copie e mantenha o endereço IP particular da nova principal.

    2. Conecte-se por SSH à VM bastion.

      psql -h NEW_PRIMARY_PRIVATE_IP -U postgres
      

      Substitua NEW_PRIMARY_PRIVATE_IP pelo endereço IP privado do novo primário que você copiou na etapa anterior.

    3. Na nova instância principal, execute os seguintes comandos.

      SELECT
          slot_name,
          slot_type,
          active,
          synced,
          active_pid,
          pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), confirmed_flush_lsn)) AS replication_lag
      FROM pg_replication_slots
      WHERE slot_type = 'logical';
      

      O slot (por exemplo, my_subscription) precisa ser active = t.

      SELECT
          application_name,
          state
      FROM pg_stat_replication;
      

      pg_stat_replication vai mostrar o assinante conectado.

Limpar o slot de replicação órfão na nova réplica

Depois que a operação de alternância e failover for concluída, a instância principal original ($PRIMARY_INSTANCE_NAME) será uma réplica. Essa nova instância de réplica ainda retém o slot de replicação lógica original chamado my_subscription no disco. Este slot my_subscription agora está órfão porque o assinante precisa se conectar ao novo primário ($DR_REPLICA_NAME) pelo endpoint de gravação de acesso a serviços particulares.

O Cloud SQL não remove automaticamente esse slot órfão da nova réplica. Isso acontece porque o Cloud SQL não consegue determinar se o assinante foi configurado para usar o endereço IP da instância em vez do endpoint de gravação de acesso a serviços particulares. O assinante ainda pode estar tentando se conectar a esse slot antigo na nova réplica até que a assinatura seja alterada manualmente. A remoção automática do slot pode prejudicar essas configurações.

A presença desse slot órfão na nova réplica ($PRIMARY_INSTANCE_NAME) faz com que o processo de trabalho slotsync nessa instância gere erros nos registros. Talvez você veja uma mensagem de erro como esta no postgres.log da nova réplica. Esse erro continua se repetindo enquanto o worker slotsync tenta.

ERROR: exiting from slot synchronization because same name slot "my_subscription" already exists on the standby

Para evitar esses erros e permitir que o worker slotsync estabeleça corretamente uma nova versão sincronizada do slot my_subscription nessa réplica, é necessário descartar manualmente o slot órfão. Isso garante que a instância esteja preparada adequadamente se você pretende fazer o switchback no futuro.

  1. Recupere o endereço IP particular da nova réplica ($PRIMARY_INSTANCE_NAME).

    gcloud sql instances describe $PRIMARY_INSTANCE_NAME \
      --project=$PROJECT --format="value(ipAddresses[0].ipAddress)"
    

    Copie e mantenha o endereço IP privado da nova réplica.

  2. Conecte-se por SSH à VM bastion.

    gcloud compute ssh $BASTION_VM_NAME \
      --zone=$VM_ZONE \
      --project=$PROJECT
    
  3. Na VM bastion, conecte-se à nova réplica.

    psql -h NEW_REPLICA_IP -U postgres
    

    Substitua NEW_REPLICA_IP pelo endereço IP da nova réplica que você copiou na Etapa 1 deste procedimento.

  4. Quando uma senha for solicitada, insira a variável $POSTGRES_PASSWORD.

  5. Na nova réplica ($PRIMARY_INSTANCE_NAME), descarte o slot órfão.

    SELECT slot_name, slot_type, temporary, failover, synced, active
    FROM pg_replication_slots
    WHERE slot_name = 'my_subscription';
    

    Confirme se o slot existe com synced = false e active = false e solte-o.

    SELECT pg_drop_replication_slot('my_subscription');
    

    O slot órfão é removido.

Resincronização automática de slots

Quando o slot órfão é descartado, o worker slotsync na nova réplica ($PRIMARY_INSTANCE_NAME) se conecta automaticamente à nova instância primária ($DR_REPLICA_NAME) no próximo ciclo. Ele cria um novo slot my_subscription local que é sincronizado com o slot ativo do novo primário.

Você pode conferir mensagens no postgres.log da nova réplica indicando sucesso, semelhante a esta:

LOG: newly created slot "my_subscription" is sync-ready now

O novo slot sincronizado tem failover=true e, eventualmente, se torna permanente (temporary=false), garantindo que, se você fizer o switchback mais tarde, essa instância estará pronta.

Opcional: faça o retorno

  1. Agora, mude de volta, tornando $PRIMARY_INSTANCE_NAME a instância principal novamente.

    gcloud sql instances switchover $PRIMARY_INSTANCE_NAME \
      --project=$PROJECT
    
  2. Faça a verificação após o switchback.

    1. Verifique o status da instância do assinante.

      SELECT subname, pid IS NOT NULL AS is_active
      FROM pg_stat_subscription;
      

      A assinatura ainda precisa estar ativa, ou seja, is_active = t.

    2. Verifique o status do slot do novo primário ($PRIMARY_INSTANCE_NAME).

      SELECT slot_name, active FROM pg_replication_slots WHERE slot_type = 'logical';
      SELECT * FROM pg_stat_replication;
      

      O slot precisa estar ativo e o assinante conectado.

Resolver problemas

Problema Solução de problemas

Erro na nova réplica (ou seja, a antiga instância principal) após a troca:

"exiting from slot synchronization because same name slot already exists on the standby"

Siga as etapas em Limpar o slot de replicação órfão na nova réplica.