Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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

Original file line number Diff line number Diff line change
Expand Up @@ -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(),
}
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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...)

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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()),
Expand All @@ -803,17 +804,26 @@ 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,
),
)
}

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),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -357,15 +357,15 @@ 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) {
mdb := newTestReplicaSet()
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) {
Expand All @@ -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)
})
}
Expand Down