Skip to content

fix: prevent subscription breakage on rolling updates#429

Open
tsivaprasad wants to merge 2 commits into
mainfrom
PLAT-665-rolling-database-update-fails-on-multi-replica-nodes
Open

fix: prevent subscription breakage on rolling updates#429
tsivaprasad wants to merge 2 commits into
mainfrom
PLAT-665-rolling-database-update-fails-on-multi-replica-nodes

Conversation

@tsivaprasad

@tsivaprasad tsivaprasad commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR fixes rolling database updates failing on multi-node databases with replica instances.

Changes

  • instance_resource.go — Add waitForReplicationSlots helper. When
    initializeInstance runs on a replica instance, it now polls
    pg_replication_slots for every subscription where this node is the
    provider, blocking until each slot exists before marking the instance
    available. Addresses the Spock 5.x race where the worker takes up to
    60 s to create failover slots after a switchover.
  • postgres/create_db.go — Add ReplicationSlotExists query used by
    the helper above.
  • operations/update_nodes.go — Comment out the SwitchoverResource
    addition that forced a second Patroni switchover to restore the original
    primary after a rolling update. This second switchover was the trigger for
    the subscription breakage. The SwitchoverResource type is kept so
    "retain primary" behaviour can be re-enabled later.
  • update_nodes_test.go — Remove SwitchoverResource from all
    expected states to match the new behaviour.

Testing

Verification:

  1. Created a cluster
  2. Created DB
cp1-req create-database < ../demo/Images/create_db_lower_image.json | cp-follow-task
{
  "database": {
    "created_at": "2026-07-08T11:42:11Z",
    "id": "storefront",
    "spec": {
      "database_name": "storefront",
      "database_users": [
        {
          "attributes": [
            "SUPERUSER",
            "LOGIN"
          ],
          "db_owner": true,
          "username": "admin"
        }
      ],
      "nodes": [
        {
          "host_ids": [
            "host-1",
            "host-2"
          ],
          "name": "n1"
        },
        {
          "host_ids": [
            "host-3",
            "host-4"
          ],
          "name": "n2"
        }
      ],
      "postgres_version": "17.9",
      "spock_version": "5"
    },
    "state": "creating",
    "updated_at": "2026-07-08T11:42:11Z"
  },
  "task": {
    "created_at": "2026-07-08T11:42:11Z",
    "database_id": "storefront",
    "entity_id": "storefront",
    "scope": "database",
    "status": "pending",
    "task_id": "019f4189-16d3-714a-b7d7-318de3dc1aed",
    "type": "create"
  }
}
[2026-07-08T11:42:12Z] refreshing current state
[2026-07-08T11:42:12Z] finished refreshing current state (took 189.450625ms)
[2026-07-08T11:42:15Z] creating resource common.patroni_cluster::n1
[2026-07-08T11:42:15Z] finished creating resource common.patroni_cluster::n1 (took 24.458µs)
[2026-07-08T11:42:15Z] creating resource common.patroni_cluster::n2
[2026-07-08T11:42:15Z] finished creating resource common.patroni_cluster::n2 (took 7.625µs)
[2026-07-08T11:42:15Z] creating resource swarm.network::storefront-database
[2026-07-08T11:42:15Z] finished creating resource swarm.network::storefront-database (took 6.642167ms)
[2026-07-08T11:42:15Z] creating resource filesystem.dir::storefront-n2-ant97dj4-instance
[2026-07-08T11:42:15Z] finished creating resource filesystem.dir::storefront-n2-ant97dj4-instance (took 635.792µs)
[2026-07-08T11:42:15Z] creating resource filesystem.dir::storefront-n1-689qacsi-instance
[2026-07-08T11:42:15Z] finished creating resource filesystem.dir::storefront-n1-689qacsi-instance (took 696.583µs)
[2026-07-08T11:42:15Z] creating resource common.patroni_member::storefront-n1-689qacsi
[2026-07-08T11:42:15Z] finished creating resource common.patroni_member::storefront-n1-689qacsi (took 37.125µs)
[2026-07-08T11:42:15Z] creating resource common.patroni_member::storefront-n2-ant97dj4
[2026-07-08T11:42:15Z] finished creating resource common.patroni_member::storefront-n2-ant97dj4 (took 13.292µs)
[2026-07-08T11:42:15Z] creating resource filesystem.dir::storefront-n2-ant97dj4-configs
[2026-07-08T11:42:15Z] finished creating resource filesystem.dir::storefront-n2-ant97dj4-configs (took 749.417µs)
[2026-07-08T11:42:15Z] creating resource filesystem.dir::storefront-n2-ant97dj4-data
[2026-07-08T11:42:15Z] finished creating resource filesystem.dir::storefront-n2-ant97dj4-data (took 488.791µs)
[2026-07-08T11:42:15Z] creating resource filesystem.dir::storefront-n2-ant97dj4-certificates
[2026-07-08T11:42:15Z] finished creating resource filesystem.dir::storefront-n2-ant97dj4-certificates (took 689.583µs)
[2026-07-08T11:42:15Z] creating resource filesystem.dir::storefront-n1-689qacsi-configs
[2026-07-08T11:42:15Z] finished creating resource filesystem.dir::storefront-n1-689qacsi-configs (took 1.097916ms)
[2026-07-08T11:42:15Z] creating resource filesystem.dir::storefront-n1-689qacsi-data
[2026-07-08T11:42:15Z] finished creating resource filesystem.dir::storefront-n1-689qacsi-data (took 669µs)
[2026-07-08T11:42:15Z] creating resource filesystem.dir::storefront-n1-689qacsi-certificates
[2026-07-08T11:42:15Z] finished creating resource filesystem.dir::storefront-n1-689qacsi-certificates (took 402.083µs)
[2026-07-08T11:42:16Z] creating resource common.etcd_creds::storefront-n2-ant97dj4
[2026-07-08T11:42:16Z] creating resource common.postgres_certs::storefront-n2-ant97dj4
[2026-07-08T11:42:16Z] finished creating resource common.postgres_certs::storefront-n2-ant97dj4 (took 10.922959ms)
[2026-07-08T11:42:16Z] creating resource common.etcd_creds::storefront-n1-689qacsi
[2026-07-08T11:42:16Z] finished creating resource common.etcd_creds::storefront-n2-ant97dj4 (took 80.764875ms)
[2026-07-08T11:42:16Z] finished creating resource common.etcd_creds::storefront-n1-689qacsi (took 64.586625ms)
[2026-07-08T11:42:16Z] creating resource common.postgres_certs::storefront-n1-689qacsi
[2026-07-08T11:42:16Z] finished creating resource common.postgres_certs::storefront-n1-689qacsi (took 6.720583ms)
[2026-07-08T11:42:16Z] creating resource swarm.patroni_config::storefront-n2-ant97dj4
[2026-07-08T11:42:16Z] finished creating resource swarm.patroni_config::storefront-n2-ant97dj4 (took 3.551083ms)
[2026-07-08T11:42:16Z] creating resource swarm.patroni_config::storefront-n1-689qacsi
[2026-07-08T11:42:16Z] finished creating resource swarm.patroni_config::storefront-n1-689qacsi (took 2.931875ms)
[2026-07-08T11:42:17Z] creating resource swarm.postgres_service_spec::storefront-n2-ant97dj4
[2026-07-08T11:42:17Z] finished creating resource swarm.postgres_service_spec::storefront-n2-ant97dj4 (took 170.459µs)
[2026-07-08T11:42:17Z] creating resource swarm.postgres_service_spec::storefront-n1-689qacsi
[2026-07-08T11:42:17Z] finished creating resource swarm.postgres_service_spec::storefront-n1-689qacsi (took 172µs)
[2026-07-08T11:42:17Z] creating resource swarm.check_will_restart::storefront-n1-689qacsi
[2026-07-08T11:42:17Z] finished creating resource swarm.check_will_restart::storefront-n1-689qacsi (took 3.801125ms)
[2026-07-08T11:42:17Z] creating resource swarm.check_will_restart::storefront-n2-ant97dj4
[2026-07-08T11:42:17Z] finished creating resource swarm.check_will_restart::storefront-n2-ant97dj4 (took 1.643333ms)
[2026-07-08T11:42:17Z] creating resource swarm.switchover::storefront-n2-ant97dj4
[2026-07-08T11:42:17Z] finished creating resource swarm.switchover::storefront-n2-ant97dj4 (took 16.458µs)
[2026-07-08T11:42:17Z] creating resource swarm.switchover::storefront-n1-689qacsi
[2026-07-08T11:42:17Z] finished creating resource swarm.switchover::storefront-n1-689qacsi (took 16.292µs)
[2026-07-08T11:42:18Z] creating resource swarm.postgres_service::storefront-n2-ant97dj4
[2026-07-08T11:42:18Z] creating resource swarm.postgres_service::storefront-n1-689qacsi
[2026-07-08T11:42:33Z] finished creating resource swarm.postgres_service::storefront-n2-ant97dj4 (took 15.014274591s)
[2026-07-08T11:42:33Z] finished creating resource swarm.postgres_service::storefront-n1-689qacsi (took 15.010869007s)
[2026-07-08T11:42:33Z] creating resource database.instance::storefront-n2-ant97dj4
[2026-07-08T11:42:33Z] creating resource database.instance::storefront-n1-689qacsi
[2026-07-08T11:42:43Z] finished creating resource database.instance::storefront-n2-ant97dj4 (took 10.050499463s)
[2026-07-08T11:42:43Z] finished creating resource database.instance::storefront-n1-689qacsi (took 10.037279463s)
[2026-07-08T11:42:43Z] creating resource filesystem.dir::storefront-n1-9ptayhma-instance
[2026-07-08T11:42:43Z] finished creating resource filesystem.dir::storefront-n1-9ptayhma-instance (took 826µs)
[2026-07-08T11:42:43Z] creating resource filesystem.dir::storefront-n2-2kqnsx2a-instance
[2026-07-08T11:42:43Z] finished creating resource filesystem.dir::storefront-n2-2kqnsx2a-instance (took 664.792µs)
[2026-07-08T11:42:44Z] creating resource common.patroni_member::storefront-n2-2kqnsx2a
[2026-07-08T11:42:44Z] finished creating resource common.patroni_member::storefront-n2-2kqnsx2a (took 10.792µs)
[2026-07-08T11:42:44Z] creating resource common.patroni_member::storefront-n1-9ptayhma
[2026-07-08T11:42:44Z] finished creating resource common.patroni_member::storefront-n1-9ptayhma (took 9.542µs)
[2026-07-08T11:42:44Z] creating resource filesystem.dir::storefront-n1-9ptayhma-configs
[2026-07-08T11:42:44Z] finished creating resource filesystem.dir::storefront-n1-9ptayhma-configs (took 464.833µs)
[2026-07-08T11:42:44Z] creating resource filesystem.dir::storefront-n1-9ptayhma-certificates
[2026-07-08T11:42:44Z] finished creating resource filesystem.dir::storefront-n1-9ptayhma-certificates (took 452.167µs)
[2026-07-08T11:42:44Z] creating resource filesystem.dir::storefront-n1-9ptayhma-data
[2026-07-08T11:42:44Z] finished creating resource filesystem.dir::storefront-n1-9ptayhma-data (took 412.875µs)
[2026-07-08T11:42:44Z] creating resource filesystem.dir::storefront-n2-2kqnsx2a-data
[2026-07-08T11:42:44Z] finished creating resource filesystem.dir::storefront-n2-2kqnsx2a-data (took 452.083µs)
[2026-07-08T11:42:44Z] creating resource filesystem.dir::storefront-n2-2kqnsx2a-configs
[2026-07-08T11:42:44Z] finished creating resource filesystem.dir::storefront-n2-2kqnsx2a-configs (took 364.833µs)
[2026-07-08T11:42:44Z] creating resource filesystem.dir::storefront-n2-2kqnsx2a-certificates
[2026-07-08T11:42:44Z] finished creating resource filesystem.dir::storefront-n2-2kqnsx2a-certificates (took 651µs)
[2026-07-08T11:42:44Z] creating resource common.etcd_creds::storefront-n1-9ptayhma
[2026-07-08T11:42:44Z] creating resource common.postgres_certs::storefront-n1-9ptayhma
[2026-07-08T11:42:44Z] finished creating resource common.postgres_certs::storefront-n1-9ptayhma (took 9.192333ms)
[2026-07-08T11:42:44Z] creating resource common.postgres_certs::storefront-n2-2kqnsx2a
[2026-07-08T11:42:44Z] creating resource common.etcd_creds::storefront-n2-2kqnsx2a
[2026-07-08T11:42:44Z] finished creating resource common.postgres_certs::storefront-n2-2kqnsx2a (took 13.296375ms)
[2026-07-08T11:42:44Z] finished creating resource common.etcd_creds::storefront-n1-9ptayhma (took 76.868125ms)
[2026-07-08T11:42:44Z] finished creating resource common.etcd_creds::storefront-n2-2kqnsx2a (took 65.79975ms)
[2026-07-08T11:42:45Z] creating resource swarm.patroni_config::storefront-n1-9ptayhma
[2026-07-08T11:42:45Z] finished creating resource swarm.patroni_config::storefront-n1-9ptayhma (took 2.532167ms)
[2026-07-08T11:42:45Z] creating resource swarm.patroni_config::storefront-n2-2kqnsx2a
[2026-07-08T11:42:45Z] finished creating resource swarm.patroni_config::storefront-n2-2kqnsx2a (took 3.031666ms)
[2026-07-08T11:42:45Z] creating resource swarm.postgres_service_spec::storefront-n1-9ptayhma
[2026-07-08T11:42:45Z] finished creating resource swarm.postgres_service_spec::storefront-n1-9ptayhma (took 127.334µs)
[2026-07-08T11:42:45Z] creating resource swarm.postgres_service_spec::storefront-n2-2kqnsx2a
[2026-07-08T11:42:45Z] finished creating resource swarm.postgres_service_spec::storefront-n2-2kqnsx2a (took 129.375µs)
[2026-07-08T11:42:45Z] creating resource swarm.check_will_restart::storefront-n1-9ptayhma
[2026-07-08T11:42:45Z] finished creating resource swarm.check_will_restart::storefront-n1-9ptayhma (took 1.688708ms)
[2026-07-08T11:42:45Z] creating resource swarm.check_will_restart::storefront-n2-2kqnsx2a
[2026-07-08T11:42:45Z] finished creating resource swarm.check_will_restart::storefront-n2-2kqnsx2a (took 1.408667ms)
[2026-07-08T11:42:46Z] creating resource swarm.switchover::storefront-n1-9ptayhma
[2026-07-08T11:42:46Z] finished creating resource swarm.switchover::storefront-n1-9ptayhma (took 13.75µs)
[2026-07-08T11:42:46Z] creating resource swarm.switchover::storefront-n2-2kqnsx2a
[2026-07-08T11:42:46Z] finished creating resource swarm.switchover::storefront-n2-2kqnsx2a (took 14µs)
[2026-07-08T11:42:46Z] creating resource swarm.postgres_service::storefront-n1-9ptayhma
[2026-07-08T11:42:46Z] creating resource swarm.postgres_service::storefront-n2-2kqnsx2a
[2026-07-08T11:42:56Z] finished creating resource swarm.postgres_service::storefront-n1-9ptayhma (took 10.012425213s)
[2026-07-08T11:42:56Z] finished creating resource swarm.postgres_service::storefront-n2-2kqnsx2a (took 10.010445172s)
[2026-07-08T11:42:56Z] creating resource database.instance::storefront-n1-9ptayhma
[2026-07-08T11:42:56Z] creating resource database.instance::storefront-n2-2kqnsx2a
[2026-07-08T11:43:06Z] finished creating resource database.instance::storefront-n1-9ptayhma (took 10.02312963s)
[2026-07-08T11:43:06Z] finished creating resource database.instance::storefront-n2-2kqnsx2a (took 10.019455046s)
[2026-07-08T11:43:07Z] creating resource database.node::n2
[2026-07-08T11:43:07Z] finished creating resource database.node::n2 (took 106.084µs)
[2026-07-08T11:43:07Z] creating resource database.node::n1
[2026-07-08T11:43:07Z] finished creating resource database.node::n1 (took 74.459µs)
[2026-07-08T11:43:07Z] creating resource monitor.instance::storefront-n2-ant97dj4
[2026-07-08T11:43:07Z] finished creating resource monitor.instance::storefront-n2-ant97dj4 (took 12.093583ms)
[2026-07-08T11:43:07Z] creating resource monitor.instance::storefront-n1-9ptayhma
[2026-07-08T11:43:07Z] finished creating resource monitor.instance::storefront-n1-9ptayhma (took 11.915667ms)
[2026-07-08T11:43:07Z] creating resource monitor.instance::storefront-n1-689qacsi
[2026-07-08T11:43:07Z] creating resource monitor.instance::storefront-n2-2kqnsx2a
[2026-07-08T11:43:07Z] finished creating resource monitor.instance::storefront-n1-689qacsi (took 19.870291ms)
[2026-07-08T11:43:07Z] finished creating resource monitor.instance::storefront-n2-2kqnsx2a (took 13.436292ms)
[2026-07-08T11:43:07Z] creating resource database.postgres_database::n2:storefront
[2026-07-08T11:43:07Z] creating resource database.postgres_database::n1:storefront
[2026-07-08T11:43:08Z] finished creating resource database.postgres_database::n2:storefront (took 397.428417ms)
[2026-07-08T11:43:08Z] finished creating resource database.postgres_database::n1:storefront (took 408.602209ms)
[2026-07-08T11:43:08Z] creating resource database.replication_slot::n2:n1:storefront
[2026-07-08T11:43:08Z] finished creating resource database.replication_slot::n2:n1:storefront (took 26.708µs)
[2026-07-08T11:43:08Z] creating resource database.replication_slot::n1:n2:storefront
[2026-07-08T11:43:08Z] finished creating resource database.replication_slot::n1:n2:storefront (took 10.375µs)
[2026-07-08T11:43:08Z] creating resource database.subscription::n1:n2:storefront
[2026-07-08T11:43:08Z] finished creating resource database.subscription::n1:n2:storefront (took 30.142958ms)
[2026-07-08T11:43:08Z] creating resource database.subscription::n2:n1:storefront
[2026-07-08T11:43:08Z] finished creating resource database.subscription::n2:n1:storefront (took 19.272708ms)

database entity storefront task 019f4189-16d3-714a-b7d7-318de3dc1aed completed

  1. Insert data and verify
  2. Update database
➜  control-plane git:(PLAT-665-rolling-database-update-fails-on-multi-replica-nodes) cp1-req update-database storefront  < ../demo/Images/create_db_for_upgrade.json | cp-follow-task 
{
  "database": {
    "created_at": "2026-07-08T11:42:11Z",
    "id": "storefront",
    "instances": [
      {
        "created_at": "2026-07-08T11:42:15Z",
        "host_id": "host-1",
        "id": "storefront-n1-689qacsi",
        "node_name": "n1",
        "postgres": {
          "patroni_state": "running",
          "role": "primary",
          "version": "17.9"
        },
        "spock": {
          "read_only": "off",
          "subscriptions": [
            {
              "name": "sub_n2_n1",
              "provider_node": "n2",
              "status": "replicating"
            }
          ],
          "version": "5.0.6"
        },
        "state": "available",
        "status_updated_at": "2026-07-08T12:23:13Z",
        "updated_at": "2026-07-08T12:17:58Z"
      },
      {
        "created_at": "2026-07-08T11:42:43Z",
        "host_id": "host-2",
        "id": "storefront-n1-9ptayhma",
        "node_name": "n1",
        "postgres": {
          "patroni_state": "running",
          "role": "replica",
          "version": "17.9"
        },
        "state": "available",
        "status_updated_at": "2026-07-08T12:23:12Z",
        "updated_at": "2026-07-08T12:17:58Z"
      },
      {
        "created_at": "2026-07-08T11:42:43Z",
        "host_id": "host-4",
        "id": "storefront-n2-2kqnsx2a",
        "node_name": "n2",
        "postgres": {
          "patroni_state": "running",
          "role": "replica",
          "version": "17.9"
        },
        "state": "available",
        "status_updated_at": "2026-07-08T12:23:13Z",
        "updated_at": "2026-07-08T12:17:58Z"
      },
      {
        "created_at": "2026-07-08T11:42:15Z",
        "host_id": "host-3",
        "id": "storefront-n2-ant97dj4",
        "node_name": "n2",
        "postgres": {
          "patroni_state": "running",
          "role": "primary",
          "version": "17.9"
        },
        "spock": {
          "read_only": "off",
          "subscriptions": [
            {
              "name": "sub_n1_n2",
              "provider_node": "n1",
              "status": "replicating"
            }
          ],
          "version": "5.0.6"
        },
        "state": "available",
        "status_updated_at": "2026-07-08T12:23:12Z",
        "updated_at": "2026-07-08T12:17:57Z"
      }
    ],
    "spec": {
      "database_name": "storefront",
      "database_users": [
        {
          "attributes": [
            "SUPERUSER",
            "LOGIN"
          ],
          "db_owner": true,
          "username": "admin"
        }
      ],
      "nodes": [
        {
          "host_ids": [
            "host-1",
            "host-2"
          ],
          "name": "n1"
        },
        {
          "host_ids": [
            "host-3",
            "host-4"
          ],
          "name": "n2"
        }
      ],
      "postgres_version": "17.10",
      "spock_version": "5"
    },
    "state": "modifying",
    "updated_at": "2026-07-08T12:23:13Z"
  },
  "task": {
    "created_at": "2026-07-08T12:23:13Z",
    "database_id": "storefront",
    "entity_id": "storefront",
    "scope": "database",
    "status": "pending",
    "task_id": "019f41ae-a8ba-713f-b799-fbb735219d69",
    "type": "update"
  }
}
[2026-07-08T12:23:15Z] refreshing current state
[2026-07-08T12:23:24Z] finished refreshing current state (took 9.593077462s)
[2026-07-08T12:23:26Z] updating resource swarm.postgres_service_spec::storefront-n1-9ptayhma
[2026-07-08T12:23:26Z] finished updating resource swarm.postgres_service_spec::storefront-n1-9ptayhma (took 123.875µs)
[2026-07-08T12:23:27Z] updating resource swarm.check_will_restart::storefront-n1-9ptayhma
[2026-07-08T12:23:27Z] finished updating resource swarm.check_will_restart::storefront-n1-9ptayhma (took 5.80075ms)
[2026-07-08T12:23:27Z] updating resource swarm.switchover::storefront-n1-9ptayhma
[2026-07-08T12:23:27Z] finished updating resource swarm.switchover::storefront-n1-9ptayhma (took 1.362458ms)
[2026-07-08T12:23:27Z] updating resource swarm.postgres_service::storefront-n1-9ptayhma
[2026-07-08T12:23:52Z] finished updating resource swarm.postgres_service::storefront-n1-9ptayhma (took 25.019166929s)
[2026-07-08T12:23:52Z] updating resource database.instance::storefront-n1-9ptayhma
[2026-07-08T12:24:02Z] finished updating resource database.instance::storefront-n1-9ptayhma (took 10.028921255s)
[2026-07-08T12:24:02Z] updating resource database.node::n1
[2026-07-08T12:24:02Z] finished updating resource database.node::n1 (took 81.875µs)
[2026-07-08T12:24:02Z] updating resource monitor.instance::storefront-n1-9ptayhma
[2026-07-08T12:24:02Z] finished updating resource monitor.instance::storefront-n1-9ptayhma (took 13.568416ms)
[2026-07-08T12:24:03Z] updating resource database.postgres_database::n1:storefront
[2026-07-08T12:24:03Z] finished updating resource database.postgres_database::n1:storefront (took 19.99825ms)
[2026-07-08T12:24:03Z] updating resource database.replication_slot::n1:n2:storefront
[2026-07-08T12:24:03Z] finished updating resource database.replication_slot::n1:n2:storefront (took 16.042µs)
[2026-07-08T12:24:03Z] updating resource database.subscription::n1:n2:storefront
[2026-07-08T12:24:03Z] finished updating resource database.subscription::n1:n2:storefront (took 6.880584ms)
[2026-07-08T12:24:04Z] updating resource database.subscription::n2:n1:storefront
[2026-07-08T12:24:04Z] finished updating resource database.subscription::n2:n1:storefront (took 10.561083ms)
[2026-07-08T12:24:04Z] updating resource swarm.postgres_service_spec::storefront-n1-689qacsi
[2026-07-08T12:24:04Z] finished updating resource swarm.postgres_service_spec::storefront-n1-689qacsi (took 123.084µs)
[2026-07-08T12:24:04Z] updating resource swarm.check_will_restart::storefront-n1-689qacsi
[2026-07-08T12:24:04Z] finished updating resource swarm.check_will_restart::storefront-n1-689qacsi (took 4.192042ms)
[2026-07-08T12:24:05Z] updating resource swarm.switchover::storefront-n1-689qacsi
[2026-07-08T12:24:17Z] finished updating resource swarm.switchover::storefront-n1-689qacsi (took 12.067031089s)
[2026-07-08T12:24:17Z] updating resource swarm.postgres_service::storefront-n1-689qacsi
[2026-07-08T12:24:42Z] finished updating resource swarm.postgres_service::storefront-n1-689qacsi (took 25.012601427s)
[2026-07-08T12:24:42Z] updating resource database.instance::storefront-n1-689qacsi
[2026-07-08T12:24:52Z] finished updating resource database.instance::storefront-n1-689qacsi (took 10.028515255s)
[2026-07-08T12:24:53Z] updating resource database.node::n1
[2026-07-08T12:24:53Z] finished updating resource database.node::n1 (took 84µs)
[2026-07-08T12:24:53Z] updating resource monitor.instance::storefront-n1-689qacsi
[2026-07-08T12:24:53Z] finished updating resource monitor.instance::storefront-n1-689qacsi (took 13.058833ms)
[2026-07-08T12:24:53Z] updating resource database.postgres_database::n1:storefront
[2026-07-08T12:24:53Z] finished updating resource database.postgres_database::n1:storefront (took 33.26725ms)
[2026-07-08T12:24:53Z] updating resource database.replication_slot::n1:n2:storefront
[2026-07-08T12:24:53Z] finished updating resource database.replication_slot::n1:n2:storefront (took 30.167µs)
[2026-07-08T12:24:53Z] updating resource database.subscription::n1:n2:storefront
[2026-07-08T12:24:53Z] finished updating resource database.subscription::n1:n2:storefront (took 11.128916ms)
[2026-07-08T12:24:53Z] updating resource database.subscription::n2:n1:storefront
[2026-07-08T12:24:53Z] finished updating resource database.subscription::n2:n1:storefront (took 6.446542ms)
[2026-07-08T12:24:54Z] updating resource swarm.postgres_service_spec::storefront-n2-2kqnsx2a
[2026-07-08T12:24:54Z] finished updating resource swarm.postgres_service_spec::storefront-n2-2kqnsx2a (took 135.166µs)
[2026-07-08T12:24:54Z] updating resource swarm.check_will_restart::storefront-n2-2kqnsx2a
[2026-07-08T12:24:54Z] finished updating resource swarm.check_will_restart::storefront-n2-2kqnsx2a (took 4.440834ms)
[2026-07-08T12:24:55Z] updating resource swarm.switchover::storefront-n2-2kqnsx2a
[2026-07-08T12:24:55Z] finished updating resource swarm.switchover::storefront-n2-2kqnsx2a (took 1.111ms)
[2026-07-08T12:24:55Z] updating resource swarm.postgres_service::storefront-n2-2kqnsx2a
[2026-07-08T12:25:20Z] finished updating resource swarm.postgres_service::storefront-n2-2kqnsx2a (took 25.015156845s)
[2026-07-08T12:25:20Z] updating resource database.instance::storefront-n2-2kqnsx2a
[2026-07-08T12:25:30Z] finished updating resource database.instance::storefront-n2-2kqnsx2a (took 10.029629754s)
[2026-07-08T12:25:30Z] updating resource database.node::n2
[2026-07-08T12:25:30Z] finished updating resource database.node::n2 (took 78.792µs)
[2026-07-08T12:25:31Z] updating resource monitor.instance::storefront-n2-2kqnsx2a
[2026-07-08T12:25:31Z] finished updating resource monitor.instance::storefront-n2-2kqnsx2a (took 15.29325ms)
[2026-07-08T12:25:31Z] updating resource database.postgres_database::n2:storefront
[2026-07-08T12:25:31Z] finished updating resource database.postgres_database::n2:storefront (took 24.493583ms)
[2026-07-08T12:25:31Z] updating resource database.replication_slot::n2:n1:storefront
[2026-07-08T12:25:31Z] finished updating resource database.replication_slot::n2:n1:storefront (took 12.75µs)
[2026-07-08T12:25:31Z] updating resource database.subscription::n1:n2:storefront
[2026-07-08T12:25:31Z] finished updating resource database.subscription::n1:n2:storefront (took 10.84625ms)
[2026-07-08T12:25:31Z] updating resource database.subscription::n2:n1:storefront
[2026-07-08T12:25:31Z] finished updating resource database.subscription::n2:n1:storefront (took 5.976583ms)
[2026-07-08T12:25:32Z] updating resource swarm.postgres_service_spec::storefront-n2-ant97dj4
[2026-07-08T12:25:32Z] finished updating resource swarm.postgres_service_spec::storefront-n2-ant97dj4 (took 138.25µs)
[2026-07-08T12:25:32Z] updating resource swarm.check_will_restart::storefront-n2-ant97dj4
[2026-07-08T12:25:32Z] finished updating resource swarm.check_will_restart::storefront-n2-ant97dj4 (took 4.294417ms)
[2026-07-08T12:25:32Z] updating resource swarm.switchover::storefront-n2-ant97dj4
[2026-07-08T12:25:44Z] finished updating resource swarm.switchover::storefront-n2-ant97dj4 (took 12.060070589s)
[2026-07-08T12:25:44Z] updating resource swarm.postgres_service::storefront-n2-ant97dj4
[2026-07-08T12:26:09Z] finished updating resource swarm.postgres_service::storefront-n2-ant97dj4 (took 25.015293054s)
[2026-07-08T12:26:10Z] updating resource database.instance::storefront-n2-ant97dj4
[2026-07-08T12:26:20Z] finished updating resource database.instance::storefront-n2-ant97dj4 (took 10.030142338s)
[2026-07-08T12:26:20Z] updating resource database.node::n2
[2026-07-08T12:26:20Z] finished updating resource database.node::n2 (took 76.167µs)
[2026-07-08T12:26:20Z] updating resource monitor.instance::storefront-n2-ant97dj4
[2026-07-08T12:26:20Z] finished updating resource monitor.instance::storefront-n2-ant97dj4 (took 12.799042ms)
[2026-07-08T12:26:20Z] updating resource database.postgres_database::n2:storefront
[2026-07-08T12:26:20Z] finished updating resource database.postgres_database::n2:storefront (took 32.182791ms)
[2026-07-08T12:26:21Z] updating resource database.replication_slot::n2:n1:storefront
[2026-07-08T12:26:21Z] finished updating resource database.replication_slot::n2:n1:storefront (took 30.292µs)
[2026-07-08T12:26:21Z] updating resource database.subscription::n2:n1:storefront
[2026-07-08T12:26:21Z] finished updating resource database.subscription::n2:n1:storefront (took 12.909584ms)
[2026-07-08T12:26:21Z] updating resource database.subscription::n1:n2:storefront
[2026-07-08T12:26:21Z] finished updating resource database.subscription::n1:n2:storefront (took 9.79975ms)
[2026-07-08T12:26:21Z] creating resource database.switchover::storefront-n2-ant97dj4
[2026-07-08T12:26:22Z] creating resource database.switchover::storefront-n1-689qacsi
[2026-07-08T12:26:37Z] finished creating resource database.switchover::storefront-n2-ant97dj4 (took 15.064995922s)
[2026-07-08T12:26:40Z] finished creating resource database.switchover::storefront-n1-689qacsi (took 18.067677758s)

database entity storefront task 019f41ae-a8ba-713f-b799-fbb735219d69 completed

  1. Insert some data and verify

Checklist

  • Tests added or updated (unit and/or e2e)

PLAT-665

@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 1 medium

Results:
1 new issue

Category Results
Complexity 1 medium

View in Codacy

🟢 Metrics 14 complexity · 0 duplication

Metric Results
Complexity 14
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@tsivaprasad, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 48 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d90b1223-1934-461b-92ed-ab1071ef77d9

📥 Commits

Reviewing files that changed from the base of the PR and between 1258dfd and d626c7c.

📒 Files selected for processing (8)
  • server/internal/database/instance_resource.go
  • server/internal/database/operations/common.go
  • server/internal/database/operations/end.go
  • server/internal/database/operations/golden_test/TestRestoreDatabase/single_node_restore_in_two-node_db_with_replica.json
  • server/internal/database/operations/golden_test/TestUpdateDatabase/adding_a_replica.json
  • server/internal/database/operations/golden_test/TestUpdateDatabase/adding_a_replica_with_primary_update.json
  • server/internal/database/operations/golden_test/TestUpdateDatabase/adding_multiple_replicas_concurrent.json
  • server/internal/database/operations/golden_test/TestUpdateDatabase/adding_multiple_replicas_rolling.json
📝 Walkthrough

Walkthrough

This PR adds replication-slot readiness checks before marking replica instances available, removes switchover-retention logic from node update and end-state paths, updates related tests and fixtures, and expands e2e coverage for rolling Postgres upgrades on a replicated two-node topology.

Changes

Replication-aware rolling updates

Layer / File(s) Summary
Replication slot readiness
server/internal/postgres/create_db.go, server/internal/database/instance_resource.go
Adds ReplicationSlotExists and waits for provider/subscriber replication slots before marking non-primary instances available.
Remove switchover retention
server/internal/database/operations/update_nodes.go, server/internal/database/operations/end.go
Removes the active switchover creation path and replaces it with commented TODO blocks in node update and end-state logic.
Update expectations and fixtures
server/internal/database/operations/update_nodes_test.go, server/internal/database/operations/golden_test/...
Adjusts unit-test and golden-fixture expectations to remove switchover resources and reflect monitor-instance creation instead.
Rolling upgrade replication tests
e2e/minor_version_upgrade_test.go
Adds a replication-preserving rolling upgrade test and extends the minor-version upgrade test to cover a second node and post-update primary checks.

Poem

A rabbit peeks at replication light,
and hops through upgrades, left and right. 🐇
Slots appear, then primaries bloom,
no switchover thump, just sync in tune.
Two nodes hum after the rolling spree—
carrot-bright stability! 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main fix: preventing rolling-update subscription breakage.
Description check ✅ Passed The description includes the required Summary, Changes, Testing, and Checklist sections with substantive detail.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch PLAT-665-rolling-database-update-fails-on-multi-replica-nodes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
server/internal/database/operations/update_nodes.go (1)

45-61: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Commented-out block will not compile if re-enabled as-is.

The disabled block still references database.SwitchoverResource, patroni.InstanceRolePrimary, and the now-removed primaryHostID. Since the database/patroni imports and primaryHostID were dropped in this change, anyone re-enabling this per the TODO must also re-add those imports and re-introduce primaryHostID tracking during instance iteration. Consider adding a short note in the TODO to that effect (or tracking the removed logic via git history / a feature flag) so the re-enable path isn't a silent compile break.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/internal/database/operations/update_nodes.go` around lines 45 - 61,
The commented-out switchover block in updateNodes will fail to compile if
re-enabled because it still depends on database.SwitchoverResource,
patroni.InstanceRolePrimary, and primaryHostID that were removed from this
change. Update the TODO near updateNodes so it explicitly notes that re-enabling
this logic also requires restoring the database and patroni imports plus
reintroducing primaryHostID tracking in the instance loop, or otherwise keep the
removed logic behind a guarded feature path instead of a bare comment.
server/internal/database/instance_resource.go (1)

255-301: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Use select instead of time.Sleep for cancellable polling.

time.Sleep(pollInterval) blocks for 2 seconds without responding to context cancellation. If the context is canceled mid-sleep, the function waits the full interval before checking ctx.Err() at the top of the next iteration. Replace with a select on ctx.Done() and time.After for prompt cancellation responsiveness.

♻️ Proposed refactor
 		if exists {
 			break
 		}
-		time.Sleep(pollInterval)
+		select {
+		case <-ctx.Done():
+			return ctx.Err()
+		case <-time.After(pollInterval):
+		}
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/internal/database/instance_resource.go` around lines 255 - 301,
`waitForReplicationSlots` uses `time.Sleep(pollInterval)` inside its polling
loop, which delays cancellation handling; replace that sleep with a `select` on
`ctx.Done()` and `time.After(pollInterval)` so the loop exits promptly when the
context is canceled. Keep the existing replication-slot check flow in
`waitForReplicationSlots` and preserve the `ctx.Err()`/error returns, but make
the wait cancellable between `postgres.ReplicationSlotExists(...).Scalar(ctx,
conn)` retries.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@server/internal/database/instance_resource.go`:
- Around line 255-301: `waitForReplicationSlots` uses `time.Sleep(pollInterval)`
inside its polling loop, which delays cancellation handling; replace that sleep
with a `select` on `ctx.Done()` and `time.After(pollInterval)` so the loop exits
promptly when the context is canceled. Keep the existing replication-slot check
flow in `waitForReplicationSlots` and preserve the `ctx.Err()`/error returns,
but make the wait cancellable between
`postgres.ReplicationSlotExists(...).Scalar(ctx, conn)` retries.

In `@server/internal/database/operations/update_nodes.go`:
- Around line 45-61: The commented-out switchover block in updateNodes will fail
to compile if re-enabled because it still depends on
database.SwitchoverResource, patroni.InstanceRolePrimary, and primaryHostID that
were removed from this change. Update the TODO near updateNodes so it explicitly
notes that re-enabling this logic also requires restoring the database and
patroni imports plus reintroducing primaryHostID tracking in the instance loop,
or otherwise keep the removed logic behind a guarded feature path instead of a
bare comment.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 25080471-7475-4bb1-aeed-96fe8761c3cd

📥 Commits

Reviewing files that changed from the base of the PR and between e23b733 and e758637.

📒 Files selected for processing (5)
  • e2e/minor_version_upgrade_test.go
  • server/internal/database/instance_resource.go
  • server/internal/database/operations/update_nodes.go
  • server/internal/database/operations/update_nodes_test.go
  • server/internal/postgres/create_db.go
💤 Files with no reviewable changes (1)
  • server/internal/database/operations/update_nodes_test.go

@tsivaprasad tsivaprasad force-pushed the PLAT-665-rolling-database-update-fails-on-multi-replica-nodes branch from 9eb283b to d626c7c Compare July 9, 2026 05:51
@tsivaprasad tsivaprasad requested a review from jason-lynch July 9, 2026 14:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant