Skip to content

Fix async engine missing pool_recycle and pool_pre_ping configuration - #65276

Merged
kaxil merged 1 commit into
apache:mainfrom
astronomer:fix/async-engine-pool-config
Apr 21, 2026
Merged

Fix async engine missing pool_recycle and pool_pre_ping configuration#65276
kaxil merged 1 commit into
apache:mainfrom
astronomer:fix/async-engine-pool-config

Conversation

@kaxil

@kaxilkaxil commented Apr 15, 2026

Copy link
Copy Markdown
Member

The async SQLAlchemy engine was created with zero pool configuration while the sync engine got pool_size, pool_recycle, pool_pre_ping, and max_overflow from [database] config.

How this is hit

The async engine serves all Execution API requests that go through async session dependencies. The API server (gunicorn) creates the async engine at startup via _configure_async_session(). Without pool health settings:

  • pool_recycle=-1 (SQLAlchemy default) -- connections are never recycled, so connections that exceed PostgreSQL's idle_in_transaction_session_timeout or pgbouncer's server_idle_timeout sit dead in the pool
  • pool_pre_ping=False (SQLAlchemy default) -- dead connections are never detected before checkout, so the first query on a stale connection fails with a closed-connection error

The sync engine has had these settings since day one. The async engine missed them when _configure_async_session() was extracted from configure_orm() in PR #51920.

Fix

Read the same [database] config section values (pool_size, pool_recycle, pool_pre_ping, max_overflow) for the async engine. Also respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching the sync engine behavior for pgbouncer setups that require it.

The create_async_metadata_engine() signature gains an optional engine_args parameter (defaulting to None for backward compatibility with existing airflow_local_settings.py overrides). Upstream PR should update the cluster-policies.rst docs to show the new signature.

@kaxil
kaxil requested review from dstandish and uranusjrApril 15, 2026 01:52
@kaxilkaxil added this to the Airflow 3.2.2 milestone Apr 15, 2026

@pierrejeambrunpierrejeambrun left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR!

Just one comment

Comment threadairflow-core/src/airflow/settings.py
@kaxil
kaxilforce-pushed the fix/async-engine-pool-config branch from 822ad26 to 8df1808CompareApril 15, 2026 10:42
Comment threadairflow-core/src/airflow/settings.py Outdated
@kaxil
kaxilforce-pushed the fix/async-engine-pool-config branch from 8df1808 to 0e78bfaCompareApril 15, 2026 13:18
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
@kaxil
kaxilforce-pushed the fix/async-engine-pool-config branch from 0e78bfa to 1c730acCompareApril 15, 2026 16:14

@pierrejeambrunpierrejeambrun left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can have a 'common' function that builds the 'common' part only of the engine args.

And then let the async/sync specific parts like you have now.

But that's not super important, if you want to go with full duplication that's not a big deal.

@kaxil
kaxil merged commit 981b0e2 into apache:mainApr 21, 2026
79 checks passed
@kaxil
kaxil deleted the fix/async-engine-pool-config branch April 21, 2026 13:53
Cloud-Architect-Emma pushed a commit to Cloud-Architect-Emma/airflow that referenced this pull request Apr 30, 2026
…apache#65276)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
@vatsrahul1001

Copy link
Copy Markdown
Contributor

Manual backport #66866

vatsrahul1001 added a commit that referenced this pull request May 15, 2026
…#65276) (#66866)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
(cherry picked from commit 981b0e2)
Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com>
vatsrahul1001 added a commit that referenced this pull request May 20, 2026
…#65276) (#66866)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
(cherry picked from commit 981b0e2)
Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com>
vatsrahul1001 added a commit that referenced this pull request May 20, 2026
…#65276) (#66866)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
(cherry picked from commit 981b0e2)
Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com>
vatsrahul1001 added a commit that referenced this pull request May 21, 2026
…#65276) (#66866)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
(cherry picked from commit 981b0e2)
Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@kaxil@vatsrahul1001@pierrejeambrun
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Fix async engine missing `pool_recycle` and `pool_pre_ping` configuration by kaxil · Pull Request #65276 · apache/airflow · GitHub
Skip to content

Fix async engine missing pool_recycle and pool_pre_ping configuration - #65276

Merged
kaxil merged 1 commit into
apache:mainfrom
astronomer:fix/async-engine-pool-config
Apr 21, 2026
Merged

Fix async engine missing pool_recycle and pool_pre_ping configuration#65276
kaxil merged 1 commit into
apache:mainfrom
astronomer:fix/async-engine-pool-config

Conversation

@kaxil

@kaxilkaxil commented Apr 15, 2026

Copy link
Copy Markdown
Member

The async SQLAlchemy engine was created with zero pool configuration while the sync engine got pool_size, pool_recycle, pool_pre_ping, and max_overflow from [database] config.

How this is hit

The async engine serves all Execution API requests that go through async session dependencies. The API server (gunicorn) creates the async engine at startup via _configure_async_session(). Without pool health settings:

  • pool_recycle=-1 (SQLAlchemy default) -- connections are never recycled, so connections that exceed PostgreSQL's idle_in_transaction_session_timeout or pgbouncer's server_idle_timeout sit dead in the pool
  • pool_pre_ping=False (SQLAlchemy default) -- dead connections are never detected before checkout, so the first query on a stale connection fails with a closed-connection error

The sync engine has had these settings since day one. The async engine missed them when _configure_async_session() was extracted from configure_orm() in PR #51920.

Fix

Read the same [database] config section values (pool_size, pool_recycle, pool_pre_ping, max_overflow) for the async engine. Also respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching the sync engine behavior for pgbouncer setups that require it.

The create_async_metadata_engine() signature gains an optional engine_args parameter (defaulting to None for backward compatibility with existing airflow_local_settings.py overrides). Upstream PR should update the cluster-policies.rst docs to show the new signature.

@kaxil
kaxil requested review from dstandish and uranusjrApril 15, 2026 01:52
@kaxilkaxil added this to the Airflow 3.2.2 milestone Apr 15, 2026

@pierrejeambrunpierrejeambrun left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR!

Just one comment

Comment threadairflow-core/src/airflow/settings.py
@kaxil
kaxilforce-pushed the fix/async-engine-pool-config branch from 822ad26 to 8df1808CompareApril 15, 2026 10:42
Comment threadairflow-core/src/airflow/settings.py Outdated
@kaxil
kaxilforce-pushed the fix/async-engine-pool-config branch from 8df1808 to 0e78bfaCompareApril 15, 2026 13:18
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
@kaxil
kaxilforce-pushed the fix/async-engine-pool-config branch from 0e78bfa to 1c730acCompareApril 15, 2026 16:14

@pierrejeambrunpierrejeambrun left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can have a 'common' function that builds the 'common' part only of the engine args.

And then let the async/sync specific parts like you have now.

But that's not super important, if you want to go with full duplication that's not a big deal.

@kaxil
kaxil merged commit 981b0e2 into apache:mainApr 21, 2026
79 checks passed
@kaxil
kaxil deleted the fix/async-engine-pool-config branch April 21, 2026 13:53
Cloud-Architect-Emma pushed a commit to Cloud-Architect-Emma/airflow that referenced this pull request Apr 30, 2026
…apache#65276)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
@vatsrahul1001

Copy link
Copy Markdown
Contributor

Manual backport #66866

vatsrahul1001 added a commit that referenced this pull request May 15, 2026
…#65276) (#66866)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
(cherry picked from commit 981b0e2)
Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com>
vatsrahul1001 added a commit that referenced this pull request May 20, 2026
…#65276) (#66866)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
(cherry picked from commit 981b0e2)
Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com>
vatsrahul1001 added a commit that referenced this pull request May 20, 2026
…#65276) (#66866)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
(cherry picked from commit 981b0e2)
Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com>
vatsrahul1001 added a commit that referenced this pull request May 21, 2026
…#65276) (#66866)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
(cherry picked from commit 981b0e2)
Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@kaxil@vatsrahul1001@pierrejeambrun
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Fix async engine missing `pool_recycle` and `pool_pre_ping` configuration by kaxil · Pull Request #65276 · apache/airflow · GitHub
Skip to content

Fix async engine missing pool_recycle and pool_pre_ping configuration - #65276

Merged
kaxil merged 1 commit into
apache:mainfrom
astronomer:fix/async-engine-pool-config
Apr 21, 2026
Merged

Fix async engine missing pool_recycle and pool_pre_ping configuration#65276
kaxil merged 1 commit into
apache:mainfrom
astronomer:fix/async-engine-pool-config

Conversation

@kaxil

@kaxilkaxil commented Apr 15, 2026

Copy link
Copy Markdown
Member

The async SQLAlchemy engine was created with zero pool configuration while the sync engine got pool_size, pool_recycle, pool_pre_ping, and max_overflow from [database] config.

How this is hit

The async engine serves all Execution API requests that go through async session dependencies. The API server (gunicorn) creates the async engine at startup via _configure_async_session(). Without pool health settings:

  • pool_recycle=-1 (SQLAlchemy default) -- connections are never recycled, so connections that exceed PostgreSQL's idle_in_transaction_session_timeout or pgbouncer's server_idle_timeout sit dead in the pool
  • pool_pre_ping=False (SQLAlchemy default) -- dead connections are never detected before checkout, so the first query on a stale connection fails with a closed-connection error

The sync engine has had these settings since day one. The async engine missed them when _configure_async_session() was extracted from configure_orm() in PR #51920.

Fix

Read the same [database] config section values (pool_size, pool_recycle, pool_pre_ping, max_overflow) for the async engine. Also respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching the sync engine behavior for pgbouncer setups that require it.

The create_async_metadata_engine() signature gains an optional engine_args parameter (defaulting to None for backward compatibility with existing airflow_local_settings.py overrides). Upstream PR should update the cluster-policies.rst docs to show the new signature.

@kaxil
kaxil requested review from dstandish and uranusjrApril 15, 2026 01:52
@kaxilkaxil added this to the Airflow 3.2.2 milestone Apr 15, 2026

@pierrejeambrunpierrejeambrun left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR!

Just one comment

Comment threadairflow-core/src/airflow/settings.py
@kaxil
kaxilforce-pushed the fix/async-engine-pool-config branch from 822ad26 to 8df1808CompareApril 15, 2026 10:42
Comment threadairflow-core/src/airflow/settings.py Outdated
@kaxil
kaxilforce-pushed the fix/async-engine-pool-config branch from 8df1808 to 0e78bfaCompareApril 15, 2026 13:18
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
@kaxil
kaxilforce-pushed the fix/async-engine-pool-config branch from 0e78bfa to 1c730acCompareApril 15, 2026 16:14

@pierrejeambrunpierrejeambrun left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can have a 'common' function that builds the 'common' part only of the engine args.

And then let the async/sync specific parts like you have now.

But that's not super important, if you want to go with full duplication that's not a big deal.

@kaxil
kaxil merged commit 981b0e2 into apache:mainApr 21, 2026
79 checks passed
@kaxil
kaxil deleted the fix/async-engine-pool-config branch April 21, 2026 13:53
Cloud-Architect-Emma pushed a commit to Cloud-Architect-Emma/airflow that referenced this pull request Apr 30, 2026
…apache#65276)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
@vatsrahul1001

Copy link
Copy Markdown
Contributor

Manual backport #66866

vatsrahul1001 added a commit that referenced this pull request May 15, 2026
…#65276) (#66866)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
(cherry picked from commit 981b0e2)
Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com>
vatsrahul1001 added a commit that referenced this pull request May 20, 2026
…#65276) (#66866)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
(cherry picked from commit 981b0e2)
Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com>
vatsrahul1001 added a commit that referenced this pull request May 20, 2026
…#65276) (#66866)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
(cherry picked from commit 981b0e2)
Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com>
vatsrahul1001 added a commit that referenced this pull request May 21, 2026
…#65276) (#66866)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
(cherry picked from commit 981b0e2)
Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@kaxil@vatsrahul1001@pierrejeambrun
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Fix async engine missing `pool_recycle` and `pool_pre_ping` configuration by kaxil · Pull Request #65276 · apache/airflow · GitHub
Skip to content

Fix async engine missing pool_recycle and pool_pre_ping configuration - #65276

Merged
kaxil merged 1 commit into
apache:mainfrom
astronomer:fix/async-engine-pool-config
Apr 21, 2026
Merged

Fix async engine missing pool_recycle and pool_pre_ping configuration#65276
kaxil merged 1 commit into
apache:mainfrom
astronomer:fix/async-engine-pool-config

Conversation

@kaxil

@kaxilkaxil commented Apr 15, 2026

Copy link
Copy Markdown
Member

The async SQLAlchemy engine was created with zero pool configuration while the sync engine got pool_size, pool_recycle, pool_pre_ping, and max_overflow from [database] config.

How this is hit

The async engine serves all Execution API requests that go through async session dependencies. The API server (gunicorn) creates the async engine at startup via _configure_async_session(). Without pool health settings:

  • pool_recycle=-1 (SQLAlchemy default) -- connections are never recycled, so connections that exceed PostgreSQL's idle_in_transaction_session_timeout or pgbouncer's server_idle_timeout sit dead in the pool
  • pool_pre_ping=False (SQLAlchemy default) -- dead connections are never detected before checkout, so the first query on a stale connection fails with a closed-connection error

The sync engine has had these settings since day one. The async engine missed them when _configure_async_session() was extracted from configure_orm() in PR #51920.

Fix

Read the same [database] config section values (pool_size, pool_recycle, pool_pre_ping, max_overflow) for the async engine. Also respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching the sync engine behavior for pgbouncer setups that require it.

The create_async_metadata_engine() signature gains an optional engine_args parameter (defaulting to None for backward compatibility with existing airflow_local_settings.py overrides). Upstream PR should update the cluster-policies.rst docs to show the new signature.

@kaxil
kaxil requested review from dstandish and uranusjrApril 15, 2026 01:52
@kaxilkaxil added this to the Airflow 3.2.2 milestone Apr 15, 2026

@pierrejeambrunpierrejeambrun left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR!

Just one comment

Comment threadairflow-core/src/airflow/settings.py
@kaxil
kaxilforce-pushed the fix/async-engine-pool-config branch from 822ad26 to 8df1808CompareApril 15, 2026 10:42
Comment threadairflow-core/src/airflow/settings.py Outdated
@kaxil
kaxilforce-pushed the fix/async-engine-pool-config branch from 8df1808 to 0e78bfaCompareApril 15, 2026 13:18
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
@kaxil
kaxilforce-pushed the fix/async-engine-pool-config branch from 0e78bfa to 1c730acCompareApril 15, 2026 16:14

@pierrejeambrunpierrejeambrun left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can have a 'common' function that builds the 'common' part only of the engine args.

And then let the async/sync specific parts like you have now.

But that's not super important, if you want to go with full duplication that's not a big deal.

@kaxil
kaxil merged commit 981b0e2 into apache:mainApr 21, 2026
79 checks passed
@kaxil
kaxil deleted the fix/async-engine-pool-config branch April 21, 2026 13:53
Cloud-Architect-Emma pushed a commit to Cloud-Architect-Emma/airflow that referenced this pull request Apr 30, 2026
…apache#65276)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
@vatsrahul1001

Copy link
Copy Markdown
Contributor

Manual backport #66866

vatsrahul1001 added a commit that referenced this pull request May 15, 2026
…#65276) (#66866)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
(cherry picked from commit 981b0e2)
Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com>
vatsrahul1001 added a commit that referenced this pull request May 20, 2026
…#65276) (#66866)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
(cherry picked from commit 981b0e2)
Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com>
vatsrahul1001 added a commit that referenced this pull request May 20, 2026
…#65276) (#66866)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
(cherry picked from commit 981b0e2)
Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com>
vatsrahul1001 added a commit that referenced this pull request May 21, 2026
…#65276) (#66866)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
(cherry picked from commit 981b0e2)
Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@kaxil@vatsrahul1001@pierrejeambrun
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Fix async engine missing `pool_recycle` and `pool_pre_ping` configuration by kaxil · Pull Request #65276 · apache/airflow · GitHub
Skip to content

Fix async engine missing pool_recycle and pool_pre_ping configuration - #65276

Merged
kaxil merged 1 commit into
apache:mainfrom
astronomer:fix/async-engine-pool-config
Apr 21, 2026
Merged

Fix async engine missing pool_recycle and pool_pre_ping configuration#65276
kaxil merged 1 commit into
apache:mainfrom
astronomer:fix/async-engine-pool-config

Conversation

@kaxil

@kaxilkaxil commented Apr 15, 2026

Copy link
Copy Markdown
Member

The async SQLAlchemy engine was created with zero pool configuration while the sync engine got pool_size, pool_recycle, pool_pre_ping, and max_overflow from [database] config.

How this is hit

The async engine serves all Execution API requests that go through async session dependencies. The API server (gunicorn) creates the async engine at startup via _configure_async_session(). Without pool health settings:

  • pool_recycle=-1 (SQLAlchemy default) -- connections are never recycled, so connections that exceed PostgreSQL's idle_in_transaction_session_timeout or pgbouncer's server_idle_timeout sit dead in the pool
  • pool_pre_ping=False (SQLAlchemy default) -- dead connections are never detected before checkout, so the first query on a stale connection fails with a closed-connection error

The sync engine has had these settings since day one. The async engine missed them when _configure_async_session() was extracted from configure_orm() in PR #51920.

Fix

Read the same [database] config section values (pool_size, pool_recycle, pool_pre_ping, max_overflow) for the async engine. Also respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching the sync engine behavior for pgbouncer setups that require it.

The create_async_metadata_engine() signature gains an optional engine_args parameter (defaulting to None for backward compatibility with existing airflow_local_settings.py overrides). Upstream PR should update the cluster-policies.rst docs to show the new signature.

@kaxil
kaxil requested review from dstandish and uranusjrApril 15, 2026 01:52
@kaxilkaxil added this to the Airflow 3.2.2 milestone Apr 15, 2026

@pierrejeambrunpierrejeambrun left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR!

Just one comment

Comment threadairflow-core/src/airflow/settings.py
@kaxil
kaxilforce-pushed the fix/async-engine-pool-config branch from 822ad26 to 8df1808CompareApril 15, 2026 10:42
Comment threadairflow-core/src/airflow/settings.py Outdated
@kaxil
kaxilforce-pushed the fix/async-engine-pool-config branch from 8df1808 to 0e78bfaCompareApril 15, 2026 13:18
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
@kaxil
kaxilforce-pushed the fix/async-engine-pool-config branch from 0e78bfa to 1c730acCompareApril 15, 2026 16:14

@pierrejeambrunpierrejeambrun left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can have a 'common' function that builds the 'common' part only of the engine args.

And then let the async/sync specific parts like you have now.

But that's not super important, if you want to go with full duplication that's not a big deal.

@kaxil
kaxil merged commit 981b0e2 into apache:mainApr 21, 2026
79 checks passed
@kaxil
kaxil deleted the fix/async-engine-pool-config branch April 21, 2026 13:53
Cloud-Architect-Emma pushed a commit to Cloud-Architect-Emma/airflow that referenced this pull request Apr 30, 2026
…apache#65276)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
@vatsrahul1001

Copy link
Copy Markdown
Contributor

Manual backport #66866

vatsrahul1001 added a commit that referenced this pull request May 15, 2026
…#65276) (#66866)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
(cherry picked from commit 981b0e2)
Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com>
vatsrahul1001 added a commit that referenced this pull request May 20, 2026
…#65276) (#66866)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
(cherry picked from commit 981b0e2)
Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com>
vatsrahul1001 added a commit that referenced this pull request May 20, 2026
…#65276) (#66866)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
(cherry picked from commit 981b0e2)
Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com>
vatsrahul1001 added a commit that referenced this pull request May 21, 2026
…#65276) (#66866)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
(cherry picked from commit 981b0e2)
Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@kaxil@vatsrahul1001@pierrejeambrun
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Fix async engine missing `pool_recycle` and `pool_pre_ping` configuration by kaxil · Pull Request #65276 · apache/airflow · GitHub
Skip to content

Fix async engine missing pool_recycle and pool_pre_ping configuration - #65276

Merged
kaxil merged 1 commit into
apache:mainfrom
astronomer:fix/async-engine-pool-config
Apr 21, 2026
Merged

Fix async engine missing pool_recycle and pool_pre_ping configuration#65276
kaxil merged 1 commit into
apache:mainfrom
astronomer:fix/async-engine-pool-config

Conversation

@kaxil

@kaxilkaxil commented Apr 15, 2026

Copy link
Copy Markdown
Member

The async SQLAlchemy engine was created with zero pool configuration while the sync engine got pool_size, pool_recycle, pool_pre_ping, and max_overflow from [database] config.

How this is hit

The async engine serves all Execution API requests that go through async session dependencies. The API server (gunicorn) creates the async engine at startup via _configure_async_session(). Without pool health settings:

  • pool_recycle=-1 (SQLAlchemy default) -- connections are never recycled, so connections that exceed PostgreSQL's idle_in_transaction_session_timeout or pgbouncer's server_idle_timeout sit dead in the pool
  • pool_pre_ping=False (SQLAlchemy default) -- dead connections are never detected before checkout, so the first query on a stale connection fails with a closed-connection error

The sync engine has had these settings since day one. The async engine missed them when _configure_async_session() was extracted from configure_orm() in PR #51920.

Fix

Read the same [database] config section values (pool_size, pool_recycle, pool_pre_ping, max_overflow) for the async engine. Also respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching the sync engine behavior for pgbouncer setups that require it.

The create_async_metadata_engine() signature gains an optional engine_args parameter (defaulting to None for backward compatibility with existing airflow_local_settings.py overrides). Upstream PR should update the cluster-policies.rst docs to show the new signature.

@kaxil
kaxil requested review from dstandish and uranusjrApril 15, 2026 01:52
@kaxilkaxil added this to the Airflow 3.2.2 milestone Apr 15, 2026

@pierrejeambrunpierrejeambrun left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR!

Just one comment

Comment threadairflow-core/src/airflow/settings.py
@kaxil
kaxilforce-pushed the fix/async-engine-pool-config branch from 822ad26 to 8df1808CompareApril 15, 2026 10:42
Comment threadairflow-core/src/airflow/settings.py Outdated
@kaxil
kaxilforce-pushed the fix/async-engine-pool-config branch from 8df1808 to 0e78bfaCompareApril 15, 2026 13:18
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
@kaxil
kaxilforce-pushed the fix/async-engine-pool-config branch from 0e78bfa to 1c730acCompareApril 15, 2026 16:14

@pierrejeambrunpierrejeambrun left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can have a 'common' function that builds the 'common' part only of the engine args.

And then let the async/sync specific parts like you have now.

But that's not super important, if you want to go with full duplication that's not a big deal.

@kaxil
kaxil merged commit 981b0e2 into apache:mainApr 21, 2026
79 checks passed
@kaxil
kaxil deleted the fix/async-engine-pool-config branch April 21, 2026 13:53
Cloud-Architect-Emma pushed a commit to Cloud-Architect-Emma/airflow that referenced this pull request Apr 30, 2026
…apache#65276)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
@vatsrahul1001

Copy link
Copy Markdown
Contributor

Manual backport #66866

vatsrahul1001 added a commit that referenced this pull request May 15, 2026
…#65276) (#66866)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
(cherry picked from commit 981b0e2)
Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com>
vatsrahul1001 added a commit that referenced this pull request May 20, 2026
…#65276) (#66866)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
(cherry picked from commit 981b0e2)
Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com>
vatsrahul1001 added a commit that referenced this pull request May 20, 2026
…#65276) (#66866)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
(cherry picked from commit 981b0e2)
Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com>
vatsrahul1001 added a commit that referenced this pull request May 21, 2026
…#65276) (#66866)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
(cherry picked from commit 981b0e2)
Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@kaxil@vatsrahul1001@pierrejeambrun
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); Fix async engine missing `pool_recycle` and `pool_pre_ping` configuration by kaxil · Pull Request #65276 · apache/airflow · GitHub
Skip to content

Fix async engine missing pool_recycle and pool_pre_ping configuration - #65276

Merged
kaxil merged 1 commit into
apache:mainfrom
astronomer:fix/async-engine-pool-config
Apr 21, 2026
Merged

Fix async engine missing pool_recycle and pool_pre_ping configuration#65276
kaxil merged 1 commit into
apache:mainfrom
astronomer:fix/async-engine-pool-config

Conversation

@kaxil

@kaxilkaxil commented Apr 15, 2026

Copy link
Copy Markdown
Member

The async SQLAlchemy engine was created with zero pool configuration while the sync engine got pool_size, pool_recycle, pool_pre_ping, and max_overflow from [database] config.

How this is hit

The async engine serves all Execution API requests that go through async session dependencies. The API server (gunicorn) creates the async engine at startup via _configure_async_session(). Without pool health settings:

  • pool_recycle=-1 (SQLAlchemy default) -- connections are never recycled, so connections that exceed PostgreSQL's idle_in_transaction_session_timeout or pgbouncer's server_idle_timeout sit dead in the pool
  • pool_pre_ping=False (SQLAlchemy default) -- dead connections are never detected before checkout, so the first query on a stale connection fails with a closed-connection error

The sync engine has had these settings since day one. The async engine missed them when _configure_async_session() was extracted from configure_orm() in PR #51920.

Fix

Read the same [database] config section values (pool_size, pool_recycle, pool_pre_ping, max_overflow) for the async engine. Also respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching the sync engine behavior for pgbouncer setups that require it.

The create_async_metadata_engine() signature gains an optional engine_args parameter (defaulting to None for backward compatibility with existing airflow_local_settings.py overrides). Upstream PR should update the cluster-policies.rst docs to show the new signature.

@kaxil
kaxil requested review from dstandish and uranusjrApril 15, 2026 01:52
@kaxilkaxil added this to the Airflow 3.2.2 milestone Apr 15, 2026

@pierrejeambrunpierrejeambrun left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR!

Just one comment

Comment threadairflow-core/src/airflow/settings.py
@kaxil
kaxilforce-pushed the fix/async-engine-pool-config branch from 822ad26 to 8df1808CompareApril 15, 2026 10:42
Comment threadairflow-core/src/airflow/settings.py Outdated
@kaxil
kaxilforce-pushed the fix/async-engine-pool-config branch from 8df1808 to 0e78bfaCompareApril 15, 2026 13:18
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
@kaxil
kaxilforce-pushed the fix/async-engine-pool-config branch from 0e78bfa to 1c730acCompareApril 15, 2026 16:14

@pierrejeambrunpierrejeambrun left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can have a 'common' function that builds the 'common' part only of the engine args.

And then let the async/sync specific parts like you have now.

But that's not super important, if you want to go with full duplication that's not a big deal.

@kaxil
kaxil merged commit 981b0e2 into apache:mainApr 21, 2026
79 checks passed
@kaxil
kaxil deleted the fix/async-engine-pool-config branch April 21, 2026 13:53
Cloud-Architect-Emma pushed a commit to Cloud-Architect-Emma/airflow that referenced this pull request Apr 30, 2026
…apache#65276)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
@vatsrahul1001

Copy link
Copy Markdown
Contributor

Manual backport #66866

vatsrahul1001 added a commit that referenced this pull request May 15, 2026
…#65276) (#66866)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
(cherry picked from commit 981b0e2)
Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com>
vatsrahul1001 added a commit that referenced this pull request May 20, 2026
…#65276) (#66866)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
(cherry picked from commit 981b0e2)
Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com>
vatsrahul1001 added a commit that referenced this pull request May 20, 2026
…#65276) (#66866)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
(cherry picked from commit 981b0e2)
Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com>
vatsrahul1001 added a commit that referenced this pull request May 21, 2026
…#65276) (#66866)
The async SQLAlchemy engine was created without any pool health
settings while the sync engine got pool_size, pool_recycle,
pool_pre_ping, and max_overflow from [database] config. This meant
dead connections from PostgreSQL idle timeouts or pgbouncer disconnects
were never detected by the async pool.
Read the same [database] config values for the async engine. Also
respect SQL_ALCHEMY_POOL_ENABLED=False by using NullPool, matching
the sync engine behavior.
(cherry picked from commit 981b0e2)
Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@kaxil@vatsrahul1001@pierrejeambrun