diff --git a/Makefile.utils b/Makefile.utils index 2230c19..cd151af 100644 --- a/Makefile.utils +++ b/Makefile.utils @@ -34,6 +34,13 @@ install-helm-package: --set "image.undermoonImageTag=$(UNDERMOON_IMG_VERSION)" \ example-undermoon "undermoon-cluster-$(OPERATOR_HELM_VERSION).tgz" +upgrade-helm-package: + $(MAKE) build-helm + helm upgrade \ + --set "image.undermoonImage=$(UNDERMOON_IMG_NAME)" \ + --set "image.undermoonImageTag=$(UNDERMOON_IMG_VERSION)" \ + example-undermoon "undermoon-cluster-$(OPERATOR_HELM_VERSION).tgz" + uninstall-helm-package: helm uninstall example-undermoon || true helm uninstall example-operator || true @@ -95,6 +102,9 @@ debug-build: debug-install: $(MAKE) install-helm-package UM_OP_DEBUG=1 +debug-upgrade: + $(MAKE) upgrade-helm-package UM_OP_DEBUG=1 + debug-run: $(MAKE) install UM_OP_DEBUG=1 $(MAKE) deploy UM_OP_DEBUG=1 diff --git a/api/v1alpha1/undermoon_types.go b/api/v1alpha1/undermoon_types.go index b986afc..4cf891c 100644 --- a/api/v1alpha1/undermoon_types.go +++ b/api/v1alpha1/undermoon_types.go @@ -46,6 +46,11 @@ type UndermoonSpec struct { ActiveRedirection bool `json:"activeRedirection"` // +kubebuilder:validation:Minimum=1 ProxyThreads uint32 `json:"proxyThreads"` + // Interval to trigger SCAN command during migration. This is in microseconds. + MigrationScanInterval uint32 `json:"migrationScanInterval"` + // COUNT arguments for SCAN command during migration. + // +kubebuilder:validation:Minimum=1 + MigrationScanCount uint32 `json:"migrationScanCount"` // +kubebuilder:validation:MinLength=1 UndermoonImage string `json:"undermoonImage"` diff --git a/config/cr/overlays/helm/replace_values.yaml b/config/cr/overlays/helm/replace_values.yaml index baf4a8f..c39c90b 100644 --- a/config/cr/overlays/helm/replace_values.yaml +++ b/config/cr/overlays/helm/replace_values.yaml @@ -20,6 +20,12 @@ - op: replace path: /spec/proxyThreads value: "{{ .Values.cluster.proxyThreads }}" +- op: replace + path: /spec/migrationScanInterval + value: "{{ .Values.cluster.migrationScanInterval }}" +- op: replace + path: /spec/migrationScanCount + value: "{{ .Values.cluster.migrationScanCount }}" - op: replace path: /spec/undermoonImage value: "{{ .Values.image.undermoonImage }}:{{ .Values.image.undermoonImageTag }}" diff --git a/config/crd/bases/undermoon.doyoubi.mydomain_undermoons.yaml b/config/crd/bases/undermoon.doyoubi.mydomain_undermoons.yaml index 736248b..996f25f 100644 --- a/config/crd/bases/undermoon.doyoubi.mydomain_undermoons.yaml +++ b/config/crd/bases/undermoon.doyoubi.mydomain_undermoons.yaml @@ -679,6 +679,16 @@ spec: format: int32 minimum: 1 type: integer + migrationScanCount: + description: COUNT arguments for SCAN command during migration. + format: int32 + minimum: 1 + type: integer + migrationScanInterval: + description: Interval to trigger SCAN command during migration. This + is in microseconds. + format: int32 + type: integer port: description: Port for the redis service. format: int32 @@ -756,6 +766,8 @@ spec: - chunkNumber - clusterName - maxMemory + - migrationScanCount + - migrationScanInterval - port - proxyThreads - redisImage diff --git a/controllers/broker_client.go b/controllers/broker_client.go index 0fa0035..fbdfc1b 100644 --- a/controllers/broker_client.go +++ b/controllers/broker_client.go @@ -446,3 +446,54 @@ func (client *brokerClient) getClusterInfo(address, clusterName string) (*cluste content := res.Body() return nil, errors.Errorf("Failed to get cluster info: invalid status code %d: %s", res.StatusCode(), string(content)) } + +type clusterConfigPayload struct { + MigrationScanInterval string `json:"migration_scan_interval"` + MigrationScanCount string `json:"migration_scan_count"` +} + +func (client *brokerClient) changeClusterConfig(address, clusterName string, scanInterval, scanCount uint32) error { + url := fmt.Sprintf("http://%s/api/v2/clusters/config/%s", address, clusterName) + payload := &clusterConfigPayload{ + MigrationScanInterval: fmt.Sprintf("%d", scanInterval), + MigrationScanCount: fmt.Sprintf("%d", scanCount), + } + res, err := client.httpClient.R(). + SetBody(payload). + SetError(&errorResponse{}). + Patch(url) + if err != nil { + return err + } + + if res.StatusCode() == 200 { + return nil + } + + if res.StatusCode() == 504 { + response, ok := res.Error().(*errorResponse) + if ok && response.Error == errStrExternalTimeout { + return errExternalTimeout + } + } + + if res.StatusCode() == 404 { + response, ok := res.Error().(*errorResponse) + if ok { + return errors.Errorf("cluster not found, error code %s", response.Error) + } + } + + if res.StatusCode() == 409 { + response, ok := res.Error().(*errorResponse) + if ok && response.Error == errStrRetry { + return errRetryReconciliation + } + if ok && response.Error == errStrMigrationRunning { + return errMigrationRunning + } + } + + content := res.Body() + return errors.Errorf("Failed to update cluster config: invalid status code %d: %s", res.StatusCode(), string(content)) +} diff --git a/controllers/meta_controller.go b/controllers/meta_controller.go index 619c48a..30addb3 100644 --- a/controllers/meta_controller.go +++ b/controllers/meta_controller.go @@ -57,6 +57,11 @@ func (con *metaController) reconcileMeta( return nil, err } + err = con.changeClusterConfig(reqLogger, masterBrokerAddress, cr) + if err != nil { + return nil, err + } + info, err := con.getClusterInfo(reqLogger, masterBrokerAddress, cr) if err != nil { return nil, err @@ -199,6 +204,29 @@ func (con *metaController) createCluster(reqLogger logr.Logger, masterBrokerAddr return nil } +func (con *metaController) changeClusterConfig(reqLogger logr.Logger, masterBrokerAddress string, cr *undermoonv1alpha1.Undermoon) error { + err := con.client.changeClusterConfig( + masterBrokerAddress, + cr.Spec.ClusterName, + cr.Spec.MigrationScanInterval, + cr.Spec.MigrationScanCount, + ) + if err != nil { + if err == errMigrationRunning { + // Undermoon will implement this restriction later. + return errRetryReconciliation + } + if err == errRetryReconciliation { + return err + } + reqLogger.Error(err, "failed to change cluster config", + "Name", cr.ObjectMeta.Name, + "ClusterName", cr.Spec.ClusterName) + return err + } + return nil +} + func (con *metaController) changeNodeNumber(reqLogger logr.Logger, masterBrokerAddress string, cr *undermoonv1alpha1.Undermoon) error { chunkNumber := int(cr.Spec.ChunkNumber) clusterName := cr.Spec.ClusterName diff --git a/helm/undermoon-cluster/templates/undermoon.yaml b/helm/undermoon-cluster/templates/undermoon.yaml index 8b42fa6..ac28162 100644 --- a/helm/undermoon-cluster/templates/undermoon.yaml +++ b/helm/undermoon-cluster/templates/undermoon.yaml @@ -11,6 +11,8 @@ spec: clusterName: {{ .Values.cluster.clusterName }} coordinatorResources: {{- toYaml .Values.resources.coordinatorResources | nindent 4 }} maxMemory: {{ .Values.cluster.maxMemory }} + migrationScanCount: {{ .Values.cluster.migrationScanCount }} + migrationScanInterval: {{ .Values.cluster.migrationScanInterval }} port: {{ .Values.cluster.port }} proxyResources: {{- toYaml .Values.resources.proxyResources | nindent 4 }} proxyThreads: {{ .Values.cluster.proxyThreads }} diff --git a/helm/undermoon-cluster/values.yaml b/helm/undermoon-cluster/values.yaml index 251cf36..1f47e1c 100644 --- a/helm/undermoon-cluster/values.yaml +++ b/helm/undermoon-cluster/values.yaml @@ -9,6 +9,8 @@ cluster: port: 5299 activeRedirection: false proxyThreads: 2 + migrationScanInterval: 1000 + migrationScanCount: 16 image: undermoonImage: doyoubi/undermoon diff --git a/helm/undermoon-operator/templates/undermoon.doyoubi.mydomain_undermoons.yaml b/helm/undermoon-operator/templates/undermoon.doyoubi.mydomain_undermoons.yaml index 51ff813..6bd0432 100644 --- a/helm/undermoon-operator/templates/undermoon.doyoubi.mydomain_undermoons.yaml +++ b/helm/undermoon-operator/templates/undermoon.doyoubi.mydomain_undermoons.yaml @@ -432,6 +432,15 @@ spec: format: int32 minimum: 1 type: integer + migrationScanCount: + description: COUNT arguments for SCAN command during migration. + format: int32 + minimum: 1 + type: integer + migrationScanInterval: + description: Interval to trigger SCAN command during migration. This is in microseconds. + format: int32 + type: integer port: description: Port for the redis service. format: int32 @@ -500,6 +509,8 @@ spec: - chunkNumber - clusterName - maxMemory + - migrationScanCount + - migrationScanInterval - port - proxyThreads - redisImage