diff --git a/changelog/20250907_fix_separate_arbiter_and_members_resources.md b/changelog/20250907_fix_separate_arbiter_and_members_resources.md new file mode 100644 index 000000000..6e2e73f17 --- /dev/null +++ b/changelog/20250907_fix_separate_arbiter_and_members_resources.md @@ -0,0 +1,12 @@ +--- +title: Separate arbiter and members resources +kind: fix +date: 2025-09-07 +--- + +**Issue:** Previously, arbiter StatefulSets were incorrectly using the same resource specifications as data-bearing members, which could lead to resource over-allocation or under-allocation for arbiters that have different resource requirements. +- Separated resource creation logic for arbiters and data-bearing members +- Implemented a default resource template specifically for arbiter nodes +- Arbiters now use their own StatefulSet configuration instead of inheriting from `spec.statefulSet` +- This ensures arbiters receive appropriate resource allocation based on their lighter workload requirements + diff --git a/mongodb-community-operator/controllers/construct/mongodbstatefulset.go b/mongodb-community-operator/controllers/construct/mongodbstatefulset.go index e0243e910..def95d4c8 100644 --- a/mongodb-community-operator/controllers/construct/mongodbstatefulset.go +++ b/mongodb-community-operator/controllers/construct/mongodbstatefulset.go @@ -120,7 +120,7 @@ type MongoDBStatefulSetOwner interface { // BuildMongoDBReplicaSetStatefulSetModificationFunction builds the parts of the replica set that are common between every resource that implements // MongoDBStatefulSetOwner. // It doesn't configure TLS or additional containers/env vars that the statefulset might need. -func BuildMongoDBReplicaSetStatefulSetModificationFunction(mdb MongoDBStatefulSetOwner, scaler scale.ReplicaSetScaler, mongodbImage, agentImage, versionUpgradeHookImage, readinessProbeImage string, withInitContainers bool, initAppDBImage string) statefulset.Modification { +func BuildMongoDBReplicaSetStatefulSetModificationFunction(mdb MongoDBStatefulSetOwner, scaler scale.ReplicaSetScaler, mongodbImage, agentImage, versionUpgradeHookImage, readinessProbeImage string, withInitContainers bool, initAppDBImage string, isArbiter bool) statefulset.Modification { labels := map[string]string{ "app": mdb.ServiceName(), } @@ -186,21 +186,8 @@ func BuildMongoDBReplicaSetStatefulSetModificationFunction(mdb MongoDBStatefulSe dataVolumeClaim := statefulset.NOOP() logVolumeClaim := statefulset.NOOP() singleModeVolumeClaim := func(s *appsv1.StatefulSet) {} - if mdb.HasSeparateDataAndLogsVolumes() { - logVolumeMount := statefulset.CreateVolumeMount(mdb.LogsVolumeName(), automationconfig.DefaultAgentLogPath) - dataVolumeMount := statefulset.CreateVolumeMount(mdb.DataVolumeName(), mdb.GetMongodConfiguration().GetDBDataDir()) - dataVolumeClaim = statefulset.WithVolumeClaim(mdb.DataVolumeName(), dataPvc(mdb.DataVolumeName())) - logVolumeClaim = statefulset.WithVolumeClaim(mdb.LogsVolumeName(), logsPvc(mdb.LogsVolumeName())) - mongodbAgentVolumeMounts = append(mongodbAgentVolumeMounts, dataVolumeMount, logVolumeMount) - mongodVolumeMounts = append(mongodVolumeMounts, dataVolumeMount, logVolumeMount) - } else { - mounts := []corev1.VolumeMount{ - statefulset.CreateVolumeMount(mdb.DataVolumeName(), mdb.GetMongodConfiguration().GetDBDataDir(), statefulset.WithSubPath("data")), - statefulset.CreateVolumeMount(mdb.DataVolumeName(), automationconfig.DefaultAgentLogPath, statefulset.WithSubPath("logs")), - } - mongodbAgentVolumeMounts = append(mongodbAgentVolumeMounts, mounts...) - mongodVolumeMounts = append(mongodVolumeMounts, mounts...) - singleModeVolumeClaim = statefulset.WithVolumeClaim(mdb.DataVolumeName(), dataPvc(mdb.DataVolumeName())) + if !isArbiter { + buildVolumesForMembers(mdb, &dataVolumeClaim, &logVolumeClaim, &singleModeVolumeClaim, &mongodbAgentVolumeMounts, &mongodVolumeMounts) } podSecurityContext, _ := podtemplatespec.WithDefaultSecurityContextsModifications() @@ -537,3 +524,25 @@ func collectEnvVars() []corev1.EnvVar { return envVars } + +// buildVolumesForMembers creates volume configurations for regular MongoDB data members +// These members need persistent storage for data and logs +func buildVolumesForMembers(mdb MongoDBStatefulSetOwner, dataVolumeClaim *statefulset.Modification, logVolumeClaim *statefulset.Modification, singleModeVolumeClaim *func(s *appsv1.StatefulSet), mongodbAgentVolumeMounts *[]corev1.VolumeMount, mongodVolumeMounts *[]corev1.VolumeMount) { + + if mdb.HasSeparateDataAndLogsVolumes() { + logVolumeMount := statefulset.CreateVolumeMount(mdb.LogsVolumeName(), automationconfig.DefaultAgentLogPath) + dataVolumeMount := statefulset.CreateVolumeMount(mdb.DataVolumeName(), mdb.GetMongodConfiguration().GetDBDataDir()) + *dataVolumeClaim = statefulset.WithVolumeClaim(mdb.DataVolumeName(), dataPvc(mdb.DataVolumeName())) + *logVolumeClaim = statefulset.WithVolumeClaim(mdb.LogsVolumeName(), logsPvc(mdb.LogsVolumeName())) + *mongodbAgentVolumeMounts = append(*mongodbAgentVolumeMounts, dataVolumeMount, logVolumeMount) + *mongodVolumeMounts = append(*mongodVolumeMounts, dataVolumeMount, logVolumeMount) + } else { + mounts := []corev1.VolumeMount{ + statefulset.CreateVolumeMount(mdb.DataVolumeName(), mdb.GetMongodConfiguration().GetDBDataDir(), statefulset.WithSubPath("data")), + statefulset.CreateVolumeMount(mdb.DataVolumeName(), automationconfig.DefaultAgentLogPath, statefulset.WithSubPath("logs")), + } + *mongodbAgentVolumeMounts = append(*mongodbAgentVolumeMounts, mounts...) + *mongodVolumeMounts = append(*mongodVolumeMounts, mounts...) + + } +} \ No newline at end of file diff --git a/mongodb-community-operator/controllers/replica_set_controller.go b/mongodb-community-operator/controllers/replica_set_controller.go index ba3e5b168..e13106779 100644 --- a/mongodb-community-operator/controllers/replica_set_controller.go +++ b/mongodb-community-operator/controllers/replica_set_controller.go @@ -505,9 +505,10 @@ func (r *ReplicaSetReconciler) createOrUpdateStatefulSet(ctx context.Context, md } mongodbImage := getMongoDBImage(r.mongodbRepoUrl, r.mongodbImage, r.mongodbImageType, mdb.GetMongoDBVersion()) - buildStatefulSetModificationFunction(mdb, mongodbImage, r.agentImage, r.versionUpgradeHookImage, r.readinessProbeImage)(&set) if isArbiter { - buildArbitersModificationFunction(mdb)(&set) + buildArbitersModificationFunction(mdb, mongodbImage, r.agentImage, r.versionUpgradeHookImage, r.readinessProbeImage)(&set) + } else { + buildMembersModificationFunction(mdb, mongodbImage, r.agentImage, r.versionUpgradeHookImage, r.readinessProbeImage)(&set) } if _, err = statefulset.CreateOrUpdate(ctx, r.client, set); err != nil { @@ -789,10 +790,10 @@ func getMongodConfigSearchModification(search *searchv1.MongoDBSearch) automatio } } -// buildStatefulSetModificationFunction takes a MongoDB resource and converts it into -// the corresponding stateful set -func buildStatefulSetModificationFunction(mdb mdbv1.MongoDBCommunity, mongodbImage, agentImage, versionUpgradeHookImage, readinessProbeImage string) statefulset.Modification { - commonModification := construct.BuildMongoDBReplicaSetStatefulSetModificationFunction(&mdb, &mdb, mongodbImage, agentImage, versionUpgradeHookImage, readinessProbeImage, true, "") +// buildBaseStatefulSetModificationFunction takes a MongoDB resource and creates statfulset base +// the tatfulset base. +func buildBaseStatefulSetModificationFunction(mdb mdbv1.MongoDBCommunity, mongodbImage, agentImage, versionUpgradeHookImage, readinessProbeImage string, isArbiter bool) statefulset.Modification { + commonModification := construct.BuildMongoDBReplicaSetStatefulSetModificationFunction(&mdb, &mdb, mongodbImage, agentImage, versionUpgradeHookImage, readinessProbeImage, true, "", isArbiter) return statefulset.Apply( commonModification, statefulset.WithOwnerReference(mdb.GetOwnerReferences()), @@ -803,8 +804,6 @@ func buildStatefulSetModificationFunction(mdb mdbv1.MongoDBCommunity, mongodbIma buildAgentX509(mdb), ), ), - - statefulset.WithCustomSpecs(mdb.Spec.StatefulSetConfiguration.SpecWrapper.Spec), statefulset.WithObjectMetadata( mdb.Spec.StatefulSetConfiguration.MetadataWrapper.Labels, mdb.Spec.StatefulSetConfiguration.MetadataWrapper.Annotations, @@ -812,8 +811,19 @@ func buildStatefulSetModificationFunction(mdb mdbv1.MongoDBCommunity, mongodbIma ) } -func buildArbitersModificationFunction(mdb mdbv1.MongoDBCommunity) statefulset.Modification { +func buildMembersModificationFunction(mdb mdbv1.MongoDBCommunity, mongodbImage, agentImage, versionUpgradeHookImage, readinessProbeImage string) statefulset.Modification { + commonModification := buildBaseStatefulSetModificationFunction(mdb, mongodbImage, agentImage, versionUpgradeHookImage, readinessProbeImage, false) + return statefulset.Apply( + commonModification, + statefulset.WithCustomSpecs(mdb.Spec.StatefulSetConfiguration.SpecWrapper.Spec), + ) +} + +func buildArbitersModificationFunction(mdb mdbv1.MongoDBCommunity, mongodbImage, agentImage, versionUpgradeHookImage, readinessProbeImage string) statefulset.Modification { + commonModification := buildBaseStatefulSetModificationFunction(mdb, mongodbImage, agentImage, versionUpgradeHookImage, readinessProbeImage, true) + return statefulset.Apply( + commonModification, statefulset.WithReplicas(mdb.StatefulSetArbitersThisReconciliation()), statefulset.WithServiceName(mdb.ServiceName()), statefulset.WithName(mdb.ArbiterNamespacedName().Name), diff --git a/mongodb-community-operator/controllers/replicaset_controller_test.go b/mongodb-community-operator/controllers/replicaset_controller_test.go index 7559db794..d1ffa25c6 100644 --- a/mongodb-community-operator/controllers/replicaset_controller_test.go +++ b/mongodb-community-operator/controllers/replicaset_controller_test.go @@ -357,7 +357,7 @@ func TestBuildStatefulSet_ConfiguresUpdateStrategyCorrectly(t *testing.T) { mdb.Spec.Version = "4.0.0" mdb.Annotations[annotations.LastAppliedMongoDBVersion] = "4.0.0" sts := appsv1.StatefulSet{} - buildStatefulSetModificationFunction(mdb, "fake-mongodbImage", AgentImage, "fake-versionUpgradeHookImage", "fake-readinessProbeImage")(&sts) + buildBaseStatefulSetModificationFunction(mdb, "fake-mongodbImage", AgentImage, "fake-versionUpgradeHookImage", "fake-readinessProbeImage", false)(&sts) assert.Equal(t, appsv1.RollingUpdateStatefulSetStrategyType, sts.Spec.UpdateStrategy.Type) }) t.Run("On No Version Change, First Version", func(t *testing.T) { @@ -365,7 +365,7 @@ func TestBuildStatefulSet_ConfiguresUpdateStrategyCorrectly(t *testing.T) { mdb.Spec.Version = "4.0.0" delete(mdb.Annotations, annotations.LastAppliedMongoDBVersion) sts := appsv1.StatefulSet{} - buildStatefulSetModificationFunction(mdb, "fake-mongodbImage", AgentImage, "fake-versionUpgradeHookImage", "fake-readinessProbeImage")(&sts) + buildBaseStatefulSetModificationFunction(mdb, "fake-mongodbImage", AgentImage, "fake-versionUpgradeHookImage", "fake-readinessProbeImage", false)(&sts) assert.Equal(t, appsv1.RollingUpdateStatefulSetStrategyType, sts.Spec.UpdateStrategy.Type) }) t.Run("On Version Change", func(t *testing.T) { @@ -382,7 +382,7 @@ func TestBuildStatefulSet_ConfiguresUpdateStrategyCorrectly(t *testing.T) { mdb.Annotations[annotations.LastAppliedMongoDBVersion] = string(bytes) sts := appsv1.StatefulSet{} - buildStatefulSetModificationFunction(mdb, "fake-mongodbImage", AgentImage, "fake-versionUpgradeHookImage", "fake-readinessProbeImage")(&sts) + buildBaseStatefulSetModificationFunction(mdb, "fake-mongodbImage", AgentImage, "fake-versionUpgradeHookImage", "fake-readinessProbeImage", false)(&sts) assert.Equal(t, appsv1.OnDeleteStatefulSetStrategyType, sts.Spec.UpdateStrategy.Type) }) }