diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index ebe4e7089..c10672919 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -119,6 +119,8 @@ spec: format: int32 minimum: 1 type: integer + connection_pooler_priority_class_name: + type: string connection_pooler_schema: default: pooler type: string diff --git a/charts/postgres-operator/crds/postgresqls.yaml b/charts/postgres-operator/crds/postgresqls.yaml index 4d534c16e..32b055b3e 100644 --- a/charts/postgres-operator/crds/postgresqls.yaml +++ b/charts/postgres-operator/crds/postgresqls.yaml @@ -168,6 +168,8 @@ spec: format: int32 minimum: 1 type: integer + priorityClassName: + type: string resources: description: Resources describes requests and limits for the cluster resouces. diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index 4ea5b6319..f86da71a2 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -474,6 +474,8 @@ configConnectionPooler: connection_pooler_mode: "transaction" # number of pooler instances connection_pooler_number_of_instances: 2 + # priorityClassName to assign to pooler pods + # connection_pooler_priority_class_name: "" # default resources connection_pooler_default_cpu_request: 500m connection_pooler_default_memory_request: 100Mi diff --git a/docs/reference/cluster_manifest.md b/docs/reference/cluster_manifest.md index 0717e411f..17a89bca3 100644 --- a/docs/reference/cluster_manifest.md +++ b/docs/reference/cluster_manifest.md @@ -656,6 +656,14 @@ for both master and replica pooler services (if `enableReplicaConnectionPooler` * **mode** In which mode to run connection pooler, transaction or session. +* **priorityClassName** + a name of the [priority + class](https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#priorityclass) + that should be assigned to the connection pooler pods. When not specified, the + value is taken from the `connection_pooler_priority_class_name` operator + parameter, if not set then the default priority class is taken. The priority + class itself must be defined in advance. Optional. + * **resources** Resource configuration for connection pooler deployment. diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index 02c6db8d0..5c4522160 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -1100,6 +1100,12 @@ operator being able to provide some reasonable defaults. * **connection_pooler_mode** Default pooler mode, `session` or `transaction`. Default is `transaction`. +* **connection_pooler_priority_class_name** + The name of the priority class to assign to connection pooler pods by default. + A cluster can override this with `connectionPooler.priorityClassName`. When + unset, the pooler pods use no priority class. The empty string signifies no + assignment. + * **connection_pooler_default_cpu_request** **connection_pooler_default_memory_reques** **connection_pooler_default_cpu_limit** diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index c7f4d2655..30e0674b1 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -21,6 +21,7 @@ data: connection_pooler_max_db_connections: "60" connection_pooler_mode: "transaction" connection_pooler_number_of_instances: "2" + # connection_pooler_priority_class_name: "" connection_pooler_schema: "pooler" connection_pooler_user: "pooler" crd_categories: "all" diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index ebe4e7089..c10672919 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -119,6 +119,8 @@ spec: format: int32 minimum: 1 type: integer + connection_pooler_priority_class_name: + type: string connection_pooler_schema: default: pooler type: string diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index e25a7b339..fd4e215bf 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -233,6 +233,7 @@ configuration: # connection_pooler_max_db_connections: 60 connection_pooler_mode: "transaction" connection_pooler_number_of_instances: 2 + # connection_pooler_priority_class_name: "" # connection_pooler_schema: "pooler" # connection_pooler_user: "pooler" patroni: diff --git a/manifests/postgresql.crd.yaml b/manifests/postgresql.crd.yaml index 0e3558638..0ab02e93f 100644 --- a/manifests/postgresql.crd.yaml +++ b/manifests/postgresql.crd.yaml @@ -168,6 +168,8 @@ spec: format: int32 minimum: 1 type: integer + priorityClassName: + type: string resources: description: Resources describes requests and limits for the cluster resouces. diff --git a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go index 046dd0761..24cd82d68 100644 --- a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go +++ b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go @@ -347,6 +347,7 @@ type ConnectionPoolerConfiguration struct { DefaultCPULimit string `json:"connection_pooler_default_cpu_limit,omitempty"` // +kubebuilder:validation:Pattern=`^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$` DefaultMemoryLimit string `json:"connection_pooler_default_memory_limit,omitempty"` + PriorityClassName string `json:"connection_pooler_priority_class_name,omitempty"` } // OperatorLogicalBackupConfiguration defines configuration for logical backup diff --git a/pkg/apis/acid.zalan.do/v1/operatorconfiguration.crd.yaml b/pkg/apis/acid.zalan.do/v1/operatorconfiguration.crd.yaml index ebe4e7089..c10672919 100644 --- a/pkg/apis/acid.zalan.do/v1/operatorconfiguration.crd.yaml +++ b/pkg/apis/acid.zalan.do/v1/operatorconfiguration.crd.yaml @@ -119,6 +119,8 @@ spec: format: int32 minimum: 1 type: integer + connection_pooler_priority_class_name: + type: string connection_pooler_schema: default: pooler type: string diff --git a/pkg/apis/acid.zalan.do/v1/postgresql.crd.yaml b/pkg/apis/acid.zalan.do/v1/postgresql.crd.yaml index 0e3558638..0ab02e93f 100644 --- a/pkg/apis/acid.zalan.do/v1/postgresql.crd.yaml +++ b/pkg/apis/acid.zalan.do/v1/postgresql.crd.yaml @@ -168,6 +168,8 @@ spec: format: int32 minimum: 1 type: integer + priorityClassName: + type: string resources: description: Resources describes requests and limits for the cluster resouces. diff --git a/pkg/apis/acid.zalan.do/v1/postgresql_type.go b/pkg/apis/acid.zalan.do/v1/postgresql_type.go index a03bb13cc..f17ea0f14 100644 --- a/pkg/apis/acid.zalan.do/v1/postgresql_type.go +++ b/pkg/apis/acid.zalan.do/v1/postgresql_type.go @@ -325,9 +325,10 @@ type ConnectionPooler struct { Schema string `json:"schema,omitempty"` User string `json:"user,omitempty"` // +kubebuilder:validation:Enum=session;transaction - Mode string `json:"mode,omitempty"` - DockerImage string `json:"dockerImage,omitempty"` - MaxDBConnections *int32 `json:"maxDBConnections,omitempty"` + Mode string `json:"mode,omitempty"` + DockerImage string `json:"dockerImage,omitempty"` + MaxDBConnections *int32 `json:"maxDBConnections,omitempty"` + PriorityClassName string `json:"priorityClassName,omitempty"` *Resources `json:"resources,omitempty"` } diff --git a/pkg/cluster/connection_pooler.go b/pkg/cluster/connection_pooler.go index 61cd9b041..15e6a52cc 100644 --- a/pkg/cluster/connection_pooler.go +++ b/pkg/cluster/connection_pooler.go @@ -471,6 +471,9 @@ func (c *Cluster) generateConnectionPoolerPodTemplate(role PostgresRole) ( Volumes: poolerVolumes, SecurityContext: &securityContext, ServiceAccountName: c.OpConfig.PodServiceAccountName, + PriorityClassName: util.Coalesce( + connectionPoolerSpec.PriorityClassName, + c.OpConfig.ConnectionPooler.PriorityClassName), }, } @@ -893,6 +896,16 @@ func (c *Cluster) needSyncConnectionPoolerDefaults(Config *Config, spec *acidv1. reasons = append(reasons, msg) } + if spec.PriorityClassName == "" { + expectedPriorityClassName := config.PriorityClassName + if podTemplate.Spec.PriorityClassName != expectedPriorityClassName { + sync = true + msg := fmt.Sprintf("priorityClassName is different (having %s, required %s)", + podTemplate.Spec.PriorityClassName, expectedPriorityClassName) + reasons = append(reasons, msg) + } + } + expectedResources, err := c.generateResourceRequirements(spec.Resources, makeDefaultConnectionPoolerResources(&Config.OpConfig), connectionPoolerContainer) diff --git a/pkg/cluster/connection_pooler_test.go b/pkg/cluster/connection_pooler_test.go index 1b41cbb02..e73a4b098 100644 --- a/pkg/cluster/connection_pooler_test.go +++ b/pkg/cluster/connection_pooler_test.go @@ -871,6 +871,72 @@ func TestConnectionPoolerDeploymentSpec(t *testing.T) { } } +func TestConnectionPoolerPriorityClassName(t *testing.T) { + newCluster := func(specPooler, opPooler, specPod, opPod string) *Cluster { + c := New( + Config{ + OpConfig: config.Config{ + Resources: config.Resources{ + PodPriorityClassName: opPod, + }, + ConnectionPooler: config.ConnectionPooler{ + PriorityClassName: opPooler, + }, + }, + }, k8sutil.KubernetesClient{}, acidv1.Postgresql{}, logger, eventRecorder) + c.Spec = acidv1.PostgresSpec{ + PodPriorityClassName: specPod, + ConnectionPooler: &acidv1.ConnectionPooler{PriorityClassName: specPooler}, + } + return c + } + + tests := []struct { + subTest string + cluster *Cluster + expected string + }{ + { + subTest: "pooler spec wins over everything", + cluster: newCluster("pooler-spec", "pooler-op", "pod-spec", "pod-op"), + expected: "pooler-spec", + }, + { + subTest: "pooler operator default when spec empty", + cluster: newCluster("", "pooler-op", "pod-spec", "pod-op"), + expected: "pooler-op", + }, + { + subTest: "does not inherit cluster pod priority class", + cluster: newCluster("", "", "pod-spec", "pod-op"), + expected: "", + }, + { + subTest: "does not inherit operator pod priority class", + cluster: newCluster("", "", "", "pod-op"), + expected: "", + }, + { + subTest: "empty when nothing set", + cluster: newCluster("", "", "", ""), + expected: "", + }, + } + + for _, role := range [2]PostgresRole{Master, Replica} { + for _, tt := range tests { + podSpec, err := tt.cluster.generateConnectionPoolerPodTemplate(role) + if err != nil { + t.Fatalf("%s [%s]: unexpected error %v", role, tt.subTest, err) + } + if podSpec.Spec.PriorityClassName != tt.expected { + t.Errorf("%s [%s]: got priorityClassName %q, expected %q", + role, tt.subTest, podSpec.Spec.PriorityClassName, tt.expected) + } + } + } +} + func testServiceAccount(cluster *Cluster, podSpec *v1.PodTemplateSpec, role PostgresRole) error { poolerServiceAccount := podSpec.Spec.ServiceAccountName diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index 667384e30..4f0269453 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -282,6 +282,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur result.ConnectionPooler.ConnectionPoolerDefaultMemoryRequest = fromCRD.ConnectionPooler.DefaultMemoryRequest result.ConnectionPooler.ConnectionPoolerDefaultCPULimit = fromCRD.ConnectionPooler.DefaultCPULimit result.ConnectionPooler.ConnectionPoolerDefaultMemoryLimit = fromCRD.ConnectionPooler.DefaultMemoryLimit + result.ConnectionPooler.PriorityClassName = fromCRD.ConnectionPooler.PriorityClassName result.ConnectionPooler.MaxDBConnections = util.CoalesceInt32( fromCRD.ConnectionPooler.MaxDBConnections, diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index 95324a747..decb86a72 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -161,6 +161,7 @@ type ConnectionPooler struct { Image string `name:"connection_pooler_image" default:"ghcr.io/zalando/postgres-operator/pgbouncer:latest"` Mode string `name:"connection_pooler_mode" default:"transaction"` MaxDBConnections *int32 `name:"connection_pooler_max_db_connections" default:"60"` + PriorityClassName string `name:"connection_pooler_priority_class_name"` ConnectionPoolerDefaultCPURequest string `name:"connection_pooler_default_cpu_request"` ConnectionPoolerDefaultMemoryRequest string `name:"connection_pooler_default_memory_request"` ConnectionPoolerDefaultCPULimit string `name:"connection_pooler_default_cpu_limit"`