Skip to content

Commit 62ed7e4

Browse files
authored
improve pooler sync (zalando#1593)
* remove role from installLookupFunction and run it on database sync, too * fix condition to decide on syncing pooler * trigger lookup from database sync only if pooler is set * use empty spec everywhere and do not sync if one lookupfunction was passed * do not sync pooler after being disabled
1 parent 7469efa commit 62ed7e4

File tree

6 files changed

+68
-76
lines changed

6 files changed

+68
-76
lines changed

pkg/cluster/cluster.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,23 +1006,23 @@ func (c *Cluster) initSystemUsers() {
10061006
// Connection pooler user is an exception, if requested it's going to be
10071007
// created by operator as a normal pgUser
10081008
if needConnectionPooler(&c.Spec) {
1009-
// initialize empty connection pooler if not done yet
1010-
if c.Spec.ConnectionPooler == nil {
1011-
c.Spec.ConnectionPooler = &acidv1.ConnectionPooler{}
1009+
connectionPoolerSpec := c.Spec.ConnectionPooler
1010+
if connectionPoolerSpec == nil {
1011+
connectionPoolerSpec = &acidv1.ConnectionPooler{}
10121012
}
10131013

10141014
// Using superuser as pooler user is not a good idea. First of all it's
10151015
// not going to be synced correctly with the current implementation,
10161016
// and second it's a bad practice.
10171017
username := c.OpConfig.ConnectionPooler.User
10181018

1019-
isSuperUser := c.Spec.ConnectionPooler.User == c.OpConfig.SuperUsername
1019+
isSuperUser := connectionPoolerSpec.User == c.OpConfig.SuperUsername
10201020
isProtectedUser := c.shouldAvoidProtectedOrSystemRole(
1021-
c.Spec.ConnectionPooler.User, "connection pool role")
1021+
connectionPoolerSpec.User, "connection pool role")
10221022

10231023
if !isSuperUser && !isProtectedUser {
10241024
username = util.Coalesce(
1025-
c.Spec.ConnectionPooler.User,
1025+
connectionPoolerSpec.User,
10261026
c.OpConfig.ConnectionPooler.User)
10271027
}
10281028

pkg/cluster/connection_pooler.go

Lines changed: 48 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package cluster
33
import (
44
"context"
55
"fmt"
6+
"reflect"
67
"strings"
78

89
"github.com/r3labs/diff"
@@ -60,7 +61,7 @@ func needMasterConnectionPooler(spec *acidv1.PostgresSpec) bool {
6061
}
6162

6263
func needMasterConnectionPoolerWorker(spec *acidv1.PostgresSpec) bool {
63-
return (nil != spec.EnableConnectionPooler && *spec.EnableConnectionPooler) ||
64+
return (spec.EnableConnectionPooler != nil && *spec.EnableConnectionPooler) ||
6465
(spec.ConnectionPooler != nil && spec.EnableConnectionPooler == nil)
6566
}
6667

@@ -114,7 +115,7 @@ func (c *Cluster) createConnectionPooler(LookupFunction InstallFunction) (SyncRe
114115
c.setProcessName("creating connection pooler")
115116

116117
//this is essentially sync with nil as oldSpec
117-
if reason, err := c.syncConnectionPooler(nil, &c.Postgresql, LookupFunction); err != nil {
118+
if reason, err := c.syncConnectionPooler(&acidv1.Postgresql{}, &c.Postgresql, LookupFunction); err != nil {
118119
return reason, err
119120
}
120121
return reason, nil
@@ -140,19 +141,23 @@ func (c *Cluster) createConnectionPooler(LookupFunction InstallFunction) (SyncRe
140141
// RESERVE_SIZE is how many additional connections to allow for a pooler.
141142
func (c *Cluster) getConnectionPoolerEnvVars() []v1.EnvVar {
142143
spec := &c.Spec
144+
connectionPoolerSpec := spec.ConnectionPooler
145+
if connectionPoolerSpec == nil {
146+
connectionPoolerSpec = &acidv1.ConnectionPooler{}
147+
}
143148
effectiveMode := util.Coalesce(
144-
spec.ConnectionPooler.Mode,
149+
connectionPoolerSpec.Mode,
145150
c.OpConfig.ConnectionPooler.Mode)
146151

147-
numberOfInstances := spec.ConnectionPooler.NumberOfInstances
152+
numberOfInstances := connectionPoolerSpec.NumberOfInstances
148153
if numberOfInstances == nil {
149154
numberOfInstances = util.CoalesceInt32(
150155
c.OpConfig.ConnectionPooler.NumberOfInstances,
151156
k8sutil.Int32ToPointer(1))
152157
}
153158

154159
effectiveMaxDBConn := util.CoalesceInt32(
155-
spec.ConnectionPooler.MaxDBConnections,
160+
connectionPoolerSpec.MaxDBConnections,
156161
c.OpConfig.ConnectionPooler.MaxDBConnections)
157162

158163
if effectiveMaxDBConn == nil {
@@ -201,17 +206,21 @@ func (c *Cluster) getConnectionPoolerEnvVars() []v1.EnvVar {
201206
func (c *Cluster) generateConnectionPoolerPodTemplate(role PostgresRole) (
202207
*v1.PodTemplateSpec, error) {
203208
spec := &c.Spec
209+
connectionPoolerSpec := spec.ConnectionPooler
210+
if connectionPoolerSpec == nil {
211+
connectionPoolerSpec = &acidv1.ConnectionPooler{}
212+
}
204213
gracePeriod := int64(c.OpConfig.PodTerminateGracePeriod.Seconds())
205214
resources, err := generateResourceRequirements(
206-
spec.ConnectionPooler.Resources,
215+
connectionPoolerSpec.Resources,
207216
makeDefaultConnectionPoolerResources(&c.OpConfig))
208217

209218
effectiveDockerImage := util.Coalesce(
210-
spec.ConnectionPooler.DockerImage,
219+
connectionPoolerSpec.DockerImage,
211220
c.OpConfig.ConnectionPooler.Image)
212221

213222
effectiveSchema := util.Coalesce(
214-
spec.ConnectionPooler.Schema,
223+
connectionPoolerSpec.Schema,
215224
c.OpConfig.ConnectionPooler.Schema)
216225

217226
if err != nil {
@@ -220,7 +229,7 @@ func (c *Cluster) generateConnectionPoolerPodTemplate(role PostgresRole) (
220229

221230
secretSelector := func(key string) *v1.SecretKeySelector {
222231
effectiveUser := util.Coalesce(
223-
spec.ConnectionPooler.User,
232+
connectionPoolerSpec.User,
224233
c.OpConfig.ConnectionPooler.User)
225234

226235
return &v1.SecretKeySelector{
@@ -321,12 +330,13 @@ func (c *Cluster) generateConnectionPoolerDeployment(connectionPooler *Connectio
321330
// default values, initialize it to an empty structure. It could be done
322331
// anywhere, but here is the earliest common entry point between sync and
323332
// create code, so init here.
324-
if spec.ConnectionPooler == nil {
325-
spec.ConnectionPooler = &acidv1.ConnectionPooler{}
333+
connectionPoolerSpec := spec.ConnectionPooler
334+
if connectionPoolerSpec == nil {
335+
connectionPoolerSpec = &acidv1.ConnectionPooler{}
326336
}
327337
podTemplate, err := c.generateConnectionPoolerPodTemplate(connectionPooler.Role)
328338

329-
numberOfInstances := spec.ConnectionPooler.NumberOfInstances
339+
numberOfInstances := connectionPoolerSpec.NumberOfInstances
330340
if numberOfInstances == nil {
331341
numberOfInstances = util.CoalesceInt32(
332342
c.OpConfig.ConnectionPooler.NumberOfInstances,
@@ -371,16 +381,6 @@ func (c *Cluster) generateConnectionPoolerDeployment(connectionPooler *Connectio
371381
func (c *Cluster) generateConnectionPoolerService(connectionPooler *ConnectionPoolerObjects) *v1.Service {
372382

373383
spec := &c.Spec
374-
// there are two ways to enable connection pooler, either to specify a
375-
// connectionPooler section or enableConnectionPooler. In the second case
376-
// spec.connectionPooler will be nil, so to make it easier to calculate
377-
// default values, initialize it to an empty structure. It could be done
378-
// anywhere, but here is the earliest common entry point between sync and
379-
// create code, so init here.
380-
if spec.ConnectionPooler == nil {
381-
spec.ConnectionPooler = &acidv1.ConnectionPooler{}
382-
}
383-
384384
serviceSpec := v1.ServiceSpec{
385385
Ports: []v1.ServicePort{
386386
{
@@ -668,12 +668,14 @@ func makeDefaultConnectionPoolerResources(config *config.Config) acidv1.Resource
668668

669669
func logPoolerEssentials(log *logrus.Entry, oldSpec, newSpec *acidv1.Postgresql) {
670670
var v []string
671-
672671
var input []*bool
672+
673+
newMasterConnectionPoolerEnabled := needMasterConnectionPoolerWorker(&newSpec.Spec)
673674
if oldSpec == nil {
674-
input = []*bool{nil, nil, newSpec.Spec.EnableConnectionPooler, newSpec.Spec.EnableReplicaConnectionPooler}
675+
input = []*bool{nil, nil, &newMasterConnectionPoolerEnabled, newSpec.Spec.EnableReplicaConnectionPooler}
675676
} else {
676-
input = []*bool{oldSpec.Spec.EnableConnectionPooler, oldSpec.Spec.EnableReplicaConnectionPooler, newSpec.Spec.EnableConnectionPooler, newSpec.Spec.EnableReplicaConnectionPooler}
677+
oldMasterConnectionPoolerEnabled := needMasterConnectionPoolerWorker(&oldSpec.Spec)
678+
input = []*bool{&oldMasterConnectionPoolerEnabled, oldSpec.Spec.EnableReplicaConnectionPooler, &newMasterConnectionPoolerEnabled, newSpec.Spec.EnableReplicaConnectionPooler}
677679
}
678680

679681
for _, b := range input {
@@ -684,25 +686,16 @@ func logPoolerEssentials(log *logrus.Entry, oldSpec, newSpec *acidv1.Postgresql)
684686
}
685687
}
686688

687-
log.Debugf("syncing connection pooler from (%v, %v) to (%v, %v)", v[0], v[1], v[2], v[3])
689+
log.Debugf("syncing connection pooler (master, replica) from (%v, %v) to (%v, %v)", v[0], v[1], v[2], v[3])
688690
}
689691

690692
func (c *Cluster) syncConnectionPooler(oldSpec, newSpec *acidv1.Postgresql, LookupFunction InstallFunction) (SyncReason, error) {
691693

692694
var reason SyncReason
693695
var err error
694-
var newNeedConnectionPooler, oldNeedConnectionPooler bool
695-
oldNeedConnectionPooler = false
696-
697-
if oldSpec == nil {
698-
oldSpec = &acidv1.Postgresql{
699-
Spec: acidv1.PostgresSpec{
700-
ConnectionPooler: &acidv1.ConnectionPooler{},
701-
},
702-
}
703-
}
696+
var connectionPoolerNeeded bool
704697

705-
needSync, _ := needSyncConnectionPoolerSpecs(oldSpec.Spec.ConnectionPooler, newSpec.Spec.ConnectionPooler, c.logger)
698+
needSync := !reflect.DeepEqual(oldSpec.Spec.ConnectionPooler, newSpec.Spec.ConnectionPooler)
706699
masterChanges, err := diff.Diff(oldSpec.Spec.EnableConnectionPooler, newSpec.Spec.EnableConnectionPooler)
707700
if err != nil {
708701
c.logger.Error("Error in getting diff of master connection pooler changes")
@@ -712,15 +705,14 @@ func (c *Cluster) syncConnectionPooler(oldSpec, newSpec *acidv1.Postgresql, Look
712705
c.logger.Error("Error in getting diff of replica connection pooler changes")
713706
}
714707

715-
// skip pooler sync only
716-
// 1. if there is no diff in spec, AND
717-
// 2. if connection pooler is already there and is also required as per newSpec
718-
//
719-
// Handling the case when connectionPooler is not there but it is required
708+
// skip pooler sync when theres no diff or it's deactivated
709+
// but, handling the case when connectionPooler is not there but it is required
720710
// as per spec, hence do not skip syncing in that case, even though there
721711
// is no diff in specs
722712
if (!needSync && len(masterChanges) <= 0 && len(replicaChanges) <= 0) &&
723-
(c.ConnectionPooler != nil && (needConnectionPooler(&newSpec.Spec))) {
713+
((!needConnectionPooler(&newSpec.Spec) && (c.ConnectionPooler == nil || !needConnectionPooler(&oldSpec.Spec))) ||
714+
(c.ConnectionPooler != nil && needConnectionPooler(&newSpec.Spec) &&
715+
(c.ConnectionPooler[Master].LookupFunction || c.ConnectionPooler[Replica].LookupFunction))) {
724716
c.logger.Debugln("syncing pooler is not required")
725717
return nil, nil
726718
}
@@ -731,15 +723,9 @@ func (c *Cluster) syncConnectionPooler(oldSpec, newSpec *acidv1.Postgresql, Look
731723
for _, role := range [2]PostgresRole{Master, Replica} {
732724

733725
if role == Master {
734-
newNeedConnectionPooler = needMasterConnectionPoolerWorker(&newSpec.Spec)
735-
if oldSpec != nil {
736-
oldNeedConnectionPooler = needMasterConnectionPoolerWorker(&oldSpec.Spec)
737-
}
726+
connectionPoolerNeeded = needMasterConnectionPoolerWorker(&newSpec.Spec)
738727
} else {
739-
newNeedConnectionPooler = needReplicaConnectionPoolerWorker(&newSpec.Spec)
740-
if oldSpec != nil {
741-
oldNeedConnectionPooler = needReplicaConnectionPoolerWorker(&oldSpec.Spec)
742-
}
728+
connectionPoolerNeeded = needReplicaConnectionPoolerWorker(&newSpec.Spec)
743729
}
744730

745731
// if the call is via createConnectionPooler, then it is required to initialize
@@ -759,24 +745,22 @@ func (c *Cluster) syncConnectionPooler(oldSpec, newSpec *acidv1.Postgresql, Look
759745
}
760746
}
761747

762-
if newNeedConnectionPooler {
748+
if connectionPoolerNeeded {
763749
// Try to sync in any case. If we didn't needed connection pooler before,
764750
// it means we want to create it. If it was already present, still sync
765751
// since it could happen that there is no difference in specs, and all
766752
// the resources are remembered, but the deployment was manually deleted
767753
// in between
768754

769-
// in this case also do not forget to install lookup function as for
770-
// creating cluster
771-
if !oldNeedConnectionPooler || !c.ConnectionPooler[role].LookupFunction {
772-
newConnectionPooler := newSpec.Spec.ConnectionPooler
773-
755+
// in this case also do not forget to install lookup function
756+
if !c.ConnectionPooler[role].LookupFunction {
757+
connectionPooler := c.Spec.ConnectionPooler
774758
specSchema := ""
775759
specUser := ""
776760

777-
if newConnectionPooler != nil {
778-
specSchema = newConnectionPooler.Schema
779-
specUser = newConnectionPooler.User
761+
if connectionPooler != nil {
762+
specSchema = connectionPooler.Schema
763+
specUser = connectionPooler.User
780764
}
781765

782766
schema := util.Coalesce(
@@ -787,9 +771,10 @@ func (c *Cluster) syncConnectionPooler(oldSpec, newSpec *acidv1.Postgresql, Look
787771
specUser,
788772
c.OpConfig.ConnectionPooler.User)
789773

790-
if err = LookupFunction(schema, user, role); err != nil {
774+
if err = LookupFunction(schema, user); err != nil {
791775
return NoSync, err
792776
}
777+
c.ConnectionPooler[role].LookupFunction = true
793778
}
794779

795780
if reason, err = c.syncConnectionPoolerWorker(oldSpec, newSpec, role); err != nil {
@@ -808,8 +793,8 @@ func (c *Cluster) syncConnectionPooler(oldSpec, newSpec *acidv1.Postgresql, Look
808793
}
809794
}
810795
}
811-
if !needMasterConnectionPoolerWorker(&newSpec.Spec) &&
812-
!needReplicaConnectionPoolerWorker(&newSpec.Spec) {
796+
if (needMasterConnectionPoolerWorker(&oldSpec.Spec) || needReplicaConnectionPoolerWorker(&oldSpec.Spec)) &&
797+
!needMasterConnectionPoolerWorker(&newSpec.Spec) && !needReplicaConnectionPoolerWorker(&newSpec.Spec) {
813798
if err = c.deleteConnectionPoolerSecret(); err != nil {
814799
c.logger.Warningf("could not remove connection pooler secret: %v", err)
815800
}
@@ -874,8 +859,6 @@ func (c *Cluster) syncConnectionPoolerWorker(oldSpec, newSpec *acidv1.Postgresql
874859
newConnectionPooler = &acidv1.ConnectionPooler{}
875860
}
876861

877-
c.logger.Infof("old: %+v, new %+v", oldConnectionPooler, newConnectionPooler)
878-
879862
var specSync bool
880863
var specReason []string
881864

pkg/cluster/connection_pooler_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import (
1919
"k8s.io/client-go/kubernetes/fake"
2020
)
2121

22-
func mockInstallLookupFunction(schema string, user string, role PostgresRole) error {
22+
func mockInstallLookupFunction(schema string, user string) error {
2323
return nil
2424
}
2525

pkg/cluster/database.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,7 @@ func (c *Cluster) execCreateOrAlterExtension(extName, schemaName, statement, doi
508508

509509
// Creates a connection pool credentials lookup function in every database to
510510
// perform remote authentication.
511-
func (c *Cluster) installLookupFunction(poolerSchema, poolerUser string, role PostgresRole) error {
511+
func (c *Cluster) installLookupFunction(poolerSchema, poolerUser string) error {
512512
var stmtBytes bytes.Buffer
513513

514514
c.logger.Info("Installing lookup function")
@@ -604,8 +604,8 @@ func (c *Cluster) installLookupFunction(poolerSchema, poolerUser string, role Po
604604
c.logger.Infof("pooler lookup function installed into %s", dbname)
605605
}
606606

607-
if len(failedDatabases) == 0 {
608-
c.ConnectionPooler[role].LookupFunction = true
607+
if len(failedDatabases) > 0 {
608+
return fmt.Errorf("could not install pooler lookup function in every specified databases")
609609
}
610610

611611
return nil

pkg/cluster/sync.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,15 @@ func (c *Cluster) syncDatabases() error {
758758
}
759759
}
760760

761+
if len(createDatabases) > 0 {
762+
// trigger creation of pooler objects in new database in syncConnectionPooler
763+
if c.ConnectionPooler != nil {
764+
for _, role := range [2]PostgresRole{Master, Replica} {
765+
c.ConnectionPooler[role].LookupFunction = false
766+
}
767+
}
768+
}
769+
761770
// set default privileges for prepared database
762771
for _, preparedDatabase := range preparedDatabases {
763772
if err := c.initDbConnWithName(preparedDatabase); err != nil {

pkg/cluster/types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ type ClusterStatus struct {
7272

7373
type TemplateParams map[string]interface{}
7474

75-
type InstallFunction func(schema string, user string, role PostgresRole) error
75+
type InstallFunction func(schema string, user string) error
7676

7777
type SyncReason []string
7878

0 commit comments

Comments
 (0)