diff --git a/api/v1alpha1/mysqlcluster_types.go b/api/v1alpha1/mysqlcluster_types.go index 092d9a84..bf2489e0 100644 --- a/api/v1alpha1/mysqlcluster_types.go +++ b/api/v1alpha1/mysqlcluster_types.go @@ -37,6 +37,8 @@ type MysqlClusterSpec struct { // Readonlys Info. // +optional ReadOnlys *ReadOnlyType `json:"readonlys,omitempty"` + // Lagged + ReplicaLag *int32 `json:"lag,omitempty"` // The number of pods from that set that must still be available after the // eviction, even in the absence of the evicted pod // +optional diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 30e30b39..78ffb1ff 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -279,6 +279,11 @@ func (in *MysqlClusterSpec) DeepCopyInto(out *MysqlClusterSpec) { *out = new(ReadOnlyType) (*in).DeepCopyInto(*out) } + if in.ReplicaLag != nil { + in, out := &in.ReplicaLag, &out.ReplicaLag + *out = new(int32) + **out = **in + } in.MysqlOpts.DeepCopyInto(&out.MysqlOpts) in.XenonOpts.DeepCopyInto(&out.XenonOpts) in.MetricsOpts.DeepCopyInto(&out.MetricsOpts) diff --git a/api/v1beta1/mysqlcluster_types.go b/api/v1beta1/mysqlcluster_types.go index 0f177287..8abb5a6f 100644 --- a/api/v1beta1/mysqlcluster_types.go +++ b/api/v1beta1/mysqlcluster_types.go @@ -37,6 +37,9 @@ type MysqlClusterSpec struct { // Readonlys Info. // +optional ReadOnlys *ReadOnlyType `json:"readonlys,omitempty"` + // Lagged + ReplicaLag *int32 `json:"lag,omitempty"` + // Username of new user to create. // Only be a combination of letters, numbers or underlines. The length can not exceed 26 characters. // +optional diff --git a/api/v1beta1/zz_generated.conversion.go b/api/v1beta1/zz_generated.conversion.go index ba01de49..ac1a307e 100644 --- a/api/v1beta1/zz_generated.conversion.go +++ b/api/v1beta1/zz_generated.conversion.go @@ -408,6 +408,7 @@ func Convert_v1alpha1_MysqlClusterList_To_v1beta1_MysqlClusterList(in *v1alpha1. func autoConvert_v1beta1_MysqlClusterSpec_To_v1alpha1_MysqlClusterSpec(in *MysqlClusterSpec, out *v1alpha1.MysqlClusterSpec, s conversion.Scope) error { out.Replicas = (*int32)(unsafe.Pointer(in.Replicas)) out.ReadOnlys = (*v1alpha1.ReadOnlyType)(unsafe.Pointer(in.ReadOnlys)) + out.ReplicaLag = (*int32)(unsafe.Pointer(in.ReplicaLag)) // WARNING: in.User requires manual conversion: does not exist in peer-type // WARNING: in.MySQLConfig requires manual conversion: does not exist in peer-type // WARNING: in.Resources requires manual conversion: does not exist in peer-type @@ -435,6 +436,7 @@ func autoConvert_v1beta1_MysqlClusterSpec_To_v1alpha1_MysqlClusterSpec(in *Mysql func autoConvert_v1alpha1_MysqlClusterSpec_To_v1beta1_MysqlClusterSpec(in *v1alpha1.MysqlClusterSpec, out *MysqlClusterSpec, s conversion.Scope) error { out.Replicas = (*int32)(unsafe.Pointer(in.Replicas)) out.ReadOnlys = (*ReadOnlyType)(unsafe.Pointer(in.ReadOnlys)) + out.ReplicaLag = (*int32)(unsafe.Pointer(in.ReplicaLag)) out.MinAvailable = in.MinAvailable // WARNING: in.MysqlOpts requires manual conversion: does not exist in peer-type // WARNING: in.XenonOpts requires manual conversion: does not exist in peer-type diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 9c52fc5b..420358d7 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -467,6 +467,11 @@ func (in *MysqlClusterSpec) DeepCopyInto(out *MysqlClusterSpec) { *out = new(ReadOnlyType) (*in).DeepCopyInto(*out) } + if in.ReplicaLag != nil { + in, out := &in.ReplicaLag, &out.ReplicaLag + *out = new(int32) + **out = **in + } in.MySQLConfig.DeepCopyInto(&out.MySQLConfig) in.Resources.DeepCopyInto(&out.Resources) if in.CustomTLSSecret != nil { diff --git a/charts/mysql-operator/templates/mysql.radondb.com_mysqlclusters.yaml b/charts/mysql-operator/templates/mysql.radondb.com_mysqlclusters.yaml index c224a0df..9c8131e5 100644 --- a/charts/mysql-operator/templates/mysql.radondb.com_mysqlclusters.yaml +++ b/charts/mysql-operator/templates/mysql.radondb.com_mysqlclusters.yaml @@ -97,6 +97,10 @@ spec: s3Schedule: type: string type: object + lag: + description: Lagged + format: int32 + type: integer metricsOpts: default: enabled: false @@ -3526,6 +3530,10 @@ spec: - Never - IfNotPresent type: string + lag: + description: Lagged + format: int32 + type: integer logOpts: description: LogOpts is the options of log settings. properties: diff --git a/config/crd/bases/mysql.radondb.com_mysqlclusters.yaml b/config/crd/bases/mysql.radondb.com_mysqlclusters.yaml index 24331713..cde866ef 100644 --- a/config/crd/bases/mysql.radondb.com_mysqlclusters.yaml +++ b/config/crd/bases/mysql.radondb.com_mysqlclusters.yaml @@ -79,6 +79,10 @@ spec: s3Schedule: type: string type: object + lag: + description: Lagged + format: int32 + type: integer metricsOpts: default: enabled: false @@ -3508,6 +3512,10 @@ spec: - Never - IfNotPresent type: string + lag: + description: Lagged + format: int32 + type: integer logOpts: description: LogOpts is the options of log settings. properties: diff --git a/config/samples/mysql_v1beta1_backup.yaml b/config/samples/mysql_v1beta1_backup.yaml index c72d3add..694dc86a 100644 --- a/config/samples/mysql_v1beta1_backup.yaml +++ b/config/samples/mysql_v1beta1_backup.yaml @@ -3,9 +3,12 @@ kind: Backup metadata: name: backup-sample spec: - # Add fields here - image: radondb/mysql57-sidecar:v3.0.0 - # hostfrom if empty, use the leader as hostfrom - hostfrom: sample-mysql-0 + backupops: + s3: + secretName: sample-backup-secret clusterName: sample - # nfsServerAddress: "" + method: xtrabackup + # schedule: + # cronExpression: "*/2 * * * *" + # type: s3 + diff --git a/config/samples/mysql_v1beta1_mysqlcluster.yaml b/config/samples/mysql_v1beta1_mysqlcluster.yaml index 12249a81..16c09ba7 100644 --- a/config/samples/mysql_v1beta1_mysqlcluster.yaml +++ b/config/samples/mysql_v1beta1_mysqlcluster.yaml @@ -1,12 +1,68 @@ apiVersion: mysql.radondb.com/v1beta1 kind: MysqlCluster metadata: - labels: - app.kubernetes.io/name: mysqlcluster - app.kubernetes.io/instance: mysqlcluster-sample - app.kubernetes.io/part-of: radondb-mysql-kubernetes - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/created-by: radondb-mysql-kubernetes - name: mysqlcluster-sample + name: sample spec: - # TODO(user): Add fields here + backupOpts: + image: radondb/mysql80-sidecar:v3.0.0 + resources: + requests: + cpu: 10m + memory: 32Mi + customTLSSecret: {} + dataSource: + S3backup: + name: "" + secretName: "" + remote: {} + image: percona/percona-server:8.0.25 + imagePullPolicy: Always + logOpts: + resources: + requests: + cpu: 10m + memory: 32Mi + maxLagTime: 30 + minAvailable: 50% + monitoringSpec: + exporter: + enabled: true + image: prom/mysqld-exporter:v0.12.1 + resources: + limits: + cpu: 100m + memory: 128Mi + requests: + cpu: 10m + memory: 32Mi + mysqlConfig: {} + mysqlVersion: "8.0" + replicas: 3 + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 100m + memory: 256Mi + storage: + accessModes: + - ReadWriteOnce + storageClassName: local-path + resources: + requests: + storage: 20Gi + user: radondb_usr + xenonOpts: + admitDefeatHearbeatCount: 5 + electionTimeout: 10000 + image: radondb/xenon:v3.0.0 + resources: + limits: + cpu: 100m + memory: 256Mi + requests: + cpu: 50m + memory: 128Mi + + diff --git a/internal/sql_runner.go b/internal/sql_runner.go index 99d323ff..70697dc4 100644 --- a/internal/sql_runner.go +++ b/internal/sql_runner.go @@ -182,13 +182,13 @@ func (s sqlRunner) QueryRows(query Query) (*sql.Rows, error) { } // CheckSlaveStatusWithRetry check the slave status with retry time. -func CheckSlaveStatusWithRetry(sqlRunner SQLRunner, retry uint32) (isLagged, isReplicating corev1.ConditionStatus, err error) { +func CheckSlaveStatusWithRetry(sqlRunner SQLRunner, retry uint32, replicaLag *int32) (isLagged, isReplicating corev1.ConditionStatus, err error) { for { if retry == 0 { break } - if isLagged, isReplicating, err = CheckSlaveStatus(sqlRunner); err == nil { + if isLagged, isReplicating, err = CheckSlaveStatus(sqlRunner, replicaLag); err == nil { return } @@ -200,7 +200,7 @@ func CheckSlaveStatusWithRetry(sqlRunner SQLRunner, retry uint32) (isLagged, isR } // CheckSlaveStatus check the slave status. -func CheckSlaveStatus(sqlRunner SQLRunner) (isLagged, isReplicating corev1.ConditionStatus, err error) { +func CheckSlaveStatus(sqlRunner SQLRunner, ReplicaLag *int32) (isLagged, isReplicating corev1.ConditionStatus, err error) { var rows *sql.Rows isLagged, isReplicating = corev1.ConditionUnknown, corev1.ConditionUnknown rows, err = sqlRunner.QueryRows(NewQuery("show slave status;")) @@ -258,7 +258,11 @@ func CheckSlaveStatus(sqlRunner SQLRunner) (isLagged, isReplicating corev1.Condi // Check whether the slave is lagged. sec, _ := strconv.ParseFloat(secondsBehindMaster, 64) - if sec > longQueryTime*100 { + lagTime := longQueryTime * 100 + if ReplicaLag != nil { + lagTime = float64(*ReplicaLag) + } + if sec > lagTime { isLagged = corev1.ConditionTrue } else { isLagged = corev1.ConditionFalse diff --git a/mysqlcluster/syncer/readonly_statefulset.go b/mysqlcluster/syncer/readonly_statefulset.go index 9e425d1d..6454dc69 100644 --- a/mysqlcluster/syncer/readonly_statefulset.go +++ b/mysqlcluster/syncer/readonly_statefulset.go @@ -313,7 +313,7 @@ func putMySQLReadOnly(s *StatefulSetSyncer, host string) error { // 3. change master var isReplicating corev1.ConditionStatus var err error - if _, isReplicating, err = internal.CheckSlaveStatus(sqlRunner); err != nil { + if _, isReplicating, err = internal.CheckSlaveStatus(sqlRunner, s.Spec.ReplicaLag); err != nil { //Notice!!! this has error, just show error message, can not return. s.log.V(1).Info("slave status has gotten error", "error", err) } diff --git a/mysqlcluster/syncer/status.go b/mysqlcluster/syncer/status.go index 9ec2a5a8..f3f8772c 100644 --- a/mysqlcluster/syncer/status.go +++ b/mysqlcluster/syncer/status.go @@ -310,7 +310,7 @@ func (s *StatusSyncer) updateNodeStatus(ctx context.Context, cli client.Client, case <-time.After(time.Second * 5): } if sqlRunner != nil { - isLagged, isReplicating, err = internal.CheckSlaveStatusWithRetry(sqlRunner, checkNodeStatusRetry) + isLagged, isReplicating, err = internal.CheckSlaveStatusWithRetry(sqlRunner, checkNodeStatusRetry, s.Spec.ReplicaLag) if err != nil { s.log.V(1).Info("failed to check slave status", "node", node.Name, "error", err) node.Message = err.Error() @@ -616,7 +616,7 @@ func (s *StatusSyncer) RoCheckStatus(ctx context.Context, cli client.Client, pod isCloseSemi = corev1.ConditionTrue } // 3. change master - if _, isReplicating, err = internal.CheckSlaveStatus(sqlRunner); err != nil { + if _, isReplicating, err = internal.CheckSlaveStatus(sqlRunner, s.Spec.ReplicaLag); err != nil { node.Message = err.Error() } }