Skip to content

Commit e0bb730

Browse files
committed
address min cpu and memory, error when autoconfig fails and continue the flow
1 parent cb7277d commit e0bb730

8 files changed

Lines changed: 160 additions & 27 deletions

File tree

api/v1/perconaservermysql_types.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,8 @@ func (t ClusterType) isValid() bool {
247247
// +kubebuilder:validation:XValidation:rule="has(self.image) && size(self.image) > 0",message="mysql.image is required"
248248
// +kubebuilder:validation:XValidation:rule="has(self.size) && self.size > 0",message="mysql.size must be greater than 0"
249249
// +kubebuilder:validation:XValidation:rule="!(has(self.autoconfig) && has(self.autoconfig.enabled) && self.autoconfig.enabled) || (has(self.resources) && ((has(self.resources.limits) && 'cpu' in self.resources.limits) || (has(self.resources.requests) && 'cpu' in self.resources.requests)) && ((has(self.resources.limits) && 'memory' in self.resources.limits) || (has(self.resources.requests) && 'memory' in self.resources.requests)))",message="mysql.resources must set cpu and memory (via limits or requests) when mysql.autoconfig.enabled is true"
250+
// +kubebuilder:validation:XValidation:rule="!(has(self.autoconfig) && has(self.autoconfig.enabled) && self.autoconfig.enabled) || sign(quantity(has(self.resources.limits) && 'cpu' in self.resources.limits ? self.resources.limits['cpu'] : self.resources.requests['cpu'])) == 1",message="mysql.resources cpu must be greater than 0 when mysql.autoconfig.enabled is true"
251+
// +kubebuilder:validation:XValidation:rule="!(has(self.autoconfig) && has(self.autoconfig.enabled) && self.autoconfig.enabled) || quantity(has(self.resources.limits) && 'memory' in self.resources.limits ? self.resources.limits['memory'] : self.resources.requests['memory']).compareTo(quantity('12Mi')) >= 0",message="mysql.resources memory must be at least 12Mi when mysql.autoconfig.enabled is true"
250252
type MySQLSpec struct {
251253
// +kubebuilder:validation:Enum=group-replication;async
252254
// +kubebuilder:default=group-replication

config/crd/bases/ps.percona.com_perconaservermysqls.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5820,6 +5820,19 @@ spec:
58205820
&& ''cpu'' in self.resources.requests)) && ((has(self.resources.limits)
58215821
&& ''memory'' in self.resources.limits) || (has(self.resources.requests)
58225822
&& ''memory'' in self.resources.requests)))'
5823+
- message: mysql.resources cpu must be greater than 0 when mysql.autoconfig.enabled
5824+
is true
5825+
rule: '!(has(self.autoconfig) && has(self.autoconfig.enabled) &&
5826+
self.autoconfig.enabled) || sign(quantity(has(self.resources.limits)
5827+
&& ''cpu'' in self.resources.limits ? self.resources.limits[''cpu'']
5828+
: self.resources.requests[''cpu''])) == 1'
5829+
- message: mysql.resources memory must be at least 12Mi when mysql.autoconfig.enabled
5830+
is true
5831+
rule: '!(has(self.autoconfig) && has(self.autoconfig.enabled) &&
5832+
self.autoconfig.enabled) || quantity(has(self.resources.limits)
5833+
&& ''memory'' in self.resources.limits ? self.resources.limits[''memory'']
5834+
: self.resources.requests[''memory'']).compareTo(quantity(''12Mi''))
5835+
>= 0'
58235836
orchestrator:
58245837
properties:
58255838
affinity:

deploy/bundle.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9825,6 +9825,19 @@ spec:
98259825
&& ''cpu'' in self.resources.requests)) && ((has(self.resources.limits)
98269826
&& ''memory'' in self.resources.limits) || (has(self.resources.requests)
98279827
&& ''memory'' in self.resources.requests)))'
9828+
- message: mysql.resources cpu must be greater than 0 when mysql.autoconfig.enabled
9829+
is true
9830+
rule: '!(has(self.autoconfig) && has(self.autoconfig.enabled) &&
9831+
self.autoconfig.enabled) || sign(quantity(has(self.resources.limits)
9832+
&& ''cpu'' in self.resources.limits ? self.resources.limits[''cpu'']
9833+
: self.resources.requests[''cpu''])) == 1'
9834+
- message: mysql.resources memory must be at least 12Mi when mysql.autoconfig.enabled
9835+
is true
9836+
rule: '!(has(self.autoconfig) && has(self.autoconfig.enabled) &&
9837+
self.autoconfig.enabled) || quantity(has(self.resources.limits)
9838+
&& ''memory'' in self.resources.limits ? self.resources.limits[''memory'']
9839+
: self.resources.requests[''memory'']).compareTo(quantity(''12Mi''))
9840+
>= 0'
98289841
orchestrator:
98299842
properties:
98309843
affinity:

deploy/crd.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9825,6 +9825,19 @@ spec:
98259825
&& ''cpu'' in self.resources.requests)) && ((has(self.resources.limits)
98269826
&& ''memory'' in self.resources.limits) || (has(self.resources.requests)
98279827
&& ''memory'' in self.resources.requests)))'
9828+
- message: mysql.resources cpu must be greater than 0 when mysql.autoconfig.enabled
9829+
is true
9830+
rule: '!(has(self.autoconfig) && has(self.autoconfig.enabled) &&
9831+
self.autoconfig.enabled) || sign(quantity(has(self.resources.limits)
9832+
&& ''cpu'' in self.resources.limits ? self.resources.limits[''cpu'']
9833+
: self.resources.requests[''cpu''])) == 1'
9834+
- message: mysql.resources memory must be at least 12Mi when mysql.autoconfig.enabled
9835+
is true
9836+
rule: '!(has(self.autoconfig) && has(self.autoconfig.enabled) &&
9837+
self.autoconfig.enabled) || quantity(has(self.resources.limits)
9838+
&& ''memory'' in self.resources.limits ? self.resources.limits[''memory'']
9839+
: self.resources.requests[''memory'']).compareTo(quantity(''12Mi''))
9840+
>= 0'
98289841
orchestrator:
98299842
properties:
98309843
affinity:

deploy/cw-bundle.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9825,6 +9825,19 @@ spec:
98259825
&& ''cpu'' in self.resources.requests)) && ((has(self.resources.limits)
98269826
&& ''memory'' in self.resources.limits) || (has(self.resources.requests)
98279827
&& ''memory'' in self.resources.requests)))'
9828+
- message: mysql.resources cpu must be greater than 0 when mysql.autoconfig.enabled
9829+
is true
9830+
rule: '!(has(self.autoconfig) && has(self.autoconfig.enabled) &&
9831+
self.autoconfig.enabled) || sign(quantity(has(self.resources.limits)
9832+
&& ''cpu'' in self.resources.limits ? self.resources.limits[''cpu'']
9833+
: self.resources.requests[''cpu''])) == 1'
9834+
- message: mysql.resources memory must be at least 12Mi when mysql.autoconfig.enabled
9835+
is true
9836+
rule: '!(has(self.autoconfig) && has(self.autoconfig.enabled) &&
9837+
self.autoconfig.enabled) || quantity(has(self.resources.limits)
9838+
&& ''memory'' in self.resources.limits ? self.resources.limits[''memory'']
9839+
: self.resources.requests[''memory'']).compareTo(quantity(''12Mi''))
9840+
>= 0'
98289841
orchestrator:
98299842
properties:
98309843
affinity:

pkg/controller/ps/controller.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1121,7 +1121,10 @@ func (r *PerconaServerMySQLReconciler) reconcileMySQLAutoConfig(ctx context.Cont
11211121
params, err = mysql.GetAutoTuneParams(cr, memory)
11221122
}
11231123
if err != nil {
1124-
return err
1124+
log.Error(err, "failed to calculate MySQL tuning parameters, starting without them")
1125+
r.Recorder.Event(cr, corev1.EventTypeWarning, "AutoConfigFailed",
1126+
fmt.Sprintf("failed to calculate MySQL tuning parameters, starting without them: %v", err))
1127+
params = ""
11251128
}
11261129
config += params
11271130
}

pkg/controller/ps/controller_test.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1171,6 +1171,108 @@ var _ = Describe("CR validations", Ordered, func() {
11711171
})
11721172
})
11731173

1174+
const autoConfigCPUMsg = "mysql.resources cpu must be greater than 0 when mysql.autoconfig.enabled is true"
1175+
const autoConfigMemoryMsg = "mysql.resources memory must be at least 12Mi when mysql.autoconfig.enabled is true"
1176+
1177+
When("autoconfig is enabled with a zero cpu limit", Ordered, func() {
1178+
cr, err := readDefaultCR("cr-validations-autoconfig-zero-cpu", ns)
1179+
Expect(err).NotTo(HaveOccurred())
1180+
1181+
cr.Spec.MySQL.ClusterType = psv1.ClusterTypeAsync
1182+
cr.Spec.Orchestrator.Enabled = true
1183+
cr.Spec.UpdateStrategy = appsv1.RollingUpdateStatefulSetStrategyType
1184+
cr.Spec.MySQL.AutoConfig.Enabled = &autoConfigEnabled
1185+
cr.Spec.MySQL.Resources.Requests = nil
1186+
cr.Spec.MySQL.Resources.Limits = corev1.ResourceList{
1187+
corev1.ResourceCPU: resource.MustParse("0"),
1188+
corev1.ResourceMemory: resource.MustParse("4Gi"),
1189+
}
1190+
It("the creation of the cluster should fail with error message", func() {
1191+
createErr := k8sClient.Create(ctx, cr)
1192+
Expect(createErr).To(HaveOccurred())
1193+
Expect(createErr.Error()).To(ContainSubstring(autoConfigCPUMsg))
1194+
})
1195+
})
1196+
1197+
When("autoconfig is enabled with a zero cpu request", Ordered, func() {
1198+
cr, err := readDefaultCR("cr-validations-autoconfig-zero-cpu-request", ns)
1199+
Expect(err).NotTo(HaveOccurred())
1200+
1201+
cr.Spec.MySQL.ClusterType = psv1.ClusterTypeAsync
1202+
cr.Spec.Orchestrator.Enabled = true
1203+
cr.Spec.UpdateStrategy = appsv1.RollingUpdateStatefulSetStrategyType
1204+
cr.Spec.MySQL.AutoConfig.Enabled = &autoConfigEnabled
1205+
cr.Spec.MySQL.Resources.Limits = nil
1206+
cr.Spec.MySQL.Resources.Requests = corev1.ResourceList{
1207+
corev1.ResourceCPU: resource.MustParse("0"),
1208+
corev1.ResourceMemory: resource.MustParse("4Gi"),
1209+
}
1210+
It("the creation of the cluster should fail with error message", func() {
1211+
createErr := k8sClient.Create(ctx, cr)
1212+
Expect(createErr).To(HaveOccurred())
1213+
Expect(createErr.Error()).To(ContainSubstring(autoConfigCPUMsg))
1214+
})
1215+
})
1216+
1217+
// Below the floor autotune's formula rejects, which is the configuration
1218+
// a cluster starts on before its MySQL version is known.
1219+
When("autoconfig is enabled with memory below 12Mi", Ordered, func() {
1220+
cr, err := readDefaultCR("cr-validations-autoconfig-low-memory", ns)
1221+
Expect(err).NotTo(HaveOccurred())
1222+
1223+
cr.Spec.MySQL.ClusterType = psv1.ClusterTypeAsync
1224+
cr.Spec.Orchestrator.Enabled = true
1225+
cr.Spec.UpdateStrategy = appsv1.RollingUpdateStatefulSetStrategyType
1226+
cr.Spec.MySQL.AutoConfig.Enabled = &autoConfigEnabled
1227+
cr.Spec.MySQL.Resources.Requests = nil
1228+
cr.Spec.MySQL.Resources.Limits = corev1.ResourceList{
1229+
corev1.ResourceCPU: resource.MustParse("2"),
1230+
corev1.ResourceMemory: resource.MustParse("8Mi"),
1231+
}
1232+
It("the creation of the cluster should fail with error message", func() {
1233+
createErr := k8sClient.Create(ctx, cr)
1234+
Expect(createErr).To(HaveOccurred())
1235+
Expect(createErr.Error()).To(ContainSubstring(autoConfigMemoryMsg))
1236+
})
1237+
})
1238+
1239+
When("autoconfig is enabled with memory exactly at 12Mi", Ordered, func() {
1240+
cr, err := readDefaultCR("cr-validations-autoconfig-min-memory", ns)
1241+
Expect(err).NotTo(HaveOccurred())
1242+
1243+
cr.Spec.MySQL.ClusterType = psv1.ClusterTypeAsync
1244+
cr.Spec.Orchestrator.Enabled = true
1245+
cr.Spec.UpdateStrategy = appsv1.RollingUpdateStatefulSetStrategyType
1246+
cr.Spec.MySQL.AutoConfig.Enabled = &autoConfigEnabled
1247+
cr.Spec.MySQL.Resources.Requests = nil
1248+
cr.Spec.MySQL.Resources.Limits = corev1.ResourceList{
1249+
corev1.ResourceCPU: resource.MustParse("2"),
1250+
corev1.ResourceMemory: resource.MustParse("12Mi"),
1251+
}
1252+
It("should create successfully", func() {
1253+
Expect(k8sClient.Create(ctx, cr)).Should(Succeed())
1254+
})
1255+
})
1256+
1257+
// The floor only applies to autoconfig; a cluster that never asks for
1258+
// tuning may be sized however the user likes.
1259+
When("autoconfig is disabled, a tiny memory limit is accepted", Ordered, func() {
1260+
cr, err := readDefaultCR("cr-validations-autoconfig-disabled-low-memory", ns)
1261+
Expect(err).NotTo(HaveOccurred())
1262+
1263+
cr.Spec.MySQL.ClusterType = psv1.ClusterTypeAsync
1264+
cr.Spec.Orchestrator.Enabled = true
1265+
cr.Spec.UpdateStrategy = appsv1.RollingUpdateStatefulSetStrategyType
1266+
cr.Spec.MySQL.AutoConfig.Enabled = &autoConfigDisabled
1267+
cr.Spec.MySQL.Resources.Requests = nil
1268+
cr.Spec.MySQL.Resources.Limits = corev1.ResourceList{
1269+
corev1.ResourceMemory: resource.MustParse("8Mi"),
1270+
}
1271+
It("should create successfully", func() {
1272+
Expect(k8sClient.Create(ctx, cr)).Should(Succeed())
1273+
})
1274+
})
1275+
11741276
When("autoconfig is disabled, resources are not required", Ordered, func() {
11751277
cr, err := readDefaultCR("cr-validations-autoconfig-disabled", ns)
11761278
Expect(err).NotTo(HaveOccurred())

pkg/mysql/autoconfig/autoconfig_test.go

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -83,29 +83,3 @@ func TestCalculate(t *testing.T) {
8383
})
8484
}
8585
}
86-
87-
// The whole allocation belongs to mysqld unless the caller says otherwise, so
88-
// nothing is held back for a proxy and a monitor that live elsewhere.
89-
func TestCalculateSharedResources(t *testing.T) {
90-
req := Request{
91-
DBType: DBTypeGroupReplication,
92-
CPU: 4000,
93-
MemoryBytes: 8 << 30,
94-
Version: Version{Major: 8, Minor: 0, Patch: 46},
95-
LoadType: LoadTypeSomeWrites,
96-
}
97-
98-
dedicated, err := Calculate(req)
99-
require.NoError(t, err)
100-
dedicatedParams, err := dedicated.MySQLdParams()
101-
require.NoError(t, err)
102-
103-
req.SharedResources = true
104-
shared, err := Calculate(req)
105-
require.NoError(t, err)
106-
sharedParams, err := shared.MySQLdParams()
107-
require.NoError(t, err)
108-
109-
assert.Equal(t, "562", dedicatedParams["max_connections"])
110-
assert.Equal(t, "442", sharedParams["max_connections"])
111-
}

0 commit comments

Comments
 (0)