Skip to content

fix: prevent subscription breakage on rolling updates#429

Merged
tsivaprasad merged 4 commits into
mainfrom
PLAT-665-rolling-database-update-fails-on-multi-replica-nodes
Jul 15, 2026
Merged

fix: prevent subscription breakage on rolling updates#429
tsivaprasad merged 4 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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 692d1f95-183b-4b0d-98d1-fb1d0d357bfb

📥 Commits

Reviewing files that changed from the base of the PR and between a091cdf and 384bac3.

📒 Files selected for processing (1)
  • server/internal/database/operations/end.go
💤 Files with no reviewable changes (1)
  • server/internal/database/operations/end.go

📝 Walkthrough

Walkthrough

This PR adds replication-slot readiness checks before replica instances become available, removes switchover-retention behavior, updates operation expectations, and expands end-to-end coverage for replicated two-node rolling PostgreSQL upgrades.

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 slots before marking non-primary instances available.
Remove switchover retention
server/internal/database/operations/common.go, server/internal/database/operations/end.go
Removes primary-instance lookup and disables end-state switchover creation.
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.
Rolling upgrade replication tests
e2e/minor_version_upgrade_test.go
Adds bidirectional replication checks across a rolling upgrade and extends minor-version coverage to two nodes with active-primary assertions.

Poem

A rabbit hops through slots so bright,
Two nodes sync from day to night. 🐇
Updates roll, primaries stay,
Rows replicate along the way.
“No race!” I cheer with carrot glee. 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main fix: preventing subscription breakage during rolling updates.
Description check ✅ Passed The PR description matches the template well, with Summary, Changes, Testing, and Checklist sections filled in.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 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
Comment thread server/internal/database/instance_resource.go
Comment thread server/internal/database/instance_resource.go Outdated
Comment thread server/internal/database/operations/update_nodes.go Outdated
@tsivaprasad
tsivaprasad requested a review from mmols July 14, 2026 19:24
@tsivaprasad
tsivaprasad merged commit fd06adb into main Jul 15, 2026
5 checks passed
@tsivaprasad
tsivaprasad deleted the PLAT-665-rolling-database-update-fails-on-multi-replica-nodes branch July 15, 2026 06:41
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.

2 participants