-
Notifications
You must be signed in to change notification settings - Fork 1k
[WIP] Connection pooler #799
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
||
// Options for connection pooler | ||
type ConnectionPool struct { | ||
NumberOfInstances *int32 `json:"instancesNumber,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My opinion is this is too many options.
pooling:
[numberOfPods: 2 #default to op config]
enabled: true #default to false from opconfig
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's why most of them are optional.
I wanted originally call it "pooling", but it's too broad and not clear, what kind of "pool" are we talking about. NumberOfPods
is good.
I would slim down some Postgres manifest options in favor of a slim user configuration. If one is not happy with the number of fields we offer, it is easy to deploy pgbouncer similar to our config manually. I also think the type can probably be hidden behind the supplied Docker image given we have good env vars specced. |
Add an initial support for a connection pooler. The idea is to make it generic enough to be able to switch a corresponding docker image to change from pgbouncer to e.g. odyssey. Operator needs to create a deployment with pooler and a service for it to access.
Set up a proper owner reference to StatefulSet, and delete with foreground policy to not leave orphans.
With convertion for config, and start tests.
d1a756a
to
6c37520
Compare
Add synchronization logic. For now get rid of podTemplate, type fields. Add crd validation & configuration part, put retry on top of lookup function installation.
Add pool configuration into CRD & charts. Add preliminary documentation. Rename NumberOfInstances to Replicas like in Deployment. Mention couple of potential improvement points for connection pool specification.
Add an initial support for a connection pooler. The idea is to make it generic enough to be able to switch a corresponding docker image to change from pgbouncer to e.g. odyssey. Operator needs to create a deployment with pooler and a service for it to access.
Set up a proper owner reference to StatefulSet, and delete with foreground policy to not leave orphans.
With convertion for config, and start tests.
6dad833
to
2384e1e
Compare
Add synchronization logic. For now get rid of podTemplate, type fields. Add crd validation & configuration part, put retry on top of lookup function installation.
It requires more accurate lookup function synchronization and couple fixes on the way (e.g. few get rid of using schema from a user secret). For lookup function, since it's idempotend, sync it when we're not sure if it was installed (e.g. when the operator was shutdown and start sync everything at the start) and then remember that it was installed.
Small typo-like fixes and proper installing of a lookup function in all the databases.
Rename application for connection pool (ideally in the future make it configurable). Take into accounts nils for MaxInt32
if env.Name == "PGUSER" { | ||
ref := env.ValueFrom.SecretKeyRef.LocalObjectReference | ||
|
||
if ref.Name != c.credentialSecretName(config.User) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think, one check is missing here. Only apply the change if the user in the spec is empty. Same for schema.
|
||
// Check if we need to synchronize connection pool deployment due to new | ||
// defaults, that are different from what we see in the DeploymentSpec | ||
func (c *Cluster) needSyncConnPoolDefaults( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maxDBConnections
syncing is missing here (?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's mostly on purpose. Options mentioned there are reasonable to change and relatively easy to track. Not sure if there would be ever need to change maxDBConnections
(e.g. pgbouncer in all my tests was always keeping it substantially lower than this limit).
* Set minimum number of pool instances to 2 * Improve logging of sync reasons * Improve logging of a new pool role
Verify the defaul values only if the schema doesn't override them.
Since connection poolers are usually cpu bounded.
pkg/cluster/database.go
Outdated
$$ LANGUAGE plpgsql SECURITY DEFINER; | ||
|
||
REVOKE ALL ON FUNCTION {{.pool_schema}}.user_lookup(text) | ||
FROM public, {{.pool_schema}}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FROM public, {{.pool_schema}}; | |
FROM public, {{.pool_user}}; |
If you don't choose pooler
as your user, it will not get created which leads to an role does not exist
error here
|
||
// in this case also do not forget to install lookup function as for | ||
// creating cluster | ||
if !oldNeedConnPool || !c.ConnectionPool.LookupFunction { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't this need to be &&
? On each sync I see the LookupFunction logs ("Installing..."). oldNeedConnPool
should be false
on sync but ConnectionPool.LookupFunction
is set to true
within the lookup function. Or is it intended?
|
||
if *numberOfInstances < constants.ConnPoolMinInstances { | ||
msg := "Adjusted number of connection pool instances from %d to %d" | ||
c.logger.Warningf(msg, numberOfInstances, constants.ConnPoolMinInstances) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
something like coalesce(numberOfInstances, c.OpConfig.ConnectionPool.NumberOfInstances) would prevent logs to look like: Adjusted number of connection pool instances from 824638671720 to 2
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, it's just a pointer needs to be dereferenced.
pkg/cluster/database.go
Outdated
} | ||
defer func() { | ||
// in case if everything went fine this can generate a warning about | ||
// trying to close an empty connection. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seen this, too. Can't you simply remove the closeDBConn
block at the end of this function?
EDIT: Ah, you're connection to mutliple DBs. Okay. And c.pgDb != nil
cannot be used here, too?
pkg/cluster/sync.go
Outdated
|
||
if newConnPool != nil { | ||
specSchema = newConnPool.Schema | ||
specUser = newConnPool.Schema |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
specUser = newConnPool.Schema | |
specUser = newConnPool.User |
} | ||
|
||
if spec.NumberOfInstances == nil && | ||
*deployment.Spec.Replicas != *config.NumberOfInstances { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should also check if deployment.Spec.Replicas
is smaller than ConnPoolMinInstances
. Otherwise, it could be set to 1 here and then raised to 2 again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, it needs to be done on the config level (e.g. when it's created).
ConnPoolContainer = 0 | ||
ConnPoolMaxDBConnections = 60 | ||
ConnPoolMaxClientConnections = 10000 | ||
ConnPoolMinInstances = 2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Number for min instances should be the same as the internal default for Connection Pool's numberOfInstances
. So raise it to two everywhere?
Set default numberOfInstances to 2. Add verifications for config. Fix schema/user typos. Avoid closing an empty connections.
pkg/cluster/k8sres.go
Outdated
ObjectMeta: metav1.ObjectMeta{ | ||
Name: c.connPoolName(), | ||
Namespace: c.Namespace, | ||
Labels: c.labelsSet(true), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this be connPoolLabelsSelector().MatchLabels
instead, like in the pod template of this deployment?
pkg/cluster/k8sres.go
Outdated
ObjectMeta: metav1.ObjectMeta{ | ||
Name: c.connPoolName(), | ||
Namespace: c.Namespace, | ||
Labels: c.labelsSet(true), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same with deployment. Use connPoolLabelsSelector().MatchLabels
instead?
👍 |
1 similar comment
👍 |
The idea is to support connection pooler creation via operator. There is no strict dependencies, one can specify which type of pooler is required, but at the beginning probably only pgbouncer would be implemented.
Suggested changes to the manifest assume a new section:
Schema & user describe into which schema a lookup function will be installed for all available databases, and which user pooler will use for connection. A set of reasonable default values must be provided with the possibility to simply specify a full pod template for deployment.