From efad5367aba63b2b2a2fc396309ec7c0a03617af Mon Sep 17 00:00:00 2001 From: Siva Date: Wed, 12 Aug 2026 12:39:23 +0530 Subject: [PATCH 1/2] fix: set output_plugin_libraries for gated PG minor versions --- changes/unreleased/Added-20260812-123758.yaml | 3 ++ changes/unreleased/Fixed-20260812-123144.yaml | 3 ++ .../common/patroni_config_generator.go | 7 +++- .../orchestrator/swarm/version-manifest.json | 6 +-- server/internal/postgres/gucs.go | 41 ++++++++++++++++++- server/internal/postgres/gucs_test.go | 31 +++++++++++++- 6 files changed, 84 insertions(+), 7 deletions(-) create mode 100644 changes/unreleased/Added-20260812-123758.yaml create mode 100644 changes/unreleased/Fixed-20260812-123144.yaml diff --git a/changes/unreleased/Added-20260812-123758.yaml b/changes/unreleased/Added-20260812-123758.yaml new file mode 100644 index 00000000..f5d93bea --- /dev/null +++ b/changes/unreleased/Added-20260812-123758.yaml @@ -0,0 +1,3 @@ +kind: Added +body: Added support for Spock 5.0.11 on Postgres 16.14, 17.10, and 18.4. +time: 2026-08-12T12:37:58.863497+05:30 diff --git a/changes/unreleased/Fixed-20260812-123144.yaml b/changes/unreleased/Fixed-20260812-123144.yaml new file mode 100644 index 00000000..80e26dc5 --- /dev/null +++ b/changes/unreleased/Fixed-20260812-123144.yaml @@ -0,0 +1,3 @@ +kind: Fixed +body: Enable spock_output for Postgres 16.15+, 17.11+, and 18.5+ to restore replication slot creation. +time: 2026-08-12T12:31:44.954828+05:30 diff --git a/server/internal/orchestrator/common/patroni_config_generator.go b/server/internal/orchestrator/common/patroni_config_generator.go index ec4932c5..7735a3c8 100644 --- a/server/internal/orchestrator/common/patroni_config_generator.go +++ b/server/internal/orchestrator/common/patroni_config_generator.go @@ -12,6 +12,7 @@ import ( "github.com/alessio/shellescape" "github.com/pgEdge/control-plane/server/internal/database" + "github.com/pgEdge/control-plane/server/internal/ds" "github.com/pgEdge/control-plane/server/internal/patroni" "github.com/pgEdge/control-plane/server/internal/postgres" "github.com/pgEdge/control-plane/server/internal/postgres/hba" @@ -59,6 +60,9 @@ type PatroniConfigGenerator struct { PatroniAllowlist []string `json:"patroni_allowlist"` // PatroniPort is the port that Patroni will listen on. PatroniPort int `json:"patroni_port"` + // PgEdgeVersion is the Postgres/Spock version for this instance. This is + // used to gate version-specific default Postgres parameters. + PgEdgeVersion *ds.PgEdgeVersion `json:"pg_edge_version,omitempty"` // PgHbaConf are user-supplied pg_hba.conf entries (one rule per element), // inserted in the user zone after the CP rules and before the catch-all. PgHbaConf []string `json:"pg_hba_conf,omitempty"` @@ -144,6 +148,7 @@ func NewPatroniConfigGenerator(opts PatroniConfigGeneratorOptions) *PatroniConfi NodeSize: opts.Instance.NodeSize, OrchestratorParameters: opts.OrchestratorParameters, PatroniPort: opts.PatroniPort, + PgEdgeVersion: opts.Instance.PgEdgeVersion, PostgresCertsDir: opts.Paths.Instance.PostgresCertificates(), PostgresPort: opts.PostgresPort, RestoreCommand: restoreCommand, @@ -207,7 +212,7 @@ func (p *PatroniConfigGenerator) AuthMethod() hba.AuthMethod { } func (p *PatroniConfigGenerator) parameters() map[string]any { - parameters := postgres.DefaultGUCs() + parameters := postgres.DefaultGUCs(p.PgEdgeVersion) maps.Copy(parameters, postgres.SpockDefaultGUCs()) maps.Copy(parameters, postgres.DefaultTunableGUCs(p.MemoryBytes, p.CPUs, p.ClusterSize)) maps.Copy(parameters, map[string]any{ diff --git a/server/internal/orchestrator/swarm/version-manifest.json b/server/internal/orchestrator/swarm/version-manifest.json index 05f7ef36..90a9465d 100644 --- a/server/internal/orchestrator/swarm/version-manifest.json +++ b/server/internal/orchestrator/swarm/version-manifest.json @@ -29,7 +29,7 @@ { "postgres_version": "16.14", "spock_version": "5", - "image": "pgedge-postgres:16.14-spock5.0.10-standard-1", + "image": "pgedge-postgres:16.14-spock5.0.11-standard-1", "stability": "stable" }, { @@ -59,7 +59,7 @@ { "postgres_version": "17.10", "spock_version": "5", - "image": "pgedge-postgres:17.10-spock5.0.10-standard-1", + "image": "pgedge-postgres:17.10-spock5.0.11-standard-1", "stability": "stable" }, { @@ -89,7 +89,7 @@ { "postgres_version": "18.4", "spock_version": "5", - "image": "pgedge-postgres:18.4-spock5.0.10-standard-1", + "image": "pgedge-postgres:18.4-spock5.0.11-standard-1", "stability": "stable", "default": true } diff --git a/server/internal/postgres/gucs.go b/server/internal/postgres/gucs.go index 6f2e6f1e..5139c377 100644 --- a/server/internal/postgres/gucs.go +++ b/server/internal/postgres/gucs.go @@ -2,10 +2,43 @@ package postgres import ( "math" + + "github.com/pgEdge/control-plane/server/internal/ds" ) -func DefaultGUCs() map[string]any { - return map[string]any{ +// minOutputPluginLibrariesVersions are the earliest Postgres minor version per +// major version that gates output plugins behind the output_plugin_libraries +// allowlist GUC. Any major version newer than the last entry here is assumed +// to require it as well. See SPOC-651 / PLAT-721. +var minOutputPluginLibrariesVersions = map[uint64]*ds.Version{ + 16: ds.MustParseVersion("16.15"), + 17: ds.MustParseVersion("17.11"), + 18: ds.MustParseVersion("18.5"), +} + +// needsOutputPluginLibraries reports whether the given Postgres version +// requires output_plugin_libraries to be set in order to allow spock_output +// to create replication slots. +func needsOutputPluginLibraries(version *ds.PgEdgeVersion) bool { + if version == nil || version.PostgresVersion == nil { + return false + } + pgVersion := version.PostgresVersion.MajorMinorVersion() + major, ok := pgVersion.Major() + if !ok { + return false + } + minVersion, ok := minOutputPluginLibrariesVersions[major] + if !ok { + // Newer majors than we know about are assumed to need it; older + // majors than we know about never had the gate. + return major > 18 + } + return pgVersion.Compare(minVersion) >= 0 +} + +func DefaultGUCs(version *ds.PgEdgeVersion) map[string]any { + gucs := map[string]any{ "archive_command": "/bin/true", "archive_mode": "on", "checkpoint_completion_target": "0.9", @@ -28,6 +61,10 @@ func DefaultGUCs() map[string]any { "wal_log_hints": "on", "wal_sender_timeout": "5s", } + if needsOutputPluginLibraries(version) { + gucs["output_plugin_libraries"] = "pgoutput, test_decoding, spock_output" + } + return gucs } func SpockDefaultGUCs() map[string]any { diff --git a/server/internal/postgres/gucs_test.go b/server/internal/postgres/gucs_test.go index 2a8559fe..97c2b89a 100644 --- a/server/internal/postgres/gucs_test.go +++ b/server/internal/postgres/gucs_test.go @@ -5,11 +5,40 @@ import ( "github.com/stretchr/testify/assert" + "github.com/pgEdge/control-plane/server/internal/ds" "github.com/pgEdge/control-plane/server/internal/postgres" ) func TestDefaultGUCs(t *testing.T) { - assert.Equal(t, "scram-sha-256", postgres.DefaultGUCs()["password_encryption"]) + assert.Equal(t, "scram-sha-256", postgres.DefaultGUCs(nil)["password_encryption"]) +} + +func TestDefaultGUCsOutputPluginLibraries(t *testing.T) { + for _, tc := range []struct { + name string + version *ds.PgEdgeVersion + expectedPresent bool + }{ + {name: "nil version", version: nil, expectedPresent: false}, + {name: "pg16 below gate", version: ds.MustParsePgEdgeVersion("16.14", "4"), expectedPresent: false}, + {name: "pg16 at gate", version: ds.MustParsePgEdgeVersion("16.15", "4"), expectedPresent: true}, + {name: "pg16 above gate", version: ds.MustParsePgEdgeVersion("16.16", "4"), expectedPresent: true}, + {name: "pg17 below gate", version: ds.MustParsePgEdgeVersion("17.10", "4"), expectedPresent: false}, + {name: "pg17 at gate", version: ds.MustParsePgEdgeVersion("17.11", "4"), expectedPresent: true}, + {name: "pg18 below gate", version: ds.MustParsePgEdgeVersion("18.4", "4"), expectedPresent: false}, + {name: "pg18 at gate", version: ds.MustParsePgEdgeVersion("18.5", "4"), expectedPresent: true}, + {name: "future major", version: ds.MustParsePgEdgeVersion("19.0", "4"), expectedPresent: true}, + {name: "older major", version: ds.MustParsePgEdgeVersion("15.10", "4"), expectedPresent: false}, + } { + t.Run(tc.name, func(t *testing.T) { + gucs := postgres.DefaultGUCs(tc.version) + value, ok := gucs["output_plugin_libraries"] + assert.Equal(t, tc.expectedPresent, ok) + if tc.expectedPresent { + assert.Equal(t, "pgoutput, test_decoding, spock_output", value) + } + }) + } } func TestDefaultTunableGUCs(t *testing.T) { From e5e61e8662fbf2ce0b012be8eea54a8548042475 Mon Sep 17 00:00:00 2001 From: Siva Date: Wed, 12 Aug 2026 19:20:43 +0530 Subject: [PATCH 2/2] revert: drop Spock 5.0.11 manifest changes --- changes/unreleased/Added-20260812-123758.yaml | 3 --- server/internal/orchestrator/swarm/version-manifest.json | 6 +++--- 2 files changed, 3 insertions(+), 6 deletions(-) delete mode 100644 changes/unreleased/Added-20260812-123758.yaml diff --git a/changes/unreleased/Added-20260812-123758.yaml b/changes/unreleased/Added-20260812-123758.yaml deleted file mode 100644 index f5d93bea..00000000 --- a/changes/unreleased/Added-20260812-123758.yaml +++ /dev/null @@ -1,3 +0,0 @@ -kind: Added -body: Added support for Spock 5.0.11 on Postgres 16.14, 17.10, and 18.4. -time: 2026-08-12T12:37:58.863497+05:30 diff --git a/server/internal/orchestrator/swarm/version-manifest.json b/server/internal/orchestrator/swarm/version-manifest.json index 90a9465d..05f7ef36 100644 --- a/server/internal/orchestrator/swarm/version-manifest.json +++ b/server/internal/orchestrator/swarm/version-manifest.json @@ -29,7 +29,7 @@ { "postgres_version": "16.14", "spock_version": "5", - "image": "pgedge-postgres:16.14-spock5.0.11-standard-1", + "image": "pgedge-postgres:16.14-spock5.0.10-standard-1", "stability": "stable" }, { @@ -59,7 +59,7 @@ { "postgres_version": "17.10", "spock_version": "5", - "image": "pgedge-postgres:17.10-spock5.0.11-standard-1", + "image": "pgedge-postgres:17.10-spock5.0.10-standard-1", "stability": "stable" }, { @@ -89,7 +89,7 @@ { "postgres_version": "18.4", "spock_version": "5", - "image": "pgedge-postgres:18.4-spock5.0.11-standard-1", + "image": "pgedge-postgres:18.4-spock5.0.10-standard-1", "stability": "stable", "default": true }