Skip to content

Commit d2d7256

Browse files
committed
fix: Prevent unnecessary Pod restarts by applying StatefulSet last
1 parent 39d98ec commit d2d7256

3 files changed

Lines changed: 58 additions & 48 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@
1414
### Fixed
1515

1616
- Default `API_WORKERS` to 1 (instead of letting Airflow default to 4) to prevent crashloop and update/correct docs to reflect this ([#727]).
17+
- Prevent unnecessary Pod restarts when initially creating an AirflowCluster.
18+
This is achieved by applying the StatefulSet after all ConfigMaps and Secrets that it mounts ([#XXX]).
1719

1820
[#726]: https://github.com/stackabletech/airflow-operator/pull/726
1921
[#727]: https://github.com/stackabletech/airflow-operator/pull/727
2022
[#733]: https://github.com/stackabletech/airflow-operator/pull/733
23+
[#XXX]: https://github.com/stackabletech/airflow-operator/pull/XXX
2124

2225
## [25.11.0] - 2025-11-07
2326

rust/operator-binary/src/airflow_controller.rs

Lines changed: 50 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,36 @@ pub async fn reconcile_airflow(
519519
role: role_name.to_string(),
520520
})?;
521521

522+
if let Some(GenericRoleConfig {
523+
pod_disruption_budget: pdb,
524+
}) = airflow.role_config(&airflow_role)
525+
{
526+
add_pdbs(&pdb, airflow, &airflow_role, client, &mut cluster_resources)
527+
.await
528+
.context(FailedToCreatePdbSnafu)?;
529+
}
530+
531+
if let Some(listener_class) = airflow_role.listener_class_name(airflow) {
532+
if let Some(listener_group_name) = airflow.group_listener_name(&airflow_role) {
533+
let rg_group_listener = build_group_listener(
534+
airflow,
535+
build_recommended_labels(
536+
airflow,
537+
AIRFLOW_CONTROLLER_NAME,
538+
&resolved_product_image.app_version_label_value,
539+
role_name,
540+
"none",
541+
),
542+
listener_class.to_string(),
543+
listener_group_name,
544+
)?;
545+
cluster_resources
546+
.add(client, rg_group_listener)
547+
.await
548+
.context(ApplyGroupListenerSnafu)?;
549+
}
550+
}
551+
522552
for (rolegroup_name, rolegroup_config) in role_config.iter() {
523553
let rolegroup = RoleGroupRef {
524554
cluster: ObjectRef::from_obj(airflow),
@@ -587,6 +617,26 @@ pub async fn reconcile_airflow(
587617
rolegroup: rolegroup.clone(),
588618
})?;
589619

620+
let rg_configmap = build_rolegroup_config_map(
621+
airflow,
622+
&resolved_product_image,
623+
&rolegroup,
624+
rolegroup_config,
625+
&authentication_config,
626+
&authorization_config,
627+
&merged_airflow_config.logging,
628+
&Container::Airflow,
629+
)?;
630+
cluster_resources
631+
.add(client, rg_configmap)
632+
.await
633+
.with_context(|_| ApplyRoleGroupConfigSnafu {
634+
rolegroup: rolegroup.clone(),
635+
})?;
636+
637+
// Note: The StatefulSet needs to be applied after all ConfigMaps and Secrets it mounts
638+
// to prevent unnecessary Pod restarts.
639+
// See https://github.com/stackabletech/commons-operator/issues/111 for details.
590640
let rg_statefulset = build_server_rolegroup_statefulset(
591641
airflow,
592642
&resolved_product_image,
@@ -609,54 +659,6 @@ pub async fn reconcile_airflow(
609659
rolegroup: rolegroup.clone(),
610660
})?,
611661
);
612-
613-
let rg_configmap = build_rolegroup_config_map(
614-
airflow,
615-
&resolved_product_image,
616-
&rolegroup,
617-
rolegroup_config,
618-
&authentication_config,
619-
&authorization_config,
620-
&merged_airflow_config.logging,
621-
&Container::Airflow,
622-
)?;
623-
cluster_resources
624-
.add(client, rg_configmap)
625-
.await
626-
.with_context(|_| ApplyRoleGroupConfigSnafu {
627-
rolegroup: rolegroup.clone(),
628-
})?;
629-
}
630-
631-
let role_config = airflow.role_config(&airflow_role);
632-
if let Some(GenericRoleConfig {
633-
pod_disruption_budget: pdb,
634-
}) = role_config
635-
{
636-
add_pdbs(&pdb, airflow, &airflow_role, client, &mut cluster_resources)
637-
.await
638-
.context(FailedToCreatePdbSnafu)?;
639-
}
640-
641-
if let Some(listener_class) = airflow_role.listener_class_name(airflow) {
642-
if let Some(listener_group_name) = airflow.group_listener_name(&airflow_role) {
643-
let rg_group_listener = build_group_listener(
644-
airflow,
645-
build_recommended_labels(
646-
airflow,
647-
AIRFLOW_CONTROLLER_NAME,
648-
&resolved_product_image.app_version_label_value,
649-
role_name,
650-
"none",
651-
),
652-
listener_class.to_string(),
653-
listener_group_name,
654-
)?;
655-
cluster_resources
656-
.add(client, rg_group_listener)
657-
.await
658-
.context(ApplyGroupListenerSnafu)?;
659-
}
660662
}
661663
}
662664

tests/templates/kuttl/smoke/40-assert.yaml.j2

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ apiVersion: apps/v1
1717
kind: StatefulSet
1818
metadata:
1919
name: airflow-webserver-default
20+
generation: 1 # There should be no unneeded Pod restarts
2021
spec:
2122
template:
2223
spec:
@@ -30,6 +31,7 @@ apiVersion: apps/v1
3031
kind: StatefulSet
3132
metadata:
3233
name: airflow-worker-default
34+
generation: 1 # There should be no unneeded Pod restarts
3335
spec:
3436
template:
3537
spec:
@@ -43,6 +45,7 @@ apiVersion: apps/v1
4345
kind: StatefulSet
4446
metadata:
4547
name: airflow-scheduler-default
48+
generation: 1 # There should be no unneeded Pod restarts
4649
spec:
4750
template:
4851
spec:
@@ -55,6 +58,7 @@ apiVersion: apps/v1
5558
kind: StatefulSet
5659
metadata:
5760
name: airflow-dagprocessor-default
61+
generation: 1 # There should be no unneeded Pod restarts
5862
spec:
5963
template:
6064
spec:
@@ -67,6 +71,7 @@ apiVersion: apps/v1
6771
kind: StatefulSet
6872
metadata:
6973
name: airflow-triggerer-default
74+
generation: 1 # There should be no unneeded Pod restarts
7075
spec:
7176
template:
7277
spec:

0 commit comments

Comments
 (0)