Call me pooler, not pool (#883)
* rename pooler parts and add example to manifest * update codegen * fix manifest and add more details to docs * reflect renaming also in e2e tests
This commit is contained in:
parent
e6eb10d28a
commit
b43b22dfcc
|
|
@ -17,8 +17,9 @@ pipelines with no access to Kubernetes directly.
|
||||||
|
|
||||||
* Rolling updates on Postgres cluster changes
|
* Rolling updates on Postgres cluster changes
|
||||||
* Volume resize without Pod restarts
|
* Volume resize without Pod restarts
|
||||||
|
* Database connection pooler
|
||||||
* Cloning Postgres clusters
|
* Cloning Postgres clusters
|
||||||
* Logical Backups to S3 Bucket
|
* Logical backups to S3 Bucket
|
||||||
* Standby cluster from S3 WAL archive
|
* Standby cluster from S3 WAL archive
|
||||||
* Configurable for non-cloud environments
|
* Configurable for non-cloud environments
|
||||||
* UI to create and edit Postgres cluster manifests
|
* UI to create and edit Postgres cluster manifests
|
||||||
|
|
|
||||||
|
|
@ -318,44 +318,44 @@ spec:
|
||||||
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
|
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
|
||||||
scalyr_server_url:
|
scalyr_server_url:
|
||||||
type: string
|
type: string
|
||||||
connection_pool:
|
connection_pooler:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
connection_pool_schema:
|
connection_pooler_schema:
|
||||||
type: string
|
type: string
|
||||||
#default: "pooler"
|
#default: "pooler"
|
||||||
connection_pool_user:
|
connection_pooler_user:
|
||||||
type: string
|
type: string
|
||||||
#default: "pooler"
|
#default: "pooler"
|
||||||
connection_pool_image:
|
connection_pooler_image:
|
||||||
type: string
|
type: string
|
||||||
#default: "registry.opensource.zalan.do/acid/pgbouncer"
|
#default: "registry.opensource.zalan.do/acid/pgbouncer"
|
||||||
connection_pool_max_db_connections:
|
connection_pooler_max_db_connections:
|
||||||
type: integer
|
type: integer
|
||||||
#default: 60
|
#default: 60
|
||||||
connection_pool_mode:
|
connection_pooler_mode:
|
||||||
type: string
|
type: string
|
||||||
enum:
|
enum:
|
||||||
- "session"
|
- "session"
|
||||||
- "transaction"
|
- "transaction"
|
||||||
#default: "transaction"
|
#default: "transaction"
|
||||||
connection_pool_number_of_instances:
|
connection_pooler_number_of_instances:
|
||||||
type: integer
|
type: integer
|
||||||
minimum: 2
|
minimum: 2
|
||||||
#default: 2
|
#default: 2
|
||||||
connection_pool_default_cpu_limit:
|
connection_pooler_default_cpu_limit:
|
||||||
type: string
|
type: string
|
||||||
pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
|
pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
|
||||||
#default: "1"
|
#default: "1"
|
||||||
connection_pool_default_cpu_request:
|
connection_pooler_default_cpu_request:
|
||||||
type: string
|
type: string
|
||||||
pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
|
pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
|
||||||
#default: "500m"
|
#default: "500m"
|
||||||
connection_pool_default_memory_limit:
|
connection_pooler_default_memory_limit:
|
||||||
type: string
|
type: string
|
||||||
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
|
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
|
||||||
#default: "100Mi"
|
#default: "100Mi"
|
||||||
connection_pool_default_memory_request:
|
connection_pooler_default_memory_request:
|
||||||
type: string
|
type: string
|
||||||
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
|
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
|
||||||
#default: "100Mi"
|
#default: "100Mi"
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ spec:
|
||||||
uid:
|
uid:
|
||||||
format: uuid
|
format: uuid
|
||||||
type: string
|
type: string
|
||||||
connectionPool:
|
connectionPooler:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
dockerImage:
|
dockerImage:
|
||||||
|
|
@ -162,7 +162,7 @@ spec:
|
||||||
# Note: usernames specified here as database owners must be declared in the users key of the spec key.
|
# Note: usernames specified here as database owners must be declared in the users key of the spec key.
|
||||||
dockerImage:
|
dockerImage:
|
||||||
type: string
|
type: string
|
||||||
enableConnectionPool:
|
enableConnectionPooler:
|
||||||
type: boolean
|
type: boolean
|
||||||
enableLogicalBackup:
|
enableLogicalBackup:
|
||||||
type: boolean
|
type: boolean
|
||||||
|
|
|
||||||
|
|
@ -20,5 +20,5 @@ data:
|
||||||
{{ toYaml .Values.configDebug | indent 2 }}
|
{{ toYaml .Values.configDebug | indent 2 }}
|
||||||
{{ toYaml .Values.configLoggingRestApi | indent 2 }}
|
{{ toYaml .Values.configLoggingRestApi | indent 2 }}
|
||||||
{{ toYaml .Values.configTeamsApi | indent 2 }}
|
{{ toYaml .Values.configTeamsApi | indent 2 }}
|
||||||
{{ toYaml .Values.configConnectionPool | indent 2 }}
|
{{ toYaml .Values.configConnectionPooler | indent 2 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,6 @@ configuration:
|
||||||
{{ toYaml .Values.configLoggingRestApi | indent 4 }}
|
{{ toYaml .Values.configLoggingRestApi | indent 4 }}
|
||||||
scalyr:
|
scalyr:
|
||||||
{{ toYaml .Values.configScalyr | indent 4 }}
|
{{ toYaml .Values.configScalyr | indent 4 }}
|
||||||
connection_pool:
|
connection_pooler:
|
||||||
{{ toYaml .Values.configConnectionPool | indent 4 }}
|
{{ toYaml .Values.configConnectionPooler | indent 4 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
|
||||||
|
|
@ -267,24 +267,24 @@ configScalyr:
|
||||||
# Memory request value for the Scalyr sidecar
|
# Memory request value for the Scalyr sidecar
|
||||||
scalyr_memory_request: 50Mi
|
scalyr_memory_request: 50Mi
|
||||||
|
|
||||||
configConnectionPool:
|
configConnectionPooler:
|
||||||
# db schema to install lookup function into
|
# db schema to install lookup function into
|
||||||
connection_pool_schema: "pooler"
|
connection_pooler_schema: "pooler"
|
||||||
# db user for pooler to use
|
# db user for pooler to use
|
||||||
connection_pool_user: "pooler"
|
connection_pooler_user: "pooler"
|
||||||
# docker image
|
# docker image
|
||||||
connection_pool_image: "registry.opensource.zalan.do/acid/pgbouncer"
|
connection_pooler_image: "registry.opensource.zalan.do/acid/pgbouncer"
|
||||||
# max db connections the pooler should hold
|
# max db connections the pooler should hold
|
||||||
connection_pool_max_db_connections: 60
|
connection_pooler_max_db_connections: 60
|
||||||
# default pooling mode
|
# default pooling mode
|
||||||
connection_pool_mode: "transaction"
|
connection_pooler_mode: "transaction"
|
||||||
# number of pooler instances
|
# number of pooler instances
|
||||||
connection_pool_number_of_instances: 2
|
connection_pooler_number_of_instances: 2
|
||||||
# default resources
|
# default resources
|
||||||
connection_pool_default_cpu_request: 500m
|
connection_pooler_default_cpu_request: 500m
|
||||||
connection_pool_default_memory_request: 100Mi
|
connection_pooler_default_memory_request: 100Mi
|
||||||
connection_pool_default_cpu_limit: "1"
|
connection_pooler_default_cpu_limit: "1"
|
||||||
connection_pool_default_memory_limit: 100Mi
|
connection_pooler_default_memory_limit: 100Mi
|
||||||
|
|
||||||
rbac:
|
rbac:
|
||||||
# Specifies whether RBAC resources should be created
|
# Specifies whether RBAC resources should be created
|
||||||
|
|
|
||||||
|
|
@ -244,24 +244,24 @@ configTeamsApi:
|
||||||
# teams_api_url: http://fake-teams-api.default.svc.cluster.local
|
# teams_api_url: http://fake-teams-api.default.svc.cluster.local
|
||||||
|
|
||||||
# configure connection pooler deployment created by the operator
|
# configure connection pooler deployment created by the operator
|
||||||
configConnectionPool:
|
configConnectionPooler:
|
||||||
# db schema to install lookup function into
|
# db schema to install lookup function into
|
||||||
connection_pool_schema: "pooler"
|
connection_pooler_schema: "pooler"
|
||||||
# db user for pooler to use
|
# db user for pooler to use
|
||||||
connection_pool_user: "pooler"
|
connection_pooler_user: "pooler"
|
||||||
# docker image
|
# docker image
|
||||||
connection_pool_image: "registry.opensource.zalan.do/acid/pgbouncer"
|
connection_pooler_image: "registry.opensource.zalan.do/acid/pgbouncer"
|
||||||
# max db connections the pooler should hold
|
# max db connections the pooler should hold
|
||||||
connection_pool_max_db_connections: 60
|
connection_pooler_max_db_connections: 60
|
||||||
# default pooling mode
|
# default pooling mode
|
||||||
connection_pool_mode: "transaction"
|
connection_pooler_mode: "transaction"
|
||||||
# number of pooler instances
|
# number of pooler instances
|
||||||
connection_pool_number_of_instances: 2
|
connection_pooler_number_of_instances: 2
|
||||||
# default resources
|
# default resources
|
||||||
connection_pool_default_cpu_request: 500m
|
connection_pooler_default_cpu_request: 500m
|
||||||
connection_pool_default_memory_request: 100Mi
|
connection_pooler_default_memory_request: 100Mi
|
||||||
connection_pool_default_cpu_limit: "1"
|
connection_pooler_default_cpu_limit: "1"
|
||||||
connection_pool_default_memory_limit: 100Mi
|
connection_pooler_default_memory_limit: 100Mi
|
||||||
|
|
||||||
rbac:
|
rbac:
|
||||||
# Specifies whether RBAC resources should be created
|
# Specifies whether RBAC resources should be created
|
||||||
|
|
|
||||||
|
|
@ -140,10 +140,10 @@ These parameters are grouped directly under the `spec` key in the manifest.
|
||||||
is `false`, then no volume will be mounted no matter how operator was
|
is `false`, then no volume will be mounted no matter how operator was
|
||||||
configured (so you can override the operator configuration). Optional.
|
configured (so you can override the operator configuration). Optional.
|
||||||
|
|
||||||
* **enableConnectionPool**
|
* **enableConnectionPooler**
|
||||||
Tells the operator to create a connection pool with a database. If this
|
Tells the operator to create a connection pooler with a database. If this
|
||||||
field is true, a connection pool deployment will be created even if
|
field is true, a connection pooler deployment will be created even if
|
||||||
`connectionPool` section is empty. Optional, not set by default.
|
`connectionPooler` section is empty. Optional, not set by default.
|
||||||
|
|
||||||
* **enableLogicalBackup**
|
* **enableLogicalBackup**
|
||||||
Determines if the logical backup of this cluster should be taken and uploaded
|
Determines if the logical backup of this cluster should be taken and uploaded
|
||||||
|
|
@ -365,34 +365,34 @@ CPU and memory limits for the sidecar container.
|
||||||
memory limits for the sidecar container. Optional, overrides the
|
memory limits for the sidecar container. Optional, overrides the
|
||||||
`default_memory_limits` operator configuration parameter. Optional.
|
`default_memory_limits` operator configuration parameter. Optional.
|
||||||
|
|
||||||
## Connection pool
|
## Connection pooler
|
||||||
|
|
||||||
Parameters are grouped under the `connectionPool` top-level key and specify
|
Parameters are grouped under the `connectionPooler` top-level key and specify
|
||||||
configuration for connection pool. If this section is not empty, a connection
|
configuration for connection pooler. If this section is not empty, a connection
|
||||||
pool will be created for a database even if `enableConnectionPool` is not
|
pooler will be created for a database even if `enableConnectionPooler` is not
|
||||||
present.
|
present.
|
||||||
|
|
||||||
* **numberOfInstances**
|
* **numberOfInstances**
|
||||||
How many instances of connection pool to create.
|
How many instances of connection pooler to create.
|
||||||
|
|
||||||
* **schema**
|
* **schema**
|
||||||
Schema to create for credentials lookup function.
|
Schema to create for credentials lookup function.
|
||||||
|
|
||||||
* **user**
|
* **user**
|
||||||
User to create for connection pool to be able to connect to a database.
|
User to create for connection pooler to be able to connect to a database.
|
||||||
|
|
||||||
* **dockerImage**
|
* **dockerImage**
|
||||||
Which docker image to use for connection pool deployment.
|
Which docker image to use for connection pooler deployment.
|
||||||
|
|
||||||
* **maxDBConnections**
|
* **maxDBConnections**
|
||||||
How many connections the pooler can max hold. This value is divided among the
|
How many connections the pooler can max hold. This value is divided among the
|
||||||
pooler pods.
|
pooler pods.
|
||||||
|
|
||||||
* **mode**
|
* **mode**
|
||||||
In which mode to run connection pool, transaction or session.
|
In which mode to run connection pooler, transaction or session.
|
||||||
|
|
||||||
* **resources**
|
* **resources**
|
||||||
Resource configuration for connection pool deployment.
|
Resource configuration for connection pooler deployment.
|
||||||
|
|
||||||
## Custom TLS certificates
|
## Custom TLS certificates
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -597,39 +597,39 @@ scalyr sidecar. In the CRD-based configuration they are grouped under the
|
||||||
* **scalyr_memory_limit**
|
* **scalyr_memory_limit**
|
||||||
Memory limit value for the Scalyr sidecar. The default is `500Mi`.
|
Memory limit value for the Scalyr sidecar. The default is `500Mi`.
|
||||||
|
|
||||||
## Connection pool configuration
|
## Connection pooler configuration
|
||||||
|
|
||||||
Parameters are grouped under the `connection_pool` top-level key and specify
|
Parameters are grouped under the `connection_pooler` top-level key and specify
|
||||||
default configuration for connection pool, if a postgres manifest requests it
|
default configuration for connection pooler, if a postgres manifest requests it
|
||||||
but do not specify some of the parameters. All of them are optional with the
|
but do not specify some of the parameters. All of them are optional with the
|
||||||
operator being able to provide some reasonable defaults.
|
operator being able to provide some reasonable defaults.
|
||||||
|
|
||||||
* **connection_pool_number_of_instances**
|
* **connection_pooler_number_of_instances**
|
||||||
How many instances of connection pool to create. Default is 2 which is also
|
How many instances of connection pooler to create. Default is 2 which is also
|
||||||
the required minimum.
|
the required minimum.
|
||||||
|
|
||||||
* **connection_pool_schema**
|
* **connection_pooler_schema**
|
||||||
Schema to create for credentials lookup function. Default is `pooler`.
|
Schema to create for credentials lookup function. Default is `pooler`.
|
||||||
|
|
||||||
* **connection_pool_user**
|
* **connection_pooler_user**
|
||||||
User to create for connection pool to be able to connect to a database.
|
User to create for connection pooler to be able to connect to a database.
|
||||||
Default is `pooler`.
|
Default is `pooler`.
|
||||||
|
|
||||||
* **connection_pool_image**
|
* **connection_pooler_image**
|
||||||
Docker image to use for connection pool deployment.
|
Docker image to use for connection pooler deployment.
|
||||||
Default: "registry.opensource.zalan.do/acid/pgbouncer"
|
Default: "registry.opensource.zalan.do/acid/pgbouncer"
|
||||||
|
|
||||||
* **connection_pool_max_db_connections**
|
* **connection_pooler_max_db_connections**
|
||||||
How many connections the pooler can max hold. This value is divided among the
|
How many connections the pooler can max hold. This value is divided among the
|
||||||
pooler pods. Default is 60 which will make up 30 connections per pod for the
|
pooler pods. Default is 60 which will make up 30 connections per pod for the
|
||||||
default setup with two instances.
|
default setup with two instances.
|
||||||
|
|
||||||
* **connection_pool_mode**
|
* **connection_pooler_mode**
|
||||||
Default pool mode, `session` or `transaction`. Default is `transaction`.
|
Default pooler mode, `session` or `transaction`. Default is `transaction`.
|
||||||
|
|
||||||
* **connection_pool_default_cpu_request**
|
* **connection_pooler_default_cpu_request**
|
||||||
**connection_pool_default_memory_reques**
|
**connection_pooler_default_memory_reques**
|
||||||
**connection_pool_default_cpu_limit**
|
**connection_pooler_default_cpu_limit**
|
||||||
**connection_pool_default_memory_limit**
|
**connection_pooler_default_memory_limit**
|
||||||
Default resource configuration for connection pool deployment. The internal
|
Default resource configuration for connection pooler deployment. The internal
|
||||||
default for memory request and limit is `100Mi`, for CPU it is `500m` and `1`.
|
default for memory request and limit is `100Mi`, for CPU it is `500m` and `1`.
|
||||||
|
|
|
||||||
47
docs/user.md
47
docs/user.md
|
|
@ -512,39 +512,38 @@ monitoring is outside the scope of operator responsibilities. See
|
||||||
[administrator documentation](administrator.md) for details on how backups are
|
[administrator documentation](administrator.md) for details on how backups are
|
||||||
executed.
|
executed.
|
||||||
|
|
||||||
## Connection pool
|
## Connection pooler
|
||||||
|
|
||||||
The operator can create a database side connection pool for those applications,
|
The operator can create a database side connection pooler for those applications
|
||||||
where an application side pool is not feasible, but a number of connections is
|
where an application side pooler is not feasible, but a number of connections is
|
||||||
high. To create a connection pool together with a database, modify the
|
high. To create a connection pooler together with a database, modify the
|
||||||
manifest:
|
manifest:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
spec:
|
spec:
|
||||||
enableConnectionPool: true
|
enableConnectionPooler: true
|
||||||
```
|
```
|
||||||
|
|
||||||
This will tell the operator to create a connection pool with default
|
This will tell the operator to create a connection pooler with default
|
||||||
configuration, through which one can access the master via a separate service
|
configuration, through which one can access the master via a separate service
|
||||||
`{cluster-name}-pooler`. In most of the cases provided default configuration
|
`{cluster-name}-pooler`. In most of the cases the
|
||||||
should be good enough.
|
[default configuration](reference/operator_parameters.md#connection-pool-configuration)
|
||||||
|
should be good enough. To configure a new connection pooler individually for
|
||||||
To configure a new connection pool, specify:
|
each Postgres cluster, specify:
|
||||||
|
|
||||||
```
|
```
|
||||||
spec:
|
spec:
|
||||||
connectionPool:
|
connectionPooler:
|
||||||
# how many instances of connection pool to create
|
# how many instances of connection pooler to create
|
||||||
number_of_instances: 2
|
numberOfInstances: 2
|
||||||
|
|
||||||
# in which mode to run, session or transaction
|
# in which mode to run, session or transaction
|
||||||
mode: "transaction"
|
mode: "transaction"
|
||||||
|
|
||||||
# schema, which operator will create to install credentials lookup
|
# schema, which operator will create to install credentials lookup function
|
||||||
# function
|
|
||||||
schema: "pooler"
|
schema: "pooler"
|
||||||
|
|
||||||
# user, which operator will create for connection pool
|
# user, which operator will create for connection pooler
|
||||||
user: "pooler"
|
user: "pooler"
|
||||||
|
|
||||||
# resources for each instance
|
# resources for each instance
|
||||||
|
|
@ -557,13 +556,17 @@ spec:
|
||||||
memory: 100Mi
|
memory: 100Mi
|
||||||
```
|
```
|
||||||
|
|
||||||
By default `pgbouncer` is used to create a connection pool. To find out about
|
The `enableConnectionPooler` flag is not required when the `connectionPooler`
|
||||||
pool modes see [docs](https://www.pgbouncer.org/config.html#pool_mode) (but it
|
section is present in the manifest. But, it can be used to disable/remove the
|
||||||
should be general approach between different implementation).
|
pooler while keeping its configuration.
|
||||||
|
|
||||||
Note, that using `pgbouncer` means meaningful resource CPU limit should be less
|
By default, `pgbouncer` is used as connection pooler. To find out about pooler
|
||||||
than 1 core (there is a way to utilize more than one, but in K8S it's easier
|
modes read the `pgbouncer` [docs](https://www.pgbouncer.org/config.html#pooler_mode)
|
||||||
just to spin up more instances).
|
(but it should be the general approach between different implementation).
|
||||||
|
|
||||||
|
Note, that using `pgbouncer` a meaningful resource CPU limit should be 1 core
|
||||||
|
or less (there is a way to utilize more than one, but in K8s it's easier just to
|
||||||
|
spin up more instances).
|
||||||
|
|
||||||
## Custom TLS certificates
|
## Custom TLS certificates
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -70,32 +70,32 @@ class EndToEndTestCase(unittest.TestCase):
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@timeout_decorator.timeout(TEST_TIMEOUT_SEC)
|
@timeout_decorator.timeout(TEST_TIMEOUT_SEC)
|
||||||
def test_enable_disable_connection_pool(self):
|
def test_enable_disable_connection_pooler(self):
|
||||||
'''
|
'''
|
||||||
For a database without connection pool, then turns it on, scale up,
|
For a database without connection pooler, then turns it on, scale up,
|
||||||
turn off and on again. Test with different ways of doing this (via
|
turn off and on again. Test with different ways of doing this (via
|
||||||
enableConnectionPool or connectionPool configuration section). At the
|
enableConnectionPooler or connectionPooler configuration section). At
|
||||||
end turn the connection pool off to not interfere with other tests.
|
the end turn connection pooler off to not interfere with other tests.
|
||||||
'''
|
'''
|
||||||
k8s = self.k8s
|
k8s = self.k8s
|
||||||
service_labels = {
|
service_labels = {
|
||||||
'cluster-name': 'acid-minimal-cluster',
|
'cluster-name': 'acid-minimal-cluster',
|
||||||
}
|
}
|
||||||
pod_labels = dict({
|
pod_labels = dict({
|
||||||
'connection-pool': 'acid-minimal-cluster-pooler',
|
'connection-pooler': 'acid-minimal-cluster-pooler',
|
||||||
})
|
})
|
||||||
|
|
||||||
pod_selector = to_selector(pod_labels)
|
pod_selector = to_selector(pod_labels)
|
||||||
service_selector = to_selector(service_labels)
|
service_selector = to_selector(service_labels)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# enable connection pool
|
# enable connection pooler
|
||||||
k8s.api.custom_objects_api.patch_namespaced_custom_object(
|
k8s.api.custom_objects_api.patch_namespaced_custom_object(
|
||||||
'acid.zalan.do', 'v1', 'default',
|
'acid.zalan.do', 'v1', 'default',
|
||||||
'postgresqls', 'acid-minimal-cluster',
|
'postgresqls', 'acid-minimal-cluster',
|
||||||
{
|
{
|
||||||
'spec': {
|
'spec': {
|
||||||
'enableConnectionPool': True,
|
'enableConnectionPooler': True,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
k8s.wait_for_pod_start(pod_selector)
|
k8s.wait_for_pod_start(pod_selector)
|
||||||
|
|
@ -104,7 +104,7 @@ class EndToEndTestCase(unittest.TestCase):
|
||||||
'default', label_selector=pod_selector
|
'default', label_selector=pod_selector
|
||||||
).items
|
).items
|
||||||
|
|
||||||
self.assertTrue(pods, 'No connection pool pods')
|
self.assertTrue(pods, 'No connection pooler pods')
|
||||||
|
|
||||||
k8s.wait_for_service(service_selector)
|
k8s.wait_for_service(service_selector)
|
||||||
services = k8s.api.core_v1.list_namespaced_service(
|
services = k8s.api.core_v1.list_namespaced_service(
|
||||||
|
|
@ -115,15 +115,15 @@ class EndToEndTestCase(unittest.TestCase):
|
||||||
if s.metadata.name.endswith('pooler')
|
if s.metadata.name.endswith('pooler')
|
||||||
]
|
]
|
||||||
|
|
||||||
self.assertTrue(services, 'No connection pool service')
|
self.assertTrue(services, 'No connection pooler service')
|
||||||
|
|
||||||
# scale up connection pool deployment
|
# scale up connection pooler deployment
|
||||||
k8s.api.custom_objects_api.patch_namespaced_custom_object(
|
k8s.api.custom_objects_api.patch_namespaced_custom_object(
|
||||||
'acid.zalan.do', 'v1', 'default',
|
'acid.zalan.do', 'v1', 'default',
|
||||||
'postgresqls', 'acid-minimal-cluster',
|
'postgresqls', 'acid-minimal-cluster',
|
||||||
{
|
{
|
||||||
'spec': {
|
'spec': {
|
||||||
'connectionPool': {
|
'connectionPooler': {
|
||||||
'numberOfInstances': 2,
|
'numberOfInstances': 2,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -137,7 +137,7 @@ class EndToEndTestCase(unittest.TestCase):
|
||||||
'postgresqls', 'acid-minimal-cluster',
|
'postgresqls', 'acid-minimal-cluster',
|
||||||
{
|
{
|
||||||
'spec': {
|
'spec': {
|
||||||
'enableConnectionPool': False,
|
'enableConnectionPooler': False,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
k8s.wait_for_pods_to_stop(pod_selector)
|
k8s.wait_for_pods_to_stop(pod_selector)
|
||||||
|
|
@ -147,7 +147,7 @@ class EndToEndTestCase(unittest.TestCase):
|
||||||
'postgresqls', 'acid-minimal-cluster',
|
'postgresqls', 'acid-minimal-cluster',
|
||||||
{
|
{
|
||||||
'spec': {
|
'spec': {
|
||||||
'enableConnectionPool': True,
|
'enableConnectionPooler': True,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
k8s.wait_for_pod_start(pod_selector)
|
k8s.wait_for_pod_start(pod_selector)
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ spec:
|
||||||
- createdb
|
- createdb
|
||||||
enableMasterLoadBalancer: false
|
enableMasterLoadBalancer: false
|
||||||
enableReplicaLoadBalancer: false
|
enableReplicaLoadBalancer: false
|
||||||
|
# enableConnectionPooler: true # not needed when connectionPooler section is present (see below)
|
||||||
allowedSourceRanges: # load balancers' source ranges for both master and replica services
|
allowedSourceRanges: # load balancers' source ranges for both master and replica services
|
||||||
- 127.0.0.1/32
|
- 127.0.0.1/32
|
||||||
databases:
|
databases:
|
||||||
|
|
@ -85,6 +86,19 @@ spec:
|
||||||
# - 01:00-06:00 #UTC
|
# - 01:00-06:00 #UTC
|
||||||
# - Sat:00:00-04:00
|
# - Sat:00:00-04:00
|
||||||
|
|
||||||
|
connectionPooler:
|
||||||
|
numberOfInstances: 2
|
||||||
|
mode: "transaction"
|
||||||
|
schema: "pooler"
|
||||||
|
user: "pooler"
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 300m
|
||||||
|
memory: 100Mi
|
||||||
|
limits:
|
||||||
|
cpu: "1"
|
||||||
|
memory: 100Mi
|
||||||
|
|
||||||
initContainers:
|
initContainers:
|
||||||
- name: date
|
- name: date
|
||||||
image: busybox
|
image: busybox
|
||||||
|
|
|
||||||
|
|
@ -11,16 +11,16 @@ data:
|
||||||
cluster_history_entries: "1000"
|
cluster_history_entries: "1000"
|
||||||
cluster_labels: application:spilo
|
cluster_labels: application:spilo
|
||||||
cluster_name_label: cluster-name
|
cluster_name_label: cluster-name
|
||||||
# connection_pool_default_cpu_limit: "1"
|
# connection_pooler_default_cpu_limit: "1"
|
||||||
# connection_pool_default_cpu_request: "500m"
|
# connection_pooler_default_cpu_request: "500m"
|
||||||
# connection_pool_default_memory_limit: 100Mi
|
# connection_pooler_default_memory_limit: 100Mi
|
||||||
# connection_pool_default_memory_request: 100Mi
|
# connection_pooler_default_memory_request: 100Mi
|
||||||
connection_pool_image: "registry.opensource.zalan.do/acid/pgbouncer:master-5"
|
connection_pooler_image: "registry.opensource.zalan.do/acid/pgbouncer:master-5"
|
||||||
# connection_pool_max_db_connections: 60
|
# connection_pooler_max_db_connections: 60
|
||||||
# connection_pool_mode: "transaction"
|
# connection_pooler_mode: "transaction"
|
||||||
# connection_pool_number_of_instances: 2
|
# connection_pooler_number_of_instances: 2
|
||||||
# connection_pool_schema: "pooler"
|
# connection_pooler_schema: "pooler"
|
||||||
# connection_pool_user: "pooler"
|
# connection_pooler_user: "pooler"
|
||||||
# custom_service_annotations: "keyx:valuez,keya:valuea"
|
# custom_service_annotations: "keyx:valuez,keya:valuea"
|
||||||
# custom_pod_annotations: "keya:valuea,keyb:valueb"
|
# custom_pod_annotations: "keya:valuea,keyb:valueb"
|
||||||
db_hosted_zone: db.example.com
|
db_hosted_zone: db.example.com
|
||||||
|
|
|
||||||
|
|
@ -294,44 +294,44 @@ spec:
|
||||||
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
|
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
|
||||||
scalyr_server_url:
|
scalyr_server_url:
|
||||||
type: string
|
type: string
|
||||||
connection_pool:
|
connection_pooler:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
connection_pool_schema:
|
connection_pooler_schema:
|
||||||
type: string
|
type: string
|
||||||
#default: "pooler"
|
#default: "pooler"
|
||||||
connection_pool_user:
|
connection_pooler_user:
|
||||||
type: string
|
type: string
|
||||||
#default: "pooler"
|
#default: "pooler"
|
||||||
connection_pool_image:
|
connection_pooler_image:
|
||||||
type: string
|
type: string
|
||||||
#default: "registry.opensource.zalan.do/acid/pgbouncer"
|
#default: "registry.opensource.zalan.do/acid/pgbouncer"
|
||||||
connection_pool_max_db_connections:
|
connection_pooler_max_db_connections:
|
||||||
type: integer
|
type: integer
|
||||||
#default: 60
|
#default: 60
|
||||||
connection_pool_mode:
|
connection_pooler_mode:
|
||||||
type: string
|
type: string
|
||||||
enum:
|
enum:
|
||||||
- "session"
|
- "session"
|
||||||
- "transaction"
|
- "transaction"
|
||||||
#default: "transaction"
|
#default: "transaction"
|
||||||
connection_pool_number_of_instances:
|
connection_pooler_number_of_instances:
|
||||||
type: integer
|
type: integer
|
||||||
minimum: 2
|
minimum: 2
|
||||||
#default: 2
|
#default: 2
|
||||||
connection_pool_default_cpu_limit:
|
connection_pooler_default_cpu_limit:
|
||||||
type: string
|
type: string
|
||||||
pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
|
pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
|
||||||
#default: "1"
|
#default: "1"
|
||||||
connection_pool_default_cpu_request:
|
connection_pooler_default_cpu_request:
|
||||||
type: string
|
type: string
|
||||||
pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
|
pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
|
||||||
#default: "500m"
|
#default: "500m"
|
||||||
connection_pool_default_memory_limit:
|
connection_pooler_default_memory_limit:
|
||||||
type: string
|
type: string
|
||||||
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
|
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
|
||||||
#default: "100Mi"
|
#default: "100Mi"
|
||||||
connection_pool_default_memory_request:
|
connection_pooler_default_memory_request:
|
||||||
type: string
|
type: string
|
||||||
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
|
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
|
||||||
#default: "100Mi"
|
#default: "100Mi"
|
||||||
|
|
|
||||||
|
|
@ -121,14 +121,14 @@ configuration:
|
||||||
scalyr_memory_limit: 500Mi
|
scalyr_memory_limit: 500Mi
|
||||||
scalyr_memory_request: 50Mi
|
scalyr_memory_request: 50Mi
|
||||||
# scalyr_server_url: ""
|
# scalyr_server_url: ""
|
||||||
connection_pool:
|
connection_pooler:
|
||||||
connection_pool_default_cpu_limit: "1"
|
connection_pooler_default_cpu_limit: "1"
|
||||||
connection_pool_default_cpu_request: "500m"
|
connection_pooler_default_cpu_request: "500m"
|
||||||
connection_pool_default_memory_limit: 100Mi
|
connection_pooler_default_memory_limit: 100Mi
|
||||||
connection_pool_default_memory_request: 100Mi
|
connection_pooler_default_memory_request: 100Mi
|
||||||
connection_pool_image: "registry.opensource.zalan.do/acid/pgbouncer:master-5"
|
connection_pooler_image: "registry.opensource.zalan.do/acid/pgbouncer:master-5"
|
||||||
# connection_pool_max_db_connections: 60
|
# connection_pooler_max_db_connections: 60
|
||||||
connection_pool_mode: "transaction"
|
connection_pooler_mode: "transaction"
|
||||||
connection_pool_number_of_instances: 2
|
connection_pooler_number_of_instances: 2
|
||||||
# connection_pool_schema: "pooler"
|
# connection_pooler_schema: "pooler"
|
||||||
# connection_pool_user: "pooler"
|
# connection_pooler_user: "pooler"
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ spec:
|
||||||
uid:
|
uid:
|
||||||
format: uuid
|
format: uuid
|
||||||
type: string
|
type: string
|
||||||
connectionPool:
|
connectionPooler:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
dockerImage:
|
dockerImage:
|
||||||
|
|
@ -126,7 +126,7 @@ spec:
|
||||||
# Note: usernames specified here as database owners must be declared in the users key of the spec key.
|
# Note: usernames specified here as database owners must be declared in the users key of the spec key.
|
||||||
dockerImage:
|
dockerImage:
|
||||||
type: string
|
type: string
|
||||||
enableConnectionPool:
|
enableConnectionPooler:
|
||||||
type: boolean
|
type: boolean
|
||||||
enableLogicalBackup:
|
enableLogicalBackup:
|
||||||
type: boolean
|
type: boolean
|
||||||
|
|
|
||||||
|
|
@ -177,7 +177,7 @@ var PostgresCRDResourceValidation = apiextv1beta1.CustomResourceValidation{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"connectionPool": {
|
"connectionPooler": {
|
||||||
Type: "object",
|
Type: "object",
|
||||||
Properties: map[string]apiextv1beta1.JSONSchemaProps{
|
Properties: map[string]apiextv1beta1.JSONSchemaProps{
|
||||||
"dockerImage": {
|
"dockerImage": {
|
||||||
|
|
@ -259,7 +259,7 @@ var PostgresCRDResourceValidation = apiextv1beta1.CustomResourceValidation{
|
||||||
"dockerImage": {
|
"dockerImage": {
|
||||||
Type: "string",
|
Type: "string",
|
||||||
},
|
},
|
||||||
"enableConnectionPool": {
|
"enableConnectionPooler": {
|
||||||
Type: "boolean",
|
Type: "boolean",
|
||||||
},
|
},
|
||||||
"enableLogicalBackup": {
|
"enableLogicalBackup": {
|
||||||
|
|
@ -1129,32 +1129,32 @@ var OperatorConfigCRDResourceValidation = apiextv1beta1.CustomResourceValidation
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"connection_pool": {
|
"connection_pooler": {
|
||||||
Type: "object",
|
Type: "object",
|
||||||
Properties: map[string]apiextv1beta1.JSONSchemaProps{
|
Properties: map[string]apiextv1beta1.JSONSchemaProps{
|
||||||
"connection_pool_default_cpu_limit": {
|
"connection_pooler_default_cpu_limit": {
|
||||||
Type: "string",
|
Type: "string",
|
||||||
Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$",
|
Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$",
|
||||||
},
|
},
|
||||||
"connection_pool_default_cpu_request": {
|
"connection_pooler_default_cpu_request": {
|
||||||
Type: "string",
|
Type: "string",
|
||||||
Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$",
|
Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$",
|
||||||
},
|
},
|
||||||
"connection_pool_default_memory_limit": {
|
"connection_pooler_default_memory_limit": {
|
||||||
Type: "string",
|
Type: "string",
|
||||||
Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$",
|
Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$",
|
||||||
},
|
},
|
||||||
"connection_pool_default_memory_request": {
|
"connection_pooler_default_memory_request": {
|
||||||
Type: "string",
|
Type: "string",
|
||||||
Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$",
|
Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$",
|
||||||
},
|
},
|
||||||
"connection_pool_image": {
|
"connection_pooler_image": {
|
||||||
Type: "string",
|
Type: "string",
|
||||||
},
|
},
|
||||||
"connection_pool_max_db_connections": {
|
"connection_pooler_max_db_connections": {
|
||||||
Type: "integer",
|
Type: "integer",
|
||||||
},
|
},
|
||||||
"connection_pool_mode": {
|
"connection_pooler_mode": {
|
||||||
Type: "string",
|
Type: "string",
|
||||||
Enum: []apiextv1beta1.JSON{
|
Enum: []apiextv1beta1.JSON{
|
||||||
{
|
{
|
||||||
|
|
@ -1165,14 +1165,14 @@ var OperatorConfigCRDResourceValidation = apiextv1beta1.CustomResourceValidation
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"connection_pool_number_of_instances": {
|
"connection_pooler_number_of_instances": {
|
||||||
Type: "integer",
|
Type: "integer",
|
||||||
Minimum: &min2,
|
Minimum: &min2,
|
||||||
},
|
},
|
||||||
"connection_pool_schema": {
|
"connection_pooler_schema": {
|
||||||
Type: "string",
|
Type: "string",
|
||||||
},
|
},
|
||||||
"connection_pool_user": {
|
"connection_pooler_user": {
|
||||||
Type: "string",
|
Type: "string",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -153,18 +153,18 @@ type ScalyrConfiguration struct {
|
||||||
ScalyrMemoryLimit string `json:"scalyr_memory_limit,omitempty"`
|
ScalyrMemoryLimit string `json:"scalyr_memory_limit,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Defines default configuration for connection pool
|
// Defines default configuration for connection pooler
|
||||||
type ConnectionPoolConfiguration struct {
|
type ConnectionPoolerConfiguration struct {
|
||||||
NumberOfInstances *int32 `json:"connection_pool_number_of_instances,omitempty"`
|
NumberOfInstances *int32 `json:"connection_pooler_number_of_instances,omitempty"`
|
||||||
Schema string `json:"connection_pool_schema,omitempty"`
|
Schema string `json:"connection_pooler_schema,omitempty"`
|
||||||
User string `json:"connection_pool_user,omitempty"`
|
User string `json:"connection_pooler_user,omitempty"`
|
||||||
Image string `json:"connection_pool_image,omitempty"`
|
Image string `json:"connection_pooler_image,omitempty"`
|
||||||
Mode string `json:"connection_pool_mode,omitempty"`
|
Mode string `json:"connection_pooler_mode,omitempty"`
|
||||||
MaxDBConnections *int32 `json:"connection_pool_max_db_connections,omitempty"`
|
MaxDBConnections *int32 `json:"connection_pooler_max_db_connections,omitempty"`
|
||||||
DefaultCPURequest string `json:"connection_pool_default_cpu_request,omitempty"`
|
DefaultCPURequest string `json:"connection_pooler_default_cpu_request,omitempty"`
|
||||||
DefaultMemoryRequest string `json:"connection_pool_default_memory_request,omitempty"`
|
DefaultMemoryRequest string `json:"connection_pooler_default_memory_request,omitempty"`
|
||||||
DefaultCPULimit string `json:"connection_pool_default_cpu_limit,omitempty"`
|
DefaultCPULimit string `json:"connection_pooler_default_cpu_limit,omitempty"`
|
||||||
DefaultMemoryLimit string `json:"connection_pool_default_memory_limit,omitempty"`
|
DefaultMemoryLimit string `json:"connection_pooler_default_memory_limit,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// OperatorLogicalBackupConfiguration defines configuration for logical backup
|
// OperatorLogicalBackupConfiguration defines configuration for logical backup
|
||||||
|
|
@ -203,7 +203,7 @@ type OperatorConfigurationData struct {
|
||||||
LoggingRESTAPI LoggingRESTAPIConfiguration `json:"logging_rest_api"`
|
LoggingRESTAPI LoggingRESTAPIConfiguration `json:"logging_rest_api"`
|
||||||
Scalyr ScalyrConfiguration `json:"scalyr"`
|
Scalyr ScalyrConfiguration `json:"scalyr"`
|
||||||
LogicalBackup OperatorLogicalBackupConfiguration `json:"logical_backup"`
|
LogicalBackup OperatorLogicalBackupConfiguration `json:"logical_backup"`
|
||||||
ConnectionPool ConnectionPoolConfiguration `json:"connection_pool"`
|
ConnectionPooler ConnectionPoolerConfiguration `json:"connection_pooler"`
|
||||||
}
|
}
|
||||||
|
|
||||||
//Duration shortens this frequently used name
|
//Duration shortens this frequently used name
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,8 @@ type PostgresSpec struct {
|
||||||
Patroni `json:"patroni,omitempty"`
|
Patroni `json:"patroni,omitempty"`
|
||||||
Resources `json:"resources,omitempty"`
|
Resources `json:"resources,omitempty"`
|
||||||
|
|
||||||
EnableConnectionPool *bool `json:"enableConnectionPool,omitempty"`
|
EnableConnectionPooler *bool `json:"enableConnectionPooler,omitempty"`
|
||||||
ConnectionPool *ConnectionPool `json:"connectionPool,omitempty"`
|
ConnectionPooler *ConnectionPooler `json:"connectionPooler,omitempty"`
|
||||||
|
|
||||||
TeamID string `json:"teamId"`
|
TeamID string `json:"teamId"`
|
||||||
DockerImage string `json:"dockerImage,omitempty"`
|
DockerImage string `json:"dockerImage,omitempty"`
|
||||||
|
|
@ -175,10 +175,10 @@ type PostgresStatus struct {
|
||||||
// resources)
|
// resources)
|
||||||
// Type string `json:"type,omitempty"`
|
// Type string `json:"type,omitempty"`
|
||||||
//
|
//
|
||||||
// TODO: figure out what other important parameters of the connection pool it
|
// TODO: figure out what other important parameters of the connection pooler it
|
||||||
// makes sense to expose. E.g. pool size (min/max boundaries), max client
|
// makes sense to expose. E.g. pool size (min/max boundaries), max client
|
||||||
// connections etc.
|
// connections etc.
|
||||||
type ConnectionPool struct {
|
type ConnectionPooler struct {
|
||||||
NumberOfInstances *int32 `json:"numberOfInstances,omitempty"`
|
NumberOfInstances *int32 `json:"numberOfInstances,omitempty"`
|
||||||
Schema string `json:"schema,omitempty"`
|
Schema string `json:"schema,omitempty"`
|
||||||
User string `json:"user,omitempty"`
|
User string `json:"user,omitempty"`
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ func (in *CloneDescription) DeepCopy() *CloneDescription {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *ConnectionPool) DeepCopyInto(out *ConnectionPool) {
|
func (in *ConnectionPooler) DeepCopyInto(out *ConnectionPooler) {
|
||||||
*out = *in
|
*out = *in
|
||||||
if in.NumberOfInstances != nil {
|
if in.NumberOfInstances != nil {
|
||||||
in, out := &in.NumberOfInstances, &out.NumberOfInstances
|
in, out := &in.NumberOfInstances, &out.NumberOfInstances
|
||||||
|
|
@ -85,18 +85,18 @@ func (in *ConnectionPool) DeepCopyInto(out *ConnectionPool) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConnectionPool.
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConnectionPooler.
|
||||||
func (in *ConnectionPool) DeepCopy() *ConnectionPool {
|
func (in *ConnectionPooler) DeepCopy() *ConnectionPooler {
|
||||||
if in == nil {
|
if in == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
out := new(ConnectionPool)
|
out := new(ConnectionPooler)
|
||||||
in.DeepCopyInto(out)
|
in.DeepCopyInto(out)
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *ConnectionPoolConfiguration) DeepCopyInto(out *ConnectionPoolConfiguration) {
|
func (in *ConnectionPoolerConfiguration) DeepCopyInto(out *ConnectionPoolerConfiguration) {
|
||||||
*out = *in
|
*out = *in
|
||||||
if in.NumberOfInstances != nil {
|
if in.NumberOfInstances != nil {
|
||||||
in, out := &in.NumberOfInstances, &out.NumberOfInstances
|
in, out := &in.NumberOfInstances, &out.NumberOfInstances
|
||||||
|
|
@ -111,12 +111,12 @@ func (in *ConnectionPoolConfiguration) DeepCopyInto(out *ConnectionPoolConfigura
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConnectionPoolConfiguration.
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConnectionPoolerConfiguration.
|
||||||
func (in *ConnectionPoolConfiguration) DeepCopy() *ConnectionPoolConfiguration {
|
func (in *ConnectionPoolerConfiguration) DeepCopy() *ConnectionPoolerConfiguration {
|
||||||
if in == nil {
|
if in == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
out := new(ConnectionPoolConfiguration)
|
out := new(ConnectionPoolerConfiguration)
|
||||||
in.DeepCopyInto(out)
|
in.DeepCopyInto(out)
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
@ -308,7 +308,7 @@ func (in *OperatorConfigurationData) DeepCopyInto(out *OperatorConfigurationData
|
||||||
out.LoggingRESTAPI = in.LoggingRESTAPI
|
out.LoggingRESTAPI = in.LoggingRESTAPI
|
||||||
out.Scalyr = in.Scalyr
|
out.Scalyr = in.Scalyr
|
||||||
out.LogicalBackup = in.LogicalBackup
|
out.LogicalBackup = in.LogicalBackup
|
||||||
in.ConnectionPool.DeepCopyInto(&out.ConnectionPool)
|
in.ConnectionPooler.DeepCopyInto(&out.ConnectionPooler)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -471,14 +471,14 @@ func (in *PostgresSpec) DeepCopyInto(out *PostgresSpec) {
|
||||||
out.Volume = in.Volume
|
out.Volume = in.Volume
|
||||||
in.Patroni.DeepCopyInto(&out.Patroni)
|
in.Patroni.DeepCopyInto(&out.Patroni)
|
||||||
out.Resources = in.Resources
|
out.Resources = in.Resources
|
||||||
if in.EnableConnectionPool != nil {
|
if in.EnableConnectionPooler != nil {
|
||||||
in, out := &in.EnableConnectionPool, &out.EnableConnectionPool
|
in, out := &in.EnableConnectionPooler, &out.EnableConnectionPooler
|
||||||
*out = new(bool)
|
*out = new(bool)
|
||||||
**out = **in
|
**out = **in
|
||||||
}
|
}
|
||||||
if in.ConnectionPool != nil {
|
if in.ConnectionPooler != nil {
|
||||||
in, out := &in.ConnectionPool, &out.ConnectionPool
|
in, out := &in.ConnectionPooler, &out.ConnectionPooler
|
||||||
*out = new(ConnectionPool)
|
*out = new(ConnectionPooler)
|
||||||
(*in).DeepCopyInto(*out)
|
(*in).DeepCopyInto(*out)
|
||||||
}
|
}
|
||||||
if in.SpiloFSGroup != nil {
|
if in.SpiloFSGroup != nil {
|
||||||
|
|
|
||||||
|
|
@ -50,14 +50,14 @@ type Config struct {
|
||||||
PodServiceAccountRoleBinding *rbacv1.RoleBinding
|
PodServiceAccountRoleBinding *rbacv1.RoleBinding
|
||||||
}
|
}
|
||||||
|
|
||||||
// K8S objects that are belongs to a connection pool
|
// K8S objects that are belongs to a connection pooler
|
||||||
type ConnectionPoolObjects struct {
|
type ConnectionPoolerObjects struct {
|
||||||
Deployment *appsv1.Deployment
|
Deployment *appsv1.Deployment
|
||||||
Service *v1.Service
|
Service *v1.Service
|
||||||
|
|
||||||
// It could happen that a connection pool was enabled, but the operator was
|
// It could happen that a connection pooler was enabled, but the operator
|
||||||
// not able to properly process a corresponding event or was restarted. In
|
// was not able to properly process a corresponding event or was restarted.
|
||||||
// this case we will miss missing/require situation and a lookup function
|
// In this case we will miss missing/require situation and a lookup function
|
||||||
// will not be installed. To avoid synchronizing it all the time to prevent
|
// will not be installed. To avoid synchronizing it all the time to prevent
|
||||||
// this, we can remember the result in memory at least until the next
|
// this, we can remember the result in memory at least until the next
|
||||||
// restart.
|
// restart.
|
||||||
|
|
@ -69,7 +69,7 @@ type kubeResources struct {
|
||||||
Endpoints map[PostgresRole]*v1.Endpoints
|
Endpoints map[PostgresRole]*v1.Endpoints
|
||||||
Secrets map[types.UID]*v1.Secret
|
Secrets map[types.UID]*v1.Secret
|
||||||
Statefulset *appsv1.StatefulSet
|
Statefulset *appsv1.StatefulSet
|
||||||
ConnectionPool *ConnectionPoolObjects
|
ConnectionPooler *ConnectionPoolerObjects
|
||||||
PodDisruptionBudget *policybeta1.PodDisruptionBudget
|
PodDisruptionBudget *policybeta1.PodDisruptionBudget
|
||||||
//Pods are treated separately
|
//Pods are treated separately
|
||||||
//PVCs are treated separately
|
//PVCs are treated separately
|
||||||
|
|
@ -337,24 +337,24 @@ func (c *Cluster) Create() error {
|
||||||
c.logger.Errorf("could not list resources: %v", err)
|
c.logger.Errorf("could not list resources: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create connection pool deployment and services if necessary. Since we
|
// Create connection pooler deployment and services if necessary. Since we
|
||||||
// need to peform some operations with the database itself (e.g. install
|
// need to perform some operations with the database itself (e.g. install
|
||||||
// lookup function), do it as the last step, when everything is available.
|
// lookup function), do it as the last step, when everything is available.
|
||||||
//
|
//
|
||||||
// Do not consider connection pool as a strict requirement, and if
|
// Do not consider connection pooler as a strict requirement, and if
|
||||||
// something fails, report warning
|
// something fails, report warning
|
||||||
if c.needConnectionPool() {
|
if c.needConnectionPooler() {
|
||||||
if c.ConnectionPool != nil {
|
if c.ConnectionPooler != nil {
|
||||||
c.logger.Warning("Connection pool already exists in the cluster")
|
c.logger.Warning("Connection pooler already exists in the cluster")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
connPool, err := c.createConnectionPool(c.installLookupFunction)
|
connectionPooler, err := c.createConnectionPooler(c.installLookupFunction)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Warningf("could not create connection pool: %v", err)
|
c.logger.Warningf("could not create connection pooler: %v", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
c.logger.Infof("connection pool %q has been successfully created",
|
c.logger.Infof("connection pooler %q has been successfully created",
|
||||||
util.NameFromMeta(connPool.Deployment.ObjectMeta))
|
util.NameFromMeta(connectionPooler.Deployment.ObjectMeta))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -612,11 +612,11 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// connection pool needs one system user created, which is done in
|
// connection pooler needs one system user created, which is done in
|
||||||
// initUsers. Check if it needs to be called.
|
// initUsers. Check if it needs to be called.
|
||||||
sameUsers := reflect.DeepEqual(oldSpec.Spec.Users, newSpec.Spec.Users)
|
sameUsers := reflect.DeepEqual(oldSpec.Spec.Users, newSpec.Spec.Users)
|
||||||
needConnPool := c.needConnectionPoolWorker(&newSpec.Spec)
|
needConnectionPooler := c.needConnectionPoolerWorker(&newSpec.Spec)
|
||||||
if !sameUsers || needConnPool {
|
if !sameUsers || needConnectionPooler {
|
||||||
c.logger.Debugf("syncing secrets")
|
c.logger.Debugf("syncing secrets")
|
||||||
if err := c.initUsers(); err != nil {
|
if err := c.initUsers(); err != nil {
|
||||||
c.logger.Errorf("could not init users: %v", err)
|
c.logger.Errorf("could not init users: %v", err)
|
||||||
|
|
@ -740,9 +740,9 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sync connection pool
|
// sync connection pooler
|
||||||
if err := c.syncConnectionPool(oldSpec, newSpec, c.installLookupFunction); err != nil {
|
if err := c.syncConnectionPooler(oldSpec, newSpec, c.installLookupFunction); err != nil {
|
||||||
return fmt.Errorf("could not sync connection pool: %v", err)
|
return fmt.Errorf("could not sync connection pooler: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -796,11 +796,11 @@ func (c *Cluster) Delete() {
|
||||||
c.logger.Warningf("could not remove leftover patroni objects; %v", err)
|
c.logger.Warningf("could not remove leftover patroni objects; %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete connection pool objects anyway, even if it's not mentioned in the
|
// Delete connection pooler objects anyway, even if it's not mentioned in the
|
||||||
// manifest, just to not keep orphaned components in case if something went
|
// manifest, just to not keep orphaned components in case if something went
|
||||||
// wrong
|
// wrong
|
||||||
if err := c.deleteConnectionPool(); err != nil {
|
if err := c.deleteConnectionPooler(); err != nil {
|
||||||
c.logger.Warningf("could not remove connection pool: %v", err)
|
c.logger.Warningf("could not remove connection pooler: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -869,32 +869,32 @@ func (c *Cluster) initSystemUsers() {
|
||||||
Password: util.RandomPassword(constants.PasswordLength),
|
Password: util.RandomPassword(constants.PasswordLength),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connection pool user is an exception, if requested it's going to be
|
// Connection pooler user is an exception, if requested it's going to be
|
||||||
// created by operator as a normal pgUser
|
// created by operator as a normal pgUser
|
||||||
if c.needConnectionPool() {
|
if c.needConnectionPooler() {
|
||||||
// initialize empty connection pool if not done yet
|
// initialize empty connection pooler if not done yet
|
||||||
if c.Spec.ConnectionPool == nil {
|
if c.Spec.ConnectionPooler == nil {
|
||||||
c.Spec.ConnectionPool = &acidv1.ConnectionPool{}
|
c.Spec.ConnectionPooler = &acidv1.ConnectionPooler{}
|
||||||
}
|
}
|
||||||
|
|
||||||
username := util.Coalesce(
|
username := util.Coalesce(
|
||||||
c.Spec.ConnectionPool.User,
|
c.Spec.ConnectionPooler.User,
|
||||||
c.OpConfig.ConnectionPool.User)
|
c.OpConfig.ConnectionPooler.User)
|
||||||
|
|
||||||
// connection pooler application should be able to login with this role
|
// connection pooler application should be able to login with this role
|
||||||
connPoolUser := spec.PgUser{
|
connectionPoolerUser := spec.PgUser{
|
||||||
Origin: spec.RoleConnectionPool,
|
Origin: spec.RoleConnectionPooler,
|
||||||
Name: username,
|
Name: username,
|
||||||
Flags: []string{constants.RoleFlagLogin},
|
Flags: []string{constants.RoleFlagLogin},
|
||||||
Password: util.RandomPassword(constants.PasswordLength),
|
Password: util.RandomPassword(constants.PasswordLength),
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, exists := c.pgUsers[username]; !exists {
|
if _, exists := c.pgUsers[username]; !exists {
|
||||||
c.pgUsers[username] = connPoolUser
|
c.pgUsers[username] = connectionPoolerUser
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, exists := c.systemUsers[constants.ConnectionPoolUserKeyName]; !exists {
|
if _, exists := c.systemUsers[constants.ConnectionPoolerUserKeyName]; !exists {
|
||||||
c.systemUsers[constants.ConnectionPoolUserKeyName] = connPoolUser
|
c.systemUsers[constants.ConnectionPoolerUserKeyName] = connectionPoolerUser
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1224,10 +1224,10 @@ func (c *Cluster) deletePatroniClusterConfigMaps() error {
|
||||||
return c.deleteClusterObject(get, deleteConfigMapFn, "configmap")
|
return c.deleteClusterObject(get, deleteConfigMapFn, "configmap")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test if two connection pool configuration needs to be synced. For simplicity
|
// Test if two connection pooler configuration needs to be synced. For simplicity
|
||||||
// compare not the actual K8S objects, but the configuration itself and request
|
// compare not the actual K8S objects, but the configuration itself and request
|
||||||
// sync if there is any difference.
|
// sync if there is any difference.
|
||||||
func (c *Cluster) needSyncConnPoolSpecs(oldSpec, newSpec *acidv1.ConnectionPool) (sync bool, reasons []string) {
|
func (c *Cluster) needSyncConnectionPoolerSpecs(oldSpec, newSpec *acidv1.ConnectionPooler) (sync bool, reasons []string) {
|
||||||
reasons = []string{}
|
reasons = []string{}
|
||||||
sync = false
|
sync = false
|
||||||
|
|
||||||
|
|
@ -1264,21 +1264,21 @@ func syncResources(a, b *v1.ResourceRequirements) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we need to synchronize connection pool deployment due to new
|
// Check if we need to synchronize connection pooler deployment due to new
|
||||||
// defaults, that are different from what we see in the DeploymentSpec
|
// defaults, that are different from what we see in the DeploymentSpec
|
||||||
func (c *Cluster) needSyncConnPoolDefaults(
|
func (c *Cluster) needSyncConnectionPoolerDefaults(
|
||||||
spec *acidv1.ConnectionPool,
|
spec *acidv1.ConnectionPooler,
|
||||||
deployment *appsv1.Deployment) (sync bool, reasons []string) {
|
deployment *appsv1.Deployment) (sync bool, reasons []string) {
|
||||||
|
|
||||||
reasons = []string{}
|
reasons = []string{}
|
||||||
sync = false
|
sync = false
|
||||||
|
|
||||||
config := c.OpConfig.ConnectionPool
|
config := c.OpConfig.ConnectionPooler
|
||||||
podTemplate := deployment.Spec.Template
|
podTemplate := deployment.Spec.Template
|
||||||
poolContainer := podTemplate.Spec.Containers[constants.ConnPoolContainer]
|
poolerContainer := podTemplate.Spec.Containers[constants.ConnectionPoolerContainer]
|
||||||
|
|
||||||
if spec == nil {
|
if spec == nil {
|
||||||
spec = &acidv1.ConnectionPool{}
|
spec = &acidv1.ConnectionPooler{}
|
||||||
}
|
}
|
||||||
|
|
||||||
if spec.NumberOfInstances == nil &&
|
if spec.NumberOfInstances == nil &&
|
||||||
|
|
@ -1291,25 +1291,25 @@ func (c *Cluster) needSyncConnPoolDefaults(
|
||||||
}
|
}
|
||||||
|
|
||||||
if spec.DockerImage == "" &&
|
if spec.DockerImage == "" &&
|
||||||
poolContainer.Image != config.Image {
|
poolerContainer.Image != config.Image {
|
||||||
|
|
||||||
sync = true
|
sync = true
|
||||||
msg := fmt.Sprintf("DockerImage is different (having %s, required %s)",
|
msg := fmt.Sprintf("DockerImage is different (having %s, required %s)",
|
||||||
poolContainer.Image, config.Image)
|
poolerContainer.Image, config.Image)
|
||||||
reasons = append(reasons, msg)
|
reasons = append(reasons, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedResources, err := generateResourceRequirements(spec.Resources,
|
expectedResources, err := generateResourceRequirements(spec.Resources,
|
||||||
c.makeDefaultConnPoolResources())
|
c.makeDefaultConnectionPoolerResources())
|
||||||
|
|
||||||
// An error to generate expected resources means something is not quite
|
// An error to generate expected resources means something is not quite
|
||||||
// right, but for the purpose of robustness do not panic here, just report
|
// right, but for the purpose of robustness do not panic here, just report
|
||||||
// and ignore resources comparison (in the worst case there will be no
|
// and ignore resources comparison (in the worst case there will be no
|
||||||
// updates for new resource values).
|
// updates for new resource values).
|
||||||
if err == nil && syncResources(&poolContainer.Resources, expectedResources) {
|
if err == nil && syncResources(&poolerContainer.Resources, expectedResources) {
|
||||||
sync = true
|
sync = true
|
||||||
msg := fmt.Sprintf("Resources are different (having %+v, required %+v)",
|
msg := fmt.Sprintf("Resources are different (having %+v, required %+v)",
|
||||||
poolContainer.Resources, expectedResources)
|
poolerContainer.Resources, expectedResources)
|
||||||
reasons = append(reasons, msg)
|
reasons = append(reasons, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1317,13 +1317,13 @@ func (c *Cluster) needSyncConnPoolDefaults(
|
||||||
c.logger.Warningf("Cannot generate expected resources, %v", err)
|
c.logger.Warningf("Cannot generate expected resources, %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, env := range poolContainer.Env {
|
for _, env := range poolerContainer.Env {
|
||||||
if spec.User == "" && env.Name == "PGUSER" {
|
if spec.User == "" && env.Name == "PGUSER" {
|
||||||
ref := env.ValueFrom.SecretKeyRef.LocalObjectReference
|
ref := env.ValueFrom.SecretKeyRef.LocalObjectReference
|
||||||
|
|
||||||
if ref.Name != c.credentialSecretName(config.User) {
|
if ref.Name != c.credentialSecretName(config.User) {
|
||||||
sync = true
|
sync = true
|
||||||
msg := fmt.Sprintf("Pool user is different (having %s, required %s)",
|
msg := fmt.Sprintf("pooler user is different (having %s, required %s)",
|
||||||
ref.Name, config.User)
|
ref.Name, config.User)
|
||||||
reasons = append(reasons, msg)
|
reasons = append(reasons, msg)
|
||||||
}
|
}
|
||||||
|
|
@ -1331,7 +1331,7 @@ func (c *Cluster) needSyncConnPoolDefaults(
|
||||||
|
|
||||||
if spec.Schema == "" && env.Name == "PGSCHEMA" && env.Value != config.Schema {
|
if spec.Schema == "" && env.Name == "PGSCHEMA" && env.Value != config.Schema {
|
||||||
sync = true
|
sync = true
|
||||||
msg := fmt.Sprintf("Pool schema is different (having %s, required %s)",
|
msg := fmt.Sprintf("pooler schema is different (having %s, required %s)",
|
||||||
env.Value, config.Schema)
|
env.Value, config.Schema)
|
||||||
reasons = append(reasons, msg)
|
reasons = append(reasons, msg)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -709,16 +709,16 @@ func TestServiceAnnotations(t *testing.T) {
|
||||||
func TestInitSystemUsers(t *testing.T) {
|
func TestInitSystemUsers(t *testing.T) {
|
||||||
testName := "Test system users initialization"
|
testName := "Test system users initialization"
|
||||||
|
|
||||||
// default cluster without connection pool
|
// default cluster without connection pooler
|
||||||
cl.initSystemUsers()
|
cl.initSystemUsers()
|
||||||
if _, exist := cl.systemUsers[constants.ConnectionPoolUserKeyName]; exist {
|
if _, exist := cl.systemUsers[constants.ConnectionPoolerUserKeyName]; exist {
|
||||||
t.Errorf("%s, connection pool user is present", testName)
|
t.Errorf("%s, connection pooler user is present", testName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// cluster with connection pool
|
// cluster with connection pooler
|
||||||
cl.Spec.EnableConnectionPool = boolToPointer(true)
|
cl.Spec.EnableConnectionPooler = boolToPointer(true)
|
||||||
cl.initSystemUsers()
|
cl.initSystemUsers()
|
||||||
if _, exist := cl.systemUsers[constants.ConnectionPoolUserKeyName]; !exist {
|
if _, exist := cl.systemUsers[constants.ConnectionPoolerUserKeyName]; !exist {
|
||||||
t.Errorf("%s, connection pool user is not present", testName)
|
t.Errorf("%s, connection pooler user is not present", testName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,10 +30,10 @@ const (
|
||||||
getDatabasesSQL = `SELECT datname, pg_get_userbyid(datdba) AS owner FROM pg_database;`
|
getDatabasesSQL = `SELECT datname, pg_get_userbyid(datdba) AS owner FROM pg_database;`
|
||||||
createDatabaseSQL = `CREATE DATABASE "%s" OWNER "%s";`
|
createDatabaseSQL = `CREATE DATABASE "%s" OWNER "%s";`
|
||||||
alterDatabaseOwnerSQL = `ALTER DATABASE "%s" OWNER TO "%s";`
|
alterDatabaseOwnerSQL = `ALTER DATABASE "%s" OWNER TO "%s";`
|
||||||
connectionPoolLookup = `
|
connectionPoolerLookup = `
|
||||||
CREATE SCHEMA IF NOT EXISTS {{.pool_schema}};
|
CREATE SCHEMA IF NOT EXISTS {{.pooler_schema}};
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION {{.pool_schema}}.user_lookup(
|
CREATE OR REPLACE FUNCTION {{.pooler_schema}}.user_lookup(
|
||||||
in i_username text, out uname text, out phash text)
|
in i_username text, out uname text, out phash text)
|
||||||
RETURNS record AS $$
|
RETURNS record AS $$
|
||||||
BEGIN
|
BEGIN
|
||||||
|
|
@ -43,11 +43,11 @@ const (
|
||||||
END;
|
END;
|
||||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||||
|
|
||||||
REVOKE ALL ON FUNCTION {{.pool_schema}}.user_lookup(text)
|
REVOKE ALL ON FUNCTION {{.pooler_schema}}.user_lookup(text)
|
||||||
FROM public, {{.pool_user}};
|
FROM public, {{.pooler_user}};
|
||||||
GRANT EXECUTE ON FUNCTION {{.pool_schema}}.user_lookup(text)
|
GRANT EXECUTE ON FUNCTION {{.pooler_schema}}.user_lookup(text)
|
||||||
TO {{.pool_user}};
|
TO {{.pooler_user}};
|
||||||
GRANT USAGE ON SCHEMA {{.pool_schema}} TO {{.pool_user}};
|
GRANT USAGE ON SCHEMA {{.pooler_schema}} TO {{.pooler_user}};
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -278,9 +278,9 @@ func makeUserFlags(rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a connection pool credentials lookup function in every database to
|
// Creates a connection pooler credentials lookup function in every database to
|
||||||
// perform remote authentification.
|
// perform remote authentication.
|
||||||
func (c *Cluster) installLookupFunction(poolSchema, poolUser string) error {
|
func (c *Cluster) installLookupFunction(poolerSchema, poolerUser string) error {
|
||||||
var stmtBytes bytes.Buffer
|
var stmtBytes bytes.Buffer
|
||||||
c.logger.Info("Installing lookup function")
|
c.logger.Info("Installing lookup function")
|
||||||
|
|
||||||
|
|
@ -299,11 +299,11 @@ func (c *Cluster) installLookupFunction(poolSchema, poolUser string) error {
|
||||||
|
|
||||||
currentDatabases, err := c.getDatabases()
|
currentDatabases, err := c.getDatabases()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg := "could not get databases to install pool lookup function: %v"
|
msg := "could not get databases to install pooler lookup function: %v"
|
||||||
return fmt.Errorf(msg, err)
|
return fmt.Errorf(msg, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
templater := template.Must(template.New("sql").Parse(connectionPoolLookup))
|
templater := template.Must(template.New("sql").Parse(connectionPoolerLookup))
|
||||||
|
|
||||||
for dbname, _ := range currentDatabases {
|
for dbname, _ := range currentDatabases {
|
||||||
if dbname == "template0" || dbname == "template1" {
|
if dbname == "template0" || dbname == "template1" {
|
||||||
|
|
@ -314,11 +314,11 @@ func (c *Cluster) installLookupFunction(poolSchema, poolUser string) error {
|
||||||
return fmt.Errorf("could not init database connection to %s", dbname)
|
return fmt.Errorf("could not init database connection to %s", dbname)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.logger.Infof("Install pool lookup function into %s", dbname)
|
c.logger.Infof("Install pooler lookup function into %s", dbname)
|
||||||
|
|
||||||
params := TemplateParams{
|
params := TemplateParams{
|
||||||
"pool_schema": poolSchema,
|
"pooler_schema": poolerSchema,
|
||||||
"pool_user": poolUser,
|
"pooler_user": poolerUser,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := templater.Execute(&stmtBytes, params); err != nil {
|
if err := templater.Execute(&stmtBytes, params); err != nil {
|
||||||
|
|
@ -353,12 +353,12 @@ func (c *Cluster) installLookupFunction(poolSchema, poolUser string) error {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
c.logger.Infof("Pool lookup function installed into %s", dbname)
|
c.logger.Infof("pooler lookup function installed into %s", dbname)
|
||||||
if err := c.closeDbConn(); err != nil {
|
if err := c.closeDbConn(); err != nil {
|
||||||
c.logger.Errorf("could not close database connection: %v", err)
|
c.logger.Errorf("could not close database connection: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ConnectionPool.LookupFunction = true
|
c.ConnectionPooler.LookupFunction = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ const (
|
||||||
patroniPGParametersParameterName = "parameters"
|
patroniPGParametersParameterName = "parameters"
|
||||||
patroniPGHBAConfParameterName = "pg_hba"
|
patroniPGHBAConfParameterName = "pg_hba"
|
||||||
localHost = "127.0.0.1/32"
|
localHost = "127.0.0.1/32"
|
||||||
connectionPoolContainer = "connection-pool"
|
connectionPoolerContainer = "connection-pooler"
|
||||||
pgPort = 5432
|
pgPort = 5432
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -72,7 +72,7 @@ func (c *Cluster) statefulSetName() string {
|
||||||
return c.Name
|
return c.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cluster) connPoolName() string {
|
func (c *Cluster) connectionPoolerName() string {
|
||||||
return c.Name + "-pooler"
|
return c.Name + "-pooler"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -139,18 +139,18 @@ func (c *Cluster) makeDefaultResources() acidv1.Resources {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate default resource section for connection pool deployment, to be used
|
// Generate default resource section for connection pooler deployment, to be
|
||||||
// if nothing custom is specified in the manifest
|
// used if nothing custom is specified in the manifest
|
||||||
func (c *Cluster) makeDefaultConnPoolResources() acidv1.Resources {
|
func (c *Cluster) makeDefaultConnectionPoolerResources() acidv1.Resources {
|
||||||
config := c.OpConfig
|
config := c.OpConfig
|
||||||
|
|
||||||
defaultRequests := acidv1.ResourceDescription{
|
defaultRequests := acidv1.ResourceDescription{
|
||||||
CPU: config.ConnectionPool.ConnPoolDefaultCPURequest,
|
CPU: config.ConnectionPooler.ConnectionPoolerDefaultCPURequest,
|
||||||
Memory: config.ConnectionPool.ConnPoolDefaultMemoryRequest,
|
Memory: config.ConnectionPooler.ConnectionPoolerDefaultMemoryRequest,
|
||||||
}
|
}
|
||||||
defaultLimits := acidv1.ResourceDescription{
|
defaultLimits := acidv1.ResourceDescription{
|
||||||
CPU: config.ConnectionPool.ConnPoolDefaultCPULimit,
|
CPU: config.ConnectionPooler.ConnectionPoolerDefaultCPULimit,
|
||||||
Memory: config.ConnectionPool.ConnPoolDefaultMemoryLimit,
|
Memory: config.ConnectionPooler.ConnectionPoolerDefaultMemoryLimit,
|
||||||
}
|
}
|
||||||
|
|
||||||
return acidv1.Resources{
|
return acidv1.Resources{
|
||||||
|
|
@ -1851,33 +1851,33 @@ func (c *Cluster) getLogicalBackupJobName() (jobName string) {
|
||||||
//
|
//
|
||||||
// DEFAULT_SIZE is a pool size per db/user (having in mind the use case when
|
// DEFAULT_SIZE is a pool size per db/user (having in mind the use case when
|
||||||
// most of the queries coming through a connection pooler are from the same
|
// most of the queries coming through a connection pooler are from the same
|
||||||
// user to the same db). In case if we want to spin up more connection pool
|
// user to the same db). In case if we want to spin up more connection pooler
|
||||||
// instances, take this into account and maintain the same number of
|
// instances, take this into account and maintain the same number of
|
||||||
// connections.
|
// connections.
|
||||||
//
|
//
|
||||||
// MIN_SIZE is a pool minimal size, to prevent situation when sudden workload
|
// MIN_SIZE is a pool's minimal size, to prevent situation when sudden workload
|
||||||
// have to wait for spinning up a new connections.
|
// have to wait for spinning up a new connections.
|
||||||
//
|
//
|
||||||
// RESERVE_SIZE is how many additional connections to allow for a pool.
|
// RESERVE_SIZE is how many additional connections to allow for a pooler.
|
||||||
func (c *Cluster) getConnPoolEnvVars(spec *acidv1.PostgresSpec) []v1.EnvVar {
|
func (c *Cluster) getConnectionPoolerEnvVars(spec *acidv1.PostgresSpec) []v1.EnvVar {
|
||||||
effectiveMode := util.Coalesce(
|
effectiveMode := util.Coalesce(
|
||||||
spec.ConnectionPool.Mode,
|
spec.ConnectionPooler.Mode,
|
||||||
c.OpConfig.ConnectionPool.Mode)
|
c.OpConfig.ConnectionPooler.Mode)
|
||||||
|
|
||||||
numberOfInstances := spec.ConnectionPool.NumberOfInstances
|
numberOfInstances := spec.ConnectionPooler.NumberOfInstances
|
||||||
if numberOfInstances == nil {
|
if numberOfInstances == nil {
|
||||||
numberOfInstances = util.CoalesceInt32(
|
numberOfInstances = util.CoalesceInt32(
|
||||||
c.OpConfig.ConnectionPool.NumberOfInstances,
|
c.OpConfig.ConnectionPooler.NumberOfInstances,
|
||||||
k8sutil.Int32ToPointer(1))
|
k8sutil.Int32ToPointer(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
effectiveMaxDBConn := util.CoalesceInt32(
|
effectiveMaxDBConn := util.CoalesceInt32(
|
||||||
spec.ConnectionPool.MaxDBConnections,
|
spec.ConnectionPooler.MaxDBConnections,
|
||||||
c.OpConfig.ConnectionPool.MaxDBConnections)
|
c.OpConfig.ConnectionPooler.MaxDBConnections)
|
||||||
|
|
||||||
if effectiveMaxDBConn == nil {
|
if effectiveMaxDBConn == nil {
|
||||||
effectiveMaxDBConn = k8sutil.Int32ToPointer(
|
effectiveMaxDBConn = k8sutil.Int32ToPointer(
|
||||||
constants.ConnPoolMaxDBConnections)
|
constants.ConnectionPoolerMaxDBConnections)
|
||||||
}
|
}
|
||||||
|
|
||||||
maxDBConn := *effectiveMaxDBConn / *numberOfInstances
|
maxDBConn := *effectiveMaxDBConn / *numberOfInstances
|
||||||
|
|
@ -1888,51 +1888,51 @@ func (c *Cluster) getConnPoolEnvVars(spec *acidv1.PostgresSpec) []v1.EnvVar {
|
||||||
|
|
||||||
return []v1.EnvVar{
|
return []v1.EnvVar{
|
||||||
{
|
{
|
||||||
Name: "CONNECTION_POOL_PORT",
|
Name: "CONNECTION_POOLER_PORT",
|
||||||
Value: fmt.Sprint(pgPort),
|
Value: fmt.Sprint(pgPort),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "CONNECTION_POOL_MODE",
|
Name: "CONNECTION_POOLER_MODE",
|
||||||
Value: effectiveMode,
|
Value: effectiveMode,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "CONNECTION_POOL_DEFAULT_SIZE",
|
Name: "CONNECTION_POOLER_DEFAULT_SIZE",
|
||||||
Value: fmt.Sprint(defaultSize),
|
Value: fmt.Sprint(defaultSize),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "CONNECTION_POOL_MIN_SIZE",
|
Name: "CONNECTION_POOLER_MIN_SIZE",
|
||||||
Value: fmt.Sprint(minSize),
|
Value: fmt.Sprint(minSize),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "CONNECTION_POOL_RESERVE_SIZE",
|
Name: "CONNECTION_POOLER_RESERVE_SIZE",
|
||||||
Value: fmt.Sprint(reserveSize),
|
Value: fmt.Sprint(reserveSize),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "CONNECTION_POOL_MAX_CLIENT_CONN",
|
Name: "CONNECTION_POOLER_MAX_CLIENT_CONN",
|
||||||
Value: fmt.Sprint(constants.ConnPoolMaxClientConnections),
|
Value: fmt.Sprint(constants.ConnectionPoolerMaxClientConnections),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "CONNECTION_POOL_MAX_DB_CONN",
|
Name: "CONNECTION_POOLER_MAX_DB_CONN",
|
||||||
Value: fmt.Sprint(maxDBConn),
|
Value: fmt.Sprint(maxDBConn),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cluster) generateConnPoolPodTemplate(spec *acidv1.PostgresSpec) (
|
func (c *Cluster) generateConnectionPoolerPodTemplate(spec *acidv1.PostgresSpec) (
|
||||||
*v1.PodTemplateSpec, error) {
|
*v1.PodTemplateSpec, error) {
|
||||||
|
|
||||||
gracePeriod := int64(c.OpConfig.PodTerminateGracePeriod.Seconds())
|
gracePeriod := int64(c.OpConfig.PodTerminateGracePeriod.Seconds())
|
||||||
resources, err := generateResourceRequirements(
|
resources, err := generateResourceRequirements(
|
||||||
spec.ConnectionPool.Resources,
|
spec.ConnectionPooler.Resources,
|
||||||
c.makeDefaultConnPoolResources())
|
c.makeDefaultConnectionPoolerResources())
|
||||||
|
|
||||||
effectiveDockerImage := util.Coalesce(
|
effectiveDockerImage := util.Coalesce(
|
||||||
spec.ConnectionPool.DockerImage,
|
spec.ConnectionPooler.DockerImage,
|
||||||
c.OpConfig.ConnectionPool.Image)
|
c.OpConfig.ConnectionPooler.Image)
|
||||||
|
|
||||||
effectiveSchema := util.Coalesce(
|
effectiveSchema := util.Coalesce(
|
||||||
spec.ConnectionPool.Schema,
|
spec.ConnectionPooler.Schema,
|
||||||
c.OpConfig.ConnectionPool.Schema)
|
c.OpConfig.ConnectionPooler.Schema)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not generate resource requirements: %v", err)
|
return nil, fmt.Errorf("could not generate resource requirements: %v", err)
|
||||||
|
|
@ -1940,8 +1940,8 @@ func (c *Cluster) generateConnPoolPodTemplate(spec *acidv1.PostgresSpec) (
|
||||||
|
|
||||||
secretSelector := func(key string) *v1.SecretKeySelector {
|
secretSelector := func(key string) *v1.SecretKeySelector {
|
||||||
effectiveUser := util.Coalesce(
|
effectiveUser := util.Coalesce(
|
||||||
spec.ConnectionPool.User,
|
spec.ConnectionPooler.User,
|
||||||
c.OpConfig.ConnectionPool.User)
|
c.OpConfig.ConnectionPooler.User)
|
||||||
|
|
||||||
return &v1.SecretKeySelector{
|
return &v1.SecretKeySelector{
|
||||||
LocalObjectReference: v1.LocalObjectReference{
|
LocalObjectReference: v1.LocalObjectReference{
|
||||||
|
|
@ -1967,7 +1967,7 @@ func (c *Cluster) generateConnPoolPodTemplate(spec *acidv1.PostgresSpec) (
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// the convention is to use the same schema name as
|
// the convention is to use the same schema name as
|
||||||
// connection pool username
|
// connection pooler username
|
||||||
{
|
{
|
||||||
Name: "PGSCHEMA",
|
Name: "PGSCHEMA",
|
||||||
Value: effectiveSchema,
|
Value: effectiveSchema,
|
||||||
|
|
@ -1980,10 +1980,10 @@ func (c *Cluster) generateConnPoolPodTemplate(spec *acidv1.PostgresSpec) (
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
envVars = append(envVars, c.getConnPoolEnvVars(spec)...)
|
envVars = append(envVars, c.getConnectionPoolerEnvVars(spec)...)
|
||||||
|
|
||||||
poolerContainer := v1.Container{
|
poolerContainer := v1.Container{
|
||||||
Name: connectionPoolContainer,
|
Name: connectionPoolerContainer,
|
||||||
Image: effectiveDockerImage,
|
Image: effectiveDockerImage,
|
||||||
ImagePullPolicy: v1.PullIfNotPresent,
|
ImagePullPolicy: v1.PullIfNotPresent,
|
||||||
Resources: *resources,
|
Resources: *resources,
|
||||||
|
|
@ -1998,7 +1998,7 @@ func (c *Cluster) generateConnPoolPodTemplate(spec *acidv1.PostgresSpec) (
|
||||||
|
|
||||||
podTemplate := &v1.PodTemplateSpec{
|
podTemplate := &v1.PodTemplateSpec{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Labels: c.connPoolLabelsSelector().MatchLabels,
|
Labels: c.connectionPoolerLabelsSelector().MatchLabels,
|
||||||
Namespace: c.Namespace,
|
Namespace: c.Namespace,
|
||||||
Annotations: c.generatePodAnnotations(spec),
|
Annotations: c.generatePodAnnotations(spec),
|
||||||
},
|
},
|
||||||
|
|
@ -2040,32 +2040,32 @@ func (c *Cluster) ownerReferences() []metav1.OwnerReference {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cluster) generateConnPoolDeployment(spec *acidv1.PostgresSpec) (
|
func (c *Cluster) generateConnectionPoolerDeployment(spec *acidv1.PostgresSpec) (
|
||||||
*appsv1.Deployment, error) {
|
*appsv1.Deployment, error) {
|
||||||
|
|
||||||
// there are two ways to enable connection pooler, either to specify a
|
// there are two ways to enable connection pooler, either to specify a
|
||||||
// connectionPool section or enableConnectionPool. In the second case
|
// connectionPooler section or enableConnectionPooler. In the second case
|
||||||
// spec.connectionPool will be nil, so to make it easier to calculate
|
// spec.connectionPooler will be nil, so to make it easier to calculate
|
||||||
// default values, initialize it to an empty structure. It could be done
|
// default values, initialize it to an empty structure. It could be done
|
||||||
// anywhere, but here is the earliest common entry point between sync and
|
// anywhere, but here is the earliest common entry point between sync and
|
||||||
// create code, so init here.
|
// create code, so init here.
|
||||||
if spec.ConnectionPool == nil {
|
if spec.ConnectionPooler == nil {
|
||||||
spec.ConnectionPool = &acidv1.ConnectionPool{}
|
spec.ConnectionPooler = &acidv1.ConnectionPooler{}
|
||||||
}
|
}
|
||||||
|
|
||||||
podTemplate, err := c.generateConnPoolPodTemplate(spec)
|
podTemplate, err := c.generateConnectionPoolerPodTemplate(spec)
|
||||||
numberOfInstances := spec.ConnectionPool.NumberOfInstances
|
numberOfInstances := spec.ConnectionPooler.NumberOfInstances
|
||||||
if numberOfInstances == nil {
|
if numberOfInstances == nil {
|
||||||
numberOfInstances = util.CoalesceInt32(
|
numberOfInstances = util.CoalesceInt32(
|
||||||
c.OpConfig.ConnectionPool.NumberOfInstances,
|
c.OpConfig.ConnectionPooler.NumberOfInstances,
|
||||||
k8sutil.Int32ToPointer(1))
|
k8sutil.Int32ToPointer(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
if *numberOfInstances < constants.ConnPoolMinInstances {
|
if *numberOfInstances < constants.ConnectionPoolerMinInstances {
|
||||||
msg := "Adjusted number of connection pool instances from %d to %d"
|
msg := "Adjusted number of connection pooler instances from %d to %d"
|
||||||
c.logger.Warningf(msg, numberOfInstances, constants.ConnPoolMinInstances)
|
c.logger.Warningf(msg, numberOfInstances, constants.ConnectionPoolerMinInstances)
|
||||||
|
|
||||||
*numberOfInstances = constants.ConnPoolMinInstances
|
*numberOfInstances = constants.ConnectionPoolerMinInstances
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -2074,9 +2074,9 @@ func (c *Cluster) generateConnPoolDeployment(spec *acidv1.PostgresSpec) (
|
||||||
|
|
||||||
deployment := &appsv1.Deployment{
|
deployment := &appsv1.Deployment{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: c.connPoolName(),
|
Name: c.connectionPoolerName(),
|
||||||
Namespace: c.Namespace,
|
Namespace: c.Namespace,
|
||||||
Labels: c.connPoolLabelsSelector().MatchLabels,
|
Labels: c.connectionPoolerLabelsSelector().MatchLabels,
|
||||||
Annotations: map[string]string{},
|
Annotations: map[string]string{},
|
||||||
// make StatefulSet object its owner to represent the dependency.
|
// make StatefulSet object its owner to represent the dependency.
|
||||||
// By itself StatefulSet is being deleted with "Orphaned"
|
// By itself StatefulSet is being deleted with "Orphaned"
|
||||||
|
|
@ -2088,7 +2088,7 @@ func (c *Cluster) generateConnPoolDeployment(spec *acidv1.PostgresSpec) (
|
||||||
},
|
},
|
||||||
Spec: appsv1.DeploymentSpec{
|
Spec: appsv1.DeploymentSpec{
|
||||||
Replicas: numberOfInstances,
|
Replicas: numberOfInstances,
|
||||||
Selector: c.connPoolLabelsSelector(),
|
Selector: c.connectionPoolerLabelsSelector(),
|
||||||
Template: *podTemplate,
|
Template: *podTemplate,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -2096,37 +2096,37 @@ func (c *Cluster) generateConnPoolDeployment(spec *acidv1.PostgresSpec) (
|
||||||
return deployment, nil
|
return deployment, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cluster) generateConnPoolService(spec *acidv1.PostgresSpec) *v1.Service {
|
func (c *Cluster) generateConnectionPoolerService(spec *acidv1.PostgresSpec) *v1.Service {
|
||||||
|
|
||||||
// there are two ways to enable connection pooler, either to specify a
|
// there are two ways to enable connection pooler, either to specify a
|
||||||
// connectionPool section or enableConnectionPool. In the second case
|
// connectionPooler section or enableConnectionPooler. In the second case
|
||||||
// spec.connectionPool will be nil, so to make it easier to calculate
|
// spec.connectionPooler will be nil, so to make it easier to calculate
|
||||||
// default values, initialize it to an empty structure. It could be done
|
// default values, initialize it to an empty structure. It could be done
|
||||||
// anywhere, but here is the earliest common entry point between sync and
|
// anywhere, but here is the earliest common entry point between sync and
|
||||||
// create code, so init here.
|
// create code, so init here.
|
||||||
if spec.ConnectionPool == nil {
|
if spec.ConnectionPooler == nil {
|
||||||
spec.ConnectionPool = &acidv1.ConnectionPool{}
|
spec.ConnectionPooler = &acidv1.ConnectionPooler{}
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceSpec := v1.ServiceSpec{
|
serviceSpec := v1.ServiceSpec{
|
||||||
Ports: []v1.ServicePort{
|
Ports: []v1.ServicePort{
|
||||||
{
|
{
|
||||||
Name: c.connPoolName(),
|
Name: c.connectionPoolerName(),
|
||||||
Port: pgPort,
|
Port: pgPort,
|
||||||
TargetPort: intstr.IntOrString{StrVal: c.servicePort(Master)},
|
TargetPort: intstr.IntOrString{StrVal: c.servicePort(Master)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Type: v1.ServiceTypeClusterIP,
|
Type: v1.ServiceTypeClusterIP,
|
||||||
Selector: map[string]string{
|
Selector: map[string]string{
|
||||||
"connection-pool": c.connPoolName(),
|
"connection-pooler": c.connectionPoolerName(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
service := &v1.Service{
|
service := &v1.Service{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: c.connPoolName(),
|
Name: c.connectionPoolerName(),
|
||||||
Namespace: c.Namespace,
|
Namespace: c.Namespace,
|
||||||
Labels: c.connPoolLabelsSelector().MatchLabels,
|
Labels: c.connectionPoolerLabelsSelector().MatchLabels,
|
||||||
Annotations: map[string]string{},
|
Annotations: map[string]string{},
|
||||||
// make StatefulSet object its owner to represent the dependency.
|
// make StatefulSet object its owner to represent the dependency.
|
||||||
// By itself StatefulSet is being deleted with "Orphaned"
|
// By itself StatefulSet is being deleted with "Orphaned"
|
||||||
|
|
|
||||||
|
|
@ -587,38 +587,38 @@ func TestSecretVolume(t *testing.T) {
|
||||||
|
|
||||||
func testResources(cluster *Cluster, podSpec *v1.PodTemplateSpec) error {
|
func testResources(cluster *Cluster, podSpec *v1.PodTemplateSpec) error {
|
||||||
cpuReq := podSpec.Spec.Containers[0].Resources.Requests["cpu"]
|
cpuReq := podSpec.Spec.Containers[0].Resources.Requests["cpu"]
|
||||||
if cpuReq.String() != cluster.OpConfig.ConnectionPool.ConnPoolDefaultCPURequest {
|
if cpuReq.String() != cluster.OpConfig.ConnectionPooler.ConnectionPoolerDefaultCPURequest {
|
||||||
return fmt.Errorf("CPU request doesn't match, got %s, expected %s",
|
return fmt.Errorf("CPU request doesn't match, got %s, expected %s",
|
||||||
cpuReq.String(), cluster.OpConfig.ConnectionPool.ConnPoolDefaultCPURequest)
|
cpuReq.String(), cluster.OpConfig.ConnectionPooler.ConnectionPoolerDefaultCPURequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
memReq := podSpec.Spec.Containers[0].Resources.Requests["memory"]
|
memReq := podSpec.Spec.Containers[0].Resources.Requests["memory"]
|
||||||
if memReq.String() != cluster.OpConfig.ConnectionPool.ConnPoolDefaultMemoryRequest {
|
if memReq.String() != cluster.OpConfig.ConnectionPooler.ConnectionPoolerDefaultMemoryRequest {
|
||||||
return fmt.Errorf("Memory request doesn't match, got %s, expected %s",
|
return fmt.Errorf("Memory request doesn't match, got %s, expected %s",
|
||||||
memReq.String(), cluster.OpConfig.ConnectionPool.ConnPoolDefaultMemoryRequest)
|
memReq.String(), cluster.OpConfig.ConnectionPooler.ConnectionPoolerDefaultMemoryRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
cpuLim := podSpec.Spec.Containers[0].Resources.Limits["cpu"]
|
cpuLim := podSpec.Spec.Containers[0].Resources.Limits["cpu"]
|
||||||
if cpuLim.String() != cluster.OpConfig.ConnectionPool.ConnPoolDefaultCPULimit {
|
if cpuLim.String() != cluster.OpConfig.ConnectionPooler.ConnectionPoolerDefaultCPULimit {
|
||||||
return fmt.Errorf("CPU limit doesn't match, got %s, expected %s",
|
return fmt.Errorf("CPU limit doesn't match, got %s, expected %s",
|
||||||
cpuLim.String(), cluster.OpConfig.ConnectionPool.ConnPoolDefaultCPULimit)
|
cpuLim.String(), cluster.OpConfig.ConnectionPooler.ConnectionPoolerDefaultCPULimit)
|
||||||
}
|
}
|
||||||
|
|
||||||
memLim := podSpec.Spec.Containers[0].Resources.Limits["memory"]
|
memLim := podSpec.Spec.Containers[0].Resources.Limits["memory"]
|
||||||
if memLim.String() != cluster.OpConfig.ConnectionPool.ConnPoolDefaultMemoryLimit {
|
if memLim.String() != cluster.OpConfig.ConnectionPooler.ConnectionPoolerDefaultMemoryLimit {
|
||||||
return fmt.Errorf("Memory limit doesn't match, got %s, expected %s",
|
return fmt.Errorf("Memory limit doesn't match, got %s, expected %s",
|
||||||
memLim.String(), cluster.OpConfig.ConnectionPool.ConnPoolDefaultMemoryLimit)
|
memLim.String(), cluster.OpConfig.ConnectionPooler.ConnectionPoolerDefaultMemoryLimit)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func testLabels(cluster *Cluster, podSpec *v1.PodTemplateSpec) error {
|
func testLabels(cluster *Cluster, podSpec *v1.PodTemplateSpec) error {
|
||||||
poolLabels := podSpec.ObjectMeta.Labels["connection-pool"]
|
poolerLabels := podSpec.ObjectMeta.Labels["connection-pooler"]
|
||||||
|
|
||||||
if poolLabels != cluster.connPoolLabelsSelector().MatchLabels["connection-pool"] {
|
if poolerLabels != cluster.connectionPoolerLabelsSelector().MatchLabels["connection-pooler"] {
|
||||||
return fmt.Errorf("Pod labels do not match, got %+v, expected %+v",
|
return fmt.Errorf("Pod labels do not match, got %+v, expected %+v",
|
||||||
podSpec.ObjectMeta.Labels, cluster.connPoolLabelsSelector().MatchLabels)
|
podSpec.ObjectMeta.Labels, cluster.connectionPoolerLabelsSelector().MatchLabels)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -631,8 +631,8 @@ func testEnvs(cluster *Cluster, podSpec *v1.PodTemplateSpec) error {
|
||||||
"PGUSER": false,
|
"PGUSER": false,
|
||||||
"PGSCHEMA": false,
|
"PGSCHEMA": false,
|
||||||
"PGPASSWORD": false,
|
"PGPASSWORD": false,
|
||||||
"CONNECTION_POOL_MODE": false,
|
"CONNECTION_POOLER_MODE": false,
|
||||||
"CONNECTION_POOL_PORT": false,
|
"CONNECTION_POOLER_PORT": false,
|
||||||
}
|
}
|
||||||
|
|
||||||
envs := podSpec.Spec.Containers[0].Env
|
envs := podSpec.Spec.Containers[0].Env
|
||||||
|
|
@ -658,8 +658,8 @@ func testCustomPodTemplate(cluster *Cluster, podSpec *v1.PodTemplateSpec) error
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConnPoolPodSpec(t *testing.T) {
|
func TestConnectionPoolerPodSpec(t *testing.T) {
|
||||||
testName := "Test connection pool pod template generation"
|
testName := "Test connection pooler pod template generation"
|
||||||
var cluster = New(
|
var cluster = New(
|
||||||
Config{
|
Config{
|
||||||
OpConfig: config.Config{
|
OpConfig: config.Config{
|
||||||
|
|
@ -668,12 +668,12 @@ func TestConnPoolPodSpec(t *testing.T) {
|
||||||
SuperUsername: superUserName,
|
SuperUsername: superUserName,
|
||||||
ReplicationUsername: replicationUserName,
|
ReplicationUsername: replicationUserName,
|
||||||
},
|
},
|
||||||
ConnectionPool: config.ConnectionPool{
|
ConnectionPooler: config.ConnectionPooler{
|
||||||
MaxDBConnections: int32ToPointer(60),
|
MaxDBConnections: int32ToPointer(60),
|
||||||
ConnPoolDefaultCPURequest: "100m",
|
ConnectionPoolerDefaultCPURequest: "100m",
|
||||||
ConnPoolDefaultCPULimit: "100m",
|
ConnectionPoolerDefaultCPULimit: "100m",
|
||||||
ConnPoolDefaultMemoryRequest: "100Mi",
|
ConnectionPoolerDefaultMemoryRequest: "100Mi",
|
||||||
ConnPoolDefaultMemoryLimit: "100Mi",
|
ConnectionPoolerDefaultMemoryLimit: "100Mi",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, k8sutil.KubernetesClient{}, acidv1.Postgresql{}, logger)
|
}, k8sutil.KubernetesClient{}, acidv1.Postgresql{}, logger)
|
||||||
|
|
@ -686,7 +686,7 @@ func TestConnPoolPodSpec(t *testing.T) {
|
||||||
SuperUsername: superUserName,
|
SuperUsername: superUserName,
|
||||||
ReplicationUsername: replicationUserName,
|
ReplicationUsername: replicationUserName,
|
||||||
},
|
},
|
||||||
ConnectionPool: config.ConnectionPool{},
|
ConnectionPooler: config.ConnectionPooler{},
|
||||||
},
|
},
|
||||||
}, k8sutil.KubernetesClient{}, acidv1.Postgresql{}, logger)
|
}, k8sutil.KubernetesClient{}, acidv1.Postgresql{}, logger)
|
||||||
|
|
||||||
|
|
@ -702,7 +702,7 @@ func TestConnPoolPodSpec(t *testing.T) {
|
||||||
{
|
{
|
||||||
subTest: "default configuration",
|
subTest: "default configuration",
|
||||||
spec: &acidv1.PostgresSpec{
|
spec: &acidv1.PostgresSpec{
|
||||||
ConnectionPool: &acidv1.ConnectionPool{},
|
ConnectionPooler: &acidv1.ConnectionPooler{},
|
||||||
},
|
},
|
||||||
expected: nil,
|
expected: nil,
|
||||||
cluster: cluster,
|
cluster: cluster,
|
||||||
|
|
@ -711,7 +711,7 @@ func TestConnPoolPodSpec(t *testing.T) {
|
||||||
{
|
{
|
||||||
subTest: "no default resources",
|
subTest: "no default resources",
|
||||||
spec: &acidv1.PostgresSpec{
|
spec: &acidv1.PostgresSpec{
|
||||||
ConnectionPool: &acidv1.ConnectionPool{},
|
ConnectionPooler: &acidv1.ConnectionPooler{},
|
||||||
},
|
},
|
||||||
expected: errors.New(`could not generate resource requirements: could not fill resource requests: could not parse default CPU quantity: quantities must match the regular expression '^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$'`),
|
expected: errors.New(`could not generate resource requirements: could not fill resource requests: could not parse default CPU quantity: quantities must match the regular expression '^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$'`),
|
||||||
cluster: clusterNoDefaultRes,
|
cluster: clusterNoDefaultRes,
|
||||||
|
|
@ -720,7 +720,7 @@ func TestConnPoolPodSpec(t *testing.T) {
|
||||||
{
|
{
|
||||||
subTest: "default resources are set",
|
subTest: "default resources are set",
|
||||||
spec: &acidv1.PostgresSpec{
|
spec: &acidv1.PostgresSpec{
|
||||||
ConnectionPool: &acidv1.ConnectionPool{},
|
ConnectionPooler: &acidv1.ConnectionPooler{},
|
||||||
},
|
},
|
||||||
expected: nil,
|
expected: nil,
|
||||||
cluster: cluster,
|
cluster: cluster,
|
||||||
|
|
@ -729,7 +729,7 @@ func TestConnPoolPodSpec(t *testing.T) {
|
||||||
{
|
{
|
||||||
subTest: "labels for service",
|
subTest: "labels for service",
|
||||||
spec: &acidv1.PostgresSpec{
|
spec: &acidv1.PostgresSpec{
|
||||||
ConnectionPool: &acidv1.ConnectionPool{},
|
ConnectionPooler: &acidv1.ConnectionPooler{},
|
||||||
},
|
},
|
||||||
expected: nil,
|
expected: nil,
|
||||||
cluster: cluster,
|
cluster: cluster,
|
||||||
|
|
@ -738,7 +738,7 @@ func TestConnPoolPodSpec(t *testing.T) {
|
||||||
{
|
{
|
||||||
subTest: "required envs",
|
subTest: "required envs",
|
||||||
spec: &acidv1.PostgresSpec{
|
spec: &acidv1.PostgresSpec{
|
||||||
ConnectionPool: &acidv1.ConnectionPool{},
|
ConnectionPooler: &acidv1.ConnectionPooler{},
|
||||||
},
|
},
|
||||||
expected: nil,
|
expected: nil,
|
||||||
cluster: cluster,
|
cluster: cluster,
|
||||||
|
|
@ -746,7 +746,7 @@ func TestConnPoolPodSpec(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
podSpec, err := tt.cluster.generateConnPoolPodTemplate(tt.spec)
|
podSpec, err := tt.cluster.generateConnectionPoolerPodTemplate(tt.spec)
|
||||||
|
|
||||||
if err != tt.expected && err.Error() != tt.expected.Error() {
|
if err != tt.expected && err.Error() != tt.expected.Error() {
|
||||||
t.Errorf("%s [%s]: Could not generate pod template,\n %+v, expected\n %+v",
|
t.Errorf("%s [%s]: Could not generate pod template,\n %+v, expected\n %+v",
|
||||||
|
|
@ -774,9 +774,9 @@ func testDeploymentOwnwerReference(cluster *Cluster, deployment *appsv1.Deployme
|
||||||
|
|
||||||
func testSelector(cluster *Cluster, deployment *appsv1.Deployment) error {
|
func testSelector(cluster *Cluster, deployment *appsv1.Deployment) error {
|
||||||
labels := deployment.Spec.Selector.MatchLabels
|
labels := deployment.Spec.Selector.MatchLabels
|
||||||
expected := cluster.connPoolLabelsSelector().MatchLabels
|
expected := cluster.connectionPoolerLabelsSelector().MatchLabels
|
||||||
|
|
||||||
if labels["connection-pool"] != expected["connection-pool"] {
|
if labels["connection-pooler"] != expected["connection-pooler"] {
|
||||||
return fmt.Errorf("Labels are incorrect, got %+v, expected %+v",
|
return fmt.Errorf("Labels are incorrect, got %+v, expected %+v",
|
||||||
labels, expected)
|
labels, expected)
|
||||||
}
|
}
|
||||||
|
|
@ -784,8 +784,8 @@ func testSelector(cluster *Cluster, deployment *appsv1.Deployment) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConnPoolDeploymentSpec(t *testing.T) {
|
func TestConnectionPoolerDeploymentSpec(t *testing.T) {
|
||||||
testName := "Test connection pool deployment spec generation"
|
testName := "Test connection pooler deployment spec generation"
|
||||||
var cluster = New(
|
var cluster = New(
|
||||||
Config{
|
Config{
|
||||||
OpConfig: config.Config{
|
OpConfig: config.Config{
|
||||||
|
|
@ -794,11 +794,11 @@ func TestConnPoolDeploymentSpec(t *testing.T) {
|
||||||
SuperUsername: superUserName,
|
SuperUsername: superUserName,
|
||||||
ReplicationUsername: replicationUserName,
|
ReplicationUsername: replicationUserName,
|
||||||
},
|
},
|
||||||
ConnectionPool: config.ConnectionPool{
|
ConnectionPooler: config.ConnectionPooler{
|
||||||
ConnPoolDefaultCPURequest: "100m",
|
ConnectionPoolerDefaultCPURequest: "100m",
|
||||||
ConnPoolDefaultCPULimit: "100m",
|
ConnectionPoolerDefaultCPULimit: "100m",
|
||||||
ConnPoolDefaultMemoryRequest: "100Mi",
|
ConnectionPoolerDefaultMemoryRequest: "100Mi",
|
||||||
ConnPoolDefaultMemoryLimit: "100Mi",
|
ConnectionPoolerDefaultMemoryLimit: "100Mi",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, k8sutil.KubernetesClient{}, acidv1.Postgresql{}, logger)
|
}, k8sutil.KubernetesClient{}, acidv1.Postgresql{}, logger)
|
||||||
|
|
@ -822,7 +822,7 @@ func TestConnPoolDeploymentSpec(t *testing.T) {
|
||||||
{
|
{
|
||||||
subTest: "default configuration",
|
subTest: "default configuration",
|
||||||
spec: &acidv1.PostgresSpec{
|
spec: &acidv1.PostgresSpec{
|
||||||
ConnectionPool: &acidv1.ConnectionPool{},
|
ConnectionPooler: &acidv1.ConnectionPooler{},
|
||||||
},
|
},
|
||||||
expected: nil,
|
expected: nil,
|
||||||
cluster: cluster,
|
cluster: cluster,
|
||||||
|
|
@ -831,7 +831,7 @@ func TestConnPoolDeploymentSpec(t *testing.T) {
|
||||||
{
|
{
|
||||||
subTest: "owner reference",
|
subTest: "owner reference",
|
||||||
spec: &acidv1.PostgresSpec{
|
spec: &acidv1.PostgresSpec{
|
||||||
ConnectionPool: &acidv1.ConnectionPool{},
|
ConnectionPooler: &acidv1.ConnectionPooler{},
|
||||||
},
|
},
|
||||||
expected: nil,
|
expected: nil,
|
||||||
cluster: cluster,
|
cluster: cluster,
|
||||||
|
|
@ -840,7 +840,7 @@ func TestConnPoolDeploymentSpec(t *testing.T) {
|
||||||
{
|
{
|
||||||
subTest: "selector",
|
subTest: "selector",
|
||||||
spec: &acidv1.PostgresSpec{
|
spec: &acidv1.PostgresSpec{
|
||||||
ConnectionPool: &acidv1.ConnectionPool{},
|
ConnectionPooler: &acidv1.ConnectionPooler{},
|
||||||
},
|
},
|
||||||
expected: nil,
|
expected: nil,
|
||||||
cluster: cluster,
|
cluster: cluster,
|
||||||
|
|
@ -848,7 +848,7 @@ func TestConnPoolDeploymentSpec(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
deployment, err := tt.cluster.generateConnPoolDeployment(tt.spec)
|
deployment, err := tt.cluster.generateConnectionPoolerDeployment(tt.spec)
|
||||||
|
|
||||||
if err != tt.expected && err.Error() != tt.expected.Error() {
|
if err != tt.expected && err.Error() != tt.expected.Error() {
|
||||||
t.Errorf("%s [%s]: Could not generate deployment spec,\n %+v, expected\n %+v",
|
t.Errorf("%s [%s]: Could not generate deployment spec,\n %+v, expected\n %+v",
|
||||||
|
|
@ -877,16 +877,16 @@ func testServiceOwnwerReference(cluster *Cluster, service *v1.Service) error {
|
||||||
func testServiceSelector(cluster *Cluster, service *v1.Service) error {
|
func testServiceSelector(cluster *Cluster, service *v1.Service) error {
|
||||||
selector := service.Spec.Selector
|
selector := service.Spec.Selector
|
||||||
|
|
||||||
if selector["connection-pool"] != cluster.connPoolName() {
|
if selector["connection-pooler"] != cluster.connectionPoolerName() {
|
||||||
return fmt.Errorf("Selector is incorrect, got %s, expected %s",
|
return fmt.Errorf("Selector is incorrect, got %s, expected %s",
|
||||||
selector["connection-pool"], cluster.connPoolName())
|
selector["connection-pooler"], cluster.connectionPoolerName())
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConnPoolServiceSpec(t *testing.T) {
|
func TestConnectionPoolerServiceSpec(t *testing.T) {
|
||||||
testName := "Test connection pool service spec generation"
|
testName := "Test connection pooler service spec generation"
|
||||||
var cluster = New(
|
var cluster = New(
|
||||||
Config{
|
Config{
|
||||||
OpConfig: config.Config{
|
OpConfig: config.Config{
|
||||||
|
|
@ -895,11 +895,11 @@ func TestConnPoolServiceSpec(t *testing.T) {
|
||||||
SuperUsername: superUserName,
|
SuperUsername: superUserName,
|
||||||
ReplicationUsername: replicationUserName,
|
ReplicationUsername: replicationUserName,
|
||||||
},
|
},
|
||||||
ConnectionPool: config.ConnectionPool{
|
ConnectionPooler: config.ConnectionPooler{
|
||||||
ConnPoolDefaultCPURequest: "100m",
|
ConnectionPoolerDefaultCPURequest: "100m",
|
||||||
ConnPoolDefaultCPULimit: "100m",
|
ConnectionPoolerDefaultCPULimit: "100m",
|
||||||
ConnPoolDefaultMemoryRequest: "100Mi",
|
ConnectionPoolerDefaultMemoryRequest: "100Mi",
|
||||||
ConnPoolDefaultMemoryLimit: "100Mi",
|
ConnectionPoolerDefaultMemoryLimit: "100Mi",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, k8sutil.KubernetesClient{}, acidv1.Postgresql{}, logger)
|
}, k8sutil.KubernetesClient{}, acidv1.Postgresql{}, logger)
|
||||||
|
|
@ -922,7 +922,7 @@ func TestConnPoolServiceSpec(t *testing.T) {
|
||||||
{
|
{
|
||||||
subTest: "default configuration",
|
subTest: "default configuration",
|
||||||
spec: &acidv1.PostgresSpec{
|
spec: &acidv1.PostgresSpec{
|
||||||
ConnectionPool: &acidv1.ConnectionPool{},
|
ConnectionPooler: &acidv1.ConnectionPooler{},
|
||||||
},
|
},
|
||||||
cluster: cluster,
|
cluster: cluster,
|
||||||
check: noCheck,
|
check: noCheck,
|
||||||
|
|
@ -930,7 +930,7 @@ func TestConnPoolServiceSpec(t *testing.T) {
|
||||||
{
|
{
|
||||||
subTest: "owner reference",
|
subTest: "owner reference",
|
||||||
spec: &acidv1.PostgresSpec{
|
spec: &acidv1.PostgresSpec{
|
||||||
ConnectionPool: &acidv1.ConnectionPool{},
|
ConnectionPooler: &acidv1.ConnectionPooler{},
|
||||||
},
|
},
|
||||||
cluster: cluster,
|
cluster: cluster,
|
||||||
check: testServiceOwnwerReference,
|
check: testServiceOwnwerReference,
|
||||||
|
|
@ -938,14 +938,14 @@ func TestConnPoolServiceSpec(t *testing.T) {
|
||||||
{
|
{
|
||||||
subTest: "selector",
|
subTest: "selector",
|
||||||
spec: &acidv1.PostgresSpec{
|
spec: &acidv1.PostgresSpec{
|
||||||
ConnectionPool: &acidv1.ConnectionPool{},
|
ConnectionPooler: &acidv1.ConnectionPooler{},
|
||||||
},
|
},
|
||||||
cluster: cluster,
|
cluster: cluster,
|
||||||
check: testServiceSelector,
|
check: testServiceSelector,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
service := tt.cluster.generateConnPoolService(tt.spec)
|
service := tt.cluster.generateConnectionPoolerService(tt.spec)
|
||||||
|
|
||||||
if err := tt.check(cluster, service); err != nil {
|
if err := tt.check(cluster, service); err != nil {
|
||||||
t.Errorf("%s [%s]: Service spec is incorrect, %+v",
|
t.Errorf("%s [%s]: Service spec is incorrect, %+v",
|
||||||
|
|
|
||||||
|
|
@ -94,37 +94,37 @@ func (c *Cluster) createStatefulSet() (*appsv1.StatefulSet, error) {
|
||||||
return statefulSet, nil
|
return statefulSet, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare the database for connection pool to be used, i.e. install lookup
|
// Prepare the database for connection pooler to be used, i.e. install lookup
|
||||||
// function (do it first, because it should be fast and if it didn't succeed,
|
// function (do it first, because it should be fast and if it didn't succeed,
|
||||||
// it doesn't makes sense to create more K8S objects. At this moment we assume
|
// it doesn't makes sense to create more K8S objects. At this moment we assume
|
||||||
// that necessary connection pool user exists.
|
// that necessary connection pooler user exists.
|
||||||
//
|
//
|
||||||
// After that create all the objects for connection pool, namely a deployment
|
// After that create all the objects for connection pooler, namely a deployment
|
||||||
// with a chosen pooler and a service to expose it.
|
// with a chosen pooler and a service to expose it.
|
||||||
func (c *Cluster) createConnectionPool(lookup InstallFunction) (*ConnectionPoolObjects, error) {
|
func (c *Cluster) createConnectionPooler(lookup InstallFunction) (*ConnectionPoolerObjects, error) {
|
||||||
var msg string
|
var msg string
|
||||||
c.setProcessName("creating connection pool")
|
c.setProcessName("creating connection pooler")
|
||||||
|
|
||||||
schema := c.Spec.ConnectionPool.Schema
|
schema := c.Spec.ConnectionPooler.Schema
|
||||||
if schema == "" {
|
if schema == "" {
|
||||||
schema = c.OpConfig.ConnectionPool.Schema
|
schema = c.OpConfig.ConnectionPooler.Schema
|
||||||
}
|
}
|
||||||
|
|
||||||
user := c.Spec.ConnectionPool.User
|
user := c.Spec.ConnectionPooler.User
|
||||||
if user == "" {
|
if user == "" {
|
||||||
user = c.OpConfig.ConnectionPool.User
|
user = c.OpConfig.ConnectionPooler.User
|
||||||
}
|
}
|
||||||
|
|
||||||
err := lookup(schema, user)
|
err := lookup(schema, user)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg = "could not prepare database for connection pool: %v"
|
msg = "could not prepare database for connection pooler: %v"
|
||||||
return nil, fmt.Errorf(msg, err)
|
return nil, fmt.Errorf(msg, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
deploymentSpec, err := c.generateConnPoolDeployment(&c.Spec)
|
deploymentSpec, err := c.generateConnectionPoolerDeployment(&c.Spec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg = "could not generate deployment for connection pool: %v"
|
msg = "could not generate deployment for connection pooler: %v"
|
||||||
return nil, fmt.Errorf(msg, err)
|
return nil, fmt.Errorf(msg, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -139,7 +139,7 @@ func (c *Cluster) createConnectionPool(lookup InstallFunction) (*ConnectionPoolO
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceSpec := c.generateConnPoolService(&c.Spec)
|
serviceSpec := c.generateConnectionPoolerService(&c.Spec)
|
||||||
service, err := c.KubeClient.
|
service, err := c.KubeClient.
|
||||||
Services(serviceSpec.Namespace).
|
Services(serviceSpec.Namespace).
|
||||||
Create(context.TODO(), serviceSpec, metav1.CreateOptions{})
|
Create(context.TODO(), serviceSpec, metav1.CreateOptions{})
|
||||||
|
|
@ -148,31 +148,31 @@ func (c *Cluster) createConnectionPool(lookup InstallFunction) (*ConnectionPoolO
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ConnectionPool = &ConnectionPoolObjects{
|
c.ConnectionPooler = &ConnectionPoolerObjects{
|
||||||
Deployment: deployment,
|
Deployment: deployment,
|
||||||
Service: service,
|
Service: service,
|
||||||
}
|
}
|
||||||
c.logger.Debugf("created new connection pool %q, uid: %q",
|
c.logger.Debugf("created new connection pooler %q, uid: %q",
|
||||||
util.NameFromMeta(deployment.ObjectMeta), deployment.UID)
|
util.NameFromMeta(deployment.ObjectMeta), deployment.UID)
|
||||||
|
|
||||||
return c.ConnectionPool, nil
|
return c.ConnectionPooler, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cluster) deleteConnectionPool() (err error) {
|
func (c *Cluster) deleteConnectionPooler() (err error) {
|
||||||
c.setProcessName("deleting connection pool")
|
c.setProcessName("deleting connection pooler")
|
||||||
c.logger.Debugln("deleting connection pool")
|
c.logger.Debugln("deleting connection pooler")
|
||||||
|
|
||||||
// Lack of connection pooler objects is not a fatal error, just log it if
|
// Lack of connection pooler objects is not a fatal error, just log it if
|
||||||
// it was present before in the manifest
|
// it was present before in the manifest
|
||||||
if c.ConnectionPool == nil {
|
if c.ConnectionPooler == nil {
|
||||||
c.logger.Infof("No connection pool to delete")
|
c.logger.Infof("No connection pooler to delete")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up the deployment object. If deployment resource we've remembered
|
// Clean up the deployment object. If deployment resource we've remembered
|
||||||
// is somehow empty, try to delete based on what would we generate
|
// is somehow empty, try to delete based on what would we generate
|
||||||
deploymentName := c.connPoolName()
|
deploymentName := c.connectionPoolerName()
|
||||||
deployment := c.ConnectionPool.Deployment
|
deployment := c.ConnectionPooler.Deployment
|
||||||
|
|
||||||
if deployment != nil {
|
if deployment != nil {
|
||||||
deploymentName = deployment.Name
|
deploymentName = deployment.Name
|
||||||
|
|
@ -187,16 +187,16 @@ func (c *Cluster) deleteConnectionPool() (err error) {
|
||||||
Delete(context.TODO(), deploymentName, options)
|
Delete(context.TODO(), deploymentName, options)
|
||||||
|
|
||||||
if !k8sutil.ResourceNotFound(err) {
|
if !k8sutil.ResourceNotFound(err) {
|
||||||
c.logger.Debugf("Connection pool deployment was already deleted")
|
c.logger.Debugf("Connection pooler deployment was already deleted")
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return fmt.Errorf("could not delete deployment: %v", err)
|
return fmt.Errorf("could not delete deployment: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.logger.Infof("Connection pool deployment %q has been deleted", deploymentName)
|
c.logger.Infof("Connection pooler deployment %q has been deleted", deploymentName)
|
||||||
|
|
||||||
// Repeat the same for the service object
|
// Repeat the same for the service object
|
||||||
service := c.ConnectionPool.Service
|
service := c.ConnectionPooler.Service
|
||||||
serviceName := c.connPoolName()
|
serviceName := c.connectionPoolerName()
|
||||||
|
|
||||||
if service != nil {
|
if service != nil {
|
||||||
serviceName = service.Name
|
serviceName = service.Name
|
||||||
|
|
@ -209,14 +209,14 @@ func (c *Cluster) deleteConnectionPool() (err error) {
|
||||||
Delete(context.TODO(), serviceName, options)
|
Delete(context.TODO(), serviceName, options)
|
||||||
|
|
||||||
if !k8sutil.ResourceNotFound(err) {
|
if !k8sutil.ResourceNotFound(err) {
|
||||||
c.logger.Debugf("Connection pool service was already deleted")
|
c.logger.Debugf("Connection pooler service was already deleted")
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return fmt.Errorf("could not delete service: %v", err)
|
return fmt.Errorf("could not delete service: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.logger.Infof("Connection pool service %q has been deleted", serviceName)
|
c.logger.Infof("Connection pooler service %q has been deleted", serviceName)
|
||||||
|
|
||||||
c.ConnectionPool = nil
|
c.ConnectionPooler = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -816,12 +816,12 @@ func (c *Cluster) GetPodDisruptionBudget() *policybeta1.PodDisruptionBudget {
|
||||||
return c.PodDisruptionBudget
|
return c.PodDisruptionBudget
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform actual patching of a connection pool deployment, assuming that all
|
// Perform actual patching of a connection pooler deployment, assuming that all
|
||||||
// the check were already done before.
|
// the check were already done before.
|
||||||
func (c *Cluster) updateConnPoolDeployment(oldDeploymentSpec, newDeployment *appsv1.Deployment) (*appsv1.Deployment, error) {
|
func (c *Cluster) updateConnectionPoolerDeployment(oldDeploymentSpec, newDeployment *appsv1.Deployment) (*appsv1.Deployment, error) {
|
||||||
c.setProcessName("updating connection pool")
|
c.setProcessName("updating connection pooler")
|
||||||
if c.ConnectionPool == nil || c.ConnectionPool.Deployment == nil {
|
if c.ConnectionPooler == nil || c.ConnectionPooler.Deployment == nil {
|
||||||
return nil, fmt.Errorf("there is no connection pool in the cluster")
|
return nil, fmt.Errorf("there is no connection pooler in the cluster")
|
||||||
}
|
}
|
||||||
|
|
||||||
patchData, err := specPatch(newDeployment.Spec)
|
patchData, err := specPatch(newDeployment.Spec)
|
||||||
|
|
@ -833,9 +833,9 @@ func (c *Cluster) updateConnPoolDeployment(oldDeploymentSpec, newDeployment *app
|
||||||
// worker at one time will try to update it chances of conflicts are
|
// worker at one time will try to update it chances of conflicts are
|
||||||
// minimal.
|
// minimal.
|
||||||
deployment, err := c.KubeClient.
|
deployment, err := c.KubeClient.
|
||||||
Deployments(c.ConnectionPool.Deployment.Namespace).Patch(
|
Deployments(c.ConnectionPooler.Deployment.Namespace).Patch(
|
||||||
context.TODO(),
|
context.TODO(),
|
||||||
c.ConnectionPool.Deployment.Name,
|
c.ConnectionPooler.Deployment.Name,
|
||||||
types.MergePatchType,
|
types.MergePatchType,
|
||||||
patchData,
|
patchData,
|
||||||
metav1.PatchOptions{},
|
metav1.PatchOptions{},
|
||||||
|
|
@ -844,7 +844,7 @@ func (c *Cluster) updateConnPoolDeployment(oldDeploymentSpec, newDeployment *app
|
||||||
return nil, fmt.Errorf("could not patch deployment: %v", err)
|
return nil, fmt.Errorf("could not patch deployment: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ConnectionPool.Deployment = deployment
|
c.ConnectionPooler.Deployment = deployment
|
||||||
|
|
||||||
return deployment, nil
|
return deployment, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,8 @@ func boolToPointer(value bool) *bool {
|
||||||
return &value
|
return &value
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConnPoolCreationAndDeletion(t *testing.T) {
|
func TestConnectionPoolerCreationAndDeletion(t *testing.T) {
|
||||||
testName := "Test connection pool creation"
|
testName := "Test connection pooler creation"
|
||||||
var cluster = New(
|
var cluster = New(
|
||||||
Config{
|
Config{
|
||||||
OpConfig: config.Config{
|
OpConfig: config.Config{
|
||||||
|
|
@ -29,11 +29,11 @@ func TestConnPoolCreationAndDeletion(t *testing.T) {
|
||||||
SuperUsername: superUserName,
|
SuperUsername: superUserName,
|
||||||
ReplicationUsername: replicationUserName,
|
ReplicationUsername: replicationUserName,
|
||||||
},
|
},
|
||||||
ConnectionPool: config.ConnectionPool{
|
ConnectionPooler: config.ConnectionPooler{
|
||||||
ConnPoolDefaultCPURequest: "100m",
|
ConnectionPoolerDefaultCPURequest: "100m",
|
||||||
ConnPoolDefaultCPULimit: "100m",
|
ConnectionPoolerDefaultCPULimit: "100m",
|
||||||
ConnPoolDefaultMemoryRequest: "100Mi",
|
ConnectionPoolerDefaultMemoryRequest: "100Mi",
|
||||||
ConnPoolDefaultMemoryLimit: "100Mi",
|
ConnectionPoolerDefaultMemoryLimit: "100Mi",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, k8sutil.NewMockKubernetesClient(), acidv1.Postgresql{}, logger)
|
}, k8sutil.NewMockKubernetesClient(), acidv1.Postgresql{}, logger)
|
||||||
|
|
@ -45,31 +45,31 @@ func TestConnPoolCreationAndDeletion(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
cluster.Spec = acidv1.PostgresSpec{
|
cluster.Spec = acidv1.PostgresSpec{
|
||||||
ConnectionPool: &acidv1.ConnectionPool{},
|
ConnectionPooler: &acidv1.ConnectionPooler{},
|
||||||
}
|
}
|
||||||
poolResources, err := cluster.createConnectionPool(mockInstallLookupFunction)
|
poolerResources, err := cluster.createConnectionPooler(mockInstallLookupFunction)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("%s: Cannot create connection pool, %s, %+v",
|
t.Errorf("%s: Cannot create connection pooler, %s, %+v",
|
||||||
testName, err, poolResources)
|
testName, err, poolerResources)
|
||||||
}
|
}
|
||||||
|
|
||||||
if poolResources.Deployment == nil {
|
if poolerResources.Deployment == nil {
|
||||||
t.Errorf("%s: Connection pool deployment is empty", testName)
|
t.Errorf("%s: Connection pooler deployment is empty", testName)
|
||||||
}
|
}
|
||||||
|
|
||||||
if poolResources.Service == nil {
|
if poolerResources.Service == nil {
|
||||||
t.Errorf("%s: Connection pool service is empty", testName)
|
t.Errorf("%s: Connection pooler service is empty", testName)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = cluster.deleteConnectionPool()
|
err = cluster.deleteConnectionPooler()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("%s: Cannot delete connection pool, %s", testName, err)
|
t.Errorf("%s: Cannot delete connection pooler, %s", testName, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNeedConnPool(t *testing.T) {
|
func TestNeedConnectionPooler(t *testing.T) {
|
||||||
testName := "Test how connection pool can be enabled"
|
testName := "Test how connection pooler can be enabled"
|
||||||
var cluster = New(
|
var cluster = New(
|
||||||
Config{
|
Config{
|
||||||
OpConfig: config.Config{
|
OpConfig: config.Config{
|
||||||
|
|
@ -78,50 +78,50 @@ func TestNeedConnPool(t *testing.T) {
|
||||||
SuperUsername: superUserName,
|
SuperUsername: superUserName,
|
||||||
ReplicationUsername: replicationUserName,
|
ReplicationUsername: replicationUserName,
|
||||||
},
|
},
|
||||||
ConnectionPool: config.ConnectionPool{
|
ConnectionPooler: config.ConnectionPooler{
|
||||||
ConnPoolDefaultCPURequest: "100m",
|
ConnectionPoolerDefaultCPURequest: "100m",
|
||||||
ConnPoolDefaultCPULimit: "100m",
|
ConnectionPoolerDefaultCPULimit: "100m",
|
||||||
ConnPoolDefaultMemoryRequest: "100Mi",
|
ConnectionPoolerDefaultMemoryRequest: "100Mi",
|
||||||
ConnPoolDefaultMemoryLimit: "100Mi",
|
ConnectionPoolerDefaultMemoryLimit: "100Mi",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, k8sutil.NewMockKubernetesClient(), acidv1.Postgresql{}, logger)
|
}, k8sutil.NewMockKubernetesClient(), acidv1.Postgresql{}, logger)
|
||||||
|
|
||||||
cluster.Spec = acidv1.PostgresSpec{
|
cluster.Spec = acidv1.PostgresSpec{
|
||||||
ConnectionPool: &acidv1.ConnectionPool{},
|
ConnectionPooler: &acidv1.ConnectionPooler{},
|
||||||
}
|
}
|
||||||
|
|
||||||
if !cluster.needConnectionPool() {
|
if !cluster.needConnectionPooler() {
|
||||||
t.Errorf("%s: Connection pool is not enabled with full definition",
|
t.Errorf("%s: Connection pooler is not enabled with full definition",
|
||||||
testName)
|
testName)
|
||||||
}
|
}
|
||||||
|
|
||||||
cluster.Spec = acidv1.PostgresSpec{
|
cluster.Spec = acidv1.PostgresSpec{
|
||||||
EnableConnectionPool: boolToPointer(true),
|
EnableConnectionPooler: boolToPointer(true),
|
||||||
}
|
}
|
||||||
|
|
||||||
if !cluster.needConnectionPool() {
|
if !cluster.needConnectionPooler() {
|
||||||
t.Errorf("%s: Connection pool is not enabled with flag",
|
t.Errorf("%s: Connection pooler is not enabled with flag",
|
||||||
testName)
|
testName)
|
||||||
}
|
}
|
||||||
|
|
||||||
cluster.Spec = acidv1.PostgresSpec{
|
cluster.Spec = acidv1.PostgresSpec{
|
||||||
EnableConnectionPool: boolToPointer(false),
|
EnableConnectionPooler: boolToPointer(false),
|
||||||
ConnectionPool: &acidv1.ConnectionPool{},
|
ConnectionPooler: &acidv1.ConnectionPooler{},
|
||||||
}
|
}
|
||||||
|
|
||||||
if cluster.needConnectionPool() {
|
if cluster.needConnectionPooler() {
|
||||||
t.Errorf("%s: Connection pool is still enabled with flag being false",
|
t.Errorf("%s: Connection pooler is still enabled with flag being false",
|
||||||
testName)
|
testName)
|
||||||
}
|
}
|
||||||
|
|
||||||
cluster.Spec = acidv1.PostgresSpec{
|
cluster.Spec = acidv1.PostgresSpec{
|
||||||
EnableConnectionPool: boolToPointer(true),
|
EnableConnectionPooler: boolToPointer(true),
|
||||||
ConnectionPool: &acidv1.ConnectionPool{},
|
ConnectionPooler: &acidv1.ConnectionPooler{},
|
||||||
}
|
}
|
||||||
|
|
||||||
if !cluster.needConnectionPool() {
|
if !cluster.needConnectionPooler() {
|
||||||
t.Errorf("%s: Connection pool is not enabled with flag and full",
|
t.Errorf("%s: Connection pooler is not enabled with flag and full",
|
||||||
testName)
|
testName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -110,9 +110,9 @@ func (c *Cluster) Sync(newSpec *acidv1.Postgresql) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sync connection pool
|
// sync connection pooler
|
||||||
if err = c.syncConnectionPool(&oldSpec, newSpec, c.installLookupFunction); err != nil {
|
if err = c.syncConnectionPooler(&oldSpec, newSpec, c.installLookupFunction); err != nil {
|
||||||
return fmt.Errorf("could not sync connection pool: %v", err)
|
return fmt.Errorf("could not sync connection pooler: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
|
@ -478,12 +478,12 @@ func (c *Cluster) syncRoles() (err error) {
|
||||||
userNames = append(userNames, u.Name)
|
userNames = append(userNames, u.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.needConnectionPool() {
|
if c.needConnectionPooler() {
|
||||||
connPoolUser := c.systemUsers[constants.ConnectionPoolUserKeyName]
|
connectionPoolerUser := c.systemUsers[constants.ConnectionPoolerUserKeyName]
|
||||||
userNames = append(userNames, connPoolUser.Name)
|
userNames = append(userNames, connectionPoolerUser.Name)
|
||||||
|
|
||||||
if _, exists := c.pgUsers[connPoolUser.Name]; !exists {
|
if _, exists := c.pgUsers[connectionPoolerUser.Name]; !exists {
|
||||||
c.pgUsers[connPoolUser.Name] = connPoolUser
|
c.pgUsers[connectionPoolerUser.Name] = connectionPoolerUser
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -620,69 +620,69 @@ func (c *Cluster) syncLogicalBackupJob() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cluster) syncConnectionPool(oldSpec, newSpec *acidv1.Postgresql, lookup InstallFunction) error {
|
func (c *Cluster) syncConnectionPooler(oldSpec, newSpec *acidv1.Postgresql, lookup InstallFunction) error {
|
||||||
if c.ConnectionPool == nil {
|
if c.ConnectionPooler == nil {
|
||||||
c.ConnectionPool = &ConnectionPoolObjects{}
|
c.ConnectionPooler = &ConnectionPoolerObjects{}
|
||||||
}
|
}
|
||||||
|
|
||||||
newNeedConnPool := c.needConnectionPoolWorker(&newSpec.Spec)
|
newNeedConnectionPooler := c.needConnectionPoolerWorker(&newSpec.Spec)
|
||||||
oldNeedConnPool := c.needConnectionPoolWorker(&oldSpec.Spec)
|
oldNeedConnectionPooler := c.needConnectionPoolerWorker(&oldSpec.Spec)
|
||||||
|
|
||||||
if newNeedConnPool {
|
if newNeedConnectionPooler {
|
||||||
// Try to sync in any case. If we didn't needed connection pool before,
|
// Try to sync in any case. If we didn't needed connection pooler before,
|
||||||
// it means we want to create it. If it was already present, still sync
|
// it means we want to create it. If it was already present, still sync
|
||||||
// since it could happen that there is no difference in specs, and all
|
// since it could happen that there is no difference in specs, and all
|
||||||
// the resources are remembered, but the deployment was manualy deleted
|
// the resources are remembered, but the deployment was manually deleted
|
||||||
// in between
|
// in between
|
||||||
c.logger.Debug("syncing connection pool")
|
c.logger.Debug("syncing connection pooler")
|
||||||
|
|
||||||
// in this case also do not forget to install lookup function as for
|
// in this case also do not forget to install lookup function as for
|
||||||
// creating cluster
|
// creating cluster
|
||||||
if !oldNeedConnPool || !c.ConnectionPool.LookupFunction {
|
if !oldNeedConnectionPooler || !c.ConnectionPooler.LookupFunction {
|
||||||
newConnPool := newSpec.Spec.ConnectionPool
|
newConnectionPooler := newSpec.Spec.ConnectionPooler
|
||||||
|
|
||||||
specSchema := ""
|
specSchema := ""
|
||||||
specUser := ""
|
specUser := ""
|
||||||
|
|
||||||
if newConnPool != nil {
|
if newConnectionPooler != nil {
|
||||||
specSchema = newConnPool.Schema
|
specSchema = newConnectionPooler.Schema
|
||||||
specUser = newConnPool.User
|
specUser = newConnectionPooler.User
|
||||||
}
|
}
|
||||||
|
|
||||||
schema := util.Coalesce(
|
schema := util.Coalesce(
|
||||||
specSchema,
|
specSchema,
|
||||||
c.OpConfig.ConnectionPool.Schema)
|
c.OpConfig.ConnectionPooler.Schema)
|
||||||
|
|
||||||
user := util.Coalesce(
|
user := util.Coalesce(
|
||||||
specUser,
|
specUser,
|
||||||
c.OpConfig.ConnectionPool.User)
|
c.OpConfig.ConnectionPooler.User)
|
||||||
|
|
||||||
if err := lookup(schema, user); err != nil {
|
if err := lookup(schema, user); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.syncConnectionPoolWorker(oldSpec, newSpec); err != nil {
|
if err := c.syncConnectionPoolerWorker(oldSpec, newSpec); err != nil {
|
||||||
c.logger.Errorf("could not sync connection pool: %v", err)
|
c.logger.Errorf("could not sync connection pooler: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if oldNeedConnPool && !newNeedConnPool {
|
if oldNeedConnectionPooler && !newNeedConnectionPooler {
|
||||||
// delete and cleanup resources
|
// delete and cleanup resources
|
||||||
if err := c.deleteConnectionPool(); err != nil {
|
if err := c.deleteConnectionPooler(); err != nil {
|
||||||
c.logger.Warningf("could not remove connection pool: %v", err)
|
c.logger.Warningf("could not remove connection pooler: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !oldNeedConnPool && !newNeedConnPool {
|
if !oldNeedConnectionPooler && !newNeedConnectionPooler {
|
||||||
// delete and cleanup resources if not empty
|
// delete and cleanup resources if not empty
|
||||||
if c.ConnectionPool != nil &&
|
if c.ConnectionPooler != nil &&
|
||||||
(c.ConnectionPool.Deployment != nil ||
|
(c.ConnectionPooler.Deployment != nil ||
|
||||||
c.ConnectionPool.Service != nil) {
|
c.ConnectionPooler.Service != nil) {
|
||||||
|
|
||||||
if err := c.deleteConnectionPool(); err != nil {
|
if err := c.deleteConnectionPooler(); err != nil {
|
||||||
c.logger.Warningf("could not remove connection pool: %v", err)
|
c.logger.Warningf("could not remove connection pooler: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -690,22 +690,22 @@ func (c *Cluster) syncConnectionPool(oldSpec, newSpec *acidv1.Postgresql, lookup
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Synchronize connection pool resources. Effectively we're interested only in
|
// Synchronize connection pooler resources. Effectively we're interested only in
|
||||||
// synchronizing the corresponding deployment, but in case of deployment or
|
// synchronizing the corresponding deployment, but in case of deployment or
|
||||||
// service is missing, create it. After checking, also remember an object for
|
// service is missing, create it. After checking, also remember an object for
|
||||||
// the future references.
|
// the future references.
|
||||||
func (c *Cluster) syncConnectionPoolWorker(oldSpec, newSpec *acidv1.Postgresql) error {
|
func (c *Cluster) syncConnectionPoolerWorker(oldSpec, newSpec *acidv1.Postgresql) error {
|
||||||
deployment, err := c.KubeClient.
|
deployment, err := c.KubeClient.
|
||||||
Deployments(c.Namespace).
|
Deployments(c.Namespace).
|
||||||
Get(context.TODO(), c.connPoolName(), metav1.GetOptions{})
|
Get(context.TODO(), c.connectionPoolerName(), metav1.GetOptions{})
|
||||||
|
|
||||||
if err != nil && k8sutil.ResourceNotFound(err) {
|
if err != nil && k8sutil.ResourceNotFound(err) {
|
||||||
msg := "Deployment %s for connection pool synchronization is not found, create it"
|
msg := "Deployment %s for connection pooler synchronization is not found, create it"
|
||||||
c.logger.Warningf(msg, c.connPoolName())
|
c.logger.Warningf(msg, c.connectionPoolerName())
|
||||||
|
|
||||||
deploymentSpec, err := c.generateConnPoolDeployment(&newSpec.Spec)
|
deploymentSpec, err := c.generateConnectionPoolerDeployment(&newSpec.Spec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg = "could not generate deployment for connection pool: %v"
|
msg = "could not generate deployment for connection pooler: %v"
|
||||||
return fmt.Errorf(msg, err)
|
return fmt.Errorf(msg, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -717,31 +717,31 @@ func (c *Cluster) syncConnectionPoolWorker(oldSpec, newSpec *acidv1.Postgresql)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ConnectionPool.Deployment = deployment
|
c.ConnectionPooler.Deployment = deployment
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return fmt.Errorf("could not get connection pool deployment to sync: %v", err)
|
return fmt.Errorf("could not get connection pooler deployment to sync: %v", err)
|
||||||
} else {
|
} else {
|
||||||
c.ConnectionPool.Deployment = deployment
|
c.ConnectionPooler.Deployment = deployment
|
||||||
|
|
||||||
// actual synchronization
|
// actual synchronization
|
||||||
oldConnPool := oldSpec.Spec.ConnectionPool
|
oldConnectionPooler := oldSpec.Spec.ConnectionPooler
|
||||||
newConnPool := newSpec.Spec.ConnectionPool
|
newConnectionPooler := newSpec.Spec.ConnectionPooler
|
||||||
specSync, specReason := c.needSyncConnPoolSpecs(oldConnPool, newConnPool)
|
specSync, specReason := c.needSyncConnectionPoolerSpecs(oldConnectionPooler, newConnectionPooler)
|
||||||
defaultsSync, defaultsReason := c.needSyncConnPoolDefaults(newConnPool, deployment)
|
defaultsSync, defaultsReason := c.needSyncConnectionPoolerDefaults(newConnectionPooler, deployment)
|
||||||
reason := append(specReason, defaultsReason...)
|
reason := append(specReason, defaultsReason...)
|
||||||
if specSync || defaultsSync {
|
if specSync || defaultsSync {
|
||||||
c.logger.Infof("Update connection pool deployment %s, reason: %+v",
|
c.logger.Infof("Update connection pooler deployment %s, reason: %+v",
|
||||||
c.connPoolName(), reason)
|
c.connectionPoolerName(), reason)
|
||||||
|
|
||||||
newDeploymentSpec, err := c.generateConnPoolDeployment(&newSpec.Spec)
|
newDeploymentSpec, err := c.generateConnectionPoolerDeployment(&newSpec.Spec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg := "could not generate deployment for connection pool: %v"
|
msg := "could not generate deployment for connection pooler: %v"
|
||||||
return fmt.Errorf(msg, err)
|
return fmt.Errorf(msg, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
oldDeploymentSpec := c.ConnectionPool.Deployment
|
oldDeploymentSpec := c.ConnectionPooler.Deployment
|
||||||
|
|
||||||
deployment, err := c.updateConnPoolDeployment(
|
deployment, err := c.updateConnectionPoolerDeployment(
|
||||||
oldDeploymentSpec,
|
oldDeploymentSpec,
|
||||||
newDeploymentSpec)
|
newDeploymentSpec)
|
||||||
|
|
||||||
|
|
@ -749,20 +749,20 @@ func (c *Cluster) syncConnectionPoolWorker(oldSpec, newSpec *acidv1.Postgresql)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ConnectionPool.Deployment = deployment
|
c.ConnectionPooler.Deployment = deployment
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
service, err := c.KubeClient.
|
service, err := c.KubeClient.
|
||||||
Services(c.Namespace).
|
Services(c.Namespace).
|
||||||
Get(context.TODO(), c.connPoolName(), metav1.GetOptions{})
|
Get(context.TODO(), c.connectionPoolerName(), metav1.GetOptions{})
|
||||||
|
|
||||||
if err != nil && k8sutil.ResourceNotFound(err) {
|
if err != nil && k8sutil.ResourceNotFound(err) {
|
||||||
msg := "Service %s for connection pool synchronization is not found, create it"
|
msg := "Service %s for connection pooler synchronization is not found, create it"
|
||||||
c.logger.Warningf(msg, c.connPoolName())
|
c.logger.Warningf(msg, c.connectionPoolerName())
|
||||||
|
|
||||||
serviceSpec := c.generateConnPoolService(&newSpec.Spec)
|
serviceSpec := c.generateConnectionPoolerService(&newSpec.Spec)
|
||||||
service, err := c.KubeClient.
|
service, err := c.KubeClient.
|
||||||
Services(serviceSpec.Namespace).
|
Services(serviceSpec.Namespace).
|
||||||
Create(context.TODO(), serviceSpec, metav1.CreateOptions{})
|
Create(context.TODO(), serviceSpec, metav1.CreateOptions{})
|
||||||
|
|
@ -771,12 +771,12 @@ func (c *Cluster) syncConnectionPoolWorker(oldSpec, newSpec *acidv1.Postgresql)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ConnectionPool.Service = service
|
c.ConnectionPooler.Service = service
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return fmt.Errorf("could not get connection pool service to sync: %v", err)
|
return fmt.Errorf("could not get connection pooler service to sync: %v", err)
|
||||||
} else {
|
} else {
|
||||||
// Service updates are not supported and probably not that useful anyway
|
// Service updates are not supported and probably not that useful anyway
|
||||||
c.ConnectionPool.Service = service
|
c.ConnectionPooler.Service = service
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,8 @@ func int32ToPointer(value int32) *int32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
func deploymentUpdated(cluster *Cluster, err error) error {
|
func deploymentUpdated(cluster *Cluster, err error) error {
|
||||||
if cluster.ConnectionPool.Deployment.Spec.Replicas == nil ||
|
if cluster.ConnectionPooler.Deployment.Spec.Replicas == nil ||
|
||||||
*cluster.ConnectionPool.Deployment.Spec.Replicas != 2 {
|
*cluster.ConnectionPooler.Deployment.Spec.Replicas != 2 {
|
||||||
return fmt.Errorf("Wrong nubmer of instances")
|
return fmt.Errorf("Wrong nubmer of instances")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -27,15 +27,15 @@ func deploymentUpdated(cluster *Cluster, err error) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func objectsAreSaved(cluster *Cluster, err error) error {
|
func objectsAreSaved(cluster *Cluster, err error) error {
|
||||||
if cluster.ConnectionPool == nil {
|
if cluster.ConnectionPooler == nil {
|
||||||
return fmt.Errorf("Connection pool resources are empty")
|
return fmt.Errorf("Connection pooler resources are empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
if cluster.ConnectionPool.Deployment == nil {
|
if cluster.ConnectionPooler.Deployment == nil {
|
||||||
return fmt.Errorf("Deployment was not saved")
|
return fmt.Errorf("Deployment was not saved")
|
||||||
}
|
}
|
||||||
|
|
||||||
if cluster.ConnectionPool.Service == nil {
|
if cluster.ConnectionPooler.Service == nil {
|
||||||
return fmt.Errorf("Service was not saved")
|
return fmt.Errorf("Service was not saved")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,15 +43,15 @@ func objectsAreSaved(cluster *Cluster, err error) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func objectsAreDeleted(cluster *Cluster, err error) error {
|
func objectsAreDeleted(cluster *Cluster, err error) error {
|
||||||
if cluster.ConnectionPool != nil {
|
if cluster.ConnectionPooler != nil {
|
||||||
return fmt.Errorf("Connection pool was not deleted")
|
return fmt.Errorf("Connection pooler was not deleted")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConnPoolSynchronization(t *testing.T) {
|
func TestConnectionPoolerSynchronization(t *testing.T) {
|
||||||
testName := "Test connection pool synchronization"
|
testName := "Test connection pooler synchronization"
|
||||||
var cluster = New(
|
var cluster = New(
|
||||||
Config{
|
Config{
|
||||||
OpConfig: config.Config{
|
OpConfig: config.Config{
|
||||||
|
|
@ -60,11 +60,11 @@ func TestConnPoolSynchronization(t *testing.T) {
|
||||||
SuperUsername: superUserName,
|
SuperUsername: superUserName,
|
||||||
ReplicationUsername: replicationUserName,
|
ReplicationUsername: replicationUserName,
|
||||||
},
|
},
|
||||||
ConnectionPool: config.ConnectionPool{
|
ConnectionPooler: config.ConnectionPooler{
|
||||||
ConnPoolDefaultCPURequest: "100m",
|
ConnectionPoolerDefaultCPURequest: "100m",
|
||||||
ConnPoolDefaultCPULimit: "100m",
|
ConnectionPoolerDefaultCPULimit: "100m",
|
||||||
ConnPoolDefaultMemoryRequest: "100Mi",
|
ConnectionPoolerDefaultMemoryRequest: "100Mi",
|
||||||
ConnPoolDefaultMemoryLimit: "100Mi",
|
ConnectionPoolerDefaultMemoryLimit: "100Mi",
|
||||||
NumberOfInstances: int32ToPointer(1),
|
NumberOfInstances: int32ToPointer(1),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -84,15 +84,15 @@ func TestConnPoolSynchronization(t *testing.T) {
|
||||||
|
|
||||||
clusterDirtyMock := *cluster
|
clusterDirtyMock := *cluster
|
||||||
clusterDirtyMock.KubeClient = k8sutil.NewMockKubernetesClient()
|
clusterDirtyMock.KubeClient = k8sutil.NewMockKubernetesClient()
|
||||||
clusterDirtyMock.ConnectionPool = &ConnectionPoolObjects{
|
clusterDirtyMock.ConnectionPooler = &ConnectionPoolerObjects{
|
||||||
Deployment: &appsv1.Deployment{},
|
Deployment: &appsv1.Deployment{},
|
||||||
Service: &v1.Service{},
|
Service: &v1.Service{},
|
||||||
}
|
}
|
||||||
|
|
||||||
clusterNewDefaultsMock := *cluster
|
clusterNewDefaultsMock := *cluster
|
||||||
clusterNewDefaultsMock.KubeClient = k8sutil.NewMockKubernetesClient()
|
clusterNewDefaultsMock.KubeClient = k8sutil.NewMockKubernetesClient()
|
||||||
cluster.OpConfig.ConnectionPool.Image = "pooler:2.0"
|
cluster.OpConfig.ConnectionPooler.Image = "pooler:2.0"
|
||||||
cluster.OpConfig.ConnectionPool.NumberOfInstances = int32ToPointer(2)
|
cluster.OpConfig.ConnectionPooler.NumberOfInstances = int32ToPointer(2)
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
subTest string
|
subTest string
|
||||||
|
|
@ -105,12 +105,12 @@ func TestConnPoolSynchronization(t *testing.T) {
|
||||||
subTest: "create if doesn't exist",
|
subTest: "create if doesn't exist",
|
||||||
oldSpec: &acidv1.Postgresql{
|
oldSpec: &acidv1.Postgresql{
|
||||||
Spec: acidv1.PostgresSpec{
|
Spec: acidv1.PostgresSpec{
|
||||||
ConnectionPool: &acidv1.ConnectionPool{},
|
ConnectionPooler: &acidv1.ConnectionPooler{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
newSpec: &acidv1.Postgresql{
|
newSpec: &acidv1.Postgresql{
|
||||||
Spec: acidv1.PostgresSpec{
|
Spec: acidv1.PostgresSpec{
|
||||||
ConnectionPool: &acidv1.ConnectionPool{},
|
ConnectionPooler: &acidv1.ConnectionPooler{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cluster: &clusterMissingObjects,
|
cluster: &clusterMissingObjects,
|
||||||
|
|
@ -123,7 +123,7 @@ func TestConnPoolSynchronization(t *testing.T) {
|
||||||
},
|
},
|
||||||
newSpec: &acidv1.Postgresql{
|
newSpec: &acidv1.Postgresql{
|
||||||
Spec: acidv1.PostgresSpec{
|
Spec: acidv1.PostgresSpec{
|
||||||
EnableConnectionPool: boolToPointer(true),
|
EnableConnectionPooler: boolToPointer(true),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cluster: &clusterMissingObjects,
|
cluster: &clusterMissingObjects,
|
||||||
|
|
@ -136,7 +136,7 @@ func TestConnPoolSynchronization(t *testing.T) {
|
||||||
},
|
},
|
||||||
newSpec: &acidv1.Postgresql{
|
newSpec: &acidv1.Postgresql{
|
||||||
Spec: acidv1.PostgresSpec{
|
Spec: acidv1.PostgresSpec{
|
||||||
ConnectionPool: &acidv1.ConnectionPool{},
|
ConnectionPooler: &acidv1.ConnectionPooler{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cluster: &clusterMissingObjects,
|
cluster: &clusterMissingObjects,
|
||||||
|
|
@ -146,7 +146,7 @@ func TestConnPoolSynchronization(t *testing.T) {
|
||||||
subTest: "delete if not needed",
|
subTest: "delete if not needed",
|
||||||
oldSpec: &acidv1.Postgresql{
|
oldSpec: &acidv1.Postgresql{
|
||||||
Spec: acidv1.PostgresSpec{
|
Spec: acidv1.PostgresSpec{
|
||||||
ConnectionPool: &acidv1.ConnectionPool{},
|
ConnectionPooler: &acidv1.ConnectionPooler{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
newSpec: &acidv1.Postgresql{
|
newSpec: &acidv1.Postgresql{
|
||||||
|
|
@ -170,14 +170,14 @@ func TestConnPoolSynchronization(t *testing.T) {
|
||||||
subTest: "update deployment",
|
subTest: "update deployment",
|
||||||
oldSpec: &acidv1.Postgresql{
|
oldSpec: &acidv1.Postgresql{
|
||||||
Spec: acidv1.PostgresSpec{
|
Spec: acidv1.PostgresSpec{
|
||||||
ConnectionPool: &acidv1.ConnectionPool{
|
ConnectionPooler: &acidv1.ConnectionPooler{
|
||||||
NumberOfInstances: int32ToPointer(1),
|
NumberOfInstances: int32ToPointer(1),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
newSpec: &acidv1.Postgresql{
|
newSpec: &acidv1.Postgresql{
|
||||||
Spec: acidv1.PostgresSpec{
|
Spec: acidv1.PostgresSpec{
|
||||||
ConnectionPool: &acidv1.ConnectionPool{
|
ConnectionPooler: &acidv1.ConnectionPooler{
|
||||||
NumberOfInstances: int32ToPointer(2),
|
NumberOfInstances: int32ToPointer(2),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -189,12 +189,12 @@ func TestConnPoolSynchronization(t *testing.T) {
|
||||||
subTest: "update image from changed defaults",
|
subTest: "update image from changed defaults",
|
||||||
oldSpec: &acidv1.Postgresql{
|
oldSpec: &acidv1.Postgresql{
|
||||||
Spec: acidv1.PostgresSpec{
|
Spec: acidv1.PostgresSpec{
|
||||||
ConnectionPool: &acidv1.ConnectionPool{},
|
ConnectionPooler: &acidv1.ConnectionPooler{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
newSpec: &acidv1.Postgresql{
|
newSpec: &acidv1.Postgresql{
|
||||||
Spec: acidv1.PostgresSpec{
|
Spec: acidv1.PostgresSpec{
|
||||||
ConnectionPool: &acidv1.ConnectionPool{},
|
ConnectionPooler: &acidv1.ConnectionPooler{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cluster: &clusterNewDefaultsMock,
|
cluster: &clusterNewDefaultsMock,
|
||||||
|
|
@ -202,7 +202,7 @@ func TestConnPoolSynchronization(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
err := tt.cluster.syncConnectionPool(tt.oldSpec, tt.newSpec, mockInstallLookupFunction)
|
err := tt.cluster.syncConnectionPooler(tt.oldSpec, tt.newSpec, mockInstallLookupFunction)
|
||||||
|
|
||||||
if err := tt.check(tt.cluster, err); err != nil {
|
if err := tt.check(tt.cluster, err); err != nil {
|
||||||
t.Errorf("%s [%s]: Could not synchronize, %+v",
|
t.Errorf("%s [%s]: Could not synchronize, %+v",
|
||||||
|
|
|
||||||
|
|
@ -415,24 +415,24 @@ func (c *Cluster) labelsSelector() *metav1.LabelSelector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return connection pool labels selector, which should from one point of view
|
// Return connection pooler labels selector, which should from one point of view
|
||||||
// inherit most of the labels from the cluster itself, but at the same time
|
// inherit most of the labels from the cluster itself, but at the same time
|
||||||
// have e.g. different `application` label, so that recreatePod operation will
|
// have e.g. different `application` label, so that recreatePod operation will
|
||||||
// not interfere with it (it lists all the pods via labels, and if there would
|
// not interfere with it (it lists all the pods via labels, and if there would
|
||||||
// be no difference, it will recreate also pooler pods).
|
// be no difference, it will recreate also pooler pods).
|
||||||
func (c *Cluster) connPoolLabelsSelector() *metav1.LabelSelector {
|
func (c *Cluster) connectionPoolerLabelsSelector() *metav1.LabelSelector {
|
||||||
connPoolLabels := labels.Set(map[string]string{})
|
connectionPoolerLabels := labels.Set(map[string]string{})
|
||||||
|
|
||||||
extraLabels := labels.Set(map[string]string{
|
extraLabels := labels.Set(map[string]string{
|
||||||
"connection-pool": c.connPoolName(),
|
"connection-pooler": c.connectionPoolerName(),
|
||||||
"application": "db-connection-pool",
|
"application": "db-connection-pooler",
|
||||||
})
|
})
|
||||||
|
|
||||||
connPoolLabels = labels.Merge(connPoolLabels, c.labelsSet(false))
|
connectionPoolerLabels = labels.Merge(connectionPoolerLabels, c.labelsSet(false))
|
||||||
connPoolLabels = labels.Merge(connPoolLabels, extraLabels)
|
connectionPoolerLabels = labels.Merge(connectionPoolerLabels, extraLabels)
|
||||||
|
|
||||||
return &metav1.LabelSelector{
|
return &metav1.LabelSelector{
|
||||||
MatchLabels: connPoolLabels,
|
MatchLabels: connectionPoolerLabels,
|
||||||
MatchExpressions: nil,
|
MatchExpressions: nil,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -510,14 +510,14 @@ func (c *Cluster) patroniUsesKubernetes() bool {
|
||||||
return c.OpConfig.EtcdHost == ""
|
return c.OpConfig.EtcdHost == ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cluster) needConnectionPoolWorker(spec *acidv1.PostgresSpec) bool {
|
func (c *Cluster) needConnectionPoolerWorker(spec *acidv1.PostgresSpec) bool {
|
||||||
if spec.EnableConnectionPool == nil {
|
if spec.EnableConnectionPooler == nil {
|
||||||
return spec.ConnectionPool != nil
|
return spec.ConnectionPooler != nil
|
||||||
} else {
|
} else {
|
||||||
return *spec.EnableConnectionPool
|
return *spec.EnableConnectionPooler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cluster) needConnectionPool() bool {
|
func (c *Cluster) needConnectionPooler() bool {
|
||||||
return c.needConnectionPoolWorker(&c.Spec)
|
return c.needConnectionPoolerWorker(&c.Spec)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -150,51 +150,51 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
|
||||||
result.ScalyrCPULimit = fromCRD.Scalyr.ScalyrCPULimit
|
result.ScalyrCPULimit = fromCRD.Scalyr.ScalyrCPULimit
|
||||||
result.ScalyrMemoryLimit = fromCRD.Scalyr.ScalyrMemoryLimit
|
result.ScalyrMemoryLimit = fromCRD.Scalyr.ScalyrMemoryLimit
|
||||||
|
|
||||||
// Connection pool. Looks like we can't use defaulting in CRD before 1.17,
|
// Connection pooler. Looks like we can't use defaulting in CRD before 1.17,
|
||||||
// so ensure default values here.
|
// so ensure default values here.
|
||||||
result.ConnectionPool.NumberOfInstances = util.CoalesceInt32(
|
result.ConnectionPooler.NumberOfInstances = util.CoalesceInt32(
|
||||||
fromCRD.ConnectionPool.NumberOfInstances,
|
fromCRD.ConnectionPooler.NumberOfInstances,
|
||||||
int32ToPointer(2))
|
int32ToPointer(2))
|
||||||
|
|
||||||
result.ConnectionPool.NumberOfInstances = util.MaxInt32(
|
result.ConnectionPooler.NumberOfInstances = util.MaxInt32(
|
||||||
result.ConnectionPool.NumberOfInstances,
|
result.ConnectionPooler.NumberOfInstances,
|
||||||
int32ToPointer(2))
|
int32ToPointer(2))
|
||||||
|
|
||||||
result.ConnectionPool.Schema = util.Coalesce(
|
result.ConnectionPooler.Schema = util.Coalesce(
|
||||||
fromCRD.ConnectionPool.Schema,
|
fromCRD.ConnectionPooler.Schema,
|
||||||
constants.ConnectionPoolSchemaName)
|
constants.ConnectionPoolerSchemaName)
|
||||||
|
|
||||||
result.ConnectionPool.User = util.Coalesce(
|
result.ConnectionPooler.User = util.Coalesce(
|
||||||
fromCRD.ConnectionPool.User,
|
fromCRD.ConnectionPooler.User,
|
||||||
constants.ConnectionPoolUserName)
|
constants.ConnectionPoolerUserName)
|
||||||
|
|
||||||
result.ConnectionPool.Image = util.Coalesce(
|
result.ConnectionPooler.Image = util.Coalesce(
|
||||||
fromCRD.ConnectionPool.Image,
|
fromCRD.ConnectionPooler.Image,
|
||||||
"registry.opensource.zalan.do/acid/pgbouncer")
|
"registry.opensource.zalan.do/acid/pgbouncer")
|
||||||
|
|
||||||
result.ConnectionPool.Mode = util.Coalesce(
|
result.ConnectionPooler.Mode = util.Coalesce(
|
||||||
fromCRD.ConnectionPool.Mode,
|
fromCRD.ConnectionPooler.Mode,
|
||||||
constants.ConnectionPoolDefaultMode)
|
constants.ConnectionPoolerDefaultMode)
|
||||||
|
|
||||||
result.ConnectionPool.ConnPoolDefaultCPURequest = util.Coalesce(
|
result.ConnectionPooler.ConnectionPoolerDefaultCPURequest = util.Coalesce(
|
||||||
fromCRD.ConnectionPool.DefaultCPURequest,
|
fromCRD.ConnectionPooler.DefaultCPURequest,
|
||||||
constants.ConnectionPoolDefaultCpuRequest)
|
constants.ConnectionPoolerDefaultCpuRequest)
|
||||||
|
|
||||||
result.ConnectionPool.ConnPoolDefaultMemoryRequest = util.Coalesce(
|
result.ConnectionPooler.ConnectionPoolerDefaultMemoryRequest = util.Coalesce(
|
||||||
fromCRD.ConnectionPool.DefaultMemoryRequest,
|
fromCRD.ConnectionPooler.DefaultMemoryRequest,
|
||||||
constants.ConnectionPoolDefaultMemoryRequest)
|
constants.ConnectionPoolerDefaultMemoryRequest)
|
||||||
|
|
||||||
result.ConnectionPool.ConnPoolDefaultCPULimit = util.Coalesce(
|
result.ConnectionPooler.ConnectionPoolerDefaultCPULimit = util.Coalesce(
|
||||||
fromCRD.ConnectionPool.DefaultCPULimit,
|
fromCRD.ConnectionPooler.DefaultCPULimit,
|
||||||
constants.ConnectionPoolDefaultCpuLimit)
|
constants.ConnectionPoolerDefaultCpuLimit)
|
||||||
|
|
||||||
result.ConnectionPool.ConnPoolDefaultMemoryLimit = util.Coalesce(
|
result.ConnectionPooler.ConnectionPoolerDefaultMemoryLimit = util.Coalesce(
|
||||||
fromCRD.ConnectionPool.DefaultMemoryLimit,
|
fromCRD.ConnectionPooler.DefaultMemoryLimit,
|
||||||
constants.ConnectionPoolDefaultMemoryLimit)
|
constants.ConnectionPoolerDefaultMemoryLimit)
|
||||||
|
|
||||||
result.ConnectionPool.MaxDBConnections = util.CoalesceInt32(
|
result.ConnectionPooler.MaxDBConnections = util.CoalesceInt32(
|
||||||
fromCRD.ConnectionPool.MaxDBConnections,
|
fromCRD.ConnectionPooler.MaxDBConnections,
|
||||||
int32ToPointer(constants.ConnPoolMaxDBConnections))
|
int32ToPointer(constants.ConnectionPoolerMaxDBConnections))
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ const (
|
||||||
RoleOriginInfrastructure
|
RoleOriginInfrastructure
|
||||||
RoleOriginTeamsAPI
|
RoleOriginTeamsAPI
|
||||||
RoleOriginSystem
|
RoleOriginSystem
|
||||||
RoleConnectionPool
|
RoleConnectionPooler
|
||||||
)
|
)
|
||||||
|
|
||||||
type syncUserOperation int
|
type syncUserOperation int
|
||||||
|
|
@ -180,8 +180,8 @@ func (r RoleOrigin) String() string {
|
||||||
return "teams API role"
|
return "teams API role"
|
||||||
case RoleOriginSystem:
|
case RoleOriginSystem:
|
||||||
return "system role"
|
return "system role"
|
||||||
case RoleConnectionPool:
|
case RoleConnectionPooler:
|
||||||
return "connection pool role"
|
return "connection pooler role"
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("bogus role origin value %d", r))
|
panic(fmt.Sprintf("bogus role origin value %d", r))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -85,17 +85,17 @@ type LogicalBackup struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Operator options for connection pooler
|
// Operator options for connection pooler
|
||||||
type ConnectionPool struct {
|
type ConnectionPooler struct {
|
||||||
NumberOfInstances *int32 `name:"connection_pool_number_of_instances" default:"2"`
|
NumberOfInstances *int32 `name:"connection_pooler_number_of_instances" default:"2"`
|
||||||
Schema string `name:"connection_pool_schema" default:"pooler"`
|
Schema string `name:"connection_pooler_schema" default:"pooler"`
|
||||||
User string `name:"connection_pool_user" default:"pooler"`
|
User string `name:"connection_pooler_user" default:"pooler"`
|
||||||
Image string `name:"connection_pool_image" default:"registry.opensource.zalan.do/acid/pgbouncer"`
|
Image string `name:"connection_pooler_image" default:"registry.opensource.zalan.do/acid/pgbouncer"`
|
||||||
Mode string `name:"connection_pool_mode" default:"transaction"`
|
Mode string `name:"connection_pooler_mode" default:"transaction"`
|
||||||
MaxDBConnections *int32 `name:"connection_pool_max_db_connections" default:"60"`
|
MaxDBConnections *int32 `name:"connection_pooler_max_db_connections" default:"60"`
|
||||||
ConnPoolDefaultCPURequest string `name:"connection_pool_default_cpu_request" default:"500m"`
|
ConnectionPoolerDefaultCPURequest string `name:"connection_pooler_default_cpu_request" default:"500m"`
|
||||||
ConnPoolDefaultMemoryRequest string `name:"connection_pool_default_memory_request" default:"100Mi"`
|
ConnectionPoolerDefaultMemoryRequest string `name:"connection_pooler_default_memory_request" default:"100Mi"`
|
||||||
ConnPoolDefaultCPULimit string `name:"connection_pool_default_cpu_limit" default:"1"`
|
ConnectionPoolerDefaultCPULimit string `name:"connection_pooler_default_cpu_limit" default:"1"`
|
||||||
ConnPoolDefaultMemoryLimit string `name:"connection_pool_default_memory_limit" default:"100Mi"`
|
ConnectionPoolerDefaultMemoryLimit string `name:"connection_pooler_default_memory_limit" default:"100Mi"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config describes operator config
|
// Config describes operator config
|
||||||
|
|
@ -105,7 +105,7 @@ type Config struct {
|
||||||
Auth
|
Auth
|
||||||
Scalyr
|
Scalyr
|
||||||
LogicalBackup
|
LogicalBackup
|
||||||
ConnectionPool
|
ConnectionPooler
|
||||||
|
|
||||||
WatchedNamespace string `name:"watched_namespace"` // special values: "*" means 'watch all namespaces', the empty string "" means 'watch a namespace where operator is deployed to'
|
WatchedNamespace string `name:"watched_namespace"` // special values: "*" means 'watch all namespaces', the empty string "" means 'watch a namespace where operator is deployed to'
|
||||||
EtcdHost string `name:"etcd_host" default:""` // special values: the empty string "" means Patroni will use K8s as a DCS
|
EtcdHost string `name:"etcd_host" default:""` // special values: the empty string "" means Patroni will use K8s as a DCS
|
||||||
|
|
@ -213,9 +213,9 @@ func validate(cfg *Config) (err error) {
|
||||||
err = fmt.Errorf("number of workers should be higher than 0")
|
err = fmt.Errorf("number of workers should be higher than 0")
|
||||||
}
|
}
|
||||||
|
|
||||||
if *cfg.ConnectionPool.NumberOfInstances < constants.ConnPoolMinInstances {
|
if *cfg.ConnectionPooler.NumberOfInstances < constants.ConnectionPoolerMinInstances {
|
||||||
msg := "number of connection pool instances should be higher than %d"
|
msg := "number of connection pooler instances should be higher than %d"
|
||||||
err = fmt.Errorf(msg, constants.ConnPoolMinInstances)
|
err = fmt.Errorf(msg, constants.ConnectionPoolerMinInstances)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
package constants
|
package constants
|
||||||
|
|
||||||
// Connection pool specific constants
|
// Connection pooler specific constants
|
||||||
const (
|
const (
|
||||||
ConnectionPoolUserName = "pooler"
|
ConnectionPoolerUserName = "pooler"
|
||||||
ConnectionPoolSchemaName = "pooler"
|
ConnectionPoolerSchemaName = "pooler"
|
||||||
ConnectionPoolDefaultType = "pgbouncer"
|
ConnectionPoolerDefaultType = "pgbouncer"
|
||||||
ConnectionPoolDefaultMode = "transaction"
|
ConnectionPoolerDefaultMode = "transaction"
|
||||||
ConnectionPoolDefaultCpuRequest = "500m"
|
ConnectionPoolerDefaultCpuRequest = "500m"
|
||||||
ConnectionPoolDefaultCpuLimit = "1"
|
ConnectionPoolerDefaultCpuLimit = "1"
|
||||||
ConnectionPoolDefaultMemoryRequest = "100Mi"
|
ConnectionPoolerDefaultMemoryRequest = "100Mi"
|
||||||
ConnectionPoolDefaultMemoryLimit = "100Mi"
|
ConnectionPoolerDefaultMemoryLimit = "100Mi"
|
||||||
|
|
||||||
ConnPoolContainer = 0
|
ConnectionPoolerContainer = 0
|
||||||
ConnPoolMaxDBConnections = 60
|
ConnectionPoolerMaxDBConnections = 60
|
||||||
ConnPoolMaxClientConnections = 10000
|
ConnectionPoolerMaxClientConnections = 10000
|
||||||
ConnPoolMinInstances = 2
|
ConnectionPoolerMinInstances = 2
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ package constants
|
||||||
const (
|
const (
|
||||||
PasswordLength = 64
|
PasswordLength = 64
|
||||||
SuperuserKeyName = "superuser"
|
SuperuserKeyName = "superuser"
|
||||||
ConnectionPoolUserKeyName = "pooler"
|
ConnectionPoolerUserKeyName = "pooler"
|
||||||
ReplicationUserKeyName = "replication"
|
ReplicationUserKeyName = "replication"
|
||||||
RoleFlagSuperuser = "SUPERUSER"
|
RoleFlagSuperuser = "SUPERUSER"
|
||||||
RoleFlagInherit = "INHERIT"
|
RoleFlagInherit = "INHERIT"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue