Skip to content

feat(core): add per-mapper max_fan_out override for partition fan-out cap - #67184

Merged
Lee-W merged 4 commits into
apache:mainfrom
astronomer:partition-fanout-max-keys
Jun 11, 2026
Merged

feat(core): add per-mapper max_fan_out override for partition fan-out cap#67184
Lee-W merged 4 commits into
apache:mainfrom
astronomer:partition-fanout-max-keys

Conversation

@Lee-W

@Lee-WLee-W commented May 19, 2026

Copy link
Copy Markdown
Member

Why

a per-mapper cap so each PartitionMapper instance can override the global [scheduler] partition_fanout_max_keys (implemented in #66030)
Lives on the mapper rather than the Dag because one Dag can bind several mappers
with very different fan-out profiles.

closes: #65760

What

  • Add max_fan_out: int | None = None to PartitionMapper base (SDK + core). Validator rejects 0, negatives, non-int, and bool. None → global; positive int → override.
  • Thread the kwarg through every subclass that overrides __init__, and through both serialization paths (per-class .serialize() and the encoders.py singledispatch overrides). Field emitted only when non-None — pre-change payloads round-trip byte-identical.
  • assets/manager.py: read the cap per target Dag, swap a cap_source fragment into the audit-log extra (max_fan_out=N vs. existing [scheduler] partition_fanout_max_keys=N).

Was generative AI tooling used to co-author this PR?
  • Yes (please specify the tool below)

  • Read the Pull Request Guidelines for more information. Note: commit author/co-author name and email in commits become permanently public when merged.
  • For fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
  • When adding dependency, check compliance with the ASF 3rd Party License Policy.
  • For significant user-facing changes create newsfragment: {pr_number}.significant.rst, in airflow-core/newsfragments. You can add this file in a follow-up commit after the PR is created so you know the PR number.

@boring-cyborgboring-cyborgBot added area:API Airflow's REST/HTTP API area:ConfigTemplates area:DAG-processing area:db-migrations PRs with DB migration area:dev-tools area:Scheduler including HA (high availability) scheduler area:task-sdk area:UI Related to UI/UX. For Frontend Developers. backport-to-v3-2-test kind:documentation labels May 19, 2026
@Lee-WLee-W changed the title feat(AIP-76): windowfeat(core): add per-mapper max_fan_out override for partition fan-out capMay 19, 2026
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 14 times, most recently from d5d2ced to af057aeCompareMay 28, 2026 10:11
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 3 times, most recently from 9f03f8d to efa8911CompareMay 29, 2026 14:13
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch from c1521f6 to 81910a9CompareJune 8, 2026 13:03
Comment threadairflow-core/src/airflow/assets/manager.py
Comment threadtask-sdk/src/airflow/sdk/definitions/partition_mappers/base.py Outdated
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 4 times, most recently from aa011c3 to 03b76c8CompareJune 9, 2026 13:17
@Lee-W
Lee-W requested a review from uranusjrJune 9, 2026 13:38
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 2 times, most recently from c15e65c to a26e224CompareJune 10, 2026 00:21
Comment threadtask-sdk/src/airflow/sdk/definitions/partition_mappers/base.py Outdated
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 2 times, most recently from c634109 to cc440c8CompareJune 10, 2026 10:07
@Lee-WLee-W removed the area:db-migrations PRs with DB migration label Jun 10, 2026
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 4 times, most recently from 1c8c418 to 9d28124CompareJune 10, 2026 15:30
Lee-W added 4 commits June 11, 2026 08:53
… fan-out cap
Add a `max_downstream_keys: int | None = None` parameter to both the SDK and
core `PartitionMapper` base classes, threaded through every subclass
constructor that defines its own `__init__`. The validator rejects 0,
negatives, floats, and strings.
Serialization is updated in two parallel paths:
- Per-class `serialize()` / `deserialize()` methods (custom-mapper path).
- `encoders.py` singledispatch overrides (built-in-mapper path).
Both paths write `max_downstream_keys` only when non-None, following the
existing convention of omitting default-valued fields from the serialized form.
In `manager.py`, the single-shot `max_downstream_keys` read is deleted;
the mapper object is retained after the `to_downstream` call so its
`max_downstream_keys` attribute is readable at the boundary check. The
effective cap is computed per target-Dag as either the mapper's own cap or the
global `[scheduler] partition_mapper_max_downstream_keys`. The `Log.extra`
string uses `max_downstream_keys=N` when the per-mapper cap trips, and keeps
the existing `[scheduler] partition_mapper_max_downstream_keys=N` wording when
the global trips.
The scheduler error log for a partition fan-out overflow now carries a
cap_source kwarg, so operators can tell whether the per-mapper
max_downstream_keys override or the global scheduler cap was the limit
that was hit.
…validator
Convert the SDK PartitionMapper base into an attrs class so the
max_downstream_keys bound is enforced through a field validator instead
of a hand-written __init__ check, matching the attrs convention used
across the rest of airflow.sdk.definitions. Behavior is unchanged.
Also add the SDK-side validator tests that previously only existed on
the core mapper hierarchy.
Make the whole SDK partition-mapper hierarchy use one mechanism so the
attrs base no longer needs a slots exception. RollupMapper, AllowedKeyMapper,
the temporal mappers, and FanOutMapper become attrs classes; ChainMapper and
ProductMapper keep their variadic __init__ via init=False. Public constructor
signatures and behavior are unchanged.
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch from 9d28124 to 5f26df1CompareJune 11, 2026 00:53
@Lee-W
Lee-W merged commit b7247b1 into apache:mainJun 11, 2026
195 of 196 checks passed
@Lee-W
Lee-W deleted the partition-fanout-max-keys branch June 11, 2026 02:26
imrichardwu pushed a commit to imrichardwu/airflow that referenced this pull request Jun 16, 2026
dingo4dev pushed a commit to dingo4dev/airflow that referenced this pull request Jun 16, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:APIAirflow's REST/HTTP APIarea:ConfigTemplatesarea:DAG-processingarea:dev-toolsarea:Schedulerincluding HA (high availability) schedulerarea:task-sdkarea:UIRelated to UI/UX. For Frontend Developers.kind:documentation

Projects

No open projects

Development

Successfully merging this pull request may close these issues.

Fan-out: explosion cap and error behavior

4 participants

@Lee-W@uranusjr@bugraoz93@phanikumv
, '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" + '
feat(core): add per-mapper max_fan_out override for partition fan-out cap by Lee-W · Pull Request #67184 · apache/airflow · GitHub
Skip to content

feat(core): add per-mapper max_fan_out override for partition fan-out cap - #67184

Merged
Lee-W merged 4 commits into
apache:mainfrom
astronomer:partition-fanout-max-keys
Jun 11, 2026
Merged

feat(core): add per-mapper max_fan_out override for partition fan-out cap#67184
Lee-W merged 4 commits into
apache:mainfrom
astronomer:partition-fanout-max-keys

Conversation

@Lee-W

@Lee-WLee-W commented May 19, 2026

Copy link
Copy Markdown
Member

Why

a per-mapper cap so each PartitionMapper instance can override the global [scheduler] partition_fanout_max_keys (implemented in #66030)
Lives on the mapper rather than the Dag because one Dag can bind several mappers
with very different fan-out profiles.

closes: #65760

What

  • Add max_fan_out: int | None = None to PartitionMapper base (SDK + core). Validator rejects 0, negatives, non-int, and bool. None → global; positive int → override.
  • Thread the kwarg through every subclass that overrides __init__, and through both serialization paths (per-class .serialize() and the encoders.py singledispatch overrides). Field emitted only when non-None — pre-change payloads round-trip byte-identical.
  • assets/manager.py: read the cap per target Dag, swap a cap_source fragment into the audit-log extra (max_fan_out=N vs. existing [scheduler] partition_fanout_max_keys=N).

Was generative AI tooling used to co-author this PR?
  • Yes (please specify the tool below)

  • Read the Pull Request Guidelines for more information. Note: commit author/co-author name and email in commits become permanently public when merged.
  • For fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
  • When adding dependency, check compliance with the ASF 3rd Party License Policy.
  • For significant user-facing changes create newsfragment: {pr_number}.significant.rst, in airflow-core/newsfragments. You can add this file in a follow-up commit after the PR is created so you know the PR number.

@boring-cyborgboring-cyborgBot added area:API Airflow's REST/HTTP API area:ConfigTemplates area:DAG-processing area:db-migrations PRs with DB migration area:dev-tools area:Scheduler including HA (high availability) scheduler area:task-sdk area:UI Related to UI/UX. For Frontend Developers. backport-to-v3-2-test kind:documentation labels May 19, 2026
@Lee-WLee-W changed the title feat(AIP-76): windowfeat(core): add per-mapper max_fan_out override for partition fan-out capMay 19, 2026
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 14 times, most recently from d5d2ced to af057aeCompareMay 28, 2026 10:11
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 3 times, most recently from 9f03f8d to efa8911CompareMay 29, 2026 14:13
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch from c1521f6 to 81910a9CompareJune 8, 2026 13:03
Comment threadairflow-core/src/airflow/assets/manager.py
Comment threadtask-sdk/src/airflow/sdk/definitions/partition_mappers/base.py Outdated
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 4 times, most recently from aa011c3 to 03b76c8CompareJune 9, 2026 13:17
@Lee-W
Lee-W requested a review from uranusjrJune 9, 2026 13:38
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 2 times, most recently from c15e65c to a26e224CompareJune 10, 2026 00:21
Comment threadtask-sdk/src/airflow/sdk/definitions/partition_mappers/base.py Outdated
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 2 times, most recently from c634109 to cc440c8CompareJune 10, 2026 10:07
@Lee-WLee-W removed the area:db-migrations PRs with DB migration label Jun 10, 2026
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 4 times, most recently from 1c8c418 to 9d28124CompareJune 10, 2026 15:30
Lee-W added 4 commits June 11, 2026 08:53
… fan-out cap
Add a `max_downstream_keys: int | None = None` parameter to both the SDK and
core `PartitionMapper` base classes, threaded through every subclass
constructor that defines its own `__init__`. The validator rejects 0,
negatives, floats, and strings.
Serialization is updated in two parallel paths:
- Per-class `serialize()` / `deserialize()` methods (custom-mapper path).
- `encoders.py` singledispatch overrides (built-in-mapper path).
Both paths write `max_downstream_keys` only when non-None, following the
existing convention of omitting default-valued fields from the serialized form.
In `manager.py`, the single-shot `max_downstream_keys` read is deleted;
the mapper object is retained after the `to_downstream` call so its
`max_downstream_keys` attribute is readable at the boundary check. The
effective cap is computed per target-Dag as either the mapper's own cap or the
global `[scheduler] partition_mapper_max_downstream_keys`. The `Log.extra`
string uses `max_downstream_keys=N` when the per-mapper cap trips, and keeps
the existing `[scheduler] partition_mapper_max_downstream_keys=N` wording when
the global trips.
The scheduler error log for a partition fan-out overflow now carries a
cap_source kwarg, so operators can tell whether the per-mapper
max_downstream_keys override or the global scheduler cap was the limit
that was hit.
…validator
Convert the SDK PartitionMapper base into an attrs class so the
max_downstream_keys bound is enforced through a field validator instead
of a hand-written __init__ check, matching the attrs convention used
across the rest of airflow.sdk.definitions. Behavior is unchanged.
Also add the SDK-side validator tests that previously only existed on
the core mapper hierarchy.
Make the whole SDK partition-mapper hierarchy use one mechanism so the
attrs base no longer needs a slots exception. RollupMapper, AllowedKeyMapper,
the temporal mappers, and FanOutMapper become attrs classes; ChainMapper and
ProductMapper keep their variadic __init__ via init=False. Public constructor
signatures and behavior are unchanged.
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch from 9d28124 to 5f26df1CompareJune 11, 2026 00:53
@Lee-W
Lee-W merged commit b7247b1 into apache:mainJun 11, 2026
195 of 196 checks passed
@Lee-W
Lee-W deleted the partition-fanout-max-keys branch June 11, 2026 02:26
imrichardwu pushed a commit to imrichardwu/airflow that referenced this pull request Jun 16, 2026
dingo4dev pushed a commit to dingo4dev/airflow that referenced this pull request Jun 16, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:APIAirflow's REST/HTTP APIarea:ConfigTemplatesarea:DAG-processingarea:dev-toolsarea:Schedulerincluding HA (high availability) schedulerarea:task-sdkarea:UIRelated to UI/UX. For Frontend Developers.kind:documentation

Projects

No open projects

Development

Successfully merging this pull request may close these issues.

Fan-out: explosion cap and error behavior

4 participants

@Lee-W@uranusjr@bugraoz93@phanikumv
, '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('^' + ".*" + ' feat(core): add per-mapper max_fan_out override for partition fan-out cap by Lee-W · Pull Request #67184 · apache/airflow · GitHub
Skip to content

feat(core): add per-mapper max_fan_out override for partition fan-out cap - #67184

Merged
Lee-W merged 4 commits into
apache:mainfrom
astronomer:partition-fanout-max-keys
Jun 11, 2026
Merged

feat(core): add per-mapper max_fan_out override for partition fan-out cap#67184
Lee-W merged 4 commits into
apache:mainfrom
astronomer:partition-fanout-max-keys

Conversation

@Lee-W

@Lee-WLee-W commented May 19, 2026

Copy link
Copy Markdown
Member

Why

a per-mapper cap so each PartitionMapper instance can override the global [scheduler] partition_fanout_max_keys (implemented in #66030)
Lives on the mapper rather than the Dag because one Dag can bind several mappers
with very different fan-out profiles.

closes: #65760

What

  • Add max_fan_out: int | None = None to PartitionMapper base (SDK + core). Validator rejects 0, negatives, non-int, and bool. None → global; positive int → override.
  • Thread the kwarg through every subclass that overrides __init__, and through both serialization paths (per-class .serialize() and the encoders.py singledispatch overrides). Field emitted only when non-None — pre-change payloads round-trip byte-identical.
  • assets/manager.py: read the cap per target Dag, swap a cap_source fragment into the audit-log extra (max_fan_out=N vs. existing [scheduler] partition_fanout_max_keys=N).

Was generative AI tooling used to co-author this PR?
  • Yes (please specify the tool below)

  • Read the Pull Request Guidelines for more information. Note: commit author/co-author name and email in commits become permanently public when merged.
  • For fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
  • When adding dependency, check compliance with the ASF 3rd Party License Policy.
  • For significant user-facing changes create newsfragment: {pr_number}.significant.rst, in airflow-core/newsfragments. You can add this file in a follow-up commit after the PR is created so you know the PR number.

@boring-cyborgboring-cyborgBot added area:API Airflow's REST/HTTP API area:ConfigTemplates area:DAG-processing area:db-migrations PRs with DB migration area:dev-tools area:Scheduler including HA (high availability) scheduler area:task-sdk area:UI Related to UI/UX. For Frontend Developers. backport-to-v3-2-test kind:documentation labels May 19, 2026
@Lee-WLee-W changed the title feat(AIP-76): windowfeat(core): add per-mapper max_fan_out override for partition fan-out capMay 19, 2026
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 14 times, most recently from d5d2ced to af057aeCompareMay 28, 2026 10:11
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 3 times, most recently from 9f03f8d to efa8911CompareMay 29, 2026 14:13
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch from c1521f6 to 81910a9CompareJune 8, 2026 13:03
Comment threadairflow-core/src/airflow/assets/manager.py
Comment threadtask-sdk/src/airflow/sdk/definitions/partition_mappers/base.py Outdated
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 4 times, most recently from aa011c3 to 03b76c8CompareJune 9, 2026 13:17
@Lee-W
Lee-W requested a review from uranusjrJune 9, 2026 13:38
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 2 times, most recently from c15e65c to a26e224CompareJune 10, 2026 00:21
Comment threadtask-sdk/src/airflow/sdk/definitions/partition_mappers/base.py Outdated
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 2 times, most recently from c634109 to cc440c8CompareJune 10, 2026 10:07
@Lee-WLee-W removed the area:db-migrations PRs with DB migration label Jun 10, 2026
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 4 times, most recently from 1c8c418 to 9d28124CompareJune 10, 2026 15:30
Lee-W added 4 commits June 11, 2026 08:53
… fan-out cap
Add a `max_downstream_keys: int | None = None` parameter to both the SDK and
core `PartitionMapper` base classes, threaded through every subclass
constructor that defines its own `__init__`. The validator rejects 0,
negatives, floats, and strings.
Serialization is updated in two parallel paths:
- Per-class `serialize()` / `deserialize()` methods (custom-mapper path).
- `encoders.py` singledispatch overrides (built-in-mapper path).
Both paths write `max_downstream_keys` only when non-None, following the
existing convention of omitting default-valued fields from the serialized form.
In `manager.py`, the single-shot `max_downstream_keys` read is deleted;
the mapper object is retained after the `to_downstream` call so its
`max_downstream_keys` attribute is readable at the boundary check. The
effective cap is computed per target-Dag as either the mapper's own cap or the
global `[scheduler] partition_mapper_max_downstream_keys`. The `Log.extra`
string uses `max_downstream_keys=N` when the per-mapper cap trips, and keeps
the existing `[scheduler] partition_mapper_max_downstream_keys=N` wording when
the global trips.
The scheduler error log for a partition fan-out overflow now carries a
cap_source kwarg, so operators can tell whether the per-mapper
max_downstream_keys override or the global scheduler cap was the limit
that was hit.
…validator
Convert the SDK PartitionMapper base into an attrs class so the
max_downstream_keys bound is enforced through a field validator instead
of a hand-written __init__ check, matching the attrs convention used
across the rest of airflow.sdk.definitions. Behavior is unchanged.
Also add the SDK-side validator tests that previously only existed on
the core mapper hierarchy.
Make the whole SDK partition-mapper hierarchy use one mechanism so the
attrs base no longer needs a slots exception. RollupMapper, AllowedKeyMapper,
the temporal mappers, and FanOutMapper become attrs classes; ChainMapper and
ProductMapper keep their variadic __init__ via init=False. Public constructor
signatures and behavior are unchanged.
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch from 9d28124 to 5f26df1CompareJune 11, 2026 00:53
@Lee-W
Lee-W merged commit b7247b1 into apache:mainJun 11, 2026
195 of 196 checks passed
@Lee-W
Lee-W deleted the partition-fanout-max-keys branch June 11, 2026 02:26
imrichardwu pushed a commit to imrichardwu/airflow that referenced this pull request Jun 16, 2026
dingo4dev pushed a commit to dingo4dev/airflow that referenced this pull request Jun 16, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:APIAirflow's REST/HTTP APIarea:ConfigTemplatesarea:DAG-processingarea:dev-toolsarea:Schedulerincluding HA (high availability) schedulerarea:task-sdkarea:UIRelated to UI/UX. For Frontend Developers.kind:documentation

Projects

No open projects

Development

Successfully merging this pull request may close these issues.

Fan-out: explosion cap and error behavior

4 participants

@Lee-W@uranusjr@bugraoz93@phanikumv
, '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('^' + ".*" + ' feat(core): add per-mapper max_fan_out override for partition fan-out cap by Lee-W · Pull Request #67184 · apache/airflow · GitHub
Skip to content

feat(core): add per-mapper max_fan_out override for partition fan-out cap - #67184

Merged
Lee-W merged 4 commits into
apache:mainfrom
astronomer:partition-fanout-max-keys
Jun 11, 2026
Merged

feat(core): add per-mapper max_fan_out override for partition fan-out cap#67184
Lee-W merged 4 commits into
apache:mainfrom
astronomer:partition-fanout-max-keys

Conversation

@Lee-W

@Lee-WLee-W commented May 19, 2026

Copy link
Copy Markdown
Member

Why

a per-mapper cap so each PartitionMapper instance can override the global [scheduler] partition_fanout_max_keys (implemented in #66030)
Lives on the mapper rather than the Dag because one Dag can bind several mappers
with very different fan-out profiles.

closes: #65760

What

  • Add max_fan_out: int | None = None to PartitionMapper base (SDK + core). Validator rejects 0, negatives, non-int, and bool. None → global; positive int → override.
  • Thread the kwarg through every subclass that overrides __init__, and through both serialization paths (per-class .serialize() and the encoders.py singledispatch overrides). Field emitted only when non-None — pre-change payloads round-trip byte-identical.
  • assets/manager.py: read the cap per target Dag, swap a cap_source fragment into the audit-log extra (max_fan_out=N vs. existing [scheduler] partition_fanout_max_keys=N).

Was generative AI tooling used to co-author this PR?
  • Yes (please specify the tool below)

  • Read the Pull Request Guidelines for more information. Note: commit author/co-author name and email in commits become permanently public when merged.
  • For fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
  • When adding dependency, check compliance with the ASF 3rd Party License Policy.
  • For significant user-facing changes create newsfragment: {pr_number}.significant.rst, in airflow-core/newsfragments. You can add this file in a follow-up commit after the PR is created so you know the PR number.

@boring-cyborgboring-cyborgBot added area:API Airflow's REST/HTTP API area:ConfigTemplates area:DAG-processing area:db-migrations PRs with DB migration area:dev-tools area:Scheduler including HA (high availability) scheduler area:task-sdk area:UI Related to UI/UX. For Frontend Developers. backport-to-v3-2-test kind:documentation labels May 19, 2026
@Lee-WLee-W changed the title feat(AIP-76): windowfeat(core): add per-mapper max_fan_out override for partition fan-out capMay 19, 2026
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 14 times, most recently from d5d2ced to af057aeCompareMay 28, 2026 10:11
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 3 times, most recently from 9f03f8d to efa8911CompareMay 29, 2026 14:13
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch from c1521f6 to 81910a9CompareJune 8, 2026 13:03
Comment threadairflow-core/src/airflow/assets/manager.py
Comment threadtask-sdk/src/airflow/sdk/definitions/partition_mappers/base.py Outdated
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 4 times, most recently from aa011c3 to 03b76c8CompareJune 9, 2026 13:17
@Lee-W
Lee-W requested a review from uranusjrJune 9, 2026 13:38
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 2 times, most recently from c15e65c to a26e224CompareJune 10, 2026 00:21
Comment threadtask-sdk/src/airflow/sdk/definitions/partition_mappers/base.py Outdated
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 2 times, most recently from c634109 to cc440c8CompareJune 10, 2026 10:07
@Lee-WLee-W removed the area:db-migrations PRs with DB migration label Jun 10, 2026
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 4 times, most recently from 1c8c418 to 9d28124CompareJune 10, 2026 15:30
Lee-W added 4 commits June 11, 2026 08:53
… fan-out cap
Add a `max_downstream_keys: int | None = None` parameter to both the SDK and
core `PartitionMapper` base classes, threaded through every subclass
constructor that defines its own `__init__`. The validator rejects 0,
negatives, floats, and strings.
Serialization is updated in two parallel paths:
- Per-class `serialize()` / `deserialize()` methods (custom-mapper path).
- `encoders.py` singledispatch overrides (built-in-mapper path).
Both paths write `max_downstream_keys` only when non-None, following the
existing convention of omitting default-valued fields from the serialized form.
In `manager.py`, the single-shot `max_downstream_keys` read is deleted;
the mapper object is retained after the `to_downstream` call so its
`max_downstream_keys` attribute is readable at the boundary check. The
effective cap is computed per target-Dag as either the mapper's own cap or the
global `[scheduler] partition_mapper_max_downstream_keys`. The `Log.extra`
string uses `max_downstream_keys=N` when the per-mapper cap trips, and keeps
the existing `[scheduler] partition_mapper_max_downstream_keys=N` wording when
the global trips.
The scheduler error log for a partition fan-out overflow now carries a
cap_source kwarg, so operators can tell whether the per-mapper
max_downstream_keys override or the global scheduler cap was the limit
that was hit.
…validator
Convert the SDK PartitionMapper base into an attrs class so the
max_downstream_keys bound is enforced through a field validator instead
of a hand-written __init__ check, matching the attrs convention used
across the rest of airflow.sdk.definitions. Behavior is unchanged.
Also add the SDK-side validator tests that previously only existed on
the core mapper hierarchy.
Make the whole SDK partition-mapper hierarchy use one mechanism so the
attrs base no longer needs a slots exception. RollupMapper, AllowedKeyMapper,
the temporal mappers, and FanOutMapper become attrs classes; ChainMapper and
ProductMapper keep their variadic __init__ via init=False. Public constructor
signatures and behavior are unchanged.
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch from 9d28124 to 5f26df1CompareJune 11, 2026 00:53
@Lee-W
Lee-W merged commit b7247b1 into apache:mainJun 11, 2026
195 of 196 checks passed
@Lee-W
Lee-W deleted the partition-fanout-max-keys branch June 11, 2026 02:26
imrichardwu pushed a commit to imrichardwu/airflow that referenced this pull request Jun 16, 2026
dingo4dev pushed a commit to dingo4dev/airflow that referenced this pull request Jun 16, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:APIAirflow's REST/HTTP APIarea:ConfigTemplatesarea:DAG-processingarea:dev-toolsarea:Schedulerincluding HA (high availability) schedulerarea:task-sdkarea:UIRelated to UI/UX. For Frontend Developers.kind:documentation

Projects

No open projects

Development

Successfully merging this pull request may close these issues.

Fan-out: explosion cap and error behavior

4 participants

@Lee-W@uranusjr@bugraoz93@phanikumv
, '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" + ' feat(core): add per-mapper max_fan_out override for partition fan-out cap by Lee-W · Pull Request #67184 · apache/airflow · GitHub
Skip to content

feat(core): add per-mapper max_fan_out override for partition fan-out cap - #67184

Merged
Lee-W merged 4 commits into
apache:mainfrom
astronomer:partition-fanout-max-keys
Jun 11, 2026
Merged

feat(core): add per-mapper max_fan_out override for partition fan-out cap#67184
Lee-W merged 4 commits into
apache:mainfrom
astronomer:partition-fanout-max-keys

Conversation

@Lee-W

@Lee-WLee-W commented May 19, 2026

Copy link
Copy Markdown
Member

Why

a per-mapper cap so each PartitionMapper instance can override the global [scheduler] partition_fanout_max_keys (implemented in #66030)
Lives on the mapper rather than the Dag because one Dag can bind several mappers
with very different fan-out profiles.

closes: #65760

What

  • Add max_fan_out: int | None = None to PartitionMapper base (SDK + core). Validator rejects 0, negatives, non-int, and bool. None → global; positive int → override.
  • Thread the kwarg through every subclass that overrides __init__, and through both serialization paths (per-class .serialize() and the encoders.py singledispatch overrides). Field emitted only when non-None — pre-change payloads round-trip byte-identical.
  • assets/manager.py: read the cap per target Dag, swap a cap_source fragment into the audit-log extra (max_fan_out=N vs. existing [scheduler] partition_fanout_max_keys=N).

Was generative AI tooling used to co-author this PR?
  • Yes (please specify the tool below)

  • Read the Pull Request Guidelines for more information. Note: commit author/co-author name and email in commits become permanently public when merged.
  • For fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
  • When adding dependency, check compliance with the ASF 3rd Party License Policy.
  • For significant user-facing changes create newsfragment: {pr_number}.significant.rst, in airflow-core/newsfragments. You can add this file in a follow-up commit after the PR is created so you know the PR number.

@boring-cyborgboring-cyborgBot added area:API Airflow's REST/HTTP API area:ConfigTemplates area:DAG-processing area:db-migrations PRs with DB migration area:dev-tools area:Scheduler including HA (high availability) scheduler area:task-sdk area:UI Related to UI/UX. For Frontend Developers. backport-to-v3-2-test kind:documentation labels May 19, 2026
@Lee-WLee-W changed the title feat(AIP-76): windowfeat(core): add per-mapper max_fan_out override for partition fan-out capMay 19, 2026
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 14 times, most recently from d5d2ced to af057aeCompareMay 28, 2026 10:11
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 3 times, most recently from 9f03f8d to efa8911CompareMay 29, 2026 14:13
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch from c1521f6 to 81910a9CompareJune 8, 2026 13:03
Comment threadairflow-core/src/airflow/assets/manager.py
Comment threadtask-sdk/src/airflow/sdk/definitions/partition_mappers/base.py Outdated
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 4 times, most recently from aa011c3 to 03b76c8CompareJune 9, 2026 13:17
@Lee-W
Lee-W requested a review from uranusjrJune 9, 2026 13:38
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 2 times, most recently from c15e65c to a26e224CompareJune 10, 2026 00:21
Comment threadtask-sdk/src/airflow/sdk/definitions/partition_mappers/base.py Outdated
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 2 times, most recently from c634109 to cc440c8CompareJune 10, 2026 10:07
@Lee-WLee-W removed the area:db-migrations PRs with DB migration label Jun 10, 2026
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 4 times, most recently from 1c8c418 to 9d28124CompareJune 10, 2026 15:30
Lee-W added 4 commits June 11, 2026 08:53
… fan-out cap
Add a `max_downstream_keys: int | None = None` parameter to both the SDK and
core `PartitionMapper` base classes, threaded through every subclass
constructor that defines its own `__init__`. The validator rejects 0,
negatives, floats, and strings.
Serialization is updated in two parallel paths:
- Per-class `serialize()` / `deserialize()` methods (custom-mapper path).
- `encoders.py` singledispatch overrides (built-in-mapper path).
Both paths write `max_downstream_keys` only when non-None, following the
existing convention of omitting default-valued fields from the serialized form.
In `manager.py`, the single-shot `max_downstream_keys` read is deleted;
the mapper object is retained after the `to_downstream` call so its
`max_downstream_keys` attribute is readable at the boundary check. The
effective cap is computed per target-Dag as either the mapper's own cap or the
global `[scheduler] partition_mapper_max_downstream_keys`. The `Log.extra`
string uses `max_downstream_keys=N` when the per-mapper cap trips, and keeps
the existing `[scheduler] partition_mapper_max_downstream_keys=N` wording when
the global trips.
The scheduler error log for a partition fan-out overflow now carries a
cap_source kwarg, so operators can tell whether the per-mapper
max_downstream_keys override or the global scheduler cap was the limit
that was hit.
…validator
Convert the SDK PartitionMapper base into an attrs class so the
max_downstream_keys bound is enforced through a field validator instead
of a hand-written __init__ check, matching the attrs convention used
across the rest of airflow.sdk.definitions. Behavior is unchanged.
Also add the SDK-side validator tests that previously only existed on
the core mapper hierarchy.
Make the whole SDK partition-mapper hierarchy use one mechanism so the
attrs base no longer needs a slots exception. RollupMapper, AllowedKeyMapper,
the temporal mappers, and FanOutMapper become attrs classes; ChainMapper and
ProductMapper keep their variadic __init__ via init=False. Public constructor
signatures and behavior are unchanged.
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch from 9d28124 to 5f26df1CompareJune 11, 2026 00:53
@Lee-W
Lee-W merged commit b7247b1 into apache:mainJun 11, 2026
195 of 196 checks passed
@Lee-W
Lee-W deleted the partition-fanout-max-keys branch June 11, 2026 02:26
imrichardwu pushed a commit to imrichardwu/airflow that referenced this pull request Jun 16, 2026
dingo4dev pushed a commit to dingo4dev/airflow that referenced this pull request Jun 16, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:APIAirflow's REST/HTTP APIarea:ConfigTemplatesarea:DAG-processingarea:dev-toolsarea:Schedulerincluding HA (high availability) schedulerarea:task-sdkarea:UIRelated to UI/UX. For Frontend Developers.kind:documentation

Projects

No open projects

Development

Successfully merging this pull request may close these issues.

Fan-out: explosion cap and error behavior

4 participants

@Lee-W@uranusjr@bugraoz93@phanikumv
, '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('^' + ".*" + ' feat(core): add per-mapper max_fan_out override for partition fan-out cap by Lee-W · Pull Request #67184 · apache/airflow · GitHub
Skip to content

feat(core): add per-mapper max_fan_out override for partition fan-out cap - #67184

Merged
Lee-W merged 4 commits into
apache:mainfrom
astronomer:partition-fanout-max-keys
Jun 11, 2026
Merged

feat(core): add per-mapper max_fan_out override for partition fan-out cap#67184
Lee-W merged 4 commits into
apache:mainfrom
astronomer:partition-fanout-max-keys

Conversation

@Lee-W

@Lee-WLee-W commented May 19, 2026

Copy link
Copy Markdown
Member

Why

a per-mapper cap so each PartitionMapper instance can override the global [scheduler] partition_fanout_max_keys (implemented in #66030)
Lives on the mapper rather than the Dag because one Dag can bind several mappers
with very different fan-out profiles.

closes: #65760

What

  • Add max_fan_out: int | None = None to PartitionMapper base (SDK + core). Validator rejects 0, negatives, non-int, and bool. None → global; positive int → override.
  • Thread the kwarg through every subclass that overrides __init__, and through both serialization paths (per-class .serialize() and the encoders.py singledispatch overrides). Field emitted only when non-None — pre-change payloads round-trip byte-identical.
  • assets/manager.py: read the cap per target Dag, swap a cap_source fragment into the audit-log extra (max_fan_out=N vs. existing [scheduler] partition_fanout_max_keys=N).

Was generative AI tooling used to co-author this PR?
  • Yes (please specify the tool below)

  • Read the Pull Request Guidelines for more information. Note: commit author/co-author name and email in commits become permanently public when merged.
  • For fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
  • When adding dependency, check compliance with the ASF 3rd Party License Policy.
  • For significant user-facing changes create newsfragment: {pr_number}.significant.rst, in airflow-core/newsfragments. You can add this file in a follow-up commit after the PR is created so you know the PR number.

@boring-cyborgboring-cyborgBot added area:API Airflow's REST/HTTP API area:ConfigTemplates area:DAG-processing area:db-migrations PRs with DB migration area:dev-tools area:Scheduler including HA (high availability) scheduler area:task-sdk area:UI Related to UI/UX. For Frontend Developers. backport-to-v3-2-test kind:documentation labels May 19, 2026
@Lee-WLee-W changed the title feat(AIP-76): windowfeat(core): add per-mapper max_fan_out override for partition fan-out capMay 19, 2026
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 14 times, most recently from d5d2ced to af057aeCompareMay 28, 2026 10:11
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 3 times, most recently from 9f03f8d to efa8911CompareMay 29, 2026 14:13
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch from c1521f6 to 81910a9CompareJune 8, 2026 13:03
Comment threadairflow-core/src/airflow/assets/manager.py
Comment threadtask-sdk/src/airflow/sdk/definitions/partition_mappers/base.py Outdated
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 4 times, most recently from aa011c3 to 03b76c8CompareJune 9, 2026 13:17
@Lee-W
Lee-W requested a review from uranusjrJune 9, 2026 13:38
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 2 times, most recently from c15e65c to a26e224CompareJune 10, 2026 00:21
Comment threadtask-sdk/src/airflow/sdk/definitions/partition_mappers/base.py Outdated
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 2 times, most recently from c634109 to cc440c8CompareJune 10, 2026 10:07
@Lee-WLee-W removed the area:db-migrations PRs with DB migration label Jun 10, 2026
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 4 times, most recently from 1c8c418 to 9d28124CompareJune 10, 2026 15:30
Lee-W added 4 commits June 11, 2026 08:53
… fan-out cap
Add a `max_downstream_keys: int | None = None` parameter to both the SDK and
core `PartitionMapper` base classes, threaded through every subclass
constructor that defines its own `__init__`. The validator rejects 0,
negatives, floats, and strings.
Serialization is updated in two parallel paths:
- Per-class `serialize()` / `deserialize()` methods (custom-mapper path).
- `encoders.py` singledispatch overrides (built-in-mapper path).
Both paths write `max_downstream_keys` only when non-None, following the
existing convention of omitting default-valued fields from the serialized form.
In `manager.py`, the single-shot `max_downstream_keys` read is deleted;
the mapper object is retained after the `to_downstream` call so its
`max_downstream_keys` attribute is readable at the boundary check. The
effective cap is computed per target-Dag as either the mapper's own cap or the
global `[scheduler] partition_mapper_max_downstream_keys`. The `Log.extra`
string uses `max_downstream_keys=N` when the per-mapper cap trips, and keeps
the existing `[scheduler] partition_mapper_max_downstream_keys=N` wording when
the global trips.
The scheduler error log for a partition fan-out overflow now carries a
cap_source kwarg, so operators can tell whether the per-mapper
max_downstream_keys override or the global scheduler cap was the limit
that was hit.
…validator
Convert the SDK PartitionMapper base into an attrs class so the
max_downstream_keys bound is enforced through a field validator instead
of a hand-written __init__ check, matching the attrs convention used
across the rest of airflow.sdk.definitions. Behavior is unchanged.
Also add the SDK-side validator tests that previously only existed on
the core mapper hierarchy.
Make the whole SDK partition-mapper hierarchy use one mechanism so the
attrs base no longer needs a slots exception. RollupMapper, AllowedKeyMapper,
the temporal mappers, and FanOutMapper become attrs classes; ChainMapper and
ProductMapper keep their variadic __init__ via init=False. Public constructor
signatures and behavior are unchanged.
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch from 9d28124 to 5f26df1CompareJune 11, 2026 00:53
@Lee-W
Lee-W merged commit b7247b1 into apache:mainJun 11, 2026
195 of 196 checks passed
@Lee-W
Lee-W deleted the partition-fanout-max-keys branch June 11, 2026 02:26
imrichardwu pushed a commit to imrichardwu/airflow that referenced this pull request Jun 16, 2026
dingo4dev pushed a commit to dingo4dev/airflow that referenced this pull request Jun 16, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:APIAirflow's REST/HTTP APIarea:ConfigTemplatesarea:DAG-processingarea:dev-toolsarea:Schedulerincluding HA (high availability) schedulerarea:task-sdkarea:UIRelated to UI/UX. For Frontend Developers.kind:documentation

Projects

No open projects

Development

Successfully merging this pull request may close these issues.

Fan-out: explosion cap and error behavior

4 participants

@Lee-W@uranusjr@bugraoz93@phanikumv
, '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); } })(); })(); feat(core): add per-mapper max_fan_out override for partition fan-out cap by Lee-W · Pull Request #67184 · apache/airflow · GitHub
Skip to content

feat(core): add per-mapper max_fan_out override for partition fan-out cap - #67184

Merged
Lee-W merged 4 commits into
apache:mainfrom
astronomer:partition-fanout-max-keys
Jun 11, 2026
Merged

feat(core): add per-mapper max_fan_out override for partition fan-out cap#67184
Lee-W merged 4 commits into
apache:mainfrom
astronomer:partition-fanout-max-keys

Conversation

@Lee-W

@Lee-WLee-W commented May 19, 2026

Copy link
Copy Markdown
Member

Why

a per-mapper cap so each PartitionMapper instance can override the global [scheduler] partition_fanout_max_keys (implemented in #66030)
Lives on the mapper rather than the Dag because one Dag can bind several mappers
with very different fan-out profiles.

closes: #65760

What

  • Add max_fan_out: int | None = None to PartitionMapper base (SDK + core). Validator rejects 0, negatives, non-int, and bool. None → global; positive int → override.
  • Thread the kwarg through every subclass that overrides __init__, and through both serialization paths (per-class .serialize() and the encoders.py singledispatch overrides). Field emitted only when non-None — pre-change payloads round-trip byte-identical.
  • assets/manager.py: read the cap per target Dag, swap a cap_source fragment into the audit-log extra (max_fan_out=N vs. existing [scheduler] partition_fanout_max_keys=N).

Was generative AI tooling used to co-author this PR?
  • Yes (please specify the tool below)

  • Read the Pull Request Guidelines for more information. Note: commit author/co-author name and email in commits become permanently public when merged.
  • For fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
  • When adding dependency, check compliance with the ASF 3rd Party License Policy.
  • For significant user-facing changes create newsfragment: {pr_number}.significant.rst, in airflow-core/newsfragments. You can add this file in a follow-up commit after the PR is created so you know the PR number.

@boring-cyborgboring-cyborgBot added area:API Airflow's REST/HTTP API area:ConfigTemplates area:DAG-processing area:db-migrations PRs with DB migration area:dev-tools area:Scheduler including HA (high availability) scheduler area:task-sdk area:UI Related to UI/UX. For Frontend Developers. backport-to-v3-2-test kind:documentation labels May 19, 2026
@Lee-WLee-W changed the title feat(AIP-76): windowfeat(core): add per-mapper max_fan_out override for partition fan-out capMay 19, 2026
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 14 times, most recently from d5d2ced to af057aeCompareMay 28, 2026 10:11
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 3 times, most recently from 9f03f8d to efa8911CompareMay 29, 2026 14:13
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch from c1521f6 to 81910a9CompareJune 8, 2026 13:03
Comment threadairflow-core/src/airflow/assets/manager.py
Comment threadtask-sdk/src/airflow/sdk/definitions/partition_mappers/base.py Outdated
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 4 times, most recently from aa011c3 to 03b76c8CompareJune 9, 2026 13:17
@Lee-W
Lee-W requested a review from uranusjrJune 9, 2026 13:38
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 2 times, most recently from c15e65c to a26e224CompareJune 10, 2026 00:21
Comment threadtask-sdk/src/airflow/sdk/definitions/partition_mappers/base.py Outdated
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 2 times, most recently from c634109 to cc440c8CompareJune 10, 2026 10:07
@Lee-WLee-W removed the area:db-migrations PRs with DB migration label Jun 10, 2026
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch 4 times, most recently from 1c8c418 to 9d28124CompareJune 10, 2026 15:30
Lee-W added 4 commits June 11, 2026 08:53
… fan-out cap
Add a `max_downstream_keys: int | None = None` parameter to both the SDK and
core `PartitionMapper` base classes, threaded through every subclass
constructor that defines its own `__init__`. The validator rejects 0,
negatives, floats, and strings.
Serialization is updated in two parallel paths:
- Per-class `serialize()` / `deserialize()` methods (custom-mapper path).
- `encoders.py` singledispatch overrides (built-in-mapper path).
Both paths write `max_downstream_keys` only when non-None, following the
existing convention of omitting default-valued fields from the serialized form.
In `manager.py`, the single-shot `max_downstream_keys` read is deleted;
the mapper object is retained after the `to_downstream` call so its
`max_downstream_keys` attribute is readable at the boundary check. The
effective cap is computed per target-Dag as either the mapper's own cap or the
global `[scheduler] partition_mapper_max_downstream_keys`. The `Log.extra`
string uses `max_downstream_keys=N` when the per-mapper cap trips, and keeps
the existing `[scheduler] partition_mapper_max_downstream_keys=N` wording when
the global trips.
The scheduler error log for a partition fan-out overflow now carries a
cap_source kwarg, so operators can tell whether the per-mapper
max_downstream_keys override or the global scheduler cap was the limit
that was hit.
…validator
Convert the SDK PartitionMapper base into an attrs class so the
max_downstream_keys bound is enforced through a field validator instead
of a hand-written __init__ check, matching the attrs convention used
across the rest of airflow.sdk.definitions. Behavior is unchanged.
Also add the SDK-side validator tests that previously only existed on
the core mapper hierarchy.
Make the whole SDK partition-mapper hierarchy use one mechanism so the
attrs base no longer needs a slots exception. RollupMapper, AllowedKeyMapper,
the temporal mappers, and FanOutMapper become attrs classes; ChainMapper and
ProductMapper keep their variadic __init__ via init=False. Public constructor
signatures and behavior are unchanged.
@Lee-W
Lee-Wforce-pushed the partition-fanout-max-keys branch from 9d28124 to 5f26df1CompareJune 11, 2026 00:53
@Lee-W
Lee-W merged commit b7247b1 into apache:mainJun 11, 2026
195 of 196 checks passed
@Lee-W
Lee-W deleted the partition-fanout-max-keys branch June 11, 2026 02:26
imrichardwu pushed a commit to imrichardwu/airflow that referenced this pull request Jun 16, 2026
dingo4dev pushed a commit to dingo4dev/airflow that referenced this pull request Jun 16, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:APIAirflow's REST/HTTP APIarea:ConfigTemplatesarea:DAG-processingarea:dev-toolsarea:Schedulerincluding HA (high availability) schedulerarea:task-sdkarea:UIRelated to UI/UX. For Frontend Developers.kind:documentation

Projects

No open projects

Development

Successfully merging this pull request may close these issues.

Fan-out: explosion cap and error behavior

4 participants

@Lee-W@uranusjr@bugraoz93@phanikumv