66 "fmt"
77 "reflect"
88 "regexp"
9+ "strconv"
910 "strings"
1011 "time"
1112
@@ -20,6 +21,14 @@ import (
2021 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2122)
2223
24+ var requireMasterRestartWhenDecreased = []string {
25+ "max_connections" ,
26+ "max_prepared_transactions" ,
27+ "max_locks_per_transaction" ,
28+ "max_worker_processes" ,
29+ "max_wal_senders" ,
30+ }
31+
2332// Sync syncs the cluster, making sure the actual Kubernetes objects correspond to what is defined in the manifest.
2433// Unlike the update, sync does not error out if some objects do not exist and takes care of creating them.
2534func (c * Cluster ) Sync (newSpec * acidv1.Postgresql ) error {
@@ -264,11 +273,9 @@ func (c *Cluster) syncPodDisruptionBudget(isUpdate bool) error {
264273
265274func (c * Cluster ) syncStatefulSet () error {
266275 var (
267- masterPod * v1.Pod
268- postgresConfig map [string ]interface {}
269- instanceRestartRequired bool
276+ restartWait uint32
277+ restartMasterFirst bool
270278 )
271-
272279 podsToRecreate := make ([]v1.Pod , 0 )
273280 switchoverCandidates := make ([]spec.NamespacedName , 0 )
274281
@@ -402,38 +409,41 @@ func (c *Cluster) syncStatefulSet() error {
402409 c .logger .Warningf ("could not get Postgres config from pod %s: %v" , podName , err )
403410 continue
404411 }
412+ restartWait = patroniConfig .LoopWait
405413
406414 // empty config probably means cluster is not fully initialized yet, e.g. restoring from backup
407415 // do not attempt a restart
408416 if ! reflect .DeepEqual (patroniConfig , emptyPatroniConfig ) || len (pgParameters ) > 0 {
409- instanceRestartRequired , err = c .checkAndSetGlobalPostgreSQLConfiguration (& pod , patroniConfig , pgParameters )
417+ restartMasterFirst , err = c .checkAndSetGlobalPostgreSQLConfiguration (& pod , patroniConfig , pgParameters )
410418 if err != nil {
411419 c .logger .Warningf ("could not set PostgreSQL configuration options for pod %s: %v" , podName , err )
412420 continue
413421 }
422+ // it could take up to LoopWait to apply the config
423+ time .Sleep (time .Duration (restartWait )* time .Second + time .Second * 2 )
414424 break
415425 }
416426 }
417427
418- // if the config update requires a restart, call Patroni restart for replicas first, then master
419- if instanceRestartRequired {
420- c .logger .Debug ("restarting Postgres server within pods" )
421- ttl , ok := postgresConfig ["ttl" ].(int32 )
422- if ! ok {
423- ttl = 30
424- }
425- for i , pod := range pods {
426- role := PostgresRole (pod .Labels [c .OpConfig .PodRoleLabel ])
427- if role == Master {
428- masterPod = & pods [i ]
429- continue
430- }
431- c .restartInstance (& pod )
432- time .Sleep (time .Duration (ttl ) * time .Second )
428+ // restart instances if required
429+ remainingPods := make ([]* v1.Pod , 0 )
430+ skipRole := Master
431+ if restartMasterFirst {
432+ skipRole = Replica
433+ }
434+ for i , pod := range pods {
435+ role := PostgresRole (pod .Labels [c .OpConfig .PodRoleLabel ])
436+ if role == skipRole {
437+ remainingPods = append (remainingPods , & pods [i ])
438+ continue
433439 }
440+ c .restartInstance (& pod , restartWait )
441+ }
434442
435- if masterPod != nil {
436- c .restartInstance (masterPod )
443+ // in most cases only the master should be left to restart
444+ if len (remainingPods ) > 0 {
445+ for _ , remainingPod := range remainingPods {
446+ c .restartInstance (remainingPod , restartWait )
437447 }
438448 }
439449
@@ -450,19 +460,27 @@ func (c *Cluster) syncStatefulSet() error {
450460 return nil
451461}
452462
453- func (c * Cluster ) restartInstance (pod * v1.Pod ) {
463+ func (c * Cluster ) restartInstance (pod * v1.Pod , restartWait uint32 ) {
454464 podName := util .NameFromMeta (pod .ObjectMeta )
455465 role := PostgresRole (pod .Labels [c .OpConfig .PodRoleLabel ])
456466
457- c . eventRecorder . Event ( c . GetReference (), v1 . EventTypeNormal , "Update" , fmt . Sprintf ( "restarting Postgres server within %s pod %s" , role , pod . Name ))
458-
459- if err := c . patroni . Restart ( pod ); err != nil {
460- c .logger .Warningf ("could not restart Postgres server within %s pod %s: %v" , role , podName , err )
467+ // if the config update requires a restart, call Patroni restart
468+ memberData , err := c . patroni . GetMemberData ( pod )
469+ if err != nil {
470+ c .logger .Debugf ("could not get member data of %s pod %s - skipping possible restart attempt : %v" , role , podName , err )
461471 return
462472 }
463473
464- c .logger .Debugf ("Postgres server successfuly restarted in %s pod %s" , role , podName )
465- c .eventRecorder .Event (c .GetReference (), v1 .EventTypeNormal , "Update" , fmt .Sprintf ("Postgres server restart done for %s pod %s" , role , pod .Name ))
474+ // do restart only when it is pending
475+ if memberData .PendingRestart {
476+ c .eventRecorder .Event (c .GetReference (), v1 .EventTypeNormal , "Update" , fmt .Sprintf ("restarting Postgres server within %s pod %s" , role , pod .Name ))
477+ if err := c .patroni .Restart (pod ); err != nil {
478+ c .logger .Warningf ("could not restart Postgres server within %s pod %s: %v" , role , podName , err )
479+ return
480+ }
481+ time .Sleep (time .Duration (restartWait ) * time .Second )
482+ c .eventRecorder .Event (c .GetReference (), v1 .EventTypeNormal , "Update" , fmt .Sprintf ("Postgres server restart done for %s pod %s" , role , pod .Name ))
483+ }
466484}
467485
468486// AnnotationsToPropagate get the annotations to update if required
@@ -502,21 +520,10 @@ func (c *Cluster) AnnotationsToPropagate(annotations map[string]string) map[stri
502520func (c * Cluster ) checkAndSetGlobalPostgreSQLConfiguration (pod * v1.Pod , patroniConfig acidv1.Patroni , effectivePgParameters map [string ]string ) (bool , error ) {
503521 configToSet := make (map [string ]interface {})
504522 parametersToSet := make (map [string ]string )
523+ restartMaster := make ([]bool , 0 )
524+ requiresMasterRestart := false
505525
506- // compare parameters under postgresql section with c.Spec.Postgresql.Parameters from manifest
507- desiredPgParameters := c .Spec .Parameters
508- for desiredOption , desiredValue := range desiredPgParameters {
509- effectiveValue := effectivePgParameters [desiredOption ]
510- if isBootstrapOnlyParameter (desiredOption ) && (effectiveValue != desiredValue ) {
511- parametersToSet [desiredOption ] = desiredValue
512- }
513- }
514-
515- if len (parametersToSet ) > 0 {
516- configToSet ["postgresql" ] = map [string ]interface {}{constants .PatroniPGParametersParameterName : parametersToSet }
517- }
518-
519- // compare other options from config with c.Spec.Patroni from manifest
526+ // compare options from config with c.Spec.Patroni from manifest
520527 desiredPatroniConfig := c .Spec .Patroni
521528 if desiredPatroniConfig .LoopWait > 0 && desiredPatroniConfig .LoopWait != patroniConfig .LoopWait {
522529 configToSet ["loop_wait" ] = desiredPatroniConfig .LoopWait
@@ -554,6 +561,35 @@ func (c *Cluster) checkAndSetGlobalPostgreSQLConfiguration(pod *v1.Pod, patroniC
554561 configToSet ["slots" ] = slotsToSet
555562 }
556563
564+ // compare parameters under postgresql section with c.Spec.Postgresql.Parameters from manifest
565+ desiredPgParameters := c .Spec .Parameters
566+ for desiredOption , desiredValue := range desiredPgParameters {
567+ effectiveValue := effectivePgParameters [desiredOption ]
568+ if isBootstrapOnlyParameter (desiredOption ) && (effectiveValue != desiredValue ) {
569+ parametersToSet [desiredOption ] = desiredValue
570+ if util .SliceContains (requireMasterRestartWhenDecreased , desiredOption ) {
571+ effectiveValueNum , errConv := strconv .Atoi (effectiveValue )
572+ desiredValueNum , errConv2 := strconv .Atoi (desiredValue )
573+ if errConv != nil || errConv2 != nil {
574+ continue
575+ }
576+ if effectiveValueNum > desiredValueNum {
577+ restartMaster = append (restartMaster , true )
578+ continue
579+ }
580+ }
581+ restartMaster = append (restartMaster , false )
582+ }
583+ }
584+
585+ if ! util .SliceContains (restartMaster , false ) && len (configToSet ) == 0 {
586+ requiresMasterRestart = true
587+ }
588+
589+ if len (parametersToSet ) > 0 {
590+ configToSet ["postgresql" ] = map [string ]interface {}{constants .PatroniPGParametersParameterName : parametersToSet }
591+ }
592+
557593 if len (configToSet ) == 0 {
558594 return false , nil
559595 }
@@ -569,10 +605,10 @@ func (c *Cluster) checkAndSetGlobalPostgreSQLConfiguration(pod *v1.Pod, patroniC
569605 c .logger .Debugf ("patching Postgres config via Patroni API on pod %s with following options: %s" ,
570606 podName , configToSetJson )
571607 if err = c .patroni .SetConfig (pod , configToSet ); err != nil {
572- return true , fmt .Errorf ("could not patch postgres parameters with a pod %s: %v" , podName , err )
608+ return requiresMasterRestart , fmt .Errorf ("could not patch postgres parameters with a pod %s: %v" , podName , err )
573609 }
574610
575- return true , nil
611+ return requiresMasterRestart , nil
576612}
577613
578614func (c * Cluster ) syncSecrets () error {
0 commit comments