Skip to content

Commit 640de87

Browse files
[DBMON-6882] Make Postgres check teardown idempotent (#24852)
* [DBMON-6882] Make Postgres check teardown idempotent _finalize() now runs at most once. The check is already cancelled more than once in practice -- run_one_check() cancels and the integration_check fixture cancels again on teardown -- so teardown re-closed connections and re-tore down the async jobs each time. Nulling log.check also moves after the final log line. CheckLoggingAdapter.process reads back through that attribute for checks whose check_id was never resolved, so the trailing debug call raised AttributeError once debug logging was enabled. Co-authored-by: Cursor <cursoragent@cursor.com> * Add changelog Co-authored-by: Cursor <cursoragent@cursor.com> * Trim teardown test coverage Drop the logging-hazard test and the rationale comment on the idempotency test. Co-authored-by: Cursor <cursoragent@cursor.com> * Do not log from the finalize guard The first teardown nulls log.check, so a second call cannot log through the adapter -- which is the very hazard this change is fixing elsewhere. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent b59c8b0 commit 640de87

3 files changed

Lines changed: 25 additions & 2 deletions

File tree

postgres/changelog.d/24852.fixed

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Only tear the check down once when it is cancelled, and keep logging usable through the end of teardown.

postgres/datadog_checks/postgres/postgres.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ def __init__(self, name, init_config, instances):
199199
self._cancel_lock = threading.Lock()
200200
self._is_running = False
201201
self._cancelled = False
202+
self._finalized = False
202203

203204
def database_monitoring_column_statistics(self, raw_event: str):
204205
self.event_platform_event(raw_event, "dbm-column-statistics")
@@ -537,19 +538,25 @@ def _register_async_jobs(self):
537538
self.data_observability = self.register_async_job(PostgresDataObservability(self, self._config))
538539

539540
def _finalize(self):
540-
"""Tear down check state. Must not run while check() is executing."""
541+
"""Tear down check state. Runs at most once, and never while check() is executing."""
542+
with self._cancel_lock:
543+
if self._finalized:
544+
return
545+
self._finalized = True
541546
self.log.debug("Finalizing check: closing connections and clearing state")
542547
self.shutdown_async_jobs()
543548
self._clean_state()
544549
self.check_initializations.clear()
545550
# TODO: move diagnosis cleanup into AgentCheck.cancel() in the base class
546551
self._diagnosis = None
547-
self.log.check = None
548552
self._query_manager = None
549553
self.health = None
550554
self._close_db()
551555
self._close_db_pool()
552556
self.log.debug("Check cleanup complete")
557+
# Must come last: the logging adapter reads back through this attribute for checks whose
558+
# check_id was never resolved, so anything logged after this would fail.
559+
self.log.check = None
553560

554561
def _clean_state(self):
555562
self.log.debug("Cleaning state")

postgres/tests/test_unit.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,21 @@ def test_run_after_cancel_returns_immediately(pg_instance):
519519
assert result == ''
520520

521521

522+
def test_finalize_runs_once_across_repeated_cancels(pg_instance):
523+
"""Verify that teardown is idempotent."""
524+
check = PostgreSql('postgres', {}, [pg_instance])
525+
conn = mock.MagicMock()
526+
check._db = conn
527+
528+
with mock.patch.object(check.db_pool, 'close_all', wraps=check.db_pool.close_all) as close_all:
529+
check.cancel()
530+
check.cancel()
531+
check._finalize()
532+
533+
conn.close.assert_called_once()
534+
close_all.assert_called_once()
535+
536+
522537
@pytest.mark.parametrize(
523538
'dbm, data_observability_enabled, expected_jobs',
524539
[

0 commit comments

Comments
 (0)