Skip to content

feat(matrix-stack): add affinity support to component workloads#1442

Open
somaz94 wants to merge 1 commit into
element-hq:mainfrom
somaz94:feat/affinity-support
Open

feat(matrix-stack): add affinity support to component workloads#1442
somaz94 wants to merge 1 commit into
element-hq:mainfrom
somaz94:feat/affinity-support

Conversation

@somaz94

@somaz94 somaz94 commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

What this does

Adds support for configuring affinity (nodeAffinity, podAffinity and podAntiAffinity) on component workloads, plus a global affinity applied to all components. A per-component affinity overrides the global one.

This follows the exact pattern of the already-merged priorityClassName support (#1438): a single affinity object passthrough, wired through every component's source fragment (.json + .yaml.j2), the global block in sub_schema_values.yaml.j2, and the _pods.tpl render ({{- with (coalesce .affinity $root.Values.affinity) }}).

Why

Operators need to control pod scheduling (node/pod affinity and anti-affinity) for HA spreading, dedicated node pools, and co-location/anti-co-location — currently only nodeSelector, tolerations, topologySpreadConstraints and priorityClassName are exposed.

related: #368

Notes

  • source/common/affinity.json uses additionalProperties: false at the top level (only the three affinity sub-keys allowed) but additionalProperties: true inside each sub-object, so the full Kubernetes affinity content passes through while typos in the three top-level keys are still caught.
  • Synapse worker pods inherit the affinity of the Synapse main process; per-worker affinity is not yet configurable (documented in the values comment).
  • Source fragments were edited and values.yaml / values.schema.json regenerated via scripts/assemble_helm_charts_from_fragments.sh (idempotent — regeneration produces no drift).

Testing

  • New tests/manifests/test_pod_affinity.py (default-absent / per-component / global / component-overrides-global).
  • Full tests/manifests suite green locally (2485 passed, 26 skipped).
  • reuse lint, ruff check, towncrier check all pass.

@somaz94 somaz94 marked this pull request as ready for review July 9, 2026 02:01
@somaz94 somaz94 requested a review from a team as a code owner July 9, 2026 02:01
"properties": {
"nodeAffinity": {
"type": "object",
"additionalProperties": true

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.

I'm fairly strongly against additionalProperties: true for these 3 objects. Unlike e.g. extraVolumes the potential options are fairly constrained. Having a strict schema that validates the structure of these is desirable IMO

{%- endif %}
## Supports nodeAffinity, podAffinity and podAntiAffinity.
## More info: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity
{{ key }}: {}

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.

Suggested change
{{ key }}: {}
{{ key }}:
nodeAffinity: {}
podAffinity: {}
podAntiAffinity: {}

With commented examples above each sub-key.

Potentially the default of {} needs to differ between globally and per-component so that you've a way of unsetting/blanking out a global e.g. nodeAffinity

{{- with (coalesce .priorityClassName $root.Values.priorityClassName) }}
priorityClassName: {{ . }}
{{- end }}
{{- with (coalesce .affinity $root.Values.affinity) }}

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.

I think the 3 affinity sub-keys need to be rendered individually. At present setting $.Values.affinity.nodeAffinity.... and $.Values.synapse.affinity.podAntiAffinity... means that Synapse loses the nodeAffinity. I don't think that's correct.

There should be a way to apply a global nodeAffinity and blank it it out, be it with null or empty dict and the tests should cover it.

Ideally podAntiAffinity should have labelSelector and matchLabelKeys set by default like how we do with topologySpreadConstraints but that's a nice to have and can be added later

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.

These tests:

  • Need to be done independently for each of the 3 affinities
  • Should show that setting one of the affinities at a component level doesn't impact others at the global level
  • Show that a global setting of one of the affinities can be blanked out on a per-component basis

Sign up for free to 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.

2 participants