dotCMS / dotCMS/core

Epic: Database Connection and Transaction Management

Open
#34,837 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Team : Enablement Type : New Functionality
Dominant language
Java
Stars
970
Forks
486
Avg merge
3d 33m
Merged PRs (30d)
170

Description

Description

dotCMS pods experience database connection leaks that can overwhelm shared RDS instances under sustained load. Investigation has uncovered a cluster of related root causes spanning three layers: application code leaking connections on background threads, a silent env var naming bug causing aggressive connection churn, and missing server-side safety nets.

This epic groups all fixes, defense-in-depth enhancements, and observability work needed to achieve stable, leak-free database connection management across all environments.


Priority Tiers

Tier 1 — Active leak fixes (deploy ASAP, each is one PR)

These directly stop the connection accumulation observed in production. Each is an independent, deployable unit of work.

# Issue What Impact
1 #34926 MetricStatsCollector ThreadLocal poisoning CountOfSitesWithThumbnailsMetricType poisons executor thread's ThreadLocal, defeating wrapConnection() fix (#34490). Defensive cleanup after each metric + warning log + fix the known poison pill. Primary active leak — 46 orphaned connections observed on Frankfurt.
2 #34921 DB_MAXWAIT naming bug + pool defaults Stops 60s connection churn on ALL deployments. One-line constant rename + safe defaults.
3 #34831 Experiments listActive() leak Wraps ExperimentsFactoryImpl.listActive() in wrapConnection(). Stops the primary daily leak from content operations.
4 #34920 EmbeddingsFactory AI/vector init leak Wraps EmbeddingsFactory callers at API layer. Stops vector extension and async embedding leaks.
5 #34922 Server-side RDS timeouts Apply statement_timeout, idle_in_transaction_session_timeout, idle_session_timeout per-role. Operational — no code deploy. Provides a ceiling on leaked connection lifetime.
Tier 2 — Defense-in-depth (prevent future leaks, reduce blast radius)

These harden the system against leak vectors that aren't actively causing production issues but could under different conditions.

# Issue What
6 #34832 Query timeouts + tcpKeepAlive + keepaliveTimesetQueryTimeout() on DotConnect, JDBC tcpKeepAlive=true, HikariCP keepaliveTime env var
7 #34839 DatabaseHealthCheck race condition — same executor+shutdownNow() pattern as pre-#34490 telemetry
8 #34840 DB_VALIDATION_QUERY semantics — deployment examples disable JDBC4 Connection.isValid(); HikariCP upgrade required for keepalive
9 #34489 try-with-resources anti-pattern — codebase sweep for DbConnectionFactory.getConnection() in try-with-resources (creates secondary leak when outer code also holds thread-local)
Tier 3 — Systemic / architectural (long-term stability)

These address root architectural issues that make connection leaks possible in the first place.

# Issue What
10 #34482 CDI Interceptors for @CloseDBIfOpened / @WrapInTransaction — makes annotations work reliably regardless of how objects are instantiated (fixes the new vs CDI proxy bypass)
11 #34833 ES calls inside @WrapInTransaction — Elasticsearch HTTP I/O inside DB transactions holds connections open for external I/O duration. Blocks tightening idle_in_transaction_session_timeout.
12 #34923 Connection pool sizing observability — baseline HikariCP metrics, right-size DB_MAX_TOTAL, align thread pool config. Requires Tier 1 fixes first so baseline isn't distorted.
13 #34930 Audit & prevent unmanaged implicit connection creation — Add runtime detection when DbConnectionFactory.getConnection() creates a new connection outside a managed context (wrapConnection(), @CloseDBIfOpened, CMSFilter). Surfaces leak sources at creation time instead of requiring production forensics. Long-term: deprecate implicit no-arg DotConnect methods, strict mode config flag.
Completed
# Issue Status
⚠️ #34480 MetricStatsCollector telemetry leak — partially fixed in 26.02.27-01 via PR #34490. wrapConnection() is correct but defeated by ThreadLocal poisoning (#34926).

Dependency Graph

Tier 1 (deploy in order):
  #34926 (ThreadLocal poisoning)  ← deploy first, fixes primary active leak (46 connections on Frankfurt)
  #34921 (DB_MAXWAIT naming bug)  ← deploy next, biggest bang for buck on pool churn
  #34831 (experiments leak)       ← deploy next
  #34920 (embeddings/AI leak)     ← deploy next
  #34922 (RDS timeouts)           ← operational, can apply any time

Tier 2 (after Tier 1):
  #34832 (query timeouts + keepalive) ← depends on #34921 for correct maxLifetime
  #34839 (health check fix)
  #34840 (validation query + HikariCP upgrade) ← depends on #34832 for keepalive
  #34489 (anti-pattern sweep)

Tier 3 (after Tier 2):
  #34482 (CDI interceptors)       ← systemic fix, eliminates class of bugs
  #34833 (ES in transaction)      ← blocks tightening #34922 timeouts
  #34923 (pool sizing)            ← depends on Tier 1 leak fixes for clean baseline
  #34930 (unmanaged connection detection) ← prevents future leaks at source; Phase 1 (warnings) can start early

Desired Outcome

  • Zero connection leaks from background threads — telemetry, experiments, embeddings, health checks
  • HikariCP maxLifetime correctly applied (30 min, not 60s) — DB_MAXWAIT naming bug fixed
  • Server-side timeouts active as backstop — no leaked connection persists indefinitely
  • @WrapInTransaction / @CloseDBIfOpened reliable regardless of instantiation pattern
  • Observability baseline established — pool utilization metrics enabling informed right-sizing
  • No unplanned pod restarts required to recover from connection exhaustion

Target Personas

  • Platform team — code fixes in Tier 1-2
  • Enablement / DevOps — RDS timeouts (#34922), k8s workarounds, observability (#34923)
  • Architecture — CDI interceptor design (#34482), ES transaction pattern (#34833)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading the Tier 1 issues and the entry points named here, including CountOfSitesWithThumbnailsMetricType, ExperimentsFactoryImpl.listActive(), EmbeddingsFactory, DatabaseHealthCheck, and DbConnectionFactory. Trace how wrapConnection() and the HikariCP settings relate across the tiers; this epic is done only when the listed leak, timeout, transaction, and observability outcomes are addressed.

Written by the indexing model from the issue text.

Assessment

Tech stack
aws, java, postgresql
Domain
backend, database, devops, infrastructure, observability
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
20/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.