From f82891242d6fec6bcd8526b690448c7375e07111 Mon Sep 17 00:00:00 2001 From: antruigon Date: Tue, 18 Aug 2026 12:15:26 +0200 Subject: [PATCH] Add terminationGracePeriodSeconds support for PgBouncer in Helm chart The chart ships a default PgBouncer preStop hook that drains client connections for up to 120 seconds, but the Deployment never sets terminationGracePeriodSeconds, so the Kubernetes default of 30s SIGKILLs the pod mid-drain on a node drain or eviction and cuts in-flight client connections. Every other long-running component in the chart already exposes this value; PgBouncer was the only one missing it, despite being the component that holds the database connections of all the others. The default of 120 matches the drain window of the default preStop hook. PgBouncer exits as soon as the last client connection is released, so the value is an upper bound rather than a fixed wait. --- .../templates/pgbouncer/pgbouncer-deployment.yaml | 1 + chart/tests/helm_tests/other/test_pgbouncer.py | 14 ++++++++++++++ chart/values.schema.json | 7 +++++++ chart/values.yaml | 6 ++++++ 4 files changed, 28 insertions(+) diff --git a/chart/templates/pgbouncer/pgbouncer-deployment.yaml b/chart/templates/pgbouncer/pgbouncer-deployment.yaml index b1aad5e1d0e08..97470b7910842 100644 --- a/chart/templates/pgbouncer/pgbouncer-deployment.yaml +++ b/chart/templates/pgbouncer/pgbouncer-deployment.yaml @@ -87,6 +87,7 @@ spec: {{- end }} tolerations: {{- toYaml $tolerations | nindent 8 }} topologySpreadConstraints: {{- toYaml $topologySpreadConstraints | nindent 8 }} + terminationGracePeriodSeconds: {{ .Values.pgbouncer.terminationGracePeriodSeconds }} serviceAccountName: {{ include "pgbouncer.serviceAccountName" . }} enableServiceLinks: {{ .Values.enableServiceLinks }} securityContext: {{ $securityContext | nindent 8 }} diff --git a/chart/tests/helm_tests/other/test_pgbouncer.py b/chart/tests/helm_tests/other/test_pgbouncer.py index cf69a39cab2f6..93b0f70ebc44e 100644 --- a/chart/tests/helm_tests/other/test_pgbouncer.py +++ b/chart/tests/helm_tests/other/test_pgbouncer.py @@ -453,6 +453,20 @@ def test_should_add_component_specific_labels(self): assert "labels" in jmespath.search("spec.template.metadata", docs[0]) assert jmespath.search("spec.template.metadata.labels", docs[0])["test_label"] == "test_label_value" + @pytest.mark.parametrize( + ("pgbouncer_values", "expected"), + [ + ({"enabled": True}, 120), + ({"enabled": True, "terminationGracePeriodSeconds": 30}, 30), + ], + ) + def test_pgbouncer_termination_grace_period_seconds(self, pgbouncer_values, expected): + docs = render_chart( + values={"pgbouncer": pgbouncer_values}, + show_only=["templates/pgbouncer/pgbouncer-deployment.yaml"], + ) + assert expected == jmespath.search("spec.template.spec.terminationGracePeriodSeconds", docs[0]) + class TestPgbouncerConfig: """Tests PgBouncer config.""" diff --git a/chart/values.schema.json b/chart/values.schema.json index 8ff8f46f2647c..30fbb3fcb937c 100644 --- a/chart/values.schema.json +++ b/chart/values.schema.json @@ -7873,6 +7873,13 @@ } ] }, + "terminationGracePeriodSeconds": { + "description": "Grace period for PgBouncer to finish after SIGTERM is sent from Kubernetes.", + "type": "integer", + "default": 120, + "minimum": 0, + "x-docsSection": "Kubernetes" + }, "securityContexts": { "description": "Security context definition for the PgBouncer.", "type": "object", diff --git a/chart/values.yaml b/chart/values.yaml index c1ebe441a77ef..a6f7cc9b47913 100644 --- a/chart/values.yaml +++ b/chart/values.yaml @@ -2936,6 +2936,12 @@ pgbouncer: # Allow existing queries clients to complete within 120 seconds command: ["/bin/sh", "-c", "killall -INT pgbouncer && sleep 120"] + # Grace period for PgBouncer to finish after SIGTERM is sent from Kubernetes. + # Matches the default preStop hook above, which needs up to 120 seconds to + # drain client connections; with a shorter grace period the pod is killed + # mid-drain and in-flight connections are cut. + terminationGracePeriodSeconds: 120 + metricsExporterSidecar: resources: {} # limits: