wglbd12 created FLINK-40623:
-------------------------------

             Summary: [cdc-base] JdbcSourceChunkSplitter keeps a pooled JDBC 
connection for the whole job lifetime; with default connection.pool.size=20 the 
21st incremental-snapshot job on the same JobManager can never start
                 Key: FLINK-40623
                 URL: https://issues.apache.org/jira/browse/FLINK-40623
             Project: Flink
          Issue Type: Bug
          Components: Flink CDC
    Affects Versions: cdc-3.6.0
            Reporter: wglbd12


h3. Environment
* Flink 1.20.0 standalone session cluster (1 JM + 1 TM), JDK 17
* flink-sql-connector-postgres-cdc 3.6.0-1.20 (Debezium 1.9.8.Final)
* PostgreSQL 15 (cloud RDS); 30 independent Flink SQL jobs, one postgres-cdc 
table each, all on the same host/port/user/database
* scan.incremental.snapshot.enabled=true, scan.startup.mode=initial, 
connection.pool.size not set (default 20)
* Same DDL on 3.2.1 runs all 30 jobs without issue

h3. Symptom
Jobs 1..20 start normally. From job 21 on every new job fails during enumerator 
creation and loops in RESTARTING:
{code}
Caused by: org.apache.flink.util.FlinkRuntimeException: Failed to discover 
captured tables for enumerator
Caused by: org.apache.flink.util.FlinkRuntimeException: 
java.sql.SQLTransientConnectionException: connection-pool-<host>:5432 - 
Connection is not available, request timed out after 30000ms.
  at 
org.apache.flink.cdc.connectors.base.relational.connection.JdbcConnectionFactory.connect(...)
  at 
org.apache.flink.cdc.connectors.postgres.source.PostgresDialect.openJdbcConnection(...)
  at 
org.apache.flink.cdc.connectors.postgres.source.PostgresDialect.discoverDataCollections(...)
  at 
org.apache.flink.cdc.connectors.postgres.source.PostgresSourceBuilder$PostgresIncrementalSource.createEnumerator(...)
{code}
* Submitting the failing job alone does not help; restarting the TaskManager 
does not help; restarting the JobManager fixes it until 20 jobs are running 
again.
* {{pg_stat_activity}} shows exactly 20 idle backends from the JobManager, one 
per running job, created at each job's start and never used again. Their last 
statement is the chunk-split query:
{code}
SELECT MAX("id") FROM (SELECT "id" FROM "<db>"."public"."<table>" WHERE "id" >= 
$1 ORDER BY "id" ASC LIMIT 8096) AS T
{code}

h3. Root cause (release-3.6 sources)
* {{JdbcSourceChunkSplitter}}: {{open()}} does {{this.jdbcConnection = 
dialect.openJdbcConnection(sourceConfig)}}, {{close()}} closes it. In 
release-3.2 {{generateSplits()}} used {{try (JdbcConnection jdbc = 
dialect.openJdbcConnection(sourceConfig))}}, so the connection was returned 
after each table. The lifecycle change came with FLINK-34688 (asynchronous 
chunk splitting, PR #3510), first shipped in cdc-3.3.0.
* {{SnapshotSplitAssigner}}: {{open()}} calls {{chunkSplitter.open()}}; 
{{chunkSplitter.close()}} is only called from the assigner's own {{close()}}, 
i.e. at job termination. {{onFinishedSplits()}} flips the assigner status to 
finished but does not release the splitter's connection.
* {{JdbcConnectionFactory.connect()}} borrows from 
{{JdbcConnectionPools.getInstance(...).getOrCreateConnectionPool(poolId, 
sourceConfig)}}; {{JdbcConnectionPools}} is a JVM-wide singleton keyed per 
host/port/user/database, therefore shared by all jobs on the same JobManager.
* {{JdbcConnectionPoolFactory.createPooledDataSource()}}: 
{{setMaximumPoolSize(sourceConfig.getConnectionPoolSize())}} (default 20) and 
{{setConnectionTimeout(sourceConfig.getConnectTimeout().toMillis())}} (default 
30 s).

Net effect: each running incremental-snapshot job pins one pooled connection on 
the JobManager for its whole lifetime, so the number of jobs that can run 
against one database from one JobManager is capped at {{connection.pool.size}}, 
and the failure surfaces as an opaque Hikari timeout in 
{{discoverDataCollections()}}.

h3. Expected behaviour
The chunk splitter should release its JDBC connection once splitting for all 
remaining tables has finished (e.g. at the end of 
{{splitChunksForRemainingTables()}}, re-opening lazily for newly added tables), 
or return to a try-with-resources per {{generateSplits()}}. At minimum, 
document the limitation and make the error mention {{connection.pool.size}}.

h3. Workaround
Set {{'connection.pool.size'}} in the table DDL to a value larger than the 
number of jobs sharing the JobManager (64 for our 30 jobs) and restart the 
JobManager; the pool size is fixed when the pool is first created.

h3. How to reproduce
# Session cluster, postgres-cdc 3.6.0, incremental snapshot enabled, 
{{connection.pool.size}} at default.
# Submit 21 independent SQL jobs, each reading one table from the same 
PostgreSQL database.
# Job 21 fails in {{createEnumerator}} with the exception above; 
{{pg_stat_activity}} shows 20 idle JobManager backends whose last query is the 
chunk-split {{SELECT MAX(...) LIMIT 8096}}.

Mailing list thread with the same analysis: 
https://lists.apache.org/thread/0vs1v0yl2t63qst0fqopy0fdvxr1qq8g



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to