mirror of
https://github.com/zalando/postgres-operator.git
synced 2026-10-06 05:21:53 +02:00
Allow drop slots when it gets deleted from the manifest (#2089)
* Allow drop slots when it gets deleted from the manifest * use leader instead replica to query slots * fix and extend unit tests for config update checks Co-authored-by: Felix Kunde <felix-kunde@gmx.de>
This commit is contained in:
co-authored by
Felix Kunde
parent
819e410959
commit
486d5d66e0
@@ -84,6 +84,7 @@ type Cluster struct {
|
||||
userSyncStrategy spec.UserSyncer
|
||||
deleteOptions metav1.DeleteOptions
|
||||
podEventsQueue *cache.FIFO
|
||||
replicationSlots map[string]interface{}
|
||||
|
||||
teamsAPIClient teams.Interface
|
||||
oauthTokenGetter OAuthTokenGetter
|
||||
@@ -140,6 +141,7 @@ func New(cfg Config, kubeClient k8sutil.KubernetesClient, pgSpec acidv1.Postgres
|
||||
podEventsQueue: podEventsQueue,
|
||||
KubeClient: kubeClient,
|
||||
currentMajorVersion: 0,
|
||||
replicationSlots: make(map[string]interface{}),
|
||||
}
|
||||
cluster.logger = logger.WithField("pkg", "cluster").WithField("cluster-name", cluster.clusterName())
|
||||
cluster.teamsAPIClient = teams.NewTeamsAPI(cfg.OpConfig.TeamsAPIUrl, logger)
|
||||
@@ -374,6 +376,10 @@ func (c *Cluster) Create() error {
|
||||
}
|
||||
}
|
||||
|
||||
for slotName, desiredSlot := range c.Spec.Patroni.Slots {
|
||||
c.replicationSlots[slotName] = desiredSlot
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+13
-2
@@ -579,8 +579,18 @@ func (c *Cluster) checkAndSetGlobalPostgreSQLConfiguration(pod *v1.Pod, effectiv
|
||||
}
|
||||
}
|
||||
|
||||
slotsToSet := make(map[string]interface{})
|
||||
// check if there is any slot deletion
|
||||
for slotName, effectiveSlot := range c.replicationSlots {
|
||||
if desiredSlot, exists := desiredPatroniConfig.Slots[slotName]; exists {
|
||||
if reflect.DeepEqual(effectiveSlot, desiredSlot) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
slotsToSet[slotName] = nil
|
||||
delete(c.replicationSlots, slotName)
|
||||
}
|
||||
// check if specified slots exist in config and if they differ
|
||||
slotsToSet := make(map[string]map[string]string)
|
||||
for slotName, desiredSlot := range desiredPatroniConfig.Slots {
|
||||
if effectiveSlot, exists := effectivePatroniConfig.Slots[slotName]; exists {
|
||||
if reflect.DeepEqual(desiredSlot, effectiveSlot) {
|
||||
@@ -588,6 +598,7 @@ func (c *Cluster) checkAndSetGlobalPostgreSQLConfiguration(pod *v1.Pod, effectiv
|
||||
}
|
||||
}
|
||||
slotsToSet[slotName] = desiredSlot
|
||||
c.replicationSlots[slotName] = desiredSlot
|
||||
}
|
||||
if len(slotsToSet) > 0 {
|
||||
configToSet["slots"] = slotsToSet
|
||||
@@ -614,7 +625,7 @@ func (c *Cluster) checkAndSetGlobalPostgreSQLConfiguration(pod *v1.Pod, effectiv
|
||||
}
|
||||
|
||||
// check if there exist only config updates that require a restart of the primary
|
||||
if !util.SliceContains(restartPrimary, false) && len(configToSet) == 0 {
|
||||
if len(restartPrimary) > 0 && !util.SliceContains(restartPrimary, false) && len(configToSet) == 0 {
|
||||
requiresMasterRestart = true
|
||||
}
|
||||
|
||||
|
||||
+121
-10
@@ -144,6 +144,13 @@ func TestCheckAndSetGlobalPostgreSQLConfiguration(t *testing.T) {
|
||||
client, _ := newFakeK8sSyncClient()
|
||||
clusterName := "acid-test-cluster"
|
||||
namespace := "default"
|
||||
testSlots := map[string]map[string]string{
|
||||
"slot1": {
|
||||
"type": "logical",
|
||||
"plugin": "wal2json",
|
||||
"database": "foo",
|
||||
},
|
||||
}
|
||||
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
@@ -208,11 +215,26 @@ func TestCheckAndSetGlobalPostgreSQLConfiguration(t *testing.T) {
|
||||
|
||||
// simulate existing config that differs from cluster.Spec
|
||||
tests := []struct {
|
||||
subtest string
|
||||
patroni acidv1.Patroni
|
||||
pgParams map[string]string
|
||||
restartPrimary bool
|
||||
subtest string
|
||||
patroni acidv1.Patroni
|
||||
desiredSlots map[string]map[string]string
|
||||
removedSlots map[string]map[string]string
|
||||
pgParams map[string]string
|
||||
shouldBePatched bool
|
||||
restartPrimary bool
|
||||
}{
|
||||
{
|
||||
subtest: "Patroni and Postgresql.Parameters do not differ",
|
||||
patroni: acidv1.Patroni{
|
||||
TTL: 20,
|
||||
},
|
||||
pgParams: map[string]string{
|
||||
"log_min_duration_statement": "200",
|
||||
"max_connections": "50",
|
||||
},
|
||||
shouldBePatched: false,
|
||||
restartPrimary: false,
|
||||
},
|
||||
{
|
||||
subtest: "Patroni and Postgresql.Parameters differ - restart replica first",
|
||||
patroni: acidv1.Patroni{
|
||||
@@ -222,7 +244,8 @@ func TestCheckAndSetGlobalPostgreSQLConfiguration(t *testing.T) {
|
||||
"log_min_duration_statement": "500", // desired 200
|
||||
"max_connections": "100", // desired 50
|
||||
},
|
||||
restartPrimary: false,
|
||||
shouldBePatched: true,
|
||||
restartPrimary: false,
|
||||
},
|
||||
{
|
||||
subtest: "multiple Postgresql.Parameters differ - restart replica first",
|
||||
@@ -231,7 +254,8 @@ func TestCheckAndSetGlobalPostgreSQLConfiguration(t *testing.T) {
|
||||
"log_min_duration_statement": "500", // desired 200
|
||||
"max_connections": "100", // desired 50
|
||||
},
|
||||
restartPrimary: false,
|
||||
shouldBePatched: true,
|
||||
restartPrimary: false,
|
||||
},
|
||||
{
|
||||
subtest: "desired max_connections bigger - restart replica first",
|
||||
@@ -240,7 +264,8 @@ func TestCheckAndSetGlobalPostgreSQLConfiguration(t *testing.T) {
|
||||
"log_min_duration_statement": "200",
|
||||
"max_connections": "30", // desired 50
|
||||
},
|
||||
restartPrimary: false,
|
||||
shouldBePatched: true,
|
||||
restartPrimary: false,
|
||||
},
|
||||
{
|
||||
subtest: "desired max_connections smaller - restart master first",
|
||||
@@ -249,19 +274,105 @@ func TestCheckAndSetGlobalPostgreSQLConfiguration(t *testing.T) {
|
||||
"log_min_duration_statement": "200",
|
||||
"max_connections": "100", // desired 50
|
||||
},
|
||||
restartPrimary: true,
|
||||
shouldBePatched: true,
|
||||
restartPrimary: true,
|
||||
},
|
||||
{
|
||||
subtest: "slot does not exist but is desired",
|
||||
patroni: acidv1.Patroni{
|
||||
TTL: 20,
|
||||
},
|
||||
desiredSlots: testSlots,
|
||||
pgParams: map[string]string{
|
||||
"log_min_duration_statement": "200",
|
||||
"max_connections": "50",
|
||||
},
|
||||
shouldBePatched: true,
|
||||
restartPrimary: false,
|
||||
},
|
||||
{
|
||||
subtest: "slot exist, nothing specified in manifest",
|
||||
patroni: acidv1.Patroni{
|
||||
TTL: 20,
|
||||
Slots: map[string]map[string]string{
|
||||
"slot1": {
|
||||
"type": "logical",
|
||||
"plugin": "pgoutput",
|
||||
"database": "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
pgParams: map[string]string{
|
||||
"log_min_duration_statement": "200",
|
||||
"max_connections": "50",
|
||||
},
|
||||
shouldBePatched: false,
|
||||
restartPrimary: false,
|
||||
},
|
||||
{
|
||||
subtest: "slot is removed from manifest",
|
||||
patroni: acidv1.Patroni{
|
||||
TTL: 20,
|
||||
Slots: map[string]map[string]string{
|
||||
"slot1": {
|
||||
"type": "logical",
|
||||
"plugin": "pgoutput",
|
||||
"database": "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
removedSlots: testSlots,
|
||||
pgParams: map[string]string{
|
||||
"log_min_duration_statement": "200",
|
||||
"max_connections": "50",
|
||||
},
|
||||
shouldBePatched: true,
|
||||
restartPrimary: false,
|
||||
},
|
||||
{
|
||||
subtest: "slot plugin differs",
|
||||
patroni: acidv1.Patroni{
|
||||
TTL: 20,
|
||||
Slots: map[string]map[string]string{
|
||||
"slot1": {
|
||||
"type": "logical",
|
||||
"plugin": "pgoutput",
|
||||
"database": "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
desiredSlots: testSlots,
|
||||
pgParams: map[string]string{
|
||||
"log_min_duration_statement": "200",
|
||||
"max_connections": "50",
|
||||
},
|
||||
shouldBePatched: true,
|
||||
restartPrimary: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
if len(tt.desiredSlots) > 0 {
|
||||
cluster.Spec.Patroni.Slots = tt.desiredSlots
|
||||
}
|
||||
if len(tt.removedSlots) > 0 {
|
||||
for slotName, removedSlot := range tt.removedSlots {
|
||||
cluster.replicationSlots[slotName] = removedSlot
|
||||
}
|
||||
}
|
||||
|
||||
configPatched, requirePrimaryRestart, err := cluster.checkAndSetGlobalPostgreSQLConfiguration(mockPod, tt.patroni, cluster.Spec.Patroni, tt.pgParams, cluster.Spec.Parameters)
|
||||
assert.NoError(t, err)
|
||||
if configPatched != true {
|
||||
if configPatched != tt.shouldBePatched {
|
||||
t.Errorf("%s - %s: expected config update did not happen", testName, tt.subtest)
|
||||
}
|
||||
if requirePrimaryRestart != tt.restartPrimary {
|
||||
t.Errorf("%s - %s: wrong master restart strategy, got restart %v, expected restart %v", testName, tt.subtest, requirePrimaryRestart, tt.restartPrimary)
|
||||
}
|
||||
|
||||
// reset slots for next tests
|
||||
cluster.Spec.Patroni.Slots = nil
|
||||
cluster.replicationSlots = make(map[string]interface{})
|
||||
}
|
||||
|
||||
testsFailsafe := []struct {
|
||||
@@ -342,7 +453,7 @@ func TestCheckAndSetGlobalPostgreSQLConfiguration(t *testing.T) {
|
||||
effectiveVal: util.True(),
|
||||
desiredVal: true,
|
||||
shouldBePatched: false, // should not require patching
|
||||
restartPrimary: true,
|
||||
restartPrimary: false,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user