From 5663d2c3e979f9768594fec46fcc39eb6df8fa3d Mon Sep 17 00:00:00 2001 From: "longpeng.zlp" Date: Wed, 2 Apr 2025 17:56:22 +0800 Subject: [PATCH 1/4] introduce odc osc migrate logic --- .../service/onlineschemachange/JsonTest.java | 4 +- .../odc/core/shared/constant/ErrorCodes.java | 4 +- .../OnlineSchemaChangeFlowableTask.java | 3 + .../OnlineSchemaChangeProperties.java | 5 + ...ineSchemaChangeScheduleTaskParameters.java | 12 + .../OnlineSchemaChangeScheduleTaskResult.java | 7 +- .../{OmsStepName.java => OscStepName.java} | 2 +- .../oms/jackson/CustomEnumDeserializer.java | 8 +- .../oms/response/OmsProjectStepVO.java | 4 +- .../oscfms/OscActionFsm.java | 6 +- .../oscfms/OscActionFsmBase.java | 6 + .../oscfms/action/ActionDelegate.java | 15 +- .../oscfms/action/CleanResourcesAction.java | 11 +- .../action/CleanResourcesActionBase.java | 95 +++++ .../oscfms/action/CreateDataTaskAction.java | 14 +- .../oscfms/action/ModifyDataTaskAction.java | 4 +- .../oscfms/action/MonitorDataTaskAction.java | 4 +- .../action/MonitorDataTaskActionBase.java | 263 +++++++++++++ .../oscfms/action/ProjectStepResult.java | 83 ++++ .../oscfms/action/SwapTableAction.java | 5 +- .../oscfms/action/SwapTableActionBase.java | 125 ++++++ .../action/odc/OdcCleanResourcesAction.java | 79 ++++ .../action/odc/OdcCreateDataTaskAction.java | 369 ++++++++++++++++++ .../action/odc/OdcModifyDataTaskAction.java | 67 ++++ .../action/odc/OdcMonitorDataTaskAction.java | 142 +++++++ .../oscfms/action/odc/OdcSwapTableAction.java | 88 +++++ .../oscfms/action/odc/OscCommandUtil.java | 225 +++++++++++ .../oscfms/action/odc/SupervisorResponse.java | 35 ++ .../action/oms/OmsCleanResourcesAction.java | 65 +-- .../action/oms/OmsCreateDataTaskAction.java | 73 +--- .../action/oms/OmsMonitorDataTaskAction.java | 207 +--------- .../oscfms/action/oms/OmsRequestUtil.java | 89 ++++- .../oscfms/action/oms/OmsSwapTableAction.java | 94 +---- .../action/oms/ProjectStepResultChecker.java | 89 +---- .../odc/service/resource/ResourceManager.java | 3 + .../service/resource/k8s/K8sResourceUtil.java | 112 ++++++ .../config/DefaultSpringJobConfiguration.java | 9 +- .../DefaultTaskFrameworkProperties.java | 3 + .../config/TaskFrameworkConfiguration.java | 4 +- .../task/config/TaskFrameworkProperties.java | 1 + .../task/dummy/LocalMockK8sJobClient.java | 13 +- .../AbstractK8sResourceOperatorBuilder.java | 7 +- .../DefaultNativeK8sOperatorBuilder.java | 13 +- .../service/task/resource/K8sPodResource.java | 19 +- .../odc/service/task/resource/PodConfig.java | 4 + .../resource/client/NativeK8sJobClient.java | 33 +- .../k8s/K8SResourceManageStrategy.java | 49 ++- .../k8s/K8sResourceContextBuilder.java | 36 +- .../OnlineSchemaChangeFlowableTaskTest.java | 14 +- .../action/CleanResourcesActionBaseTest.java | 126 ++++++ .../OmsMonitorDataTaskActionTest.java | 19 +- .../odc/OdcCreateDataTaskActionTest.java | 93 +++++ .../oms/OmsCleanResourcesActionTest.java | 90 +---- .../action/oms/OmsSwapTableActionTest.java | 28 +- .../oms/ProjectStepResultCheckerTest.java | 8 +- .../resource/k8s/K8sResourceOperatorTest.java | 2 +- .../resource/k8s/K8sResourceUtilTest.java | 58 +++ 57 files changed, 2349 insertions(+), 697 deletions(-) rename server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oms/enums/{OmsStepName.java => OscStepName.java} (98%) create mode 100644 server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/CleanResourcesActionBase.java create mode 100644 server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/MonitorDataTaskActionBase.java create mode 100644 server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/ProjectStepResult.java create mode 100644 server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/SwapTableActionBase.java create mode 100644 server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcCleanResourcesAction.java create mode 100644 server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcCreateDataTaskAction.java create mode 100644 server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcModifyDataTaskAction.java create mode 100644 server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcMonitorDataTaskAction.java create mode 100644 server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcSwapTableAction.java create mode 100644 server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OscCommandUtil.java create mode 100644 server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/SupervisorResponse.java create mode 100644 server/odc-service/src/main/java/com/oceanbase/odc/service/resource/k8s/K8sResourceUtil.java create mode 100644 server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/CleanResourcesActionBaseTest.java rename server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/{oms => }/OmsMonitorDataTaskActionTest.java (96%) create mode 100644 server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcCreateDataTaskActionTest.java create mode 100644 server/odc-service/src/test/java/com/oceanbase/odc/service/resource/k8s/K8sResourceUtilTest.java diff --git a/server/integration-test/src/test/java/com/oceanbase/odc/service/onlineschemachange/JsonTest.java b/server/integration-test/src/test/java/com/oceanbase/odc/service/onlineschemachange/JsonTest.java index f7cadf1992..94d216fbf1 100644 --- a/server/integration-test/src/test/java/com/oceanbase/odc/service/onlineschemachange/JsonTest.java +++ b/server/integration-test/src/test/java/com/oceanbase/odc/service/onlineschemachange/JsonTest.java @@ -26,7 +26,7 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.oceanbase.odc.common.json.JsonUtils; -import com.oceanbase.odc.service.onlineschemachange.oms.enums.OmsStepName; +import com.oceanbase.odc.service.onlineschemachange.oms.enums.OscStepName; import com.oceanbase.odc.service.onlineschemachange.oms.request.CommonTransferConfig; import com.oceanbase.odc.service.onlineschemachange.oms.request.CreateOmsProjectRequest; import com.oceanbase.odc.service.onlineschemachange.oms.request.DatabaseTransferObject; @@ -83,7 +83,7 @@ public void test_json_sub_type_missing_property() { Assert.assertNotNull(apiReturnResults); Assert.assertTrue(CollectionUtils.isNotEmpty(apiReturnResults.getData())); Optional first = apiReturnResults.getData().stream().filter( - a -> a.getName() == OmsStepName.FULL_TRANSFER).findFirst(); + a -> a.getName() == OscStepName.FULL_TRANSFER).findFirst(); Assert.assertTrue(first.isPresent()); FullTransferStepInfoVO fullTransferStepInfoVO = (FullTransferStepInfoVO) first.get().getStepInfo(); Assert.assertSame(fullTransferStepInfoVO.getProcessedRecords(), 100L); diff --git a/server/odc-core/src/main/java/com/oceanbase/odc/core/shared/constant/ErrorCodes.java b/server/odc-core/src/main/java/com/oceanbase/odc/core/shared/constant/ErrorCodes.java index 4d2d9f3467..5c25c8208f 100644 --- a/server/odc-core/src/main/java/com/oceanbase/odc/core/shared/constant/ErrorCodes.java +++ b/server/odc-core/src/main/java/com/oceanbase/odc/core/shared/constant/ErrorCodes.java @@ -200,8 +200,8 @@ public enum ErrorCodes implements ErrorCode { OmsGhanaOperateFailed, OmsParamError, OmsConnectivityTestFailed, - OmsPreCheckFailed, - OmsProjectExecutingFailed, + OscPreCheckFailed, + OscProjectExecutingFailed, // resource BuiltInResourceOperateNotAllowed, diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/OnlineSchemaChangeFlowableTask.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/OnlineSchemaChangeFlowableTask.java index 7fc0273903..71161796b1 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/OnlineSchemaChangeFlowableTask.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/OnlineSchemaChangeFlowableTask.java @@ -125,6 +125,9 @@ protected Void start(Long taskId, TaskService taskService, DelegateExecution exe param.setUid(uid); param.setRateLimitConfig(parameter.getRateLimitConfig()); param.setState(OscStates.YIELD_CONTEXT.getState()); + // assign when create task + param.setUseODCMigrateTool(onlineSchemaChangeProperties.isUseOdcMigrateTool()); + param.setOdcCommandURl(onlineSchemaChangeProperties.getOdcMigrateUrl()); return createScheduleTaskEntity(schedule.getId(), param); }).collect(Collectors.toList()); diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/configuration/OnlineSchemaChangeProperties.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/configuration/OnlineSchemaChangeProperties.java index 01e9286667..6da11dc070 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/configuration/OnlineSchemaChangeProperties.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/configuration/OnlineSchemaChangeProperties.java @@ -40,6 +40,11 @@ public class OnlineSchemaChangeProperties { private boolean enableFullVerify; + // if use odc migrate tool + private boolean useOdcMigrateTool = false; + // if this url provided, use provided url, or use k8s pod to create new instance + private String odcMigrateUrl = null; + @Data public static class OmsProperties { private String url; diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/model/OnlineSchemaChangeScheduleTaskParameters.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/model/OnlineSchemaChangeScheduleTaskParameters.java index fcbbd65603..e45f79e3bb 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/model/OnlineSchemaChangeScheduleTaskParameters.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/model/OnlineSchemaChangeScheduleTaskParameters.java @@ -108,6 +108,18 @@ public class OnlineSchemaChangeScheduleTaskParameters { // only set when column is dropped private List filterColumns; + // if odc migrate is enabled + private boolean useODCMigrateTool = false; + + // odc command url to migrate data + private String odcCommandURl; + + // mapper port to access, maybe a part of odcCommandURl if k8s only access by host ip + private Integer k8sMapperPort; + + // resourceID for pod if useODCMigrateTool = true, -1 means release not needed + private Long resourceID; + public String getOriginTableNameWithSchema() { return tableNameWithSchema(originTableName); } diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/model/OnlineSchemaChangeScheduleTaskResult.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/model/OnlineSchemaChangeScheduleTaskResult.java index 41594604b9..682e303278 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/model/OnlineSchemaChangeScheduleTaskResult.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/model/OnlineSchemaChangeScheduleTaskResult.java @@ -20,7 +20,7 @@ import com.oceanbase.odc.common.json.NormalDialectTypeOutput; import com.oceanbase.odc.core.shared.constant.DialectType; -import com.oceanbase.odc.service.onlineschemachange.oms.enums.OmsStepName; +import com.oceanbase.odc.service.onlineschemachange.oms.enums.OscStepName; import lombok.AllArgsConstructor; import lombok.Data; @@ -121,7 +121,10 @@ public class OnlineSchemaChangeScheduleTaskResult { */ private boolean manualSwapTableStarted; - private Map checkFailedTime = new HashMap<>(); + private Map checkFailedTime = new HashMap<>(); + + // last check failed time, it maybe not ready yet + private Long lastCheckFailedTimeSecond; public OnlineSchemaChangeScheduleTaskResult(OnlineSchemaChangeScheduleTaskParameters taskParam) { this.originTableName = taskParam.getOriginTableName(); diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oms/enums/OmsStepName.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oms/enums/OscStepName.java similarity index 98% rename from server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oms/enums/OmsStepName.java rename to server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oms/enums/OscStepName.java index e937990788..1514641f3c 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oms/enums/OmsStepName.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oms/enums/OscStepName.java @@ -25,7 +25,7 @@ */ @Getter @AllArgsConstructor -public enum OmsStepName { +public enum OscStepName { PRE_CHECK, TRANSFER_PRECHECK, diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oms/jackson/CustomEnumDeserializer.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oms/jackson/CustomEnumDeserializer.java index ec8dd42b6b..40a1fd8bae 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oms/jackson/CustomEnumDeserializer.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oms/jackson/CustomEnumDeserializer.java @@ -24,8 +24,8 @@ import com.oceanbase.odc.service.onlineschemachange.oms.enums.CheckerObjectStatus; import com.oceanbase.odc.service.onlineschemachange.oms.enums.CheckerResultType; import com.oceanbase.odc.service.onlineschemachange.oms.enums.OmsProjectStatusEnum; -import com.oceanbase.odc.service.onlineschemachange.oms.enums.OmsStepName; import com.oceanbase.odc.service.onlineschemachange.oms.enums.OmsStepStatus; +import com.oceanbase.odc.service.onlineschemachange.oms.enums.OscStepName; /** * @author yaobin @@ -69,14 +69,14 @@ public OmsProjectStatusEnum deserialize(JsonParser jsonParser, DeserializationCo } } - public static class OmsStepNameDeserializer extends JsonDeserializer { + public static class OmsStepNameDeserializer extends JsonDeserializer { @Override - public OmsStepName deserialize(JsonParser jsonParser, DeserializationContext ctxt) + public OscStepName deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException, JacksonException { String status = jsonParser.readValueAs(String.class); - return getEnum(OmsStepName.class, status, OmsStepName.UNKNOWN); + return getEnum(OscStepName.class, status, OscStepName.UNKNOWN); } } diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oms/response/OmsProjectStepVO.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oms/response/OmsProjectStepVO.java index c5848613b1..83cccc6fe2 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oms/response/OmsProjectStepVO.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oms/response/OmsProjectStepVO.java @@ -24,8 +24,8 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo.As; import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.oceanbase.odc.service.onlineschemachange.oms.enums.OmsStepName; import com.oceanbase.odc.service.onlineschemachange.oms.enums.OmsStepStatus; +import com.oceanbase.odc.service.onlineschemachange.oms.enums.OscStepName; import com.oceanbase.odc.service.onlineschemachange.oms.jackson.CustomEnumDeserializer; import lombok.Data; @@ -49,7 +49,7 @@ public class OmsProjectStepVO { * 步骤名 */ @JsonDeserialize(using = CustomEnumDeserializer.OmsStepNameDeserializer.class) - private OmsStepName name; + private OscStepName name; /** * 步骤描述(预检查/结构迁移/结构同步/全量迁移/全量同步/全量校验/索引迁移/增量日志拉取/增量同步/增量校验/正向切换) diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/OscActionFsm.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/OscActionFsm.java index 360141be48..214916af4c 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/OscActionFsm.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/OscActionFsm.java @@ -58,8 +58,8 @@ public void init() { // CREATE_DATA_TASK -> CREATE_DATA_TASK | MONITOR_DATA_TASK| COMPLETE(cnacel) registerEvent(OscStates.CREATE_DATA_TASK.getState(), - CreateDataTaskAction.ofOMSCreateDataTaskAction(dataSourceOpenApiService, omsProjectOpenApiService, - onlineSchemaChangeProperties), + CreateDataTaskAction.createDataTaskAction(dataSourceOpenApiService, omsProjectOpenApiService, + onlineSchemaChangeProperties, systemConfigService, resourceManager), statesTransfer, ImmutableSet.of(OscStates.CREATE_DATA_TASK.getState(), OscStates.MONITOR_DATA_TASK.getState(), OscStates.COMPLETE.getState())); @@ -90,7 +90,7 @@ public void init() { // CLEAN_RESOURCE -> YIELD_CONTEXT | CLEAN_RESOURCES | COMPLETE(cancel) registerEvent(OscStates.CLEAN_RESOURCE.getState(), - CleanResourcesAction.ofOMSCleanResourcesAction(omsProjectOpenApiService), statesTransfer, + CleanResourcesAction.ofCleanResourcesAction(omsProjectOpenApiService, resourceManager), statesTransfer, ImmutableSet.of(OscStates.YIELD_CONTEXT.getState(), OscStates.CLEAN_RESOURCE.getState(), OscStates.COMPLETE.getState())); // COMPLETE should not be scheduled diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/OscActionFsmBase.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/OscActionFsmBase.java index 6ba26d5121..9a10c2833d 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/OscActionFsmBase.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/OscActionFsmBase.java @@ -35,6 +35,7 @@ import com.oceanbase.odc.metadb.schedule.ScheduleEntity; import com.oceanbase.odc.metadb.schedule.ScheduleTaskEntity; import com.oceanbase.odc.metadb.schedule.ScheduleTaskRepository; +import com.oceanbase.odc.service.config.SystemConfigService; import com.oceanbase.odc.service.connection.ConnectionService; import com.oceanbase.odc.service.connection.model.ConnectionConfig; import com.oceanbase.odc.service.flow.BeanInjectedClassDelegate; @@ -49,6 +50,7 @@ import com.oceanbase.odc.service.onlineschemachange.oms.openapi.OmsProjectOpenApiService; import com.oceanbase.odc.service.onlineschemachange.oscfms.action.ConnectionProvider; import com.oceanbase.odc.service.onlineschemachange.oscfms.state.OscStates; +import com.oceanbase.odc.service.resource.ResourceManager; import com.oceanbase.odc.service.schedule.ScheduleService; import com.oceanbase.odc.service.schedule.ScheduleTaskService; import com.oceanbase.odc.service.session.DBSessionManageFacade; @@ -87,6 +89,10 @@ public abstract class OscActionFsmBase extends ActionFsm { // use delegate - protected Action action; + protected Action omsAction; + protected Action odcAction; @Override public OscActionResult execute(OscActionContext context) throws Exception { - return action.execute(context); + return chooseAction(context).execute(context); } @Override public void rollback(OscActionContext context) { - action.rollback(context); + chooseAction(context).rollback(context); + } + + protected Action chooseAction(OscActionContext context) { + if (context.getTaskParameter().isUseODCMigrateTool()) { + return odcAction; + } else { + return omsAction; + } } } diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/CleanResourcesAction.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/CleanResourcesAction.java index 2f83414a4a..508ea52f22 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/CleanResourcesAction.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/CleanResourcesAction.java @@ -18,7 +18,11 @@ import javax.validation.constraints.NotNull; import com.oceanbase.odc.service.onlineschemachange.oms.openapi.OmsProjectOpenApiService; +import com.oceanbase.odc.service.onlineschemachange.oscfms.action.odc.OdcCleanResourcesAction; import com.oceanbase.odc.service.onlineschemachange.oscfms.action.oms.OmsCleanResourcesAction; +import com.oceanbase.odc.service.resource.ResourceManager; + +import lombok.NonNull; /** * @author longpeng.zlp @@ -27,10 +31,11 @@ */ public class CleanResourcesAction extends ActionDelegate { - public static CleanResourcesAction ofOMSCleanResourcesAction( - @NotNull OmsProjectOpenApiService omsProjectOpenApiService) { + public static CleanResourcesAction ofCleanResourcesAction( + @NotNull OmsProjectOpenApiService omsProjectOpenApiService, @NonNull ResourceManager resourceManager) { CleanResourcesAction ret = new CleanResourcesAction(); - ret.action = new OmsCleanResourcesAction(omsProjectOpenApiService); + ret.omsAction = new OmsCleanResourcesAction(omsProjectOpenApiService); + ret.odcAction = new OdcCleanResourcesAction(resourceManager); return ret; } } diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/CleanResourcesActionBase.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/CleanResourcesActionBase.java new file mode 100644 index 0000000000..f755cbb7a2 --- /dev/null +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/CleanResourcesActionBase.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2023 OceanBase. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.oceanbase.odc.service.onlineschemachange.oscfms.action; + +import java.util.List; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.Lists; +import com.oceanbase.odc.common.json.JsonUtils; +import com.oceanbase.odc.core.session.ConnectionSession; +import com.oceanbase.odc.core.shared.constant.TaskErrorStrategy; +import com.oceanbase.odc.core.shared.constant.TaskStatus; +import com.oceanbase.odc.metadb.schedule.ScheduleEntity; +import com.oceanbase.odc.metadb.schedule.ScheduleTaskEntity; +import com.oceanbase.odc.service.onlineschemachange.OscTableUtil; +import com.oceanbase.odc.service.onlineschemachange.fsm.Action; +import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeParameters; +import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeScheduleTaskParameters; +import com.oceanbase.odc.service.onlineschemachange.oscfms.OscActionContext; +import com.oceanbase.odc.service.onlineschemachange.oscfms.OscActionResult; +import com.oceanbase.odc.service.onlineschemachange.oscfms.state.OscStates; + +import lombok.extern.slf4j.Slf4j; + +/** + * @author longpeng.zlp + * @date 2025/3/24 14:11 + */ +@Slf4j +public abstract class CleanResourcesActionBase implements Action { + + protected final List expectedTaskStatus = Lists.newArrayList(TaskStatus.DONE, TaskStatus.FAILED, + TaskStatus.CANCELED, TaskStatus.RUNNING, TaskStatus.ABNORMAL); + + protected boolean tryDropNewTable(OscActionContext context) { + ConnectionSession connectionSession = null; + OnlineSchemaChangeScheduleTaskParameters taskParam = context.getTaskParameter(); + String databaseName = taskParam.getDatabaseName(); + String tableName = taskParam.getNewTableNameUnwrapped(); + boolean succeed; + try { + connectionSession = context.getConnectionProvider().createConnectionSession(); + OscTableUtil.dropNewTableIfExits(databaseName, tableName, connectionSession); + succeed = true; + } catch (Throwable e) { + log.warn("osc: drop table = {}.{} failed", databaseName, tableName, e); + succeed = false; + } finally { + if (connectionSession != null) { + connectionSession.expire(); + } + } + return succeed; + } + + @VisibleForTesting + protected OscActionResult determinateNextState(ScheduleTaskEntity scheduleTask, ScheduleEntity schedule) { + Long scheduleId = schedule.getId(); + // try to dispatch to next state for done status + if (scheduleTask.getStatus() == TaskStatus.DONE) { + return new OscActionResult(OscStates.CLEAN_RESOURCE.getState(), null, OscStates.YIELD_CONTEXT.getState()); + } + // if task state is in cancel state, stop and transfer to complete state + if (scheduleTask.getStatus() == TaskStatus.CANCELED) { + log.info("Because task is canceled, so delete quartz job={}", scheduleId); + // cancel as complete + return new OscActionResult(OscStates.CLEAN_RESOURCE.getState(), null, OscStates.COMPLETE.getState()); + } + // remain failed and prepare state + OnlineSchemaChangeParameters onlineSchemaChangeParameters = JsonUtils.fromJson( + schedule.getJobParametersJson(), OnlineSchemaChangeParameters.class); + if (onlineSchemaChangeParameters.getErrorStrategy() == TaskErrorStrategy.CONTINUE) { + log.info("Because error strategy is continue, so schedule next task"); + // try schedule next task + return new OscActionResult(OscStates.CLEAN_RESOURCE.getState(), null, OscStates.YIELD_CONTEXT.getState()); + } else { + log.info("Because error strategy is abort, so delete quartz job={}", scheduleId); + // not continue for remain state, transfer to complete state + return new OscActionResult(OscStates.CLEAN_RESOURCE.getState(), null, OscStates.COMPLETE.getState()); + } + } +} diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/CreateDataTaskAction.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/CreateDataTaskAction.java index ffb4958e3c..6d2f3ed445 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/CreateDataTaskAction.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/CreateDataTaskAction.java @@ -17,10 +17,15 @@ import javax.validation.constraints.NotNull; +import com.oceanbase.odc.service.config.SystemConfigService; import com.oceanbase.odc.service.onlineschemachange.configuration.OnlineSchemaChangeProperties; import com.oceanbase.odc.service.onlineschemachange.oms.openapi.DataSourceOpenApiService; import com.oceanbase.odc.service.onlineschemachange.oms.openapi.OmsProjectOpenApiService; +import com.oceanbase.odc.service.onlineschemachange.oscfms.action.odc.OdcCreateDataTaskAction; import com.oceanbase.odc.service.onlineschemachange.oscfms.action.oms.OmsCreateDataTaskAction; +import com.oceanbase.odc.service.resource.ResourceManager; + +import lombok.NonNull; /** * @author longpeng.zlp @@ -29,12 +34,15 @@ */ public class CreateDataTaskAction extends ActionDelegate { - public static CreateDataTaskAction ofOMSCreateDataTaskAction( + public static CreateDataTaskAction createDataTaskAction( @NotNull DataSourceOpenApiService dataSourceOpenApiService, @NotNull OmsProjectOpenApiService projectOpenApiService, - @NotNull OnlineSchemaChangeProperties oscProperties) { + @NotNull OnlineSchemaChangeProperties oscProperties, + @NonNull SystemConfigService systemConfigService, + @NonNull ResourceManager resourceManager) { CreateDataTaskAction ret = new CreateDataTaskAction(); - ret.action = new OmsCreateDataTaskAction(dataSourceOpenApiService, projectOpenApiService, oscProperties); + ret.omsAction = new OmsCreateDataTaskAction(dataSourceOpenApiService, projectOpenApiService, oscProperties); + ret.odcAction = new OdcCreateDataTaskAction(systemConfigService, resourceManager, oscProperties); return ret; } } diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/ModifyDataTaskAction.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/ModifyDataTaskAction.java index 58c7d6c4f3..372261e857 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/ModifyDataTaskAction.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/ModifyDataTaskAction.java @@ -21,6 +21,7 @@ import com.oceanbase.odc.service.onlineschemachange.configuration.OnlineSchemaChangeProperties; import com.oceanbase.odc.service.onlineschemachange.oms.openapi.OmsProjectOpenApiService; +import com.oceanbase.odc.service.onlineschemachange.oscfms.action.odc.OdcModifyDataTaskAction; import com.oceanbase.odc.service.onlineschemachange.oscfms.action.oms.OmsModifyDataTaskAction; /** @@ -34,7 +35,8 @@ public static ModifyDataTaskAction ofOMSModifyDataTaskAction( @NotNull OmsProjectOpenApiService projectOpenApiService, @NotNull OnlineSchemaChangeProperties onlineSchemaChangeProperties) { ModifyDataTaskAction ret = new ModifyDataTaskAction(); - ret.action = new OmsModifyDataTaskAction(projectOpenApiService, onlineSchemaChangeProperties); + ret.omsAction = new OmsModifyDataTaskAction(projectOpenApiService, onlineSchemaChangeProperties); + ret.odcAction = new OdcModifyDataTaskAction(); return ret; } } diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/MonitorDataTaskAction.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/MonitorDataTaskAction.java index 3152408833..c7b74392d4 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/MonitorDataTaskAction.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/MonitorDataTaskAction.java @@ -21,6 +21,7 @@ import com.oceanbase.odc.service.onlineschemachange.configuration.OnlineSchemaChangeProperties; import com.oceanbase.odc.service.onlineschemachange.oms.openapi.OmsProjectOpenApiService; +import com.oceanbase.odc.service.onlineschemachange.oscfms.action.odc.OdcMonitorDataTaskAction; import com.oceanbase.odc.service.onlineschemachange.oscfms.action.oms.OmsMonitorDataTaskAction; /** @@ -35,7 +36,8 @@ public static MonitorDataTaskAction ofOMSMonitorDataTaskAction( @NotNull OmsProjectOpenApiService projectOpenApiService, @NotNull OnlineSchemaChangeProperties onlineSchemaChangeProperties) { MonitorDataTaskAction ret = new MonitorDataTaskAction(); - ret.action = new OmsMonitorDataTaskAction(projectOpenApiService, onlineSchemaChangeProperties); + ret.omsAction = new OmsMonitorDataTaskAction(projectOpenApiService, onlineSchemaChangeProperties); + ret.odcAction = new OdcMonitorDataTaskAction(onlineSchemaChangeProperties); return ret; } } diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/MonitorDataTaskActionBase.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/MonitorDataTaskActionBase.java new file mode 100644 index 0000000000..d23bc0d86b --- /dev/null +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/MonitorDataTaskActionBase.java @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2023 OceanBase. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.oceanbase.odc.service.onlineschemachange.oscfms.action; + +import java.util.List; +import java.util.Objects; + +import javax.validation.constraints.NotNull; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.Lists; +import com.oceanbase.odc.common.json.JsonUtils; +import com.oceanbase.odc.common.util.tableformat.Table; +import com.oceanbase.odc.core.shared.constant.ErrorCodes; +import com.oceanbase.odc.core.shared.constant.TaskStatus; +import com.oceanbase.odc.metadb.schedule.ScheduleTaskEntity; +import com.oceanbase.odc.service.onlineschemachange.configuration.OnlineSchemaChangeProperties; +import com.oceanbase.odc.service.onlineschemachange.exception.OscException; +import com.oceanbase.odc.service.onlineschemachange.fsm.Action; +import com.oceanbase.odc.service.onlineschemachange.logger.DefaultTableFactory; +import com.oceanbase.odc.service.onlineschemachange.model.FullVerificationResult; +import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeParameters; +import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeScheduleTaskParameters; +import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeScheduleTaskResult; +import com.oceanbase.odc.service.onlineschemachange.model.PrecheckResult; +import com.oceanbase.odc.service.onlineschemachange.model.SwapTableType; +import com.oceanbase.odc.service.onlineschemachange.oms.enums.OscStepName; +import com.oceanbase.odc.service.onlineschemachange.oscfms.OscActionContext; +import com.oceanbase.odc.service.onlineschemachange.oscfms.OscActionResult; +import com.oceanbase.odc.service.onlineschemachange.oscfms.state.OscStates; +import com.oceanbase.odc.service.onlineschemachange.rename.SwapTableUtil; + +import lombok.extern.slf4j.Slf4j; + +/** + * @author longpeng.zlp + * @date 2024/7/8 20:04 + * @since 4.3.1 + */ +@Slf4j +public abstract class MonitorDataTaskActionBase implements Action { + + protected final OnlineSchemaChangeProperties onlineSchemaChangeProperties; + + public MonitorDataTaskActionBase(@NotNull OnlineSchemaChangeProperties onlineSchemaChangeProperties) { + this.onlineSchemaChangeProperties = onlineSchemaChangeProperties; + } + + @Override + public OscActionResult execute(OscActionContext context) throws Exception { + ScheduleTaskEntity scheduleTask = context.getScheduleTask(); + log.debug("Start execute {}, schedule task id={}", getClass().getSimpleName(), scheduleTask.getId()); + + OnlineSchemaChangeScheduleTaskParameters taskParameter = context.getTaskParameter(); + OnlineSchemaChangeParameters inputParameters = context.getParameter(); + // switch state + if (shouldUpdateConfig(taskParameter, inputParameters)) { + return new OscActionResult(OscStates.MONITOR_DATA_TASK.getState(), null, + OscStates.MODIFY_DATA_TASK.getState()); + } + // get result + OnlineSchemaChangeScheduleTaskResult lastResult = JsonUtils.fromJson(scheduleTask.getResultJson(), + OnlineSchemaChangeScheduleTaskResult.class); + + OnlineSchemaChangeScheduleTaskResult result = + new OnlineSchemaChangeScheduleTaskResult(taskParameter); + + if (lastResult != null) { + result.setManualSwapTableEnabled(lastResult.isManualSwapTableEnabled()); + result.setManualSwapTableStarted(lastResult.isManualSwapTableStarted()); + } + // get osc/oms step result + ProjectStepResult projectStepResult = getProjectStepResult(taskParameter, lastResult); + if (null == projectStepResult) { + processMonitorNoResponseReceived(context, scheduleTask, taskParameter, lastResult); + return new OscActionResult(OscStates.MONITOR_DATA_TASK.getState(), null, + OscStates.MONITOR_DATA_TASK.getState()); + } + adaptResult(result, projectStepResult); + scheduleTask.setResultJson(JsonUtils.toJson(result)); + scheduleTask.setProgressPercentage(projectStepResult.getTaskPercentage()); + // update progress, can merge with following update? + recordCurrentProgress(taskParameter, result); + try { + return handleProjectStepResult(context, projectStepResult, result, + context.getParameter().getSwapTableType(), scheduleTask); + } finally { + // update schedule task once + context.getScheduleTaskRepository().update(scheduleTask); + } + } + + protected void processMonitorNoResponseReceived(OscActionContext context, ScheduleTaskEntity scheduleTask, + OnlineSchemaChangeScheduleTaskParameters taskParameter, OnlineSchemaChangeScheduleTaskResult lastResult) { + // no result provided + if (null == lastResult) { + lastResult = new OnlineSchemaChangeScheduleTaskResult(taskParameter); + } + if (null == lastResult.getLastCheckFailedTimeSecond()) { + // first failed + lastResult.setLastCheckFailedTimeSecond(System.currentTimeMillis() / 1000); + } + // check if timeout + long failedSeconds = (System.currentTimeMillis() / 1000) - lastResult.getLastCheckFailedTimeSecond(); + if (failedSeconds > onlineSchemaChangeProperties.getOms().getCheckProjectStepFailedTimeoutSeconds()) { + throw new RuntimeException( + "osc task has failed for long time, please check, taskId=" + scheduleTask.getId()); + } + scheduleTask.setResultJson(JsonUtils.toJson(lastResult)); + context.getScheduleTaskRepository().update(scheduleTask); + } + + protected abstract ProjectStepResult getProjectStepResult(OnlineSchemaChangeScheduleTaskParameters taskParameter, + OnlineSchemaChangeScheduleTaskResult lastResult); + + protected abstract boolean isMigrateTaskReady(ProjectStepResult projectStepResult); + + protected abstract String getPrintLogName(OnlineSchemaChangeScheduleTaskParameters parameters); + + @VisibleForTesting + protected OscActionResult handleProjectStepResult(OscActionContext context, ProjectStepResult projectStepResult, + OnlineSchemaChangeScheduleTaskResult result, SwapTableType swapTableType, ScheduleTaskEntity scheduleTask) { + // osc task is ready, try swap step + if (isMigrateTaskReady(projectStepResult)) { + // is manual swap table + boolean isSwapTableReady = + SwapTableUtil.isSwapTableReady(scheduleTask.getStatus(), result.getFullTransferProgressPercentage(), + result.getFullVerificationResult()); + // can't swap table + if (!isSwapTableReady) { + // keep in same state, monitor task state + return new OscActionResult(OscStates.MONITOR_DATA_TASK.getState(), null, + OscStates.MONITOR_DATA_TASK.getState()); + + } + // try do swap table + switch (swapTableType) { + case AUTO: + // auto swap, jump to swap stable state + scheduleTask.setResultJson(JsonUtils.toJson(result)); + return new OscActionResult(OscStates.MONITOR_DATA_TASK.getState(), null, + OscStates.SWAP_TABLE.getState()); + case MANUAL: + if (!result.isManualSwapTableStarted()) { + // isManualSwapTableEnabled set true to let swap table button show on front-end panel + if (!result.isManualSwapTableEnabled()) { + // open manual swap table + result.setManualSwapTableEnabled(true); + scheduleTask.setResultJson(JsonUtils.toJson(result)); + } + log.info("OSC: osc project ready, wait manual swap table triggered, task id={}", + scheduleTask.getId()); + // manual swap table not set, keep waiting + return new OscActionResult(OscStates.MONITOR_DATA_TASK.getState(), null, + OscStates.MONITOR_DATA_TASK.getState()); + } else { + // jump to swap table state + return new OscActionResult(OscStates.MONITOR_DATA_TASK.getState(), null, + OscStates.SWAP_TABLE.getState()); + } + default: + throw new IllegalStateException("invalid state for swap table type"); + } + } else { + // check osc/oms status + return continueHandleProjectStepResult(projectStepResult); + } + } + + private OscActionResult continueHandleProjectStepResult(ProjectStepResult projectStepResult) { + if (projectStepResult.getPreCheckResult() == PrecheckResult.FAILED) { + throw new OscException(ErrorCodes.OscPreCheckFailed, projectStepResult.getErrorMsg()); + } else if (projectStepResult.getTaskStatus() == TaskStatus.FAILED) { + throw new OscException(ErrorCodes.OscProjectExecutingFailed, projectStepResult.getErrorMsg()); + } else if (projectStepResult.getFullVerificationResult() == FullVerificationResult.INCONSISTENT) { + throw new OscException(ErrorCodes.OscDataCheckInconsistent, + "Task failed for origin table has inconsistent data with new table, result: " + + projectStepResult.getFullVerificationResultDescription()); + } else { + // stay in monitor state + return new OscActionResult(OscStates.MONITOR_DATA_TASK.getState(), null, + OscStates.MONITOR_DATA_TASK.getState()); + } + } + + private void adaptResult(OnlineSchemaChangeScheduleTaskResult result, ProjectStepResult projectStepResult) { + result.setFullTransferEstimatedCount(projectStepResult.getFullTransferEstimatedCount()); + result.setFullTransferFinishedCount(projectStepResult.getFullTransferFinishedCount()); + result.setFullTransferProgressPercentage(projectStepResult.getFullTransferProgressPercentage()); + result.setFullVerificationResult(projectStepResult.getFullVerificationResult()); + result.setFullVerificationResultDescription(projectStepResult.getFullVerificationResultDescription()); + result.setFullVerificationProgressPercentage(projectStepResult.getFullVerificationProgressPercentage()); + result.setCurrentStep(projectStepResult.getCurrentStep()); + result.setCurrentStepStatus(projectStepResult.getCurrentStepStatus()); + result.setPrecheckResult(projectStepResult.getPreCheckResult()); + result.setCheckFailedTime(projectStepResult.getCheckFailedTime()); + } + + @VisibleForTesting + protected boolean shouldUpdateConfig(OnlineSchemaChangeScheduleTaskParameters taskParameters, + OnlineSchemaChangeParameters inputParameters) { + // if rate limiter parameters is changed, try to stop and restart project + if (Objects.equals(inputParameters.getRateLimitConfig(), taskParameters.getRateLimitConfig())) { + log.info("Rate limiter not changed,rateLimiterConfig = {}, update osc project not required", + inputParameters.getRateLimitConfig()); + return false; + } else { + return true; + } + } + + protected void recordCurrentProgress(OnlineSchemaChangeScheduleTaskParameters parameters, + OnlineSchemaChangeScheduleTaskResult result) { + String progress = ""; + String description = ""; + OscStepName step = OscStepName.UNKNOWN; + try { + if (result.getCurrentStep() != null) { + step = OscStepName.valueOf(result.getCurrentStep()); + switch (step) { + case PRE_CHECK: + description = result.getPrecheckResultDescription(); + break; + case FULL_TRANSFER: + progress = result.getFullTransferProgressPercentage().toString(); + description = result.getFullTransferFinishedCount() + "/" + + result.getFullTransferEstimatedCount(); + break; + case FULL_VERIFIER: + progress = result.getFullVerificationProgressPercentage().toString(); + description = result.getFullVerificationResultDescription(); + break; + default: + } + } + } catch (Exception ex) { + log.warn("Get oms step progress and description occur error"); + } + + List body = + Lists.newArrayList(getPrintLogName(parameters), step == OscStepName.UNKNOWN ? "" : step.name(), + result.getCurrentStepStatus(), progress, description); + Table table = new DefaultTableFactory().generateTable(5, getHeader(), body); + log.info("\n" + table.render().toString() + "\n"); + } + + private List getHeader() { + return Lists.newArrayList("ProjectId", "Step", "Status", "Progress", "Description"); + } +} diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/ProjectStepResult.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/ProjectStepResult.java new file mode 100644 index 0000000000..eed4ef8cab --- /dev/null +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/ProjectStepResult.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2023 OceanBase. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.oceanbase.odc.service.onlineschemachange.oscfms.action; + +import java.util.Map; + +import com.oceanbase.odc.core.shared.constant.TaskStatus; +import com.oceanbase.odc.service.onlineschemachange.model.FullVerificationResult; +import com.oceanbase.odc.service.onlineschemachange.model.PrecheckResult; +import com.oceanbase.odc.service.onlineschemachange.oms.enums.OscStepName; + +import lombok.Data; + +/** + * @author longpeng.zlp + * @date 2025/3/24 15:17 + */ +@Data +public class ProjectStepResult { + private PrecheckResult preCheckResult; + + private double precheckProgressPercentage; + + private String errorMsg; + + private String currentStep; + + private String currentStepStatus; + private TaskStatus taskStatus; + + /** + * full transfer estimated rows count + */ + private Long fullTransferEstimatedCount; + + /** + * full transfer finished rows count + */ + private Long fullTransferFinishedCount; + + /** + * full transfer percentage + */ + private double fullTransferProgressPercentage; + + /** + * full verification result + */ + private FullVerificationResult fullVerificationResult; + + /** + * full verification result description + */ + private String fullVerificationResultDescription; + + /** + * full verification percentage + */ + private double fullVerificationProgressPercentage; + + /** + * task percentage + */ + private double taskPercentage; + + + private Map checkFailedTime; + + private Long incrementCheckpoint; +} diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/SwapTableAction.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/SwapTableAction.java index 13573c7f54..c65284f596 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/SwapTableAction.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/SwapTableAction.java @@ -19,6 +19,7 @@ import com.oceanbase.odc.service.onlineschemachange.configuration.OnlineSchemaChangeProperties; import com.oceanbase.odc.service.onlineschemachange.oms.openapi.OmsProjectOpenApiService; +import com.oceanbase.odc.service.onlineschemachange.oscfms.action.odc.OdcSwapTableAction; import com.oceanbase.odc.service.onlineschemachange.oscfms.action.oms.OmsSwapTableAction; import com.oceanbase.odc.service.session.DBSessionManageFacade; @@ -32,7 +33,9 @@ public static SwapTableAction ofOMSSwapTableAction(@NotNull DBSessionManageFacad @NotNull OmsProjectOpenApiService projectOpenApiService, @NotNull OnlineSchemaChangeProperties onlineSchemaChangeProperties) { SwapTableAction ret = new SwapTableAction(); - ret.action = new OmsSwapTableAction(dbSessionManageFacade, projectOpenApiService, onlineSchemaChangeProperties); + ret.omsAction = + new OmsSwapTableAction(dbSessionManageFacade, projectOpenApiService, onlineSchemaChangeProperties); + ret.odcAction = new OdcSwapTableAction(dbSessionManageFacade, onlineSchemaChangeProperties); return ret; } } diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/SwapTableActionBase.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/SwapTableActionBase.java new file mode 100644 index 0000000000..efd6845ad3 --- /dev/null +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/SwapTableActionBase.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2023 OceanBase. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.oceanbase.odc.service.onlineschemachange.oscfms.action; + +import java.util.List; +import java.util.Map; + +import javax.validation.constraints.NotNull; + +import org.apache.commons.collections4.CollectionUtils; + +import com.google.common.annotations.VisibleForTesting; +import com.oceanbase.odc.common.json.JsonUtils; +import com.oceanbase.odc.core.shared.PreConditions; +import com.oceanbase.odc.metadb.schedule.ScheduleTaskEntity; +import com.oceanbase.odc.service.connection.model.ConnectionConfig; +import com.oceanbase.odc.service.onlineschemachange.configuration.OnlineSchemaChangeProperties; +import com.oceanbase.odc.service.onlineschemachange.fsm.Action; +import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeParameters; +import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeScheduleTaskParameters; +import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeScheduleTaskResult; +import com.oceanbase.odc.service.onlineschemachange.monitor.DBUserMonitorExecutor; +import com.oceanbase.odc.service.onlineschemachange.oms.enums.OscStepName; +import com.oceanbase.odc.service.onlineschemachange.oscfms.OscActionContext; +import com.oceanbase.odc.service.onlineschemachange.oscfms.OscActionResult; +import com.oceanbase.odc.service.onlineschemachange.oscfms.state.OscStates; +import com.oceanbase.odc.service.onlineschemachange.rename.DefaultRenameTableInvoker; +import com.oceanbase.odc.service.onlineschemachange.rename.LockTableSupportDecider; +import com.oceanbase.odc.service.session.DBSessionManageFacade; + +import lombok.extern.slf4j.Slf4j; + +/** + * swap table action + * + * @author longpeng.zlp + * @date 2025/3/24 11:38 + */ +@Slf4j +public abstract class SwapTableActionBase implements Action { + + protected final DBSessionManageFacade dbSessionManageFacade; + + protected final OnlineSchemaChangeProperties onlineSchemaChangeProperties; + + public SwapTableActionBase(@NotNull DBSessionManageFacade dbSessionManageFacade, + @NotNull OnlineSchemaChangeProperties onlineSchemaChangeProperties) { + this.dbSessionManageFacade = dbSessionManageFacade; + this.onlineSchemaChangeProperties = onlineSchemaChangeProperties; + } + + @Override + public OscActionResult execute(OscActionContext context) throws Exception { + if (!checkOSCProjectReady(context)) { + // OMS state abnormal, keep waiting + log.info("OSC: swap table waiting for OSC task ready"); + return new OscActionResult(OscStates.SWAP_TABLE.getState(), null, OscStates.SWAP_TABLE.getState()); + } + // begin swap table + ScheduleTaskEntity scheduleTask = context.getScheduleTask(); + log.info("Start execute {}, schedule task id={}", getClass().getSimpleName(), scheduleTask.getId()); + + OnlineSchemaChangeScheduleTaskParameters taskParameters = context.getTaskParameter(); + PreConditions.notNull(taskParameters, "OnlineSchemaChangeScheduleTaskParameters is null"); + OnlineSchemaChangeParameters parameters = context.getParameter(); + + ConnectionConfig config = context.getConnectionProvider().connectionConfig(); + DBUserMonitorExecutor userMonitorExecutor = new DBUserMonitorExecutor(config, parameters.getLockUsers()); + try { + if (enableUserMonitor(parameters.getLockUsers())) { + userMonitorExecutor.start(); + } + DefaultRenameTableInvoker defaultRenameTableInvoker = + new DefaultRenameTableInvoker(context.getConnectionProvider(), dbSessionManageFacade, + () -> { + OnlineSchemaChangeScheduleTaskResult lastResult = JsonUtils.fromJson( + context.getScheduleTask().getResultJson(), + OnlineSchemaChangeScheduleTaskResult.class); + return isIncrementDataAppliedDone(onlineSchemaChangeProperties, + taskParameters, + lastResult.getCheckFailedTime(), 25000); + }, this::getLockTableSupportDecider); + defaultRenameTableInvoker.invoke(taskParameters, parameters); + // rename table success, jump to clean resource state + return new OscActionResult(OscStates.SWAP_TABLE.getState(), null, OscStates.CLEAN_RESOURCE.getState()); + } finally { + if (enableUserMonitor(parameters.getLockUsers())) { + userMonitorExecutor.stop(); + } + } + } + + protected LockTableSupportDecider getLockTableSupportDecider() { + String lockTableMatchers = onlineSchemaChangeProperties.getSupportLockTableObVersionJson(); + return LockTableSupportDecider.createWithJsonArrayWithDefaultValue(lockTableMatchers); + } + + protected abstract boolean checkOSCProjectReady(OscActionContext context); + + /** + * check if all increment data has applied to ghost table this check should called after source + * table locked + */ + @VisibleForTesting + protected abstract boolean isIncrementDataAppliedDone(OnlineSchemaChangeProperties onlineSchemaChangeProperties, + OnlineSchemaChangeScheduleTaskParameters parameters, + Map checkFailedTimes, int timeOutMS); + + protected boolean enableUserMonitor(List lockUsers) { + return CollectionUtils.isNotEmpty(lockUsers); + } +} diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcCleanResourcesAction.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcCleanResourcesAction.java new file mode 100644 index 0000000000..3cc46f5359 --- /dev/null +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcCleanResourcesAction.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2023 OceanBase. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.oceanbase.odc.service.onlineschemachange.oscfms.action.odc; + +import java.util.List; + +import com.google.common.collect.Lists; +import com.oceanbase.odc.core.shared.PreConditions; +import com.oceanbase.odc.core.shared.constant.ErrorCodes; +import com.oceanbase.odc.core.shared.constant.TaskStatus; +import com.oceanbase.odc.metadb.schedule.ScheduleTaskEntity; +import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeScheduleTaskParameters; +import com.oceanbase.odc.service.onlineschemachange.oscfms.OscActionContext; +import com.oceanbase.odc.service.onlineschemachange.oscfms.OscActionResult; +import com.oceanbase.odc.service.onlineschemachange.oscfms.action.CleanResourcesActionBase; +import com.oceanbase.odc.service.onlineschemachange.oscfms.state.OscStates; +import com.oceanbase.odc.service.resource.ResourceManager; + +import lombok.extern.slf4j.Slf4j; + +/** + * @author longpeng.zlp + * @date 2025/3/24 14:11 + */ +@Slf4j +public class OdcCleanResourcesAction extends CleanResourcesActionBase { + private final ResourceManager resourceManager; + + + private final List expectedTaskStatus = Lists.newArrayList(TaskStatus.DONE, TaskStatus.FAILED, + TaskStatus.CANCELED, TaskStatus.RUNNING, TaskStatus.ABNORMAL); + + public OdcCleanResourcesAction(ResourceManager resourceManager) { + this.resourceManager = resourceManager; + } + + @Override + public OscActionResult execute(OscActionContext context) throws Exception { + OnlineSchemaChangeScheduleTaskParameters taskParameters = context.getTaskParameter(); + ScheduleTaskEntity scheduleTask = context.getScheduleTask(); + PreConditions.validArgumentState(expectedTaskStatus.contains(scheduleTask.getStatus()), ErrorCodes.Unexpected, + new Object[] {scheduleTask.getStatus()}, "schedule task is not excepted status"); + tryKillOSCMigrateJob(taskParameters); + if (!tryDropNewTable(context)) { + // try drop ghost table again + return new OscActionResult(OscStates.CLEAN_RESOURCE.getState(), null, OscStates.CLEAN_RESOURCE.getState()); + } + releaseResource(taskParameters); + return determinateNextState(scheduleTask, context.getSchedule()); + } + + protected void tryKillOSCMigrateJob(OnlineSchemaChangeScheduleTaskParameters parameters) { + if (!OscCommandUtil.isOSCMigrateSupervisorAlive(parameters.getOdcCommandURl())) { + log.info("ODCCleanResourcesAction: supervisor has quit, url={}", parameters.getOdcCommandURl()); + return; + } + SupervisorResponse clearResponse = OscCommandUtil.clearTask(parameters.getOdcCommandURl()); + log.info("ODCCleanResourcesAction: clear task response = {}", clearResponse); + } + + protected void releaseResource(OnlineSchemaChangeScheduleTaskParameters parameters) throws Exception { + if (null != parameters.getResourceID()) { + resourceManager.destroy(parameters.getResourceID()); + } + } +} diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcCreateDataTaskAction.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcCreateDataTaskAction.java new file mode 100644 index 0000000000..40494a2981 --- /dev/null +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcCreateDataTaskAction.java @@ -0,0 +1,369 @@ +/* + * Copyright (c) 2023 OceanBase. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.oceanbase.odc.service.onlineschemachange.oscfms.action.odc; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.tuple.Pair; + +import com.oceanbase.odc.common.json.JsonUtils; +import com.oceanbase.odc.common.util.StringUtils; +import com.oceanbase.odc.core.session.ConnectionSession; +import com.oceanbase.odc.service.config.SystemConfigService; +import com.oceanbase.odc.service.config.model.Configuration; +import com.oceanbase.odc.service.connection.model.ConnectionConfig; +import com.oceanbase.odc.service.db.browser.DBSchemaAccessors; +import com.oceanbase.odc.service.onlineschemachange.configuration.OnlineSchemaChangeProperties; +import com.oceanbase.odc.service.onlineschemachange.ddl.DdlUtils; +import com.oceanbase.odc.service.onlineschemachange.fsm.Action; +import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeScheduleTaskParameters; +import com.oceanbase.odc.service.onlineschemachange.oms.request.CreateOceanBaseDataSourceRequest; +import com.oceanbase.odc.service.onlineschemachange.oscfms.OscActionContext; +import com.oceanbase.odc.service.onlineschemachange.oscfms.OscActionResult; +import com.oceanbase.odc.service.onlineschemachange.oscfms.action.oms.OmsRequestUtil; +import com.oceanbase.odc.service.onlineschemachange.oscfms.state.OscStates; +import com.oceanbase.odc.service.resource.ResourceLocation; +import com.oceanbase.odc.service.resource.ResourceManager; +import com.oceanbase.odc.service.resource.ResourceWithID; +import com.oceanbase.odc.service.resource.k8s.K8sResourceUtil; +import com.oceanbase.odc.service.session.factory.OBConsoleDataSourceFactory; +import com.oceanbase.odc.service.task.caller.ResourceIDUtil; +import com.oceanbase.odc.service.task.config.K8sProperties; +import com.oceanbase.odc.service.task.resource.AbstractK8sResourceOperatorBuilder; +import com.oceanbase.odc.service.task.resource.Constants; +import com.oceanbase.odc.service.task.resource.K8sPodResource; +import com.oceanbase.tools.dbbrowser.model.DBTableColumn; + +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; + +/** + * @author longpeng.zlp + * @date 2025/3/19 16:31 + */ +@Slf4j +public class OdcCreateDataTaskAction implements Action { + private final SystemConfigService systemConfigService; + private final ResourceManager resourceManager; + private final OnlineSchemaChangeProperties oscProperties; + + public OdcCreateDataTaskAction(@NonNull SystemConfigService systemConfigService, + @NonNull ResourceManager resourceManager, + @NonNull OnlineSchemaChangeProperties oscProperties) { + this.systemConfigService = systemConfigService; + this.resourceManager = resourceManager; + this.oscProperties = oscProperties; + } + + public static String buildUrlPrefix(String ip, Integer port) { + return "http://" + ip + ":" + port; + } + + @Override + public OscActionResult execute(OscActionContext context) throws Exception { + OnlineSchemaChangeScheduleTaskParameters parameters = context.getTaskParameter(); + // 1. check if task worker node has been created + if (StringUtils.isEmpty(parameters.getOdcCommandURl()) && !prepareMigrateEnv(context, parameters)) { + log.info("ODCCreateDataTaskAction: prepare migrate env not ready yet, taskID={}", + context.getScheduleTask().getId()); + return new OscActionResult(OscStates.CREATE_DATA_TASK.name(), null, OscStates.CREATE_DATA_TASK.name()); + } + // 2. check if task supervisor node has ready + if (!OscCommandUtil.isOSCMigrateSupervisorAlive(parameters.getOdcCommandURl())) { + log.info("ODCCreateDataTaskAction: supervisor service not ready yet, taskID={}", + context.getScheduleTask().getId()); + return new OscActionResult(OscStates.CREATE_DATA_TASK.name(), null, OscStates.CREATE_DATA_TASK.name()); + } + // 3. start task + startTask(context, parameters); + return new OscActionResult(OscStates.CREATE_DATA_TASK.name(), null, OscStates.MONITOR_DATA_TASK.name()); + } + + public void startTask(OscActionContext context, OnlineSchemaChangeScheduleTaskParameters parameters) + throws Exception { + log.info("ODCCreateDataTaskAction: start migrate for taskID={}", context.getScheduleTask().getId()); + Map startConfigs = new LinkedHashMap<>(); + ConnectionConfig connectionConfig = context.getConnectionProvider().connectionConfig(); + fillDataSourceRelatedConfigs(context, connectionConfig, startConfigs); + fillOscTableRelatedConfigs(context, connectionConfig, startConfigs); + SupervisorResponse startTaskResp = OscCommandUtil.startTask(parameters.getOdcCommandURl(), startConfigs); + if (null == startTaskResp || !startTaskResp.isSuccess()) { + log.info("ODCCreateDataTaskAction: start task by supervisor failed, taskID={}, response = {}", + context.getScheduleTask().getId(), startTaskResp); + throw new RuntimeException("start osc migrate task failed, taskID= " + context.getScheduleTask().getId()); + } + log.info("ODCCreateDataTaskAction: start task by supervisor done, taskID={}, response = {}", + context.getScheduleTask().getId(), startTaskResp); + } + + protected void fillOscTableRelatedConfigs(OscActionContext context, ConnectionConfig connectionConfig, + Map configs) { + OnlineSchemaChangeScheduleTaskParameters parameters = context.getTaskParameter(); + configs.put("dbname", parameters.getDatabaseName()); + configs.put("tenantName", connectionConfig.getTenantName()); + configs.put("sourceTableName", parameters.getOriginTableName()); + configs.put("targetTableName", parameters.getNewTableName()); + // add col mapper + List columns = getValidColumns(context, connectionConfig); + Map targetToSrcColumnMapper = new LinkedHashMap<>(); + for (String c : columns) { + targetToSrcColumnMapper.put(c, c); + } + configs.put("targetToSrcColMapper", JsonUtils.toJson(targetToSrcColumnMapper)); + // add rate limiter + if (parameters.getRateLimitConfig() != null && null != parameters.getRateLimitConfig().getRowLimit()) { + configs.put("throttleRps", parameters.getRateLimitConfig().getRowLimit().toString()); + } + } + + protected List getValidColumns(OscActionContext context, ConnectionConfig connectionConfig) { + OnlineSchemaChangeScheduleTaskParameters taskParam = context.getTaskParameter(); + // target missed columns, use target columns + if (CollectionUtils.isNotEmpty(taskParam.getFilterColumns())) { + return taskParam.getFilterColumns(); + } + ConnectionSession session = null; + try { + session = context.getConnectionProvider().createConnectionSession(); + // use src tables + return DBSchemaAccessors.create(session).listTableColumns(taskParam.getDatabaseName(), + DdlUtils.getUnwrappedName(taskParam.getOriginTableNameUnwrapped())).stream() + .map(DBTableColumn::getName).collect(Collectors.toList()); + } finally { + if (null != session) { + session.expire(); + } + } + } + + /** + * fill connection info and ob log connection info + * + * @param context + * @param connectionConfig + * @param configs + */ + protected void fillDataSourceRelatedConfigs(OscActionContext context, ConnectionConfig connectionConfig, + Map configs) { + ConnectionSession connectionSession = null; + try { + connectionSession = context.getConnectionProvider().createConnectionSession(); + CreateOceanBaseDataSourceRequest dataSourceRequest = OmsRequestUtil.getCreateDataSourceRequest( + connectionConfig, context.getConnectionProvider().createConnectionSession(), + context.getTaskParameter(), oscProperties); + configs.put("databaseUrl", dataSourceRequest.getIp() + ":" + dataSourceRequest.getPort()); + configs.put("databaseUser", OBConsoleDataSourceFactory.getUsername(connectionConfig)); + configs.put("databasePassword", dataSourceRequest.getPassword()); + configs.put("crawlerClusterURL", dataSourceRequest.getConfigUrl()); + configs.put("crawlerClusterUser", dataSourceRequest.getDrcUserName()); + configs.put("crawlerClusterPassword", dataSourceRequest.getDrcPassword()); + configs.put("crawlerClusterAppName", dataSourceRequest.getCluster()); + } finally { + if (null != connectionSession) { + connectionSession.expire(); + } + } + } + + // prepare node + public boolean prepareMigrateEnv(OscActionContext context, OnlineSchemaChangeScheduleTaskParameters parameters) + throws Exception { + // try create node + tryCreateMigrateNode(context, parameters); + // check node has created + return waitMigrateNodeReady(context, parameters); + } + + protected boolean waitMigrateNodeReady(OscActionContext context, + OnlineSchemaChangeScheduleTaskParameters parameters) + throws Exception { + if (StringUtils.isNotEmpty(parameters.getOdcCommandURl())) { + return true; + } + Long id = parameters.getResourceID(); + K8sPodResource podResource = K8sResourceUtil.queryIpAndAddress(resourceManager, id); + String commandURl = tryChooseUrl(podResource, parameters.getK8sMapperPort()); + if (StringUtils.isEmpty(commandURl)) { + log.info("ODCCreateDataTaskAction: node not ready yet for resourceID={}, taskID={}", id, + context.getScheduleTask().getId()); + return false; + } + log.info("ODCCreateDataTaskAction: node ready with url = {}, resourceID={}, taskID={}", commandURl, id, + context.getScheduleTask().getId()); + parameters.setOdcCommandURl(commandURl); + context.getScheduleTaskRepository().updateTaskParameters(context.getScheduleTask().getId(), + JsonUtils.toJson(parameters)); + return true; + } + + protected void tryCreateMigrateNode(OscActionContext context, OnlineSchemaChangeScheduleTaskParameters parameters) + throws Exception { + // check prev create resourceID + if (parameters.getResourceID() != null) { + return; + } + long taskId = context.getScheduleTask().getId(); + K8sProperties k8sProperties = buildK8sProperties(context.getConnectionProvider().connectionConfig()); + log.info("ODCCreateDataTaskAction: create taskId = {}, with k8s properties={}", + context.getScheduleTask().getId(), k8sProperties); + ResourceLocation resourceLocation = new ResourceLocation(k8sProperties.getRegion(), k8sProperties.getGroup()); + boolean enablePortMapper = getK8sEnablePortMapper(); + int mappedPort = -1; + List> portMapper = new ArrayList<>(); + if (enablePortMapper) { + portMapper = K8sResourceUtil.buildRandomPortMapper(18001); + mappedPort = portMapper.get(0).getRight(); + } + ResourceWithID resource = K8sResourceUtil.createK8sPodResource(resourceManager, + resourceLocation, getK8sImplType(), k8sProperties.getPodImageName(), k8sProperties, 10000000 + taskId, + portMapper, + 18001); + log.info("ODCCreateDataTaskAction: create k8s resource, resourceId = {}", resource); + parameters.setResourceID(resource.getId()); + parameters.setK8sMapperPort(mappedPort); + String commandURl = tryChooseUrl(resource.getResource(), mappedPort); + if (null != commandURl) { + parameters.setOdcCommandURl(commandURl); + } + try { + context.getScheduleTaskRepository().updateTaskParameters(taskId, JsonUtils.toJson(parameters)); + } catch (Throwable e) { + resourceManager.destroy(resource.getId()); + throw e; + } + } + + protected String tryChooseUrl(K8sPodResource resource, Integer mapperPort) { + String podIp = resource.getPodIpAddress(); + String candidateUrl = buildUrlPrefix(podIp, 18001); + if (StringUtils.isNotEmpty(podIp) && !StringUtils.equalsIgnoreCase(podIp, Constants.RESOURCE_NULL_HOST) + && OscCommandUtil.isOSCMigrateSupervisorAlive(candidateUrl)) { + log.info("ODCCreateDataTaskAction: k8s ready, podIP reached, candidateUrl = {} returned", candidateUrl); + return candidateUrl; + } + String hostIp = resource.getHostIpAddress(); + candidateUrl = buildUrlPrefix(hostIp, mapperPort); + if (StringUtils.isNotEmpty(hostIp) && !StringUtils.equalsIgnoreCase(hostIp, Constants.RESOURCE_NULL_HOST) + && mapperPort > 0 && OscCommandUtil.isOSCMigrateSupervisorAlive(candidateUrl)) { + log.info("ODCCreateDataTaskAction: k8s ready, hostIP reached, candidateUrl = {} returned", candidateUrl); + return candidateUrl; + } + return null; + } + + protected String getK8sImplType() { + List k8sImplTypeConfig = + systemConfigService.queryByKeyPrefix("odc.task-framework.k8s-properties.k8s-impl-type"); + if (CollectionUtils.isEmpty(k8sImplTypeConfig)) { + return AbstractK8sResourceOperatorBuilder.CLOUD_K8S_POD_TYPE; + } + return k8sImplTypeConfig.get(0).getValue(); + } + + protected boolean getK8sEnablePortMapper() { + List enablePortMapperConfig = + systemConfigService.queryByKeyPrefix("odc.task-framework.enable-k8s-port-mapper"); + if (CollectionUtils.isEmpty(enablePortMapperConfig)) { + return false; + } + return Boolean.valueOf(enablePortMapperConfig.get(0).getValue()); + } + + protected K8sProperties buildK8sProperties(ConnectionConfig connectionConfig) { + String prefix = "odc.task-framework.k8s-properties."; + // default configuration list + K8sProperties k8sProperties = new K8sProperties(); + // translate to map + Map oscK8sPropertiesMap = translateToMap(systemConfigService.queryByKeyPrefix(prefix)); + // set kube config, must required, only support config one k8s url + if (StringUtils.isNotEmpty(oscK8sPropertiesMap.get(prefix + "kube-url"))) { + trySetK8sPropertiesWithCheck(oscK8sPropertiesMap, prefix + "kube-url", k8sProperties::setKubeUrl); + } else { + trySetK8sPropertiesWithCheck(oscK8sPropertiesMap, prefix + "kube-config", k8sProperties::setKubeConfig); + } + // dependent configs, start with odc.osc.k8s-properties. + prefix = "odc.osc.k8s-properties."; + oscK8sPropertiesMap = translateToMap(systemConfigService.queryByKeyPrefix(prefix)); + trySetK8sPropertiesWithCheck(oscK8sPropertiesMap, prefix + "namespace", k8sProperties::setNamespace); + trySetK8sPropertiesWithCheck(oscK8sPropertiesMap, prefix + "pod-image-name", k8sProperties::setPodImageName); + // may with default + trySetK8sProperties(oscK8sPropertiesMap, prefix + "pod-pending-timeout-seconds", "3600", + (v) -> k8sProperties.setPodPendingTimeoutSeconds(Long.valueOf(v))); + // store 2cpu + writer 2 cpu + trySetK8sProperties(oscK8sPropertiesMap, prefix + "request-cpu", "4", + (v) -> k8sProperties.setRequestCpu(Double.valueOf(v))); + trySetK8sProperties(oscK8sPropertiesMap, prefix + "limit-cpu", "4", + (v) -> k8sProperties.setLimitCpu(Double.valueOf(v))); + trySetK8sProperties(oscK8sPropertiesMap, prefix + "node-cpu", "8", + (v) -> k8sProperties.setNodeCpu(Double.valueOf(v))); + + // store 10g + writer 2g + trySetK8sProperties(oscK8sPropertiesMap, prefix + "request-mem", "12288", + (v) -> k8sProperties.setRequestMem(Long.valueOf(v))); + trySetK8sProperties(oscK8sPropertiesMap, prefix + "limit-mem", "12288", + (v) -> k8sProperties.setLimitMem(Long.valueOf(v))); + trySetK8sProperties(oscK8sPropertiesMap, prefix + "node-mem-in-mb", "16384", + (v) -> k8sProperties.setNodeMemInMB(Long.valueOf(v))); + + // mount related + trySetK8sProperties(oscK8sPropertiesMap, prefix + "enable-mount", "false", + (v) -> k8sProperties.setEnableMount(Boolean.valueOf(v))); + trySetK8sProperties(oscK8sPropertiesMap, prefix + "mount-path", "/var/log/odc-task/runtime", + k8sProperties::setMountPath); + trySetK8sProperties(oscK8sPropertiesMap, prefix + "mount-disk-size", "64", + (v) -> k8sProperties.setMountDiskSize(Long.valueOf(v))); + trySetK8sProperties(oscK8sPropertiesMap, prefix + "max-node-count", "50", + (v) -> k8sProperties.setMaxNodeCount(Long.valueOf(v))); + k8sProperties.setSupervisorListenPort(18001); + k8sProperties.setExecutorListenPort(null); + // set region and group + k8sProperties.setRegion(StringUtils.isEmpty(connectionConfig.getRegion()) ? ResourceIDUtil.DEFAULT_PROP_VALUE + : connectionConfig.getRegion()); + k8sProperties + .setGroup(StringUtils.isEmpty(connectionConfig.getCloudProvider()) ? ResourceIDUtil.DEFAULT_PROP_VALUE + : connectionConfig.getCloudProvider()); + return k8sProperties; + } + + protected void trySetK8sProperties(Map oscK8sPropertiesMap, + String keyName, String defaultValue, Consumer valueConsumer) { + String value = oscK8sPropertiesMap.getOrDefault(keyName, defaultValue); + valueConsumer.accept(value); + } + + protected void trySetK8sPropertiesWithCheck(Map oscK8sPropertiesMap, + String keyName, Consumer valueConsumer) { + String value = oscK8sPropertiesMap.get(keyName); + if (null == value) { + throw new RuntimeException(keyName + " is required for osc k8s properties"); + } + valueConsumer.accept(value); + } + + protected Map translateToMap(List configurations) { + Map ret = new LinkedHashMap<>(); + for (Configuration configuration : configurations) { + ret.put(configuration.getKey(), configuration.getValue()); + } + return ret; + } +} diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcModifyDataTaskAction.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcModifyDataTaskAction.java new file mode 100644 index 0000000000..63b5c8effa --- /dev/null +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcModifyDataTaskAction.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2023 OceanBase. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.oceanbase.odc.service.onlineschemachange.oscfms.action.odc; + +import java.util.Objects; + +import com.oceanbase.odc.common.json.JsonUtils; +import com.oceanbase.odc.service.onlineschemachange.fsm.Action; +import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeParameters; +import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeScheduleTaskParameters; +import com.oceanbase.odc.service.onlineschemachange.oscfms.OscActionContext; +import com.oceanbase.odc.service.onlineschemachange.oscfms.OscActionResult; +import com.oceanbase.odc.service.onlineschemachange.oscfms.state.OscStates; + +import lombok.extern.slf4j.Slf4j; + +/** + * @author longpeng.zlp + * @date 2025/3/24 17:53 + */ +@Slf4j +public class OdcModifyDataTaskAction implements Action { + @Override + public OscActionResult execute(OscActionContext context) throws Exception { + OnlineSchemaChangeScheduleTaskParameters taskParameters = context.getTaskParameter(); + OnlineSchemaChangeParameters inputParameters = context.getParameter(); + // if rate limiter parameters is changed, try to stop and restart project + if (Objects.equals(inputParameters.getRateLimitConfig(), taskParameters.getRateLimitConfig())) { + log.info("Rate limiter not changed,rateLimiterConfig = {}, update odc project not required", + inputParameters.getRateLimitConfig()); + // swap to monitor state + return new OscActionResult(OscStates.MODIFY_DATA_TASK.getState(), null, + OscStates.MONITOR_DATA_TASK.getState()); + } + SupervisorResponse modifyResponse = OscCommandUtil.updateTask(taskParameters.getOdcCommandURl(), + inputParameters.getRateLimitConfig().getRowLimit()); + if (null == modifyResponse || !modifyResponse.isSuccess()) { + log.info("OdcModifyDataTaskAction: update config failed, response is {}", modifyResponse); + return new OscActionResult(OscStates.MODIFY_DATA_TASK.getState(), null, + OscStates.MODIFY_DATA_TASK.getState()); + } else { + Long scheduleTaskId = context.getScheduleTask().getId(); + taskParameters.setRateLimitConfig(inputParameters.getRateLimitConfig()); + int rows = context.getScheduleTaskRepository().updateTaskParameters(scheduleTaskId, + JsonUtils.toJson(taskParameters)); + if (rows > 0) { + log.info("Update throttle completed, scheduleTaskId={}", scheduleTaskId); + } + log.info("OdcModifyDataTaskAction: update config success, response is {}", modifyResponse); + return new OscActionResult(OscStates.MODIFY_DATA_TASK.getState(), null, + OscStates.MONITOR_DATA_TASK.getState()); + } + } +} diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcMonitorDataTaskAction.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcMonitorDataTaskAction.java new file mode 100644 index 0000000000..506aa3a6d6 --- /dev/null +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcMonitorDataTaskAction.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2023 OceanBase. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.oceanbase.odc.service.onlineschemachange.oscfms.action.odc; + +import java.util.Collections; +import java.util.Map; +import java.util.function.Function; + +import javax.validation.constraints.NotNull; + +import com.oceanbase.odc.common.json.JsonUtils; +import com.oceanbase.odc.common.util.MapUtils; +import com.oceanbase.odc.common.util.StringUtils; +import com.oceanbase.odc.core.shared.constant.TaskStatus; +import com.oceanbase.odc.service.onlineschemachange.configuration.OnlineSchemaChangeProperties; +import com.oceanbase.odc.service.onlineschemachange.model.FullVerificationResult; +import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeScheduleTaskParameters; +import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeScheduleTaskResult; +import com.oceanbase.odc.service.onlineschemachange.model.PrecheckResult; +import com.oceanbase.odc.service.onlineschemachange.oms.enums.OmsStepStatus; +import com.oceanbase.odc.service.onlineschemachange.oms.enums.OscStepName; +import com.oceanbase.odc.service.onlineschemachange.oscfms.action.MonitorDataTaskActionBase; +import com.oceanbase.odc.service.onlineschemachange.oscfms.action.ProjectStepResult; + +import lombok.extern.slf4j.Slf4j; + +/** + * @author longpeng.zlp + * @date 2025/03/24 20:04 + */ +@Slf4j +public class OdcMonitorDataTaskAction extends MonitorDataTaskActionBase { + + public OdcMonitorDataTaskAction(@NotNull OnlineSchemaChangeProperties onlineSchemaChangeProperties) { + super(onlineSchemaChangeProperties); + } + + /** + * rebuild ProjectStepResult from supervisor response + * + * @param taskParameter + * @return + */ + protected ProjectStepResult getProjectStepResult(OnlineSchemaChangeScheduleTaskParameters taskParameter, + OnlineSchemaChangeScheduleTaskResult lastResult) { + return getProjectStepResultInner(taskParameter); + } + + protected ProjectStepResult getProjectStepResultInner(OnlineSchemaChangeScheduleTaskParameters taskParameter) { + ProjectStepResult projectStepResult = new ProjectStepResult(); + SupervisorResponse supervisorResponse = OscCommandUtil.monitorTask(taskParameter.getOdcCommandURl()); + if (null == supervisorResponse || !supervisorResponse.isSuccess()) { + log.info("OdcMonitorDataTaskAction: supervisor response failed, response = {}", supervisorResponse); + return null; + } + // response like { + // "errorMessage":null, + // "responseData": + // {"data":"\" + // { + // \\\"checkpoint\\\":\\\"1742804267\\\", + // \\\"enableIncrementMigrator\\\":\\\"true\\\", + // \\\"estimateMigrateRows\\\":\\\"0\\\", + // \\\"enableFullMigrator\\\":\\\"true\\\", + // \\\"fullMigratorProgress\\\":\\\"0\\\", + // \\\"fullMigratorDone\\\":\\\"false\\\", + // \\\"tableTotalRows\\\":\\\"0\\\" + // }\"" + // }, + // "success":true} + Map payload = supervisorResponse.getResponseData(); + if (MapUtils.isEmpty(payload) || StringUtils.isEmpty(payload.get("data"))) { + log.info("OdcMonitorDataTaskAction: supervisor response invalid, response = {}", supervisorResponse); + return null; + } + Map dataMap = JsonUtils.fromJson(payload.get("data"), Map.class); + // fill common fields + projectStepResult.setPreCheckResult(PrecheckResult.FINISHED); + projectStepResult.setTaskStatus(TaskStatus.RUNNING); + projectStepResult.setFullVerificationResult(FullVerificationResult.UNCHECK); + // adapt steps + try { + projectStepResult.setFullTransferEstimatedCount(getAndCheckValue(dataMap, "tableTotalRows", Long::valueOf)); + projectStepResult + .setFullTransferFinishedCount(getAndCheckValue(dataMap, "estimateMigrateRows", Long::valueOf)); + projectStepResult.setFullTransferProgressPercentage( + getAndCheckValue(dataMap, "fullMigratorProgress", Double::valueOf)); + projectStepResult.setFullVerificationResult(FullVerificationResult.UNCHECK); + projectStepResult.setFullVerificationResultDescription("unchecked"); + projectStepResult.setFullVerificationProgressPercentage(0.0); + boolean fullMigrateDone = getAndCheckValue(dataMap, "fullMigratorDone", Boolean::valueOf); + if (fullMigrateDone) { + projectStepResult.setCurrentStep(OscStepName.TRANSFER_APP_SWITCH.name()); + } else { + projectStepResult.setCurrentStep(OscStepName.FULL_TRANSFER.name()); + } + projectStepResult.setCurrentStepStatus(OmsStepStatus.RUNNING.name()); + projectStepResult.setCheckFailedTime(Collections.emptyMap()); + projectStepResult.setIncrementCheckpoint(getAndCheckValue(dataMap, "checkpoint", Long::valueOf)); + projectStepResult.setTaskPercentage( + Math.min(95.0, getAndCheckValue(dataMap, "fullMigratorProgress", Double::valueOf))); + return projectStepResult; + } catch (Exception e) { + log.info("OdcMonitorDataTaskAction: supervisor response not contains all fields, response = {}", + supervisorResponse); + return null; + } + } + + + protected static T getAndCheckValue(Map data, String key, Function valCaster) { + String val = data.get(key); + if (null == val) { + throw new RuntimeException(key + " not provided"); + } + return valCaster.apply(val); + } + + protected boolean isMigrateTaskReady(ProjectStepResult projectStepResult) { + // step into increment transfer and checkpoint delay less than 5 seconds + return OscStepName.valueOf(projectStepResult.getCurrentStep()) == OscStepName.TRANSFER_APP_SWITCH && + (System.currentTimeMillis() / 1000 - projectStepResult.getIncrementCheckpoint() <= 5); + } + + @Override + protected String getPrintLogName(OnlineSchemaChangeScheduleTaskParameters parameters) { + return parameters.getOdcCommandURl(); + } +} diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcSwapTableAction.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcSwapTableAction.java new file mode 100644 index 0000000000..dfc839fa14 --- /dev/null +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcSwapTableAction.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2023 OceanBase. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.oceanbase.odc.service.onlineschemachange.oscfms.action.odc; + +import java.util.Map; + +import javax.validation.constraints.NotNull; + +import com.google.common.annotations.VisibleForTesting; +import com.oceanbase.odc.service.onlineschemachange.configuration.OnlineSchemaChangeProperties; +import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeScheduleTaskParameters; +import com.oceanbase.odc.service.onlineschemachange.oms.enums.OscStepName; +import com.oceanbase.odc.service.onlineschemachange.oscfms.OscActionContext; +import com.oceanbase.odc.service.onlineschemachange.oscfms.action.ProjectStepResult; +import com.oceanbase.odc.service.onlineschemachange.oscfms.action.SwapTableActionBase; +import com.oceanbase.odc.service.onlineschemachange.oscfms.action.oms.OmsRequestUtil; +import com.oceanbase.odc.service.session.DBSessionManageFacade; + +import lombok.extern.slf4j.Slf4j; + +/** + * swap table action + * + * @author longpeng.zlp + * @date 2024/3/24 11:38 + * @since 4.3.1 + */ +@Slf4j +public class OdcSwapTableAction extends SwapTableActionBase { + + private final OdcMonitorDataTaskAction odcMonitorDataTaskAction; + + public OdcSwapTableAction(@NotNull DBSessionManageFacade dbSessionManageFacade, + @NotNull OnlineSchemaChangeProperties onlineSchemaChangeProperties) { + super(dbSessionManageFacade, onlineSchemaChangeProperties); + this.odcMonitorDataTaskAction = new OdcMonitorDataTaskAction(onlineSchemaChangeProperties); + } + + protected boolean checkOSCProjectReady(OscActionContext context) { + OnlineSchemaChangeScheduleTaskParameters taskParameter = context.getTaskParameter(); + // get oms step result + ProjectStepResult projectStepResult = odcMonitorDataTaskAction.getProjectStepResultInner(taskParameter); + if (null == projectStepResult) { + return false; + } + return odcMonitorDataTaskAction.isMigrateTaskReady(projectStepResult); + } + + /** + * check if all increment data has applied to ghost table this check should called after source + * table locked + */ + @VisibleForTesting + protected boolean isIncrementDataAppliedDone(OnlineSchemaChangeProperties onlineSchemaChangeProperties, + OnlineSchemaChangeScheduleTaskParameters parameters, + Map checkFailedTimes, int timeOutMS) { + long safeDataCheckpoint = System.currentTimeMillis() / 1000; + // max check 25s + long checkTimeoutMs = System.currentTimeMillis() + timeOutMS; + while (true) { + ProjectStepResult projectStepResult = odcMonitorDataTaskAction.getProjectStepResultInner(parameters); + log.info("Osc check increment checkpoint, expect greater than {}, current = {}", + safeDataCheckpoint, projectStepResult.getIncrementCheckpoint()); + if (odcMonitorDataTaskAction.isMigrateTaskReady(projectStepResult) + && projectStepResult.getIncrementCheckpoint() > safeDataCheckpoint) { + return true; + } + if (System.currentTimeMillis() < checkTimeoutMs) { + OmsRequestUtil.sleep(1000); + } else { + return false; + } + } + } +} diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OscCommandUtil.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OscCommandUtil.java new file mode 100644 index 0000000000..4f6943f3e3 --- /dev/null +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OscCommandUtil.java @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2023 OceanBase. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.oceanbase.odc.service.onlineschemachange.oscfms.action.odc; + +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.apache.commons.io.IOUtils; +import org.apache.http.HttpEntity; +import org.apache.http.NameValuePair; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.utils.URLEncodedUtils; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.message.BasicNameValuePair; + +import com.oceanbase.odc.common.json.JsonUtils; + +import lombok.extern.slf4j.Slf4j; + +/** + * @author longpeng.zlp + * @date 2025/3/24 14:20 + */ +@Slf4j +public class OscCommandUtil { + private static final Integer HTTP_SUCCESS = 200; + private static final Integer HTTP_CREATED = 201; + + public static boolean isOSCMigrateSupervisorAlive(String oscSupervisorEndpoint) { + SupervisorResponse response = heartbeat(oscSupervisorEndpoint); + return null != response && response.isSuccess(); + } + + public static SupervisorResponse heartbeat(String oscSupervisorEndpoint) { + try { + String response = get(oscSupervisorEndpoint + "/heartbeat", 3000); + return JsonUtils.fromJson(response, SupervisorResponse.class); + } catch (Throwable e) { + log.warn("heartbeat osc migrate supervisor failed, cause={}", e.getMessage()); + return failedResponse(e.getMessage(), Collections.emptyMap()); + } + } + + + public static SupervisorResponse startTask(String oscSupervisorEndpoint, Map parameters) { + try { + String response = post(oscSupervisorEndpoint + "/start", JsonUtils.toJson(parameters), 10000); + return JsonUtils.fromJson(response, SupervisorResponse.class); + } catch (Throwable e) { + log.warn("start osc migrate supervisor failed, cause={}", e.getMessage()); + return failedResponse(e.getMessage(), Collections.emptyMap()); + } + } + + public static SupervisorResponse monitorTask(String oscSupervisorEndpoint) { + try { + String response = get(oscSupervisorEndpoint + "/monitor", 5000); + return JsonUtils.fromJson(response, SupervisorResponse.class); + } catch (Throwable e) { + log.warn("monitor osc migrate supervisor failed, cause={}", e.getMessage()); + return failedResponse(e.getMessage(), Collections.emptyMap()); + } + } + + public static SupervisorResponse clearTask(String oscSupervisorEndpoint) { + try { + String response = get(oscSupervisorEndpoint + "/clear", 5000); + return JsonUtils.fromJson(response, SupervisorResponse.class); + } catch (Throwable e) { + log.warn("monitor osc migrate supervisor failed, cause={}", e.getMessage()); + return failedResponse(e.getMessage(), Collections.emptyMap()); + } + } + + public static SupervisorResponse updateTask(String oscSupervisorEndpoint, int rps) { + try { + Map updatedConfig = + Collections.singletonMap("throttleRps", String.valueOf(Math.max(1, rps))); + String response = post(oscSupervisorEndpoint + "/update", JsonUtils.toJson(updatedConfig), 5000); + return JsonUtils.fromJson(response, SupervisorResponse.class); + } catch (Throwable e) { + log.warn("update osc migrate supervisor config with rps= {}, failed, cause={}", rps, e.getMessage()); + return failedResponse(e.getMessage(), Collections.emptyMap()); + } + } + + + private static SupervisorResponse failedResponse(String e, Map responseData) { + return new SupervisorResponse(false, e, responseData); + } + + private static String get(String url, int timeoutMs) throws Exception { + return get(url, Collections.emptyMap(), Collections.emptyMap(), timeoutMs); + } + + private static String get(String url, Map params, Map headers, int timeoutInMs) + throws Exception { + long timeBegin = System.currentTimeMillis(); + + if (null != params && !params.isEmpty()) { + List parameters = new ArrayList<>(); + for (String key : params.keySet()) { + parameters.add(new BasicNameValuePair(key, params.get(key))); + } + + String paramStr = URLEncodedUtils.format(parameters, StandardCharsets.UTF_8); + url = url + "?" + paramStr; + } + if (log.isDebugEnabled()) { + log.debug("sending http Get Request. url: " + url); + } + + HttpGet httpGet = new HttpGet(url); + httpGet.setConfig(RequestConfig.custom().setSocketTimeout(timeoutInMs).setConnectTimeout(timeoutInMs) + .setConnectionRequestTimeout(timeoutInMs).build()); + + if (headers != null && !headers.isEmpty()) { + for (String key : headers.keySet()) { + httpGet.setHeader(key, headers.get(key)); + } + } + RequestConfig config = RequestConfig.custom().setRedirectsEnabled(false).build(); + CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(config).build(); + CloseableHttpResponse response = httpClient.execute(httpGet); + int status = response.getStatusLine().getStatusCode(); + + String result = null; + String errorMessage = ""; + try { + HttpEntity entity = response.getEntity(); + result = IOUtils.toString(entity.getContent()); + } catch (Exception e) { + errorMessage = e.getMessage(); + log.error(String.format("GET [%s] failed with status [%d]", url, status), e); + } + + response.close(); + httpClient.close(); + + if (log.isInfoEnabled()) { + long timeFinish = System.currentTimeMillis(); + log.info("[GET] [{}] [{}] [{}] [{}]", url, + status, timeFinish - timeBegin, errorMessage); + } + + if (HTTP_SUCCESS == status) { + return result; + } + throw new IllegalStateException("GET " + url + " failed with status =" + status + ", result = " + result); + } + + private static String post(String url, String json, int timeOutMs) throws Exception { + StringEntity entity = new StringEntity(json, StandardCharsets.UTF_8); + entity.setContentType("application/json"); + return post(url, entity, Collections.emptyMap(), timeOutMs); + } + + private static String post(String url, HttpEntity reqEntity, Map headers, int timeOutMs) + throws Exception { + URL urlInstance = new URL(url); + + long timeBegin = System.currentTimeMillis(); + + HttpPost httpPost = new HttpPost(url); + httpPost.setConfig(RequestConfig.custom().setSocketTimeout(timeOutMs).setConnectTimeout(timeOutMs) + .setConnectionRequestTimeout(timeOutMs).build()); + httpPost.setEntity(reqEntity); + + if (headers != null && !headers.isEmpty()) { + for (String key : headers.keySet()) { + httpPost.setHeader(key, headers.get(key)); + } + } + + CloseableHttpClient httpClient = HttpClients.createDefault(); + CloseableHttpResponse response = httpClient.execute(httpPost); + int status = response.getStatusLine().getStatusCode(); + + String result = null; + String errorMessage = ""; + try { + HttpEntity entity = response.getEntity(); + result = IOUtils.toString(entity.getContent()); + } catch (Exception e) { + errorMessage = e.getMessage(); + log.error(String.format("POST [%s] failed with status [%d]", url, status), e); + } + + response.close(); + httpClient.close(); + + if (log.isInfoEnabled()) { + long timeFinish = System.currentTimeMillis(); + log.info("[POST] [{}] [{}] [{}] [{}] [{}]", urlInstance.getHost(), urlInstance.getPath(), + status, timeFinish - timeBegin, errorMessage); + } + + if (HTTP_SUCCESS == status || HTTP_CREATED == status) { + return result; + } + throw new IllegalStateException("POST " + url + " failed with status =" + status + ", result = " + result); + } +} diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/SupervisorResponse.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/SupervisorResponse.java new file mode 100644 index 0000000000..0abea184e8 --- /dev/null +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/SupervisorResponse.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023 OceanBase. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.oceanbase.odc.service.onlineschemachange.oscfms.action.odc; + +import java.util.Map; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * @author longpeng.zlp + * @date 2025/3/24 14:04 + */ +@Data +@AllArgsConstructor +public class SupervisorResponse { + private boolean isSuccess; + private String errorMessage; + private Map responseData; + + public SupervisorResponse() {} +} diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsCleanResourcesAction.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsCleanResourcesAction.java index a4b68a01de..52906698d6 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsCleanResourcesAction.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsCleanResourcesAction.java @@ -15,23 +15,12 @@ */ package com.oceanbase.odc.service.onlineschemachange.oscfms.action.oms; -import java.util.List; - import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.Lists; -import com.oceanbase.odc.common.json.JsonUtils; import com.oceanbase.odc.common.util.StringUtils; -import com.oceanbase.odc.core.session.ConnectionSession; import com.oceanbase.odc.core.shared.PreConditions; import com.oceanbase.odc.core.shared.constant.ErrorCodes; -import com.oceanbase.odc.core.shared.constant.TaskErrorStrategy; -import com.oceanbase.odc.core.shared.constant.TaskStatus; -import com.oceanbase.odc.metadb.schedule.ScheduleEntity; import com.oceanbase.odc.metadb.schedule.ScheduleTaskEntity; -import com.oceanbase.odc.service.onlineschemachange.OscTableUtil; import com.oceanbase.odc.service.onlineschemachange.exception.OmsException; -import com.oceanbase.odc.service.onlineschemachange.fsm.Action; -import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeParameters; import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeScheduleTaskParameters; import com.oceanbase.odc.service.onlineschemachange.oms.enums.OmsProjectStatusEnum; import com.oceanbase.odc.service.onlineschemachange.oms.openapi.OmsProjectOpenApiService; @@ -39,6 +28,7 @@ import com.oceanbase.odc.service.onlineschemachange.oms.response.OmsProjectProgressResponse; import com.oceanbase.odc.service.onlineschemachange.oscfms.OscActionContext; import com.oceanbase.odc.service.onlineschemachange.oscfms.OscActionResult; +import com.oceanbase.odc.service.onlineschemachange.oscfms.action.CleanResourcesActionBase; import com.oceanbase.odc.service.onlineschemachange.oscfms.state.OscStates; import lombok.NonNull; @@ -50,13 +40,10 @@ * @since 4.3.1 */ @Slf4j -public class OmsCleanResourcesAction implements Action { +public class OmsCleanResourcesAction extends CleanResourcesActionBase { private final OmsProjectOpenApiService projectOpenApiService; - private final List expectedTaskStatus = Lists.newArrayList(TaskStatus.DONE, TaskStatus.FAILED, - TaskStatus.CANCELED, TaskStatus.RUNNING, TaskStatus.ABNORMAL); - public OmsCleanResourcesAction(@NonNull OmsProjectOpenApiService omsProjectOpenApiService) { this.projectOpenApiService = omsProjectOpenApiService; } @@ -82,54 +69,6 @@ public OscActionResult execute(OscActionContext context) throws Exception { return determinateNextState(scheduleTask, context.getSchedule()); } - protected boolean tryDropNewTable(OscActionContext context) { - ConnectionSession connectionSession = null; - OnlineSchemaChangeScheduleTaskParameters taskParam = context.getTaskParameter(); - String databaseName = taskParam.getDatabaseName(); - String tableName = taskParam.getNewTableNameUnwrapped(); - boolean succeed; - try { - connectionSession = context.getConnectionProvider().createConnectionSession(); - OscTableUtil.dropNewTableIfExits(databaseName, tableName, connectionSession); - succeed = true; - } catch (Throwable e) { - log.warn("osc: drop table = {}.{} failed", databaseName, tableName, e); - succeed = false; - } finally { - if (connectionSession != null) { - connectionSession.expire(); - } - } - return succeed; - } - - @VisibleForTesting - protected OscActionResult determinateNextState(ScheduleTaskEntity scheduleTask, ScheduleEntity schedule) { - Long scheduleId = schedule.getId(); - // try to dispatch to next state for done status - if (scheduleTask.getStatus() == TaskStatus.DONE) { - return new OscActionResult(OscStates.CLEAN_RESOURCE.getState(), null, OscStates.YIELD_CONTEXT.getState()); - } - // if task state is in cancel state, stop and transfer to complete state - if (scheduleTask.getStatus() == TaskStatus.CANCELED) { - log.info("Because task is canceled, so delete quartz job={}", scheduleId); - // cancel as complete - return new OscActionResult(OscStates.CLEAN_RESOURCE.getState(), null, OscStates.COMPLETE.getState()); - } - // remain failed and prepare state - OnlineSchemaChangeParameters onlineSchemaChangeParameters = JsonUtils.fromJson( - schedule.getJobParametersJson(), OnlineSchemaChangeParameters.class); - if (onlineSchemaChangeParameters.getErrorStrategy() == TaskErrorStrategy.CONTINUE) { - log.info("Because error strategy is continue, so schedule next task"); - // try schedule next task - return new OscActionResult(OscStates.CLEAN_RESOURCE.getState(), null, OscStates.YIELD_CONTEXT.getState()); - } else { - log.info("Because error strategy is abort, so delete quartz job={}", scheduleId); - // not continue for remain state, transfer to complete state - return new OscActionResult(OscStates.CLEAN_RESOURCE.getState(), null, OscStates.COMPLETE.getState()); - } - } - @VisibleForTesting protected boolean checkAndReleaseProject(String omsProjectId, String uid) { if (omsProjectId == null) { diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsCreateDataTaskAction.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsCreateDataTaskAction.java index 28465d4f58..7acede1c6e 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsCreateDataTaskAction.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsCreateDataTaskAction.java @@ -16,9 +16,7 @@ package com.oceanbase.odc.service.onlineschemachange.oscfms.action.oms; import java.util.ArrayList; -import java.util.Base64; import java.util.List; -import java.util.UUID; import javax.validation.constraints.NotNull; @@ -27,17 +25,13 @@ import com.oceanbase.odc.common.json.JsonUtils; import com.oceanbase.odc.core.session.ConnectionSession; -import com.oceanbase.odc.core.session.ConnectionSessionConstants; import com.oceanbase.odc.core.shared.PreConditions; -import com.oceanbase.odc.core.shared.constant.DialectType; -import com.oceanbase.odc.core.sql.execute.SyncJdbcExecutor; import com.oceanbase.odc.service.connection.model.ConnectionConfig; import com.oceanbase.odc.service.onlineschemachange.configuration.OnlineSchemaChangeProperties; import com.oceanbase.odc.service.onlineschemachange.exception.OmsException; import com.oceanbase.odc.service.onlineschemachange.fsm.Action; import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeParameters; import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeScheduleTaskParameters; -import com.oceanbase.odc.service.onlineschemachange.oms.enums.OmsOceanBaseType; import com.oceanbase.odc.service.onlineschemachange.oms.openapi.DataSourceOpenApiService; import com.oceanbase.odc.service.onlineschemachange.oms.openapi.OmsProjectOpenApiService; import com.oceanbase.odc.service.onlineschemachange.oms.request.CommonTransferConfig; @@ -110,8 +104,8 @@ private String tryCreateOMSDataSource(OnlineSchemaChangeScheduleTaskParameters s CreateOceanBaseDataSourceRequest dataSourceRequest = null; try { connectionSession = context.getConnectionProvider().createConnectionSession(); - dataSourceRequest = getCreateDataSourceRequest(connectionConfig, connectionSession, - context.getTaskParameter()); + dataSourceRequest = OmsRequestUtil.getCreateDataSourceRequest(connectionConfig, connectionSession, + context.getTaskParameter(), oscProperties); omsDsId = dataSourceOpenApiService.createOceanBaseDataSource(dataSourceRequest); log.info("OSC create oms data source, omsDSId {}, request {}", omsDsId, dataSourceRequest); } catch (OmsException ex) { @@ -207,73 +201,10 @@ protected CreateOmsProjectRequest getCreateProjectRequest(String omsDsId, Long s protected void fillCreateProjectRequest(String omsDsId, Long scheduleId, OnlineSchemaChangeScheduleTaskParameters oscScheduleTaskParameters, CreateOmsProjectRequest request) {} - private CreateOceanBaseDataSourceRequest getCreateDataSourceRequest(ConnectionConfig config, - ConnectionSession connectionSession, OnlineSchemaChangeScheduleTaskParameters oscScheduleTaskParameters) { - - CreateOceanBaseDataSourceRequest request = new CreateOceanBaseDataSourceRequest(); - request.setName(UUID.randomUUID().toString().replace("-", "")); - request.setType(OmsOceanBaseType.from(config.getType()).name()); - request.setTenant(config.getTenantName()); - request.setCluster(config.getClusterName()); - request.setUserName(config.getUsername()); - if (config.getPassword() != null) { - request.setPassword(Base64.getEncoder().encodeToString(config.getPassword().getBytes())); - } - fillCreateDataSourceRequest(config, connectionSession, oscScheduleTaskParameters, request); - return request; - } - - /** - * fill remain variables if needed - */ - protected void fillCreateDataSourceRequest(ConnectionConfig config, ConnectionSession connectionSession, - OnlineSchemaChangeScheduleTaskParameters oscScheduleTaskParameters, - CreateOceanBaseDataSourceRequest request) { - request.setIp(config.getHost()); - request.setPort(config.getPort()); - request.setRegion(oscProperties.getOms().getRegion()); - request.setOcpName(null); - String configUrl = getConfigUrl(connectionSession); - request.setConfigUrl(configUrl); - request.setDrcUserName(config.getSysTenantUsername()); - if (config.getSysTenantPassword() != null) { - request.setDrcPassword(Base64.getEncoder().encodeToString(config.getSysTenantPassword().getBytes())); - } - if (config.getDialectType() == DialectType.OB_MYSQL && isObCE(connectionSession)) { - request.setType(OmsOceanBaseType.OB_MYSQL_CE.name()); - } - } - protected String reCreateDataSourceRequestAfterThrowsException( OnlineSchemaChangeScheduleTaskParameters oscScheduleTaskParameters, CreateOceanBaseDataSourceRequest request, OmsException ex) { return null; } - - private String getConfigUrl(ConnectionSession connectionSession) { - - SyncJdbcExecutor syncJdbcExecutor = connectionSession.getSyncJdbcExecutor( - ConnectionSessionConstants.CONSOLE_DS_KEY); - String queryClusterUrlSql = "show parameters like 'obconfig_url'"; - return syncJdbcExecutor.query(queryClusterUrlSql, rs -> { - if (!rs.next()) { - throw new IllegalArgumentException("Get ob config_url is empty"); - } - return rs.getString("value"); - }); - } - - private boolean isObCE(ConnectionSession connectionSession) { - SyncJdbcExecutor syncJdbcExecutor = connectionSession.getSyncJdbcExecutor( - ConnectionSessionConstants.CONSOLE_DS_KEY); - String queryVersionSql = "show variables like 'version_comment'"; - String versionString = syncJdbcExecutor.query(queryVersionSql, rs -> { - if (!rs.next()) { - throw new IllegalArgumentException("Get ob version is empty"); - } - return rs.getString("value"); - }); - return versionString != null && versionString.startsWith("OceanBase_CE"); - } } diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsMonitorDataTaskAction.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsMonitorDataTaskAction.java index 1053b5ca2a..c4f0c8f6dd 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsMonitorDataTaskAction.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsMonitorDataTaskAction.java @@ -15,35 +15,14 @@ */ package com.oceanbase.odc.service.onlineschemachange.oscfms.action.oms; -import java.util.List; -import java.util.Objects; - import javax.validation.constraints.NotNull; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.Lists; -import com.oceanbase.odc.common.json.JsonUtils; -import com.oceanbase.odc.common.util.tableformat.Table; -import com.oceanbase.odc.core.shared.constant.ErrorCodes; -import com.oceanbase.odc.core.shared.constant.TaskStatus; -import com.oceanbase.odc.metadb.schedule.ScheduleTaskEntity; import com.oceanbase.odc.service.onlineschemachange.configuration.OnlineSchemaChangeProperties; -import com.oceanbase.odc.service.onlineschemachange.exception.OscException; -import com.oceanbase.odc.service.onlineschemachange.fsm.Action; -import com.oceanbase.odc.service.onlineschemachange.logger.DefaultTableFactory; -import com.oceanbase.odc.service.onlineschemachange.model.FullVerificationResult; -import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeParameters; import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeScheduleTaskParameters; import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeScheduleTaskResult; -import com.oceanbase.odc.service.onlineschemachange.model.PrecheckResult; -import com.oceanbase.odc.service.onlineschemachange.model.SwapTableType; -import com.oceanbase.odc.service.onlineschemachange.oms.enums.OmsStepName; import com.oceanbase.odc.service.onlineschemachange.oms.openapi.OmsProjectOpenApiService; -import com.oceanbase.odc.service.onlineschemachange.oscfms.OscActionContext; -import com.oceanbase.odc.service.onlineschemachange.oscfms.OscActionResult; -import com.oceanbase.odc.service.onlineschemachange.oscfms.action.oms.ProjectStepResultChecker.ProjectStepResult; -import com.oceanbase.odc.service.onlineschemachange.oscfms.state.OscStates; -import com.oceanbase.odc.service.onlineschemachange.rename.SwapTableUtil; +import com.oceanbase.odc.service.onlineschemachange.oscfms.action.MonitorDataTaskActionBase; +import com.oceanbase.odc.service.onlineschemachange.oscfms.action.ProjectStepResult; import lombok.extern.slf4j.Slf4j; @@ -53,186 +32,28 @@ * @since 4.3.1 */ @Slf4j -public class OmsMonitorDataTaskAction implements Action { +public class OmsMonitorDataTaskAction extends MonitorDataTaskActionBase { private final OmsProjectOpenApiService projectOpenApiService; - private final OnlineSchemaChangeProperties onlineSchemaChangeProperties; - public OmsMonitorDataTaskAction(@NotNull OmsProjectOpenApiService projectOpenApiService, @NotNull OnlineSchemaChangeProperties onlineSchemaChangeProperties) { + super(onlineSchemaChangeProperties); this.projectOpenApiService = projectOpenApiService; - this.onlineSchemaChangeProperties = onlineSchemaChangeProperties; - } - - @Override - public OscActionResult execute(OscActionContext context) throws Exception { - ScheduleTaskEntity scheduleTask = context.getScheduleTask(); - log.debug("Start execute {}, schedule task id={}", getClass().getSimpleName(), scheduleTask.getId()); - - OnlineSchemaChangeScheduleTaskParameters taskParameter = context.getTaskParameter(); - OnlineSchemaChangeParameters inputParameters = context.getParameter(); - // switch state - if (shouldUpdateOMSConfig(taskParameter, inputParameters)) { - return new OscActionResult(OscStates.MONITOR_DATA_TASK.getState(), null, - OscStates.MODIFY_DATA_TASK.getState()); - } - // get result - OnlineSchemaChangeScheduleTaskResult lastResult = JsonUtils.fromJson(scheduleTask.getResultJson(), - OnlineSchemaChangeScheduleTaskResult.class); - - OnlineSchemaChangeScheduleTaskResult result = - new OnlineSchemaChangeScheduleTaskResult(taskParameter); - - if (lastResult != null) { - result.setManualSwapTableEnabled(lastResult.isManualSwapTableEnabled()); - result.setManualSwapTableStarted(lastResult.isManualSwapTableStarted()); - } - // get oms step result - ProjectStepResult projectStepResult = - OmsRequestUtil.buildProjectStepResult(projectOpenApiService, onlineSchemaChangeProperties, - taskParameter.getUid(), taskParameter.getOmsProjectId(), taskParameter.getDatabaseName(), - lastResult.getCheckFailedTime()); - adaptResult(result, projectStepResult); - scheduleTask.setResultJson(JsonUtils.toJson(result)); - scheduleTask.setProgressPercentage(projectStepResult.getTaskPercentage()); - // update progress, can merge with following update? - recordCurrentProgress(taskParameter.getOmsProjectId(), result); - try { - return handleOmsProjectStepResult(context, projectStepResult, result, - context.getParameter().getSwapTableType(), scheduleTask); - } finally { - // update schedule task once - context.getScheduleTaskRepository().update(scheduleTask); - } - } - - @VisibleForTesting - protected OscActionResult handleOmsProjectStepResult(OscActionContext context, ProjectStepResult projectStepResult, - OnlineSchemaChangeScheduleTaskResult result, SwapTableType swapTableType, ScheduleTaskEntity scheduleTask) { - // oms task is ready, try swap step - if (OmsRequestUtil.isOmsTaskReady(projectStepResult)) { - // is manual swap table - boolean isSwapTableReady = - SwapTableUtil.isSwapTableReady(scheduleTask.getStatus(), result.getFullTransferProgressPercentage(), - result.getFullVerificationResult()); - // can't swap table - if (!isSwapTableReady) { - // keep in same state, monitor task state - return new OscActionResult(OscStates.MONITOR_DATA_TASK.getState(), null, - OscStates.MONITOR_DATA_TASK.getState()); - - } - // try do swap table - switch (swapTableType) { - case AUTO: - // auto swap, jump to swap stable state - scheduleTask.setResultJson(JsonUtils.toJson(result)); - return new OscActionResult(OscStates.MONITOR_DATA_TASK.getState(), null, - OscStates.SWAP_TABLE.getState()); - case MANUAL: - if (!result.isManualSwapTableStarted()) { - // isManualSwapTableEnabled set true to let swap table button show on front-end panel - if (!result.isManualSwapTableEnabled()) { - // open manual swap table - result.setManualSwapTableEnabled(true); - scheduleTask.setResultJson(JsonUtils.toJson(result)); - } - log.info("OSC: oms project ready, wait manual swap table triggered, task id={}", - scheduleTask.getId()); - // manual swap table not set, keep waiting - return new OscActionResult(OscStates.MONITOR_DATA_TASK.getState(), null, - OscStates.MONITOR_DATA_TASK.getState()); - } else { - // jump to swap table state - return new OscActionResult(OscStates.MONITOR_DATA_TASK.getState(), null, - OscStates.SWAP_TABLE.getState()); - } - default: - throw new IllegalStateException("invalid state for swap table type"); - } - } else { - // check oms status - return continueHandleProjectStepResult(projectStepResult); - } } - private OscActionResult continueHandleProjectStepResult(ProjectStepResult projectStepResult) { - if (projectStepResult.getPreCheckResult() == PrecheckResult.FAILED) { - throw new OscException(ErrorCodes.OmsPreCheckFailed, projectStepResult.getErrorMsg()); - } else if (projectStepResult.getTaskStatus() == TaskStatus.FAILED) { - throw new OscException(ErrorCodes.OmsProjectExecutingFailed, projectStepResult.getErrorMsg()); - } else if (projectStepResult.getFullVerificationResult() == FullVerificationResult.INCONSISTENT) { - throw new OscException(ErrorCodes.OmsDataCheckInconsistent, - "Task failed for origin table has inconsistent data with new table, result: " - + projectStepResult.getFullVerificationResultDescription()); - } else { - // stay in monitor state - return new OscActionResult(OscStates.MONITOR_DATA_TASK.getState(), null, - OscStates.MONITOR_DATA_TASK.getState()); - } - } - - private void recordCurrentProgress(String projectId, OnlineSchemaChangeScheduleTaskResult result) { - String progress = ""; - String description = ""; - OmsStepName step = OmsStepName.UNKNOWN; - try { - if (result.getCurrentStep() != null) { - step = OmsStepName.valueOf(result.getCurrentStep()); - switch (step) { - case PRE_CHECK: - description = result.getPrecheckResultDescription(); - break; - case FULL_TRANSFER: - progress = result.getFullTransferProgressPercentage().toString(); - description = result.getFullTransferFinishedCount() + "/" - + result.getFullTransferEstimatedCount(); - break; - case FULL_VERIFIER: - progress = result.getFullVerificationProgressPercentage().toString(); - description = result.getFullVerificationResultDescription(); - break; - default: - } - } - } catch (Exception ex) { - log.warn("Get oms step progress and description occur error"); - } - - List body = Lists.newArrayList(projectId, step == OmsStepName.UNKNOWN ? "" : step.name(), - result.getCurrentStepStatus(), progress, description); - Table table = new DefaultTableFactory().generateTable(5, getHeader(), body); - log.info("\n" + table.render().toString() + "\n"); + protected ProjectStepResult getProjectStepResult(OnlineSchemaChangeScheduleTaskParameters taskParameter, + OnlineSchemaChangeScheduleTaskResult lastResult) { + return OmsRequestUtil.buildProjectStepResult(projectOpenApiService, onlineSchemaChangeProperties, + taskParameter.getUid(), taskParameter.getOmsProjectId(), taskParameter.getDatabaseName(), + lastResult.getCheckFailedTime()); } - private List getHeader() { - return Lists.newArrayList("ProjectId", "Step", "Status", "Progress", "Description"); + protected boolean isMigrateTaskReady(ProjectStepResult projectStepResult) { + return OmsRequestUtil.isOmsTaskReady(projectStepResult); } - - private void adaptResult(OnlineSchemaChangeScheduleTaskResult result, ProjectStepResult projectStepResult) { - result.setFullTransferEstimatedCount(projectStepResult.getFullTransferEstimatedCount()); - result.setFullTransferFinishedCount(projectStepResult.getFullTransferFinishedCount()); - result.setFullTransferProgressPercentage(projectStepResult.getFullTransferProgressPercentage()); - result.setFullVerificationResult(projectStepResult.getFullVerificationResult()); - result.setFullVerificationResultDescription(projectStepResult.getFullVerificationResultDescription()); - result.setFullVerificationProgressPercentage(projectStepResult.getFullVerificationProgressPercentage()); - result.setCurrentStep(projectStepResult.getCurrentStep()); - result.setCurrentStepStatus(projectStepResult.getCurrentStepStatus()); - result.setPrecheckResult(projectStepResult.getPreCheckResult()); - result.setCheckFailedTime(projectStepResult.getCheckFailedTime()); - } - - @VisibleForTesting - protected boolean shouldUpdateOMSConfig(OnlineSchemaChangeScheduleTaskParameters taskParameters, - OnlineSchemaChangeParameters inputParameters) { - // if rate limiter parameters is changed, try to stop and restart project - if (Objects.equals(inputParameters.getRateLimitConfig(), taskParameters.getRateLimitConfig())) { - log.info("Rate limiter not changed,rateLimiterConfig = {}, update oms project not required", - inputParameters.getRateLimitConfig()); - return false; - } else { - return true; - } + @Override + protected String getPrintLogName(OnlineSchemaChangeScheduleTaskParameters parameters) { + return parameters.getOmsProjectId(); } } diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsRequestUtil.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsRequestUtil.java index c9db877642..aa31a94067 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsRequestUtil.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsRequestUtil.java @@ -15,22 +15,32 @@ */ package com.oceanbase.odc.service.onlineschemachange.oscfms.action.oms; +import java.util.Base64; import java.util.List; import java.util.Map; +import java.util.UUID; import com.oceanbase.odc.common.json.JsonUtils; +import com.oceanbase.odc.core.session.ConnectionSession; +import com.oceanbase.odc.core.session.ConnectionSessionConstants; +import com.oceanbase.odc.core.shared.constant.DialectType; import com.oceanbase.odc.core.shared.constant.TaskStatus; +import com.oceanbase.odc.core.sql.execute.SyncJdbcExecutor; +import com.oceanbase.odc.service.connection.model.ConnectionConfig; import com.oceanbase.odc.service.onlineschemachange.configuration.OnlineSchemaChangeProperties; import com.oceanbase.odc.service.onlineschemachange.model.FullVerificationResult; +import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeScheduleTaskParameters; +import com.oceanbase.odc.service.onlineschemachange.oms.enums.OmsOceanBaseType; import com.oceanbase.odc.service.onlineschemachange.oms.enums.OmsProjectStatusEnum; -import com.oceanbase.odc.service.onlineschemachange.oms.enums.OmsStepName; +import com.oceanbase.odc.service.onlineschemachange.oms.enums.OscStepName; import com.oceanbase.odc.service.onlineschemachange.oms.openapi.OmsProjectOpenApiService; +import com.oceanbase.odc.service.onlineschemachange.oms.request.CreateOceanBaseDataSourceRequest; import com.oceanbase.odc.service.onlineschemachange.oms.request.ListOmsProjectFullVerifyResultRequest; import com.oceanbase.odc.service.onlineschemachange.oms.request.OmsProjectControlRequest; import com.oceanbase.odc.service.onlineschemachange.oms.response.OmsProjectFullVerifyResultResponse; import com.oceanbase.odc.service.onlineschemachange.oms.response.OmsProjectProgressResponse; import com.oceanbase.odc.service.onlineschemachange.oms.response.OmsProjectStepVO; -import com.oceanbase.odc.service.onlineschemachange.oscfms.action.oms.ProjectStepResultChecker.ProjectStepResult; +import com.oceanbase.odc.service.onlineschemachange.oscfms.action.ProjectStepResult; import lombok.extern.slf4j.Slf4j; @@ -72,7 +82,7 @@ protected static boolean isOmsTaskReady(ProjectStepResult projectStepResult) { protected static ProjectStepResult buildProjectStepResult(OmsProjectOpenApiService omsProjectOpenApiService, OnlineSchemaChangeProperties onlineSchemaChangeProperties, String uid, String projectID, String databaseName, - Map checkFailedTimes) { + Map checkFailedTimes) { // do remain job OmsProjectControlRequest projectRequest = getProjectRequest(uid, projectID); List projectSteps = omsProjectOpenApiService.describeProjectSteps(projectRequest); @@ -125,4 +135,77 @@ public static void sleep(long millionSeconds) { } catch (InterruptedException e) { // swallow exception } } + + /** + * get create data source request for oms + * + * @param config + * @param connectionSession + * @param oscScheduleTaskParameters + * @param oscProperties + * @return + */ + public static CreateOceanBaseDataSourceRequest getCreateDataSourceRequest(ConnectionConfig config, + ConnectionSession connectionSession, OnlineSchemaChangeScheduleTaskParameters oscScheduleTaskParameters, + OnlineSchemaChangeProperties oscProperties) { + + CreateOceanBaseDataSourceRequest request = new CreateOceanBaseDataSourceRequest(); + request.setName(UUID.randomUUID().toString().replace("-", "")); + request.setType(OmsOceanBaseType.from(config.getType()).name()); + request.setTenant(config.getTenantName()); + request.setCluster(config.getClusterName()); + request.setUserName(config.getUsername()); + if (config.getPassword() != null) { + request.setPassword(Base64.getEncoder().encodeToString(config.getPassword().getBytes())); + } + fillCreateDataSourceRequest(config, connectionSession, oscScheduleTaskParameters, request, oscProperties); + return request; + } + + /** + * fill remain variables if needed + */ + protected static void fillCreateDataSourceRequest(ConnectionConfig config, ConnectionSession connectionSession, + OnlineSchemaChangeScheduleTaskParameters oscScheduleTaskParameters, + CreateOceanBaseDataSourceRequest request, OnlineSchemaChangeProperties oscProperties) { + request.setIp(config.getHost()); + request.setPort(config.getPort()); + request.setRegion(oscProperties.getOms().getRegion()); + request.setOcpName(null); + String configUrl = getConfigUrl(connectionSession); + request.setConfigUrl(configUrl); + request.setDrcUserName(config.getSysTenantUsername()); + if (config.getSysTenantPassword() != null) { + request.setDrcPassword(Base64.getEncoder().encodeToString(config.getSysTenantPassword().getBytes())); + } + if (config.getDialectType() == DialectType.OB_MYSQL && isObCE(connectionSession)) { + request.setType(OmsOceanBaseType.OB_MYSQL_CE.name()); + } + } + + protected static String getConfigUrl(ConnectionSession connectionSession) { + + SyncJdbcExecutor syncJdbcExecutor = connectionSession.getSyncJdbcExecutor( + ConnectionSessionConstants.CONSOLE_DS_KEY); + String queryClusterUrlSql = "show parameters like 'obconfig_url'"; + return syncJdbcExecutor.query(queryClusterUrlSql, rs -> { + if (!rs.next()) { + throw new IllegalArgumentException("Get ob config_url is empty"); + } + return rs.getString("value"); + }); + } + + protected static boolean isObCE(ConnectionSession connectionSession) { + SyncJdbcExecutor syncJdbcExecutor = connectionSession.getSyncJdbcExecutor( + ConnectionSessionConstants.CONSOLE_DS_KEY); + String queryVersionSql = "show variables like 'version_comment'"; + String versionString = syncJdbcExecutor.query(queryVersionSql, rs -> { + if (!rs.next()) { + throw new IllegalArgumentException("Get ob version is empty"); + } + return rs.getString("value"); + }); + return versionString != null && versionString.startsWith("OceanBase_CE"); + } } diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsSwapTableAction.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsSwapTableAction.java index d54882a702..4136584af3 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsSwapTableAction.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsSwapTableAction.java @@ -15,32 +15,20 @@ */ package com.oceanbase.odc.service.onlineschemachange.oscfms.action.oms; -import java.util.List; import java.util.Map; import javax.validation.constraints.NotNull; -import org.apache.commons.collections4.CollectionUtils; - import com.google.common.annotations.VisibleForTesting; import com.oceanbase.odc.common.json.JsonUtils; -import com.oceanbase.odc.core.shared.PreConditions; -import com.oceanbase.odc.metadb.schedule.ScheduleTaskEntity; -import com.oceanbase.odc.service.connection.model.ConnectionConfig; import com.oceanbase.odc.service.onlineschemachange.configuration.OnlineSchemaChangeProperties; -import com.oceanbase.odc.service.onlineschemachange.fsm.Action; -import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeParameters; import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeScheduleTaskParameters; import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeScheduleTaskResult; -import com.oceanbase.odc.service.onlineschemachange.monitor.DBUserMonitorExecutor; -import com.oceanbase.odc.service.onlineschemachange.oms.enums.OmsStepName; +import com.oceanbase.odc.service.onlineschemachange.oms.enums.OscStepName; import com.oceanbase.odc.service.onlineschemachange.oms.openapi.OmsProjectOpenApiService; import com.oceanbase.odc.service.onlineschemachange.oscfms.OscActionContext; -import com.oceanbase.odc.service.onlineschemachange.oscfms.OscActionResult; -import com.oceanbase.odc.service.onlineschemachange.oscfms.action.oms.ProjectStepResultChecker.ProjectStepResult; -import com.oceanbase.odc.service.onlineschemachange.oscfms.state.OscStates; -import com.oceanbase.odc.service.onlineschemachange.rename.DefaultRenameTableInvoker; -import com.oceanbase.odc.service.onlineschemachange.rename.LockTableSupportDecider; +import com.oceanbase.odc.service.onlineschemachange.oscfms.action.ProjectStepResult; +import com.oceanbase.odc.service.onlineschemachange.oscfms.action.SwapTableActionBase; import com.oceanbase.odc.service.session.DBSessionManageFacade; import lombok.extern.slf4j.Slf4j; @@ -53,71 +41,18 @@ * @since 4.3.1 */ @Slf4j -public class OmsSwapTableAction implements Action { - - private final DBSessionManageFacade dbSessionManageFacade; +public class OmsSwapTableAction extends SwapTableActionBase { private final OmsProjectOpenApiService projectOpenApiService; - private final OnlineSchemaChangeProperties onlineSchemaChangeProperties; - public OmsSwapTableAction(@NotNull DBSessionManageFacade dbSessionManageFacade, @NotNull OmsProjectOpenApiService projectOpenApiService, @NotNull OnlineSchemaChangeProperties onlineSchemaChangeProperties) { - this.dbSessionManageFacade = dbSessionManageFacade; + super(dbSessionManageFacade, onlineSchemaChangeProperties); this.projectOpenApiService = projectOpenApiService; - this.onlineSchemaChangeProperties = onlineSchemaChangeProperties; } - @Override - public OscActionResult execute(OscActionContext context) throws Exception { - if (!checkOMSProject(context)) { - // OMS state abnormal, keep waiting - log.info("OSC: swap table waiting for OMS task ready"); - return new OscActionResult(OscStates.SWAP_TABLE.getState(), null, OscStates.SWAP_TABLE.getState()); - } - // begin swap table - ScheduleTaskEntity scheduleTask = context.getScheduleTask(); - log.info("Start execute {}, schedule task id={}", getClass().getSimpleName(), scheduleTask.getId()); - - OnlineSchemaChangeScheduleTaskParameters taskParameters = context.getTaskParameter(); - PreConditions.notNull(taskParameters, "OnlineSchemaChangeScheduleTaskParameters is null"); - OnlineSchemaChangeParameters parameters = context.getParameter(); - - ConnectionConfig config = context.getConnectionProvider().connectionConfig(); - DBUserMonitorExecutor userMonitorExecutor = new DBUserMonitorExecutor(config, parameters.getLockUsers()); - try { - if (enableUserMonitor(parameters.getLockUsers())) { - userMonitorExecutor.start(); - } - DefaultRenameTableInvoker defaultRenameTableInvoker = - new DefaultRenameTableInvoker(context.getConnectionProvider(), dbSessionManageFacade, - () -> { - OnlineSchemaChangeScheduleTaskResult lastResult = JsonUtils.fromJson( - context.getScheduleTask().getResultJson(), - OnlineSchemaChangeScheduleTaskResult.class); - return isIncrementDataAppliedDone(projectOpenApiService, - onlineSchemaChangeProperties, - taskParameters.getUid(), taskParameters.getOmsProjectId(), - taskParameters.getDatabaseName(), - lastResult.getCheckFailedTime(), 25000); - }, this::getLockTableSupportDecider); - defaultRenameTableInvoker.invoke(taskParameters, parameters); - // rename table success, jump to clean resource state - return new OscActionResult(OscStates.SWAP_TABLE.getState(), null, OscStates.CLEAN_RESOURCE.getState()); - } finally { - if (enableUserMonitor(parameters.getLockUsers())) { - userMonitorExecutor.stop(); - } - } - } - - protected LockTableSupportDecider getLockTableSupportDecider() { - String lockTableMatchers = onlineSchemaChangeProperties.getSupportLockTableObVersionJson(); - return LockTableSupportDecider.createWithJsonArrayWithDefaultValue(lockTableMatchers); - } - - protected boolean checkOMSProject(OscActionContext context) { + protected boolean checkOSCProjectReady(OscActionContext context) { OnlineSchemaChangeScheduleTaskParameters taskParameter = context.getTaskParameter(); // get result OnlineSchemaChangeScheduleTaskResult lastResult = JsonUtils.fromJson(context.getScheduleTask().getResultJson(), @@ -135,17 +70,16 @@ protected boolean checkOMSProject(OscActionContext context) { * table locked */ @VisibleForTesting - protected boolean isIncrementDataAppliedDone(OmsProjectOpenApiService omsProjectOpenApiService, - OnlineSchemaChangeProperties onlineSchemaChangeProperties, String uid, String omsProjectID, - String databaseName, - Map checkFailedTimes, int timeOutMS) { + protected boolean isIncrementDataAppliedDone(OnlineSchemaChangeProperties onlineSchemaChangeProperties, + OnlineSchemaChangeScheduleTaskParameters parameters, + Map checkFailedTimes, int timeOutMS) { long safeDataCheckpoint = System.currentTimeMillis() / 1000; // max check 25s long checkTimeoutMs = System.currentTimeMillis() + timeOutMS; while (true) { - ProjectStepResult projectStepResult = OmsRequestUtil.buildProjectStepResult(omsProjectOpenApiService, - onlineSchemaChangeProperties, uid, omsProjectID, - databaseName, + ProjectStepResult projectStepResult = OmsRequestUtil.buildProjectStepResult(projectOpenApiService, + onlineSchemaChangeProperties, parameters.getUid(), parameters.getOmsProjectId(), + parameters.getDatabaseName(), checkFailedTimes); log.info("Osc check oms increment checkpoint, expect greater than {}, current = {}", safeDataCheckpoint, projectStepResult.getIncrementCheckpoint()); @@ -160,8 +94,4 @@ protected boolean isIncrementDataAppliedDone(OmsProjectOpenApiService omsProject } } } - - private boolean enableUserMonitor(List lockUsers) { - return CollectionUtils.isNotEmpty(lockUsers); - } } diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/ProjectStepResultChecker.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/ProjectStepResultChecker.java index 3bfec747fc..a97e61117d 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/ProjectStepResultChecker.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/ProjectStepResultChecker.java @@ -30,15 +30,15 @@ import com.oceanbase.odc.service.onlineschemachange.model.FullVerificationResult; import com.oceanbase.odc.service.onlineschemachange.model.PrecheckResult; import com.oceanbase.odc.service.onlineschemachange.oms.enums.OmsProjectStatusEnum; -import com.oceanbase.odc.service.onlineschemachange.oms.enums.OmsStepName; import com.oceanbase.odc.service.onlineschemachange.oms.enums.OmsStepStatus; +import com.oceanbase.odc.service.onlineschemachange.oms.enums.OscStepName; import com.oceanbase.odc.service.onlineschemachange.oms.response.FullTransferStepInfoVO; import com.oceanbase.odc.service.onlineschemachange.oms.response.FullVerifyTableStatisticVO; import com.oceanbase.odc.service.onlineschemachange.oms.response.OmsProjectFullVerifyResultResponse; import com.oceanbase.odc.service.onlineschemachange.oms.response.OmsProjectProgressResponse; import com.oceanbase.odc.service.onlineschemachange.oms.response.OmsProjectStepVO; +import com.oceanbase.odc.service.onlineschemachange.oscfms.action.ProjectStepResult; -import lombok.Data; import lombok.extern.slf4j.Slf4j; /** @@ -49,8 +49,8 @@ @Slf4j public class ProjectStepResultChecker { - private final List toCheckSteps; - private final Map currentProjectStepMap; + private final List toCheckSteps; + private final Map currentProjectStepMap; private final ProjectStepResult checkerResult; private Supplier verifyResultResponseSupplier; private Supplier resumeProjectSupplier; @@ -58,21 +58,21 @@ public class ProjectStepResultChecker { private final boolean enableFullVerify; private final int checkProjectStepFailedTimeoutSeconds; - private final Map checkFailedTimes; + private final Map checkFailedTimes; public ProjectStepResultChecker(OmsProjectProgressResponse progressResponse, List projectSteps, boolean enableFullVerify, int checkProjectStepFailedTimeoutSeconds, - Map checkFailedTimes) { + Map checkFailedTimes) { this.progressResponse = progressResponse; this.currentProjectStepMap = projectSteps.stream().collect(Collectors.toMap(OmsProjectStepVO::getName, a -> a)); this.checkerResult = new ProjectStepResult(); this.enableFullVerify = enableFullVerify; this.checkProjectStepFailedTimeoutSeconds = checkProjectStepFailedTimeoutSeconds; this.checkFailedTimes = checkFailedTimes; - this.toCheckSteps = Lists.newArrayList(OmsStepName.TRANSFER_INCR_LOG_PULL, - OmsStepName.FULL_TRANSFER, OmsStepName.INCR_TRANSFER); + this.toCheckSteps = Lists.newArrayList(OscStepName.TRANSFER_INCR_LOG_PULL, + OscStepName.FULL_TRANSFER, OscStepName.INCR_TRANSFER); if (enableFullVerify) { - this.toCheckSteps.add(OmsStepName.FULL_VERIFIER); + this.toCheckSteps.add(OscStepName.FULL_VERIFIER); } } @@ -118,7 +118,7 @@ private void setCurrentStepStatus() { } try { OmsProjectStepVO projectStep = - currentProjectStepMap.get(OmsStepName.valueOf(progressResponse.getCurrentStep())); + currentProjectStepMap.get(OscStepName.valueOf(progressResponse.getCurrentStep())); if (projectStep != null) { checkerResult.setCurrentStepStatus(projectStep.getStatus().name()); } @@ -128,7 +128,7 @@ private void setCurrentStepStatus() { } private void checkPreCheckStepResult() { - OmsProjectStepVO precheckStep = currentProjectStepMap.get(OmsStepName.TRANSFER_PRECHECK); + OmsProjectStepVO precheckStep = currentProjectStepMap.get(OscStepName.TRANSFER_PRECHECK); if (precheckStep.getStatus() == OmsStepStatus.FAILED) { checkerResult.setPreCheckResult(PrecheckResult.FAILED); checkerResult.setErrorMsg(precheckStep.getExtraInfo().getErrorMsg()); @@ -148,7 +148,7 @@ private boolean isProjectFinished() { private boolean checkProjectStepFinished() { boolean finished = true; - for (OmsStepName stepName : toCheckSteps) { + for (OscStepName stepName : toCheckSteps) { if (!checkStepFinished(stepName)) { finished = false; break; @@ -158,7 +158,7 @@ private boolean checkProjectStepFinished() { } @VisibleForTesting - protected boolean checkStepFinished(OmsStepName name) { + protected boolean checkStepFinished(OscStepName name) { OmsProjectStepVO omsProjectStepVO = currentProjectStepMap.get(name); if (null == omsProjectStepVO) { return true; @@ -187,10 +187,10 @@ protected boolean checkStepFinished(OmsStepName name) { private boolean isProjectFailed() { boolean isProjectFailed = false; - for (OmsStepName stepName : toCheckSteps) { + for (OscStepName stepName : toCheckSteps) { if (currentProjectStepMap.get(stepName) != null && currentProjectStepMap.get(stepName).getStatus() == OmsStepStatus.FAILED) { - if (!enableFullVerify && stepName == OmsStepName.FULL_VERIFIER) { + if (!enableFullVerify && stepName == OscStepName.FULL_VERIFIER) { continue; } if (currentProjectStepMap.get(stepName).getExtraInfo() != null) { @@ -198,7 +198,7 @@ private boolean isProjectFailed() { } // record FULL_TRANSFER failed time - if (stepName == OmsStepName.FULL_TRANSFER) { + if (stepName == OscStepName.FULL_TRANSFER) { long failedBeginTime = checkFailedTimes.computeIfAbsent(stepName, k -> System.currentTimeMillis()); long failedAccumulateTime = (System.currentTimeMillis() - failedBeginTime) / 1000; if (failedAccumulateTime > checkProjectStepFailedTimeoutSeconds) { @@ -271,7 +271,7 @@ private FullVerificationResult getFullVerificationResult(OmsProjectFullVerifyRes } private void fillMigrateResult() { - OmsProjectStepVO fullTransferStep = currentProjectStepMap.get(OmsStepName.FULL_TRANSFER); + OmsProjectStepVO fullTransferStep = currentProjectStepMap.get(OscStepName.FULL_TRANSFER); FullTransferStepInfoVO stepInfo = (FullTransferStepInfoVO) fullTransferStep.getStepInfo(); if (stepInfo != null) { checkerResult.setFullTransferEstimatedCount(stepInfo.getCapacity()); @@ -282,67 +282,14 @@ private void fillMigrateResult() { if (enableFullVerify) { // Set full verifier process percentage - OmsProjectStepVO fullVerifyStep = currentProjectStepMap.get(OmsStepName.FULL_VERIFIER); + OmsProjectStepVO fullVerifyStep = currentProjectStepMap.get(OscStepName.FULL_VERIFIER); if (fullVerifyStep != null && fullVerifyStep.getProgress() != null) { checkerResult.setFullVerificationProgressPercentage( BigDecimal.valueOf(fullVerifyStep.getProgress()).doubleValue()); } } checkerResult.setCheckFailedTime(this.checkFailedTimes); - } - @Data - public static class ProjectStepResult { - private PrecheckResult preCheckResult; - - private double precheckProgressPercentage; - - private String errorMsg; - - private String currentStep; - - private String currentStepStatus; - private TaskStatus taskStatus; - - /** - * full transfer estimated rows count - */ - private Long fullTransferEstimatedCount; - - /** - * full transfer finished rows count - */ - private Long fullTransferFinishedCount; - - /** - * full transfer percentage - */ - private double fullTransferProgressPercentage; - - /** - * full verification result - */ - private FullVerificationResult fullVerificationResult; - - /** - * full verification result description - */ - private String fullVerificationResultDescription; - - /** - * full verification percentage - */ - private double fullVerificationProgressPercentage; - - /** - * task percentage - */ - private double taskPercentage; - - private Map checkFailedTime; - - private Long incrementCheckpoint; - } } diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/resource/ResourceManager.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/resource/ResourceManager.java index fcc81d8ac2..3ed2b16ccc 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/resource/ResourceManager.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/resource/ResourceManager.java @@ -249,6 +249,9 @@ public String destroy(@NonNull Long id) throws Exception { Optional optional = this.resourceRepository.findById(id); if (!optional.isPresent()) { throw new IllegalStateException("Resource not found by id " + id); + } else if (optional.get().getStatus() == ResourceState.DESTROYING) { + log.warn("Resource is already in destroying state, resourceID={}", id); + return null; } return doDestroy(new ResourceID(optional.get())); } diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/resource/k8s/K8sResourceUtil.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/resource/k8s/K8sResourceUtil.java new file mode 100644 index 0000000000..b0c65e63c6 --- /dev/null +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/resource/k8s/K8sResourceUtil.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2023 OceanBase. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.oceanbase.odc.service.resource.k8s; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Random; +import java.util.Set; + +import org.apache.commons.lang3.tuple.Pair; + +import com.oceanbase.odc.service.resource.Resource; +import com.oceanbase.odc.service.resource.ResourceLocation; +import com.oceanbase.odc.service.resource.ResourceManager; +import com.oceanbase.odc.service.resource.ResourceWithID; +import com.oceanbase.odc.service.task.config.K8sProperties; +import com.oceanbase.odc.service.task.resource.K8sPodResource; +import com.oceanbase.odc.service.task.resource.K8sResourceContext; +import com.oceanbase.odc.service.task.resource.manager.strategy.k8s.K8sResourceContextBuilder; + +/** + * @author longpeng.zlp + * @date 2025/3/19 17:05 + */ +public class K8sResourceUtil { + protected static final Integer MIN_PORT = 30000; + protected static final Integer MAX_PORT = 32767; + + protected static final Random PORT_RANDOM = new Random(); + + public static ResourceWithID createK8sPodResource( + ResourceManager resourceManager, ResourceLocation resourceLocation, String k8sImplType, String imageName, + K8sProperties k8sProperties, long id, + List> portMapper, Integer servicePort) + throws Exception { + K8sResourceContextBuilder contextBuilder = new K8sResourceContextBuilder(k8sProperties, portMapper, + k8sImplType, imageName); + K8sResourceContext k8sResourceContext = + contextBuilder.buildK8sResourceContext(id, resourceLocation); + ResourceWithID k8sPodResource = null; + // allocate resource failed, send alarm event and throws exception + try { + k8sPodResource = resourceManager.create(resourceLocation, + k8sResourceContext); + } catch (Exception e) { + throw e; + } + K8sPodResource podResource = k8sPodResource.getResource(); + podResource.setServicePort(String.valueOf(servicePort)); + return k8sPodResource; + } + + public static K8sPodResource queryIpAndAddress(ResourceManager resourceManager, long resourceID) throws Exception { + ResourceWithID resourceWithID = resourceManager.query(resourceID) + .orElseThrow(() -> new RuntimeException("resource not found, id = " + resourceID)); + return (K8sPodResource) resourceWithID.getResource(); + } + + // ports length should not large than 2767 + public static List> buildRandomPortMapper(Integer... ports) { + List> ret = new ArrayList<>(); + Set allocated = new HashSet<>(); + // same with k8s node range + int min = MIN_PORT; + int max = MAX_PORT; + for (Integer port : ports) { + if (null == port) { + continue; + } + int tryCount = 0; + boolean succeedAllocated = false; + int candidatePort = -1; + // try random loop + while (tryCount++ < 10) { + candidatePort = PORT_RANDOM.nextInt((max - min) + 1) + min; + if (!allocated.contains(candidatePort)) { + succeedAllocated = true; + break; + } + } + // go through + if (!succeedAllocated) { + for (candidatePort = min; candidatePort <= max; candidatePort++) { + if (!allocated.contains(candidatePort)) { + succeedAllocated = true; + break; + } + } + } + if (!succeedAllocated) { + throw new RuntimeException("invalid port allocate"); + } + allocated.add(candidatePort); + ret.add(Pair.of(port, candidatePort)); + } + return ret; + } +} diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/task/config/DefaultSpringJobConfiguration.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/task/config/DefaultSpringJobConfiguration.java index ec7f644a9c..6c3c3a6b76 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/task/config/DefaultSpringJobConfiguration.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/task/config/DefaultSpringJobConfiguration.java @@ -36,6 +36,7 @@ import com.oceanbase.odc.service.task.constants.JobConstants; import com.oceanbase.odc.service.task.dispatch.ImmediateJobDispatcher; import com.oceanbase.odc.service.task.jasypt.JasyptEncryptorConfigProperties; +import com.oceanbase.odc.service.task.resource.AbstractK8sResourceOperatorBuilder; import com.oceanbase.odc.service.task.resource.LocalProcessResource; import com.oceanbase.odc.service.task.resource.SupervisorAgentAllocator; import com.oceanbase.odc.service.task.resource.manager.TaskResourceManager; @@ -47,6 +48,7 @@ import com.oceanbase.odc.service.task.schedule.StartJobRateLimiterSupport; import com.oceanbase.odc.service.task.schedule.provider.DefaultHostUrlProvider; import com.oceanbase.odc.service.task.schedule.provider.DefaultJobImageNameProvider; +import com.oceanbase.odc.service.task.schedule.provider.JobImageNameProvider; import com.oceanbase.odc.service.task.service.SpringTransactionManager; import com.oceanbase.odc.service.task.service.StdTaskFrameworkService; import com.oceanbase.odc.service.task.service.TaskFrameworkService; @@ -129,6 +131,8 @@ protected void initTaskSupervisor(TaskFrameworkProperties taskFrameworkPropertie ResourceAllocateInfoRepository.class), new ProcessResourceManageStrategy(), taskFrameworkProperties); } else { + JobImageNameProvider jobImageNameProvider = JobConfigurationHolder.getJobConfiguration() + .getJobImageNameProvider(); // k8s mode taskResourceManager = new TaskResourceManager(ctx.getBean(SupervisorEndpointRepository.class), ctx.getBean( @@ -138,7 +142,10 @@ protected void initTaskSupervisor(TaskFrameworkProperties taskFrameworkPropertie ctx.getBean(SupervisorEndpointRepository.class), taskFrameworkProperties.isEnableK8sLocalDebugMode() ? this::getPortForLocalDebugSupervisorEndpoint - : taskFrameworkProperties.getK8sProperties()::getSupervisorListenPort), + : taskFrameworkProperties.getK8sProperties()::getSupervisorListenPort, + // default cloud k8s pod mode + AbstractK8sResourceOperatorBuilder.CLOUD_K8S_POD_TYPE, + jobImageNameProvider.provide(), taskFrameworkProperties.isEnableK8sPortMapper()), taskFrameworkProperties); } diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/task/config/DefaultTaskFrameworkProperties.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/task/config/DefaultTaskFrameworkProperties.java index e2e622a508..73c28263ba 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/task/config/DefaultTaskFrameworkProperties.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/task/config/DefaultTaskFrameworkProperties.java @@ -132,4 +132,7 @@ public class DefaultTaskFrameworkProperties implements TaskFrameworkProperties { // keep supervisor endpoint alive for a period if there is no task running on it or release it private int supervisorEndpointKeepAliveSeconds = 300; + + // if k8s port should be mapped, if enabled, then k8s may start pod failed, cause port conflict + private boolean enableK8sPortMapper = false; } diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/task/config/TaskFrameworkConfiguration.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/task/config/TaskFrameworkConfiguration.java index ac9fb63c59..a22119182f 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/task/config/TaskFrameworkConfiguration.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/task/config/TaskFrameworkConfiguration.java @@ -113,8 +113,8 @@ public TaskFrameworkEnabledProperties taskFrameworkEnabledProperties( } @Bean - @ConditionalOnProperty(value = "odc.task-framework.enable-k8s-local-debug-mode", havingValue = "true") - public DefaultNativeK8sOperatorBuilder localDebugK8sOperatorBuilder( + @ConditionalOnProperty(value = "odc.task-framework.k8s-properties.k8s-impl-type", havingValue = "nativeK8sPod") + public DefaultNativeK8sOperatorBuilder localK8sOperatorBuilder( @Autowired TaskFrameworkProperties taskFrameworkProperties, @Autowired ResourceRepository resourceRepository) throws IOException { return new DefaultNativeK8sOperatorBuilder(taskFrameworkProperties, resourceRepository); diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/task/config/TaskFrameworkProperties.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/task/config/TaskFrameworkProperties.java index 1fbb2741bf..204df3341c 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/task/config/TaskFrameworkProperties.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/task/config/TaskFrameworkProperties.java @@ -104,4 +104,5 @@ public interface TaskFrameworkProperties { int getSupervisorEndpointKeepAliveSeconds(); + boolean isEnableK8sPortMapper(); } diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/task/dummy/LocalMockK8sJobClient.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/task/dummy/LocalMockK8sJobClient.java index b5100c4a15..561f988158 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/task/dummy/LocalMockK8sJobClient.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/task/dummy/LocalMockK8sJobClient.java @@ -45,12 +45,15 @@ import com.oceanbase.odc.service.task.supervisor.endpoint.ExecutorEndpoint; import com.oceanbase.odc.service.task.supervisor.endpoint.SupervisorEndpoint; +import lombok.extern.slf4j.Slf4j; + /** * use local process to mock k8s command * * @author longpeng.zlp * @date 2024/8/28 11:18 */ +@Slf4j public class LocalMockK8sJobClient implements K8sJobClientSelector { @Override @@ -69,6 +72,8 @@ public LocalProcessClient() { @Override public K8sPodResource create(K8sResourceContext k8sResourceContext) throws JobException { + log.info("LocalMockK8sJobClient: create with mock client, context={}", + k8sResourceContext.getResourceName()); JobContext jobContext = getJobContext(k8sResourceContext.getExtraData()); if (null != jobContext) { // normal process @@ -83,7 +88,8 @@ public K8sPodResource create(K8sResourceContext k8sResourceContext) throws JobEx k8sResourceContext.type(), executorIdentifier.getNamespace(), executorIdentifier.getExecutorName(), ResourceState.AVAILABLE, - SystemUtils.getLocalIpAddress(), String.valueOf(executorIdentifier.getPort()), + SystemUtils.getLocalIpAddress(), SystemUtils.getLocalIpAddress(), + String.valueOf(executorIdentifier.getPort()), new Date(System.currentTimeMillis())); } else { // supervisor @@ -95,7 +101,7 @@ public K8sPodResource create(K8sResourceContext k8sResourceContext) throws JobEx k8sResourceContext.type(), executorIdentifier.getNamespace(), executorEndpoint.getIdentifier() + "supervisor", ResourceState.AVAILABLE, - SystemUtils.getLocalIpAddress(), + SystemUtils.getLocalIpAddress(), SystemUtils.getLocalIpAddress(), podConfig.getEnvironments().get(JobEnvKeyConstants.ODC_SUPERVISOR_LISTEN_PORT), new Date(System.currentTimeMillis())); } @@ -112,7 +118,8 @@ public Optional get(String namespace, String arn) throws JobExce K8sPodResource ret = new K8sPodResource(ResourceIDUtil.REGION_PROP_NAME, ResourceIDUtil.GROUP_PROP_NAME, AbstractK8sResourceOperatorBuilder.CLOUD_K8S_POD_TYPE, namespace, arn, ResourceState.AVAILABLE, - SystemUtils.getLocalIpAddress(), "8989", new Date(System.currentTimeMillis())); + SystemUtils.getLocalIpAddress(), SystemUtils.getLocalIpAddress(), "8989", + new Date(System.currentTimeMillis())); return Optional.of(ret); } diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/task/resource/AbstractK8sResourceOperatorBuilder.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/task/resource/AbstractK8sResourceOperatorBuilder.java index c8acf4ca0b..dd351760eb 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/task/resource/AbstractK8sResourceOperatorBuilder.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/task/resource/AbstractK8sResourceOperatorBuilder.java @@ -18,7 +18,7 @@ import java.io.IOException; import java.util.Optional; -import org.apache.commons.lang3.tuple.Pair; +import org.apache.commons.lang3.tuple.Triple; import com.oceanbase.odc.common.util.StringUtils; import com.oceanbase.odc.metadb.resource.ResourceEntity; @@ -106,8 +106,8 @@ public ResourceEntity toResourceEntity(K8sPodResource k8sResource) { @Override public K8sPodResource toResource(ResourceEntity resourceEntity, Optional runtimeResource) { - Pair ipAndPort = runtimeResource - .map(r -> Pair.of(r.getPodIpAddress(), r.getServicePort())) + Triple ipAndPort = runtimeResource + .map(r -> Triple.of(r.getPodIpAddress(), r.getHostIpAddress(), r.getServicePort())) .orElse(K8sPodResource.parseIPAndPort(resourceEntity.getEndpoint())); return new K8sPodResource( resourceEntity.getRegion(), @@ -117,6 +117,7 @@ public K8sPodResource toResource(ResourceEntity resourceEntity, Optional parseIPAndPort(String k8sEndPoint) { + public static Triple parseIPAndPort(String k8sEndPoint) { String[] infos = StringUtils.split(k8sEndPoint, "::"); - if (null == infos || infos.length != 6) { + if (null == infos || infos.length != 7) { throw new IllegalStateException( "expect k8s endpoint constructed by k8s::region::namespace::arn::ip::port, but current is " + k8sEndPoint); } - String host = "null".equalsIgnoreCase(infos[4]) ? null : infos[4]; - String port = "null".equalsIgnoreCase(infos[5]) ? null : infos[5]; - return Pair.of(host, port); + String podIp = "null".equalsIgnoreCase(infos[4]) ? null : infos[4]; + String hostIp = "null".equalsIgnoreCase(infos[5]) ? null : infos[5]; + String servicePort = "null".equalsIgnoreCase(infos[5]) ? null : infos[6]; + return Triple.of(podIp, hostIp, servicePort); } public ResourceState resourceState() { diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/task/resource/PodConfig.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/task/resource/PodConfig.java index 75252712c0..e9ccb971e5 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/task/resource/PodConfig.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/task/resource/PodConfig.java @@ -19,6 +19,8 @@ import java.util.List; import java.util.Map; +import org.apache.commons.lang3.tuple.Pair; + import com.oceanbase.odc.service.task.constants.JobConstants; import lombok.Data; @@ -66,4 +68,6 @@ public class PodConfig { private Double nodeCpu; private Long nodeMemInMB; + + private List> portsMapper; } diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/task/resource/client/NativeK8sJobClient.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/task/resource/client/NativeK8sJobClient.java index 4d84496920..20eace0e7c 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/task/resource/client/NativeK8sJobClient.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/task/resource/client/NativeK8sJobClient.java @@ -24,6 +24,7 @@ import java.io.InputStreamReader; import java.io.Reader; import java.sql.Date; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -41,7 +42,6 @@ import com.oceanbase.odc.core.shared.constant.ErrorCodes; import com.oceanbase.odc.service.resource.ResourceState; import com.oceanbase.odc.service.task.config.K8sProperties; -import com.oceanbase.odc.service.task.config.TaskFrameworkProperties; import com.oceanbase.odc.service.task.exception.JobException; import com.oceanbase.odc.service.task.resource.K8sPodResource; import com.oceanbase.odc.service.task.resource.K8sResourceContext; @@ -52,6 +52,7 @@ import io.kubernetes.client.openapi.Configuration; import io.kubernetes.client.openapi.apis.CoreV1Api; import io.kubernetes.client.openapi.models.V1Container; +import io.kubernetes.client.openapi.models.V1ContainerPort; import io.kubernetes.client.openapi.models.V1EnvVar; import io.kubernetes.client.openapi.models.V1ObjectMeta; import io.kubernetes.client.openapi.models.V1Pod; @@ -84,15 +85,9 @@ public NativeK8sJobClient(K8sProperties k8sProperties) throws IOException { this.k8sProperties = k8sProperties; ApiClient apiClient = generateNativeK8sApiClient(k8sProperties); Verify.notNull(apiClient, "k8s api client"); - Configuration.setDefaultApiClient(apiClient); - } - - public static ApiClient generateNativeK8sApiClient( - @NonNull TaskFrameworkProperties taskFrameworkProperties) throws IOException { - if (taskFrameworkProperties.getK8sProperties() == null) { - return null; + if (null == Configuration.getDefaultApiClient()) { + Configuration.setDefaultApiClient(apiClient); } - return generateNativeK8sApiClient(taskFrameworkProperties.getK8sProperties()); } public static ApiClient generateNativeK8sApiClient(@NonNull K8sProperties properties) throws IOException { @@ -122,9 +117,13 @@ public static ApiClient generateNativeK8sApiClient(@NonNull K8sProperties proper @Override public K8sPodResource create(K8sResourceContext k8sResourceContext) throws JobException { PodConfig podConfig = k8sResourceContext.getPodConfig(); - return create(k8sResourceContext.resourceNamespace(), k8sResourceContext.resourceName(), + K8sPodResource ret = create(k8sResourceContext.resourceNamespace(), k8sResourceContext.resourceName(), podConfig.getImage(), podConfig.getCommand(), podConfig); + ret.setRegion(k8sResourceContext.getRegion()); + ret.setGroup(k8sResourceContext.getGroup()); + ret.setType(k8sResourceContext.getType()); + return ret; } protected K8sPodResource create(@NonNull String namespace, @NonNull String name, @NonNull String image, @@ -139,7 +138,8 @@ protected K8sPodResource create(@NonNull String namespace, @NonNull String name, // return pod status return new K8sPodResource(null, null, null, namespace, createdJob.getMetadata().getName(), k8sPodPhaseToResourceState(createdJob.getStatus().getPhase()), - createdJob.getStatus().getPodIP(), String.valueOf(k8sProperties.getExecutorListenPort()), + createdJob.getStatus().getPodIP(), + createdJob.getStatus().getHostIP(), String.valueOf(k8sProperties.getExecutorListenPort()), new Date(System.currentTimeMillis() / 1000)); } catch (ApiException e) { Optional existedPod = null; @@ -181,6 +181,7 @@ public Optional get(@NonNull String namespace, @NonNull String a V1Pod v1Pod = v1PodOptional.get(); K8sPodResource resource = new K8sPodResource(k8sProperties.getRegion(), k8sProperties.getGroup(), null, namespace, arn, k8sPodPhaseToResourceState(v1Pod.getStatus().getPhase()), v1Pod.getStatus().getPodIP(), + v1Pod.getStatus().getHostIP(), String.valueOf(k8sProperties.getExecutorListenPort()), new Date(System.currentTimeMillis() / 1000)); return Optional.of(resource); @@ -209,6 +210,16 @@ private V1Pod getV1Pod(String jobName, String image, List command, PodCo if (CollectionUtils.isNotEmpty(command)) { container.setCommand(command); } + if (CollectionUtils.isNotEmpty(podParam.getPortsMapper())) { + List ports = new ArrayList<>(); + int index = 0; + for (Pair mapper : podParam.getPortsMapper()) { + ports.add(new V1ContainerPort().name("mapper" + index).containerPort(mapper.getLeft()) + .hostPort(mapper.getRight())); + index++; + } + container.setPorts(ports); + } if (podParam.getEnvironments().size() > 0) { List envVars = podParam.getEnvironments().entrySet().stream() diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/task/resource/manager/strategy/k8s/K8SResourceManageStrategy.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/task/resource/manager/strategy/k8s/K8SResourceManageStrategy.java index 63c99fdb37..b9e3e2128b 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/task/resource/manager/strategy/k8s/K8SResourceManageStrategy.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/task/resource/manager/strategy/k8s/K8SResourceManageStrategy.java @@ -25,6 +25,7 @@ import java.util.function.Supplier; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.tuple.Pair; import com.oceanbase.odc.common.util.StringUtils; import com.oceanbase.odc.core.alarm.AlarmEventNames; @@ -33,16 +34,15 @@ import com.oceanbase.odc.metadb.task.ResourceAllocateInfoEntity; import com.oceanbase.odc.metadb.task.SupervisorEndpointEntity; import com.oceanbase.odc.metadb.task.SupervisorEndpointRepository; -import com.oceanbase.odc.service.resource.Resource; import com.oceanbase.odc.service.resource.ResourceID; import com.oceanbase.odc.service.resource.ResourceLocation; import com.oceanbase.odc.service.resource.ResourceManager; import com.oceanbase.odc.service.resource.ResourceWithID; +import com.oceanbase.odc.service.resource.k8s.K8sResourceUtil; import com.oceanbase.odc.service.task.config.K8sProperties; import com.oceanbase.odc.service.task.exception.JobException; import com.oceanbase.odc.service.task.resource.Constants; import com.oceanbase.odc.service.task.resource.K8sPodResource; -import com.oceanbase.odc.service.task.resource.K8sResourceContext; import com.oceanbase.odc.service.task.resource.manager.ResourceManageStrategy; import com.oceanbase.odc.service.task.resource.manager.SupervisorEndpointRepositoryWrap; import com.oceanbase.odc.service.task.supervisor.SupervisorEndpointState; @@ -63,13 +63,20 @@ public class K8SResourceManageStrategy implements ResourceManageStrategy { protected final K8sProperties k8sProperties; protected final Supplier supervisorListenPortProvider; protected final SupervisorEndpointRepositoryWrap supervisorEndpointRepositoryWrap; + protected final String k8sImplType; + protected final String imageName; + protected final boolean enableK8sPortMapper; public K8SResourceManageStrategy(K8sProperties k8sProperties, ResourceManager resourceManager, - SupervisorEndpointRepository supervisorEndpointRepository, Supplier supervisorListenPortProvider) { + SupervisorEndpointRepository supervisorEndpointRepository, Supplier supervisorListenPortProvider, + String k8sImplType, String imageName, boolean enableK8sPortMapper) { this.resourceManager = resourceManager; this.k8sProperties = k8sProperties; this.supervisorListenPortProvider = supervisorListenPortProvider; this.supervisorEndpointRepositoryWrap = new SupervisorEndpointRepositoryWrap(supervisorEndpointRepository); + this.k8sImplType = k8sImplType; + this.imageName = imageName; + this.enableK8sPortMapper = enableK8sPortMapper; } /** @@ -85,26 +92,29 @@ public SupervisorEndpointEntity handleNoResourceAvailable(ResourceAllocateInfoEn ResourceLocation resourceLocation = new ResourceLocation(resourceAllocateInfoEntity.getResourceRegion(), resourceAllocateInfoEntity.getResourceGroup()); int supervisorListenPort = supervisorListenPortProvider.get(); - K8sResourceContextBuilder contextBuilder = new K8sResourceContextBuilder(k8sProperties, supervisorListenPort); - K8sResourceContext k8sResourceContext = - contextBuilder.buildK8sResourceContext(resourceAllocateInfoEntity.getTaskId(), resourceLocation); ResourceWithID k8sPodResource = null; // allocate resource failed, send alarm event and throws exception try { - k8sPodResource = resourceManager.create(resourceLocation, - k8sResourceContext); + List> portMapper = new ArrayList<>(); + if (enableK8sPortMapper) { + portMapper = K8sResourceUtil.buildRandomPortMapper(supervisorListenPort, + k8sProperties.getExecutorListenPort()); + } + k8sPodResource = K8sResourceUtil.createK8sPodResource(resourceManager, resourceLocation, k8sImplType, + imageName, k8sProperties, + resourceAllocateInfoEntity.getTaskId(), portMapper, + supervisorListenPort); } catch (Throwable e) { alarmResourceFailed(resourceAllocateInfoEntity, e); throw new JobException("create resource failed for " + resourceAllocateInfoEntity, e); } - log.info("create k8s pod = {} for allocate entity = {}", k8sPodResource, resourceAllocateInfoEntity); + K8sPodResource podResource = k8sPodResource.getResource(); + // correct it to RESOURCE_NULL_HOST to avoid null check + if (StringUtils.isEmpty(podResource.getPodIpAddress())) { + podResource.setPodIpAddress(Constants.RESOURCE_NULL_HOST); + } // save to db failed, try release resource try { - K8sPodResource podResource = k8sPodResource.getResource(); - podResource.setServicePort(String.valueOf(supervisorListenPort)); - if (StringUtils.isEmpty(podResource.getPodIpAddress())) { - podResource.setPodIpAddress(Constants.RESOURCE_NULL_HOST); - } // create with load 1 to let resource not released return supervisorEndpointRepositoryWrap.save(podResource, k8sPodResource.getId(), 0); } catch (Throwable e) { @@ -147,14 +157,13 @@ public SupervisorEndpointEntity detectIfEndpointIsAvailable(ResourceAllocateInfo @Override public void refreshSupervisorEndpoint(SupervisorEndpointEntity endpoint) { try { - ResourceWithID resourceWithID = resourceManager.query(endpoint.getResourceID()) - .orElseThrow(() -> new RuntimeException("resource not found, id = " + endpoint.getResourceID())); - K8sPodResource podResource = (K8sPodResource) resourceWithID.getResource(); - if (podResource.getPodIpAddress() != null) { - endpoint.setHost(podResource.getPodIpAddress()); + String podIpAndAddress = + K8sResourceUtil.queryIpAndAddress(resourceManager, endpoint.getResourceID()).getPodIpAddress(); + if (podIpAndAddress != null) { + endpoint.setHost(podIpAndAddress); supervisorEndpointRepositoryWrap.updateEndpointHost(endpoint); log.info("refresh pod ip address success, id = {}, host =z {}", endpoint.getResourceID(), - podResource.getPodIpAddress()); + podIpAndAddress); } } catch (Exception e) { log.warn("get pod ip address failed, resource id={}", endpoint.getResourceID(), e); diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/task/resource/manager/strategy/k8s/K8sResourceContextBuilder.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/task/resource/manager/strategy/k8s/K8sResourceContextBuilder.java index 20653b08db..90da448323 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/task/resource/manager/strategy/k8s/K8sResourceContextBuilder.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/task/resource/manager/strategy/k8s/K8sResourceContextBuilder.java @@ -17,20 +17,21 @@ import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; +import org.apache.commons.lang3.tuple.Pair; + import com.oceanbase.odc.common.util.StringUtils; import com.oceanbase.odc.common.util.SystemUtils; import com.oceanbase.odc.service.resource.ResourceLocation; -import com.oceanbase.odc.service.task.config.JobConfigurationHolder; import com.oceanbase.odc.service.task.config.K8sProperties; import com.oceanbase.odc.service.task.constants.JobConstants; import com.oceanbase.odc.service.task.constants.JobEnvKeyConstants; -import com.oceanbase.odc.service.task.resource.AbstractK8sResourceOperatorBuilder; import com.oceanbase.odc.service.task.resource.K8sResourceContext; import com.oceanbase.odc.service.task.resource.PodConfig; -import com.oceanbase.odc.service.task.schedule.provider.JobImageNameProvider; import com.oceanbase.odc.service.task.util.JobUtils; /** @@ -40,11 +41,20 @@ public class K8sResourceContextBuilder { private static final DateTimeFormatter DTF = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"); protected final K8sProperties k8sProperties; - protected final Integer supervisorListenPort; + // port mapper to host machine, null or 0 means no mapper needed + protected final List> portMapper = new ArrayList<>(); + protected final String k8sImplType; + protected final String imageName; - public K8sResourceContextBuilder(K8sProperties k8sProperties, Integer supervisorListenPort) { + public K8sResourceContextBuilder(K8sProperties k8sProperties, List> portMapper, + String k8sImplType, + String imageName) { this.k8sProperties = k8sProperties; - this.supervisorListenPort = supervisorListenPort; + if (null != portMapper) { + this.portMapper.addAll(portMapper); + } + this.k8sImplType = k8sImplType; + this.imageName = imageName; } public K8sResourceContext buildK8sResourceContext(Long taskID, ResourceLocation resourceLocation) { @@ -52,7 +62,7 @@ public K8sResourceContext buildK8sResourceContext(Long taskID, ResourceLocation PodConfig podConfig = createDefaultPodConfig(k8sProperties); return new K8sResourceContext(podConfig, jobName, resourceLocation.getRegion(), resourceLocation.getGroup(), - AbstractK8sResourceOperatorBuilder.CLOUD_K8S_POD_TYPE, null); + k8sImplType, null); } private PodConfig createDefaultPodConfig(K8sProperties k8sProperties) { @@ -60,9 +70,7 @@ private PodConfig createDefaultPodConfig(K8sProperties k8sProperties) { if (StringUtils.isNotBlank(k8sProperties.getNamespace())) { podConfig.setNamespace(k8sProperties.getNamespace()); } - JobImageNameProvider jobImageNameProvider = JobConfigurationHolder.getJobConfiguration() - .getJobImageNameProvider(); - podConfig.setImage(jobImageNameProvider.provide()); + podConfig.setImage(imageName); podConfig.setRegion(StringUtils.isNotBlank(k8sProperties.getRegion()) ? k8sProperties.getRegion() : SystemUtils.getEnvOrProperty(JobEnvKeyConstants.OB_ARN_PARTITION)); @@ -78,18 +86,20 @@ private PodConfig createDefaultPodConfig(K8sProperties k8sProperties) { podConfig.setNodeCpu(k8sProperties.getNodeCpu()); podConfig.setNodeMemInMB(k8sProperties.getNodeMemInMB()); podConfig.setPodPendingTimeoutSeconds(k8sProperties.getPodPendingTimeoutSeconds()); - podConfig.setEnvironments(buildEnv()); + podConfig.setEnvironments(buildEnv(k8sProperties)); + podConfig.setPortsMapper(portMapper); return podConfig; } + public String generateK8sPodName(long jobID) { return JobConstants.TEMPLATE_JOB_NAME_PREFIX + "supervisor-" + jobID + "-" + LocalDateTime.now().format(DTF); } - public Map buildEnv() { + public Map buildEnv(K8sProperties k8sProperties) { Map env = new HashMap<>(); env.put(JobEnvKeyConstants.ODC_SUPERVISOR_LISTEN_PORT, - String.valueOf(supervisorListenPort)); + String.valueOf(k8sProperties.getSupervisorListenPort())); env.put(JobEnvKeyConstants.ODC_LOG_DIRECTORY, JobUtils.getLogBasePath(k8sProperties.getMountPath())); return env; } diff --git a/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/OnlineSchemaChangeFlowableTaskTest.java b/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/OnlineSchemaChangeFlowableTaskTest.java index 2531d4ee95..15900736f1 100644 --- a/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/OnlineSchemaChangeFlowableTaskTest.java +++ b/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/OnlineSchemaChangeFlowableTaskTest.java @@ -34,7 +34,7 @@ import com.oceanbase.odc.metadb.task.TaskEntity; import com.oceanbase.odc.service.flow.task.model.OnlineSchemaChangeTaskResult; import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeScheduleTaskResult; -import com.oceanbase.odc.service.onlineschemachange.oms.enums.OmsStepName; +import com.oceanbase.odc.service.onlineschemachange.oms.enums.OscStepName; import com.oceanbase.odc.service.task.TaskService; /** @@ -109,15 +109,15 @@ public void testOnlineSchemaChangeFlowableTaskUpdateProgress() { public void testScheduleTasksUpdateHint() { MockOSCFlowTask mockOSCFlowTask = new MockOSCFlowTask(); List scheduleTaskEntities = Arrays.asList( - createScheduleTaskEntity(1, true, OmsStepName.FULL_TRANSFER.name()), - createScheduleTaskEntity(2, false, OmsStepName.INCR_TRANSFER.name()), + createScheduleTaskEntity(1, true, OscStepName.FULL_TRANSFER.name()), + createScheduleTaskEntity(2, false, OscStepName.INCR_TRANSFER.name()), createScheduleTaskEntity(3, false, null)); OnlineSchemaChangeFlowableTask.ScheduleTasksUpdateHint scheduleTasksUpdateHint = mockOSCFlowTask.getScheduleTasksUpdateHint(scheduleTaskEntities); Assert.assertEquals(scheduleTasksUpdateHint.getEnableManualSwapTableFlagCounts(), 1); Assert.assertEquals(scheduleTasksUpdateHint.getTaskStepsMap().size(), 2); - Assert.assertEquals(scheduleTasksUpdateHint.getTaskStepsMap().get(1L), OmsStepName.FULL_TRANSFER.name()); - Assert.assertEquals(scheduleTasksUpdateHint.getTaskStepsMap().get(2L), OmsStepName.INCR_TRANSFER.name()); + Assert.assertEquals(scheduleTasksUpdateHint.getTaskStepsMap().get(1L), OscStepName.FULL_TRANSFER.name()); + Assert.assertEquals(scheduleTasksUpdateHint.getTaskStepsMap().get(2L), OscStepName.INCR_TRANSFER.name()); Assert.assertEquals(scheduleTasksUpdateHint.getTaskStatusMap().get(2L), TaskStatus.RUNNING.name()); Map taskMap = new HashMap() { { @@ -132,9 +132,9 @@ public void testScheduleTasksUpdateHint() { scheduleTasksUpdateHint.hasDiff(new OnlineSchemaChangeFlowableTask.ScheduleTasksUpdateHint(0))); Assert.assertTrue(scheduleTasksUpdateHint.hasDiff(new OnlineSchemaChangeFlowableTask.ScheduleTasksUpdateHint(1, - Collections.singletonMap(1L, OmsStepName.FULL_TRANSFER.name()), taskMap))); + Collections.singletonMap(1L, OscStepName.FULL_TRANSFER.name()), taskMap))); Map muted = new HashMap<>(scheduleTasksUpdateHint.getTaskStepsMap()); - muted.put(2L, OmsStepName.APP_SWITCH.name()); + muted.put(2L, OscStepName.APP_SWITCH.name()); Assert.assertTrue( scheduleTasksUpdateHint .hasDiff(new OnlineSchemaChangeFlowableTask.ScheduleTasksUpdateHint(1, muted, taskMap))); diff --git a/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/CleanResourcesActionBaseTest.java b/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/CleanResourcesActionBaseTest.java new file mode 100644 index 0000000000..cb9a336ea4 --- /dev/null +++ b/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/CleanResourcesActionBaseTest.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2025 OceanBase. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.oceanbase.odc.service.onlineschemachange.oscfms.action; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentMatchers; +import org.mockito.Mockito; + +import com.oceanbase.odc.common.json.JsonUtils; +import com.oceanbase.odc.core.shared.constant.DialectType; +import com.oceanbase.odc.core.shared.constant.TaskErrorStrategy; +import com.oceanbase.odc.core.shared.constant.TaskStatus; +import com.oceanbase.odc.metadb.schedule.ScheduleEntity; +import com.oceanbase.odc.metadb.schedule.ScheduleTaskEntity; +import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeParameters; +import com.oceanbase.odc.service.onlineschemachange.oms.enums.OmsProjectStatusEnum; +import com.oceanbase.odc.service.onlineschemachange.oms.openapi.OmsProjectOpenApiService; +import com.oceanbase.odc.service.onlineschemachange.oms.response.OmsProjectProgressResponse; +import com.oceanbase.odc.service.onlineschemachange.oscfms.OscActionContext; +import com.oceanbase.odc.service.onlineschemachange.oscfms.OscActionResult; +import com.oceanbase.odc.service.onlineschemachange.oscfms.OscTestUtil; +import com.oceanbase.odc.service.onlineschemachange.oscfms.action.oms.OmsCleanResourcesAction; +import com.oceanbase.odc.service.onlineschemachange.oscfms.state.OscStates; + +/** + * @author longpeng.zlp + * @date 2024/7/30 13:50 + * @since 4.3.1 + */ +public class CleanResourcesActionBaseTest { + private OmsProjectOpenApiService omsProjectOpenApiService; + + + @Before + public void init() { + omsProjectOpenApiService = Mockito.mock(OmsProjectOpenApiService.class); + Mockito.when(omsProjectOpenApiService.createProject(ArgumentMatchers.any())).thenReturn("testProjectID"); + } + + @Test + public void testDeterminateNextState1() { + OmsProjectProgressResponse ret = new OmsProjectProgressResponse(); + ret.setStatus(OmsProjectStatusEnum.RELEASED); + Mockito.when(omsProjectOpenApiService.describeProjectProgress(ArgumentMatchers.any())).thenReturn(ret); + OmsCleanResourcesAction omsCleanResourcesAction = new OmsCleanResourcesAction(omsProjectOpenApiService); + ScheduleEntity schedule = OscTestUtil.createScheduleEntity(); + ScheduleTaskEntity scheduleTask = OscTestUtil.createScheduleTaskEntity(TaskStatus.DONE); + OscActionResult actionResult = omsCleanResourcesAction.determinateNextState(scheduleTask, schedule); + // switch to yield if done context + Assert.assertEquals(actionResult.getNextState(), OscStates.YIELD_CONTEXT.getState()); + } + + @Test + public void testDeterminateNextState2() { + OmsProjectProgressResponse ret = new OmsProjectProgressResponse(); + ret.setStatus(OmsProjectStatusEnum.RELEASED); + Mockito.when(omsProjectOpenApiService.describeProjectProgress(ArgumentMatchers.any())).thenReturn(ret); + OmsCleanResourcesAction omsCleanResourcesAction = new OmsCleanResourcesAction(omsProjectOpenApiService); + ScheduleEntity schedule = OscTestUtil.createScheduleEntity(); + ScheduleTaskEntity scheduleTask = OscTestUtil.createScheduleTaskEntity(TaskStatus.CANCELED); + OscActionResult actionResult = omsCleanResourcesAction.determinateNextState(scheduleTask, schedule); + // switch to complete if canceled + Assert.assertEquals(actionResult.getNextState(), OscStates.COMPLETE.getState()); + } + + @Test + public void testDeterminateNextState3() { + OmsProjectProgressResponse ret = new OmsProjectProgressResponse(); + ret.setStatus(OmsProjectStatusEnum.RELEASED); + Mockito.when(omsProjectOpenApiService.describeProjectProgress(ArgumentMatchers.any())).thenReturn(ret); + OmsCleanResourcesAction omsCleanResourcesAction = new OmsCleanResourcesAction(omsProjectOpenApiService); + ScheduleEntity schedule = OscTestUtil.createScheduleEntity(); + OnlineSchemaChangeParameters onlineSchemaChangeParameters = new OnlineSchemaChangeParameters(); + onlineSchemaChangeParameters.setErrorStrategy(TaskErrorStrategy.CONTINUE); + schedule.setJobParametersJson(JsonUtils.toJson(onlineSchemaChangeParameters)); + ScheduleTaskEntity scheduleTask = OscTestUtil.createScheduleTaskEntity(TaskStatus.FAILED); + OscActionResult actionResult = omsCleanResourcesAction.determinateNextState(scheduleTask, schedule); + // switch to yield context if ignore error + Assert.assertEquals(actionResult.getNextState(), OscStates.YIELD_CONTEXT.getState()); + } + + @Test + public void testDeterminateNextState4() { + OmsProjectProgressResponse ret = new OmsProjectProgressResponse(); + ret.setStatus(OmsProjectStatusEnum.RELEASED); + Mockito.when(omsProjectOpenApiService.describeProjectProgress(ArgumentMatchers.any())).thenReturn(ret); + OmsCleanResourcesAction omsCleanResourcesAction = new OmsCleanResourcesAction(omsProjectOpenApiService); + ScheduleEntity schedule = OscTestUtil.createScheduleEntity(); + OnlineSchemaChangeParameters onlineSchemaChangeParameters = new OnlineSchemaChangeParameters(); + onlineSchemaChangeParameters.setErrorStrategy(TaskErrorStrategy.ABORT); + schedule.setJobParametersJson(JsonUtils.toJson(onlineSchemaChangeParameters)); + ScheduleTaskEntity scheduleTask = OscTestUtil.createScheduleTaskEntity(TaskStatus.FAILED); + OscActionResult actionResult = omsCleanResourcesAction.determinateNextState(scheduleTask, schedule); + // switch to complete if not ignore error + Assert.assertEquals(actionResult.getNextState(), OscStates.COMPLETE.getState()); + } + + @Test + public void testOmsCleanResourceAction() throws Exception { + OmsProjectProgressResponse ret = new OmsProjectProgressResponse(); + ret.setStatus(OmsProjectStatusEnum.RUNNING); + Mockito.when(omsProjectOpenApiService.describeProjectProgress(ArgumentMatchers.any())).thenReturn(ret); + OmsCleanResourcesAction omsCleanResourcesAction = new OmsCleanResourcesAction(omsProjectOpenApiService); + OscActionContext oscActionContext = OscTestUtil.createOcsActionContext(DialectType.OB_MYSQL, + OscStates.CLEAN_RESOURCE.getState(), TaskStatus.RUNNING); + oscActionContext.getTaskParameter().setOmsProjectId("omsProjectID"); + oscActionContext.getTaskParameter().setUid("omsUid"); + OscActionResult oscActionResult = omsCleanResourcesAction.execute(oscActionContext); + Assert.assertEquals(oscActionResult.getNextState(), OscStates.CLEAN_RESOURCE.getState()); + } +} diff --git a/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsMonitorDataTaskActionTest.java b/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/OmsMonitorDataTaskActionTest.java similarity index 96% rename from server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsMonitorDataTaskActionTest.java rename to server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/OmsMonitorDataTaskActionTest.java index fc77d5dd1e..da4622e971 100644 --- a/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsMonitorDataTaskActionTest.java +++ b/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/OmsMonitorDataTaskActionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 OceanBase. + * Copyright (c) 2025 OceanBase. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.oceanbase.odc.service.onlineschemachange.oscfms.action.oms; +package com.oceanbase.odc.service.onlineschemachange.oscfms.action; import org.junit.Assert; import org.junit.Before; @@ -34,7 +34,8 @@ import com.oceanbase.odc.service.onlineschemachange.oscfms.OscActionContext; import com.oceanbase.odc.service.onlineschemachange.oscfms.OscActionResult; import com.oceanbase.odc.service.onlineschemachange.oscfms.OscTestUtil; -import com.oceanbase.odc.service.onlineschemachange.oscfms.action.oms.ProjectStepResultChecker.ProjectStepResult; +import com.oceanbase.odc.service.onlineschemachange.oscfms.action.ProjectStepResult; +import com.oceanbase.odc.service.onlineschemachange.oscfms.action.oms.OmsMonitorDataTaskAction; import com.oceanbase.odc.service.onlineschemachange.oscfms.state.OscStates; /** @@ -67,7 +68,7 @@ public void testHandleOmsProjectStepResultAutoReady() { result.setFullVerificationResult(FullVerificationResult.UNCHECK); OmsMonitorDataTaskAction omsMonitorDataTaskAction = new OmsMonitorDataTaskAction(projectOpenApiService, onlineSchemaChangeProperties); - OscActionResult actionResult = omsMonitorDataTaskAction.handleOmsProjectStepResult(context, + OscActionResult actionResult = omsMonitorDataTaskAction.handleProjectStepResult(context, createProjectStepResult(TaskStatus.DONE), result, SwapTableType.AUTO, scheduleTask); Assert.assertEquals(actionResult.getNextState(), OscStates.SWAP_TABLE.getState()); @@ -82,7 +83,7 @@ public void testHandleOmsProjectStepResultManualReady() { result.setManualSwapTableStarted(false); OmsMonitorDataTaskAction omsMonitorDataTaskAction = new OmsMonitorDataTaskAction(projectOpenApiService, onlineSchemaChangeProperties); - OscActionResult actionResult = omsMonitorDataTaskAction.handleOmsProjectStepResult(context, + OscActionResult actionResult = omsMonitorDataTaskAction.handleProjectStepResult(context, createProjectStepResult(TaskStatus.DONE), result, SwapTableType.MANUAL, scheduleTask); Assert.assertEquals(actionResult.getNextState(), OscStates.MONITOR_DATA_TASK.getState()); @@ -98,7 +99,7 @@ public void testHandleOmsProjectStepResultManualReady2() { result.setManualSwapTableStarted(true); OmsMonitorDataTaskAction omsMonitorDataTaskAction = new OmsMonitorDataTaskAction(projectOpenApiService, onlineSchemaChangeProperties); - OscActionResult actionResult = omsMonitorDataTaskAction.handleOmsProjectStepResult(context, + OscActionResult actionResult = omsMonitorDataTaskAction.handleProjectStepResult(context, createProjectStepResult(TaskStatus.DONE), result, SwapTableType.MANUAL, scheduleTask); Assert.assertEquals(actionResult.getNextState(), OscStates.SWAP_TABLE.getState()); @@ -112,7 +113,7 @@ public void testHandleOmsProjectStepResultNotReady() { result.setFullVerificationResult(FullVerificationResult.UNCHECK); OmsMonitorDataTaskAction omsMonitorDataTaskAction = new OmsMonitorDataTaskAction(projectOpenApiService, onlineSchemaChangeProperties); - OscActionResult actionResult = omsMonitorDataTaskAction.handleOmsProjectStepResult(context, + OscActionResult actionResult = omsMonitorDataTaskAction.handleProjectStepResult(context, createProjectStepResult(TaskStatus.DONE), result, SwapTableType.AUTO, scheduleTask); Assert.assertEquals(actionResult.getNextState(), OscStates.MONITOR_DATA_TASK.getState()); @@ -124,7 +125,7 @@ public void testHandleOmsProjectStepResultNotReady1() { OnlineSchemaChangeScheduleTaskResult result = OscTestUtil.createTaskResult(DialectType.OB_MYSQL); OmsMonitorDataTaskAction omsMonitorDataTaskAction = new OmsMonitorDataTaskAction(projectOpenApiService, onlineSchemaChangeProperties); - OscActionResult actionResult = omsMonitorDataTaskAction.handleOmsProjectStepResult(context, + OscActionResult actionResult = omsMonitorDataTaskAction.handleProjectStepResult(context, createProjectStepResult(TaskStatus.RUNNING), result, SwapTableType.AUTO, scheduleTask); Assert.assertEquals(actionResult.getNextState(), OscStates.MONITOR_DATA_TASK.getState()); @@ -136,7 +137,7 @@ public void testHandleOmsProjectStepResultNotReady2() { OnlineSchemaChangeScheduleTaskResult result = OscTestUtil.createTaskResult(DialectType.OB_MYSQL); OmsMonitorDataTaskAction omsMonitorDataTaskAction = new OmsMonitorDataTaskAction(projectOpenApiService, onlineSchemaChangeProperties); - OscActionResult actionResult = omsMonitorDataTaskAction.handleOmsProjectStepResult(context, + OscActionResult actionResult = omsMonitorDataTaskAction.handleProjectStepResult(context, createProjectStepResult(TaskStatus.FAILED), result, SwapTableType.AUTO, scheduleTask); Assert.assertEquals(actionResult.getNextState(), OscStates.MONITOR_DATA_TASK.getState()); diff --git a/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcCreateDataTaskActionTest.java b/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcCreateDataTaskActionTest.java new file mode 100644 index 0000000000..6be8cef838 --- /dev/null +++ b/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcCreateDataTaskActionTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2025 OceanBase. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.oceanbase.odc.service.onlineschemachange.oscfms.action.odc; + +import java.sql.Date; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentMatchers; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + +import com.oceanbase.odc.service.config.SystemConfigService; +import com.oceanbase.odc.service.onlineschemachange.configuration.OnlineSchemaChangeProperties; +import com.oceanbase.odc.service.onlineschemachange.configuration.OnlineSchemaChangeProperties.OmsProperties; +import com.oceanbase.odc.service.onlineschemachange.oscfms.action.oms.OmsRequestUtil; +import com.oceanbase.odc.service.resource.ResourceManager; +import com.oceanbase.odc.service.resource.ResourceState; +import com.oceanbase.odc.service.task.resource.K8sPodResource; +import com.sun.jna.platform.win32.OaIdl.DATE; + +/** + * @author longpeng.zlp + * @date 2025/4/2 16:45 + */ +public class OdcCreateDataTaskActionTest { + private OdcCreateDataTaskAction createDataTaskAction; + private SystemConfigService systemConfigService; + private ResourceManager resourceManager; + private OnlineSchemaChangeProperties onlineSchemaChangeProperties; + private K8sPodResource podResource; + + @Before + public void init() { + onlineSchemaChangeProperties = new OnlineSchemaChangeProperties(); + onlineSchemaChangeProperties.setEnableFullVerify(false); + OmsProperties omsProperties = new OmsProperties(); + omsProperties.setUrl("127.0.0.1:8089"); + omsProperties.setRegion("default"); + omsProperties.setAuthorization("auth"); + onlineSchemaChangeProperties.setOms(omsProperties); + systemConfigService = Mockito.mock(SystemConfigService.class); + resourceManager = Mockito.mock(ResourceManager.class); + createDataTaskAction = new OdcCreateDataTaskAction(systemConfigService, resourceManager, onlineSchemaChangeProperties); + podResource = new K8sPodResource("local", "local", "nativeK8s", "namespace", + "arn", ResourceState.CREATING, null, null, null, new Date(System.currentTimeMillis() / 1000)); + } + + @Test + public void testChooseUrlNotReadyWithNoIpReturned() { + Assert.assertNull(createDataTaskAction.tryChooseUrl(podResource, -1)); + } + + @Test + public void testChooseUrlNotReadyWithIpNotReached() { + try (MockedStatic commandUtil = Mockito.mockStatic(OscCommandUtil.class);) { + podResource.setPodIpAddress("127.0.0.1"); + commandUtil.when(() -> OscCommandUtil.isOSCMigrateSupervisorAlive(ArgumentMatchers.any())).thenReturn(false); + Assert.assertNull(createDataTaskAction.tryChooseUrl(podResource, -1)); + podResource.setHostIpAddress("127.0.0.1"); + Assert.assertNull(createDataTaskAction.tryChooseUrl(podResource, -1)); + Assert.assertNull(createDataTaskAction.tryChooseUrl(podResource, 100)); + } + } + + @Test + public void testChooseUrlReady() { + try (MockedStatic commandUtil = Mockito.mockStatic(OscCommandUtil.class);) { + podResource.setPodIpAddress("127.0.0.1"); + commandUtil.when(() -> OscCommandUtil.isOSCMigrateSupervisorAlive(ArgumentMatchers.any())).thenReturn(true); + Assert.assertEquals(createDataTaskAction.tryChooseUrl(podResource, -1), "http://127.0.0.1:18001"); + podResource.setPodIpAddress(null); + podResource.setHostIpAddress("127.0.0.1"); + Assert.assertEquals(createDataTaskAction.tryChooseUrl(podResource, 100), "http://127.0.0.1:100"); + } + } + +} diff --git a/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsCleanResourcesActionTest.java b/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsCleanResourcesActionTest.java index 96f4ab873c..9e0e098b82 100644 --- a/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsCleanResourcesActionTest.java +++ b/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsCleanResourcesActionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 OceanBase. + * Copyright (c) 2025 OceanBase. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.oceanbase.odc.service.onlineschemachange.oscfms.action.oms; import org.junit.Assert; @@ -21,25 +22,13 @@ import org.mockito.ArgumentMatchers; import org.mockito.Mockito; -import com.oceanbase.odc.common.json.JsonUtils; -import com.oceanbase.odc.core.shared.constant.DialectType; -import com.oceanbase.odc.core.shared.constant.TaskErrorStrategy; -import com.oceanbase.odc.core.shared.constant.TaskStatus; -import com.oceanbase.odc.metadb.schedule.ScheduleEntity; -import com.oceanbase.odc.metadb.schedule.ScheduleTaskEntity; -import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeParameters; import com.oceanbase.odc.service.onlineschemachange.oms.enums.OmsProjectStatusEnum; import com.oceanbase.odc.service.onlineschemachange.oms.openapi.OmsProjectOpenApiService; import com.oceanbase.odc.service.onlineschemachange.oms.response.OmsProjectProgressResponse; -import com.oceanbase.odc.service.onlineschemachange.oscfms.OscActionContext; -import com.oceanbase.odc.service.onlineschemachange.oscfms.OscActionResult; -import com.oceanbase.odc.service.onlineschemachange.oscfms.OscTestUtil; -import com.oceanbase.odc.service.onlineschemachange.oscfms.state.OscStates; /** * @author longpeng.zlp - * @date 2024/7/30 13:50 - * @since 4.3.1 + * @date 2025/4/2 16:30 */ public class OmsCleanResourcesActionTest { private OmsProjectOpenApiService omsProjectOpenApiService; @@ -51,6 +40,7 @@ public void init() { Mockito.when(omsProjectOpenApiService.createProject(ArgumentMatchers.any())).thenReturn("testProjectID"); } + @Test public void testCheckAndReleaseProjectRunning() { OmsProjectProgressResponse ret = new OmsProjectProgressResponse(); @@ -72,76 +62,4 @@ public void testCheckAndReleaseProjectDone() { Assert.assertTrue(omsCleanResourcesAction.checkAndReleaseProject("omsProjectID", "uid")); Mockito.verify(omsProjectOpenApiService, Mockito.times(0)).releaseProject(ArgumentMatchers.any()); } - - @Test - public void testDeterminateNextState1() { - OmsProjectProgressResponse ret = new OmsProjectProgressResponse(); - ret.setStatus(OmsProjectStatusEnum.RELEASED); - Mockito.when(omsProjectOpenApiService.describeProjectProgress(ArgumentMatchers.any())).thenReturn(ret); - OmsCleanResourcesAction omsCleanResourcesAction = new OmsCleanResourcesAction(omsProjectOpenApiService); - ScheduleEntity schedule = OscTestUtil.createScheduleEntity(); - ScheduleTaskEntity scheduleTask = OscTestUtil.createScheduleTaskEntity(TaskStatus.DONE); - OscActionResult actionResult = omsCleanResourcesAction.determinateNextState(scheduleTask, schedule); - // switch to yield if done context - Assert.assertEquals(actionResult.getNextState(), OscStates.YIELD_CONTEXT.getState()); - } - - @Test - public void testDeterminateNextState2() { - OmsProjectProgressResponse ret = new OmsProjectProgressResponse(); - ret.setStatus(OmsProjectStatusEnum.RELEASED); - Mockito.when(omsProjectOpenApiService.describeProjectProgress(ArgumentMatchers.any())).thenReturn(ret); - OmsCleanResourcesAction omsCleanResourcesAction = new OmsCleanResourcesAction(omsProjectOpenApiService); - ScheduleEntity schedule = OscTestUtil.createScheduleEntity(); - ScheduleTaskEntity scheduleTask = OscTestUtil.createScheduleTaskEntity(TaskStatus.CANCELED); - OscActionResult actionResult = omsCleanResourcesAction.determinateNextState(scheduleTask, schedule); - // switch to complete if canceled - Assert.assertEquals(actionResult.getNextState(), OscStates.COMPLETE.getState()); - } - - @Test - public void testDeterminateNextState3() { - OmsProjectProgressResponse ret = new OmsProjectProgressResponse(); - ret.setStatus(OmsProjectStatusEnum.RELEASED); - Mockito.when(omsProjectOpenApiService.describeProjectProgress(ArgumentMatchers.any())).thenReturn(ret); - OmsCleanResourcesAction omsCleanResourcesAction = new OmsCleanResourcesAction(omsProjectOpenApiService); - ScheduleEntity schedule = OscTestUtil.createScheduleEntity(); - OnlineSchemaChangeParameters onlineSchemaChangeParameters = new OnlineSchemaChangeParameters(); - onlineSchemaChangeParameters.setErrorStrategy(TaskErrorStrategy.CONTINUE); - schedule.setJobParametersJson(JsonUtils.toJson(onlineSchemaChangeParameters)); - ScheduleTaskEntity scheduleTask = OscTestUtil.createScheduleTaskEntity(TaskStatus.FAILED); - OscActionResult actionResult = omsCleanResourcesAction.determinateNextState(scheduleTask, schedule); - // switch to yield context if ignore error - Assert.assertEquals(actionResult.getNextState(), OscStates.YIELD_CONTEXT.getState()); - } - - @Test - public void testDeterminateNextState4() { - OmsProjectProgressResponse ret = new OmsProjectProgressResponse(); - ret.setStatus(OmsProjectStatusEnum.RELEASED); - Mockito.when(omsProjectOpenApiService.describeProjectProgress(ArgumentMatchers.any())).thenReturn(ret); - OmsCleanResourcesAction omsCleanResourcesAction = new OmsCleanResourcesAction(omsProjectOpenApiService); - ScheduleEntity schedule = OscTestUtil.createScheduleEntity(); - OnlineSchemaChangeParameters onlineSchemaChangeParameters = new OnlineSchemaChangeParameters(); - onlineSchemaChangeParameters.setErrorStrategy(TaskErrorStrategy.ABORT); - schedule.setJobParametersJson(JsonUtils.toJson(onlineSchemaChangeParameters)); - ScheduleTaskEntity scheduleTask = OscTestUtil.createScheduleTaskEntity(TaskStatus.FAILED); - OscActionResult actionResult = omsCleanResourcesAction.determinateNextState(scheduleTask, schedule); - // switch to complete if not ignore error - Assert.assertEquals(actionResult.getNextState(), OscStates.COMPLETE.getState()); - } - - @Test - public void testOmsCleanResourceAction() throws Exception { - OmsProjectProgressResponse ret = new OmsProjectProgressResponse(); - ret.setStatus(OmsProjectStatusEnum.RUNNING); - Mockito.when(omsProjectOpenApiService.describeProjectProgress(ArgumentMatchers.any())).thenReturn(ret); - OmsCleanResourcesAction omsCleanResourcesAction = new OmsCleanResourcesAction(omsProjectOpenApiService); - OscActionContext oscActionContext = OscTestUtil.createOcsActionContext(DialectType.OB_MYSQL, - OscStates.CLEAN_RESOURCE.getState(), TaskStatus.RUNNING); - oscActionContext.getTaskParameter().setOmsProjectId("omsProjectID"); - oscActionContext.getTaskParameter().setUid("omsUid"); - OscActionResult oscActionResult = omsCleanResourcesAction.execute(oscActionContext); - Assert.assertEquals(oscActionResult.getNextState(), OscStates.CLEAN_RESOURCE.getState()); - } } diff --git a/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsSwapTableActionTest.java b/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsSwapTableActionTest.java index b7e446f6c9..1c8c8bb60a 100644 --- a/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsSwapTableActionTest.java +++ b/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsSwapTableActionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 OceanBase. + * Copyright (c) 2025 OceanBase. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,9 +37,10 @@ import com.oceanbase.odc.service.onlineschemachange.configuration.OnlineSchemaChangeProperties; import com.oceanbase.odc.service.onlineschemachange.configuration.OnlineSchemaChangeProperties.OmsProperties; import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeParameters; +import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeScheduleTaskParameters; import com.oceanbase.odc.service.onlineschemachange.oms.enums.OmsProjectStatusEnum; -import com.oceanbase.odc.service.onlineschemachange.oms.enums.OmsStepName; import com.oceanbase.odc.service.onlineschemachange.oms.enums.OmsStepStatus; +import com.oceanbase.odc.service.onlineschemachange.oms.enums.OscStepName; import com.oceanbase.odc.service.onlineschemachange.oms.openapi.OmsProjectOpenApiService; import com.oceanbase.odc.service.onlineschemachange.oms.response.OmsProjectProgressResponse; import com.oceanbase.odc.service.onlineschemachange.oms.response.OmsProjectStepVO; @@ -47,7 +48,9 @@ import com.oceanbase.odc.service.onlineschemachange.oscfms.OscActionResult; import com.oceanbase.odc.service.onlineschemachange.oscfms.OscTestUtil; import com.oceanbase.odc.service.onlineschemachange.oscfms.action.ConnectionProvider; -import com.oceanbase.odc.service.onlineschemachange.oscfms.action.oms.ProjectStepResultChecker.ProjectStepResult; +import com.oceanbase.odc.service.onlineschemachange.oscfms.action.ProjectStepResult; +import com.oceanbase.odc.service.onlineschemachange.oscfms.action.oms.OmsRequestUtil; +import com.oceanbase.odc.service.onlineschemachange.oscfms.action.oms.OmsSwapTableAction; import com.oceanbase.odc.service.onlineschemachange.oscfms.state.OscStates; import com.oceanbase.odc.service.onlineschemachange.rename.DefaultRenameTableInvoker; import com.oceanbase.odc.service.onlineschemachange.rename.LockTableSupportDecider; @@ -65,6 +68,7 @@ public class OmsSwapTableActionTest { private DBSessionManageFacade dbSessionManageFacade; private OmsProjectOpenApiService omsProjectOpenApiService; private OnlineSchemaChangeProperties onlineSchemaChangeProperties; + private OnlineSchemaChangeScheduleTaskParameters onlineSchemaChangeScheduleTaskParameters; @Before public void init() { @@ -77,6 +81,10 @@ public void init() { omsProperties.setRegion("default"); omsProperties.setAuthorization("auth"); onlineSchemaChangeProperties.setOms(omsProperties); + onlineSchemaChangeScheduleTaskParameters = new OnlineSchemaChangeScheduleTaskParameters(); + onlineSchemaChangeScheduleTaskParameters.setUid("uid"); + onlineSchemaChangeScheduleTaskParameters.setOmsProjectId("projectID"); + onlineSchemaChangeScheduleTaskParameters.setDatabaseName("db"); } @Test @@ -170,8 +178,7 @@ public void testSwapTableReady() { OmsSwapTableAction swapTableAction = new OmsSwapTableAction(dbSessionManageFacade, omsProjectOpenApiService, onlineSchemaChangeProperties); Assert.assertTrue( - swapTableAction.isIncrementDataAppliedDone(omsProjectOpenApiService, onlineSchemaChangeProperties, - "uid", "projectID", "db", Collections.emptyMap(), 1000)); + swapTableAction.isIncrementDataAppliedDone(onlineSchemaChangeProperties, onlineSchemaChangeScheduleTaskParameters, Collections.emptyMap(), 1000)); } } @@ -191,8 +198,7 @@ public void testSwapTableNotReady() { onlineSchemaChangeProperties); long currentTimeMS = System.currentTimeMillis(); Assert.assertFalse( - swapTableAction.isIncrementDataAppliedDone(omsProjectOpenApiService, onlineSchemaChangeProperties, - "uid", "projectID", "db", Collections.emptyMap(), 3000)); + swapTableAction.isIncrementDataAppliedDone(onlineSchemaChangeProperties, onlineSchemaChangeScheduleTaskParameters, Collections.emptyMap(), 3000)); long endTimeMS = System.currentTimeMillis(); // test retry Assert.assertTrue(endTimeMS - currentTimeMS > 2000); @@ -207,19 +213,19 @@ private OmsProjectProgressResponse getOMSResponse(OmsProjectStatusEnum statusEnu private List getProjectSteps() { OmsProjectStepVO fullStep = new OmsProjectStepVO(); - fullStep.setName(OmsStepName.FULL_TRANSFER); + fullStep.setName(OscStepName.FULL_TRANSFER); fullStep.setProgress(100); fullStep.setStatus(OmsStepStatus.FINISHED); OmsProjectStepVO incrStep = new OmsProjectStepVO(); - incrStep.setName(OmsStepName.INCR_TRANSFER); + incrStep.setName(OscStepName.INCR_TRANSFER); incrStep.setProgress(100); incrStep.setStatus(OmsStepStatus.FINISHED); OmsProjectStepVO preCheck = new OmsProjectStepVO(); - preCheck.setName(OmsStepName.TRANSFER_PRECHECK); + preCheck.setName(OscStepName.TRANSFER_PRECHECK); preCheck.setProgress(100); preCheck.setStatus(OmsStepStatus.FINISHED); OmsProjectStepVO incrLogPull = new OmsProjectStepVO(); - incrLogPull.setName(OmsStepName.TRANSFER_INCR_LOG_PULL); + incrLogPull.setName(OscStepName.TRANSFER_INCR_LOG_PULL); incrLogPull.setProgress(100); incrLogPull.setStatus(OmsStepStatus.FINISHED); return Arrays.asList(incrLogPull, preCheck, fullStep, incrStep); diff --git a/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/ProjectStepResultCheckerTest.java b/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/ProjectStepResultCheckerTest.java index 57ba71bbb0..ea7265af73 100644 --- a/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/ProjectStepResultCheckerTest.java +++ b/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/ProjectStepResultCheckerTest.java @@ -23,8 +23,8 @@ import org.junit.Before; import org.junit.Test; -import com.oceanbase.odc.service.onlineschemachange.oms.enums.OmsStepName; import com.oceanbase.odc.service.onlineschemachange.oms.enums.OmsStepStatus; +import com.oceanbase.odc.service.onlineschemachange.oms.enums.OscStepName; import com.oceanbase.odc.service.onlineschemachange.oms.response.OmsProjectProgressResponse; import com.oceanbase.odc.service.onlineschemachange.oms.response.OmsProjectStepVO; @@ -42,7 +42,7 @@ public void init() { OmsProjectStepVO incrementStep = new OmsProjectStepVO(); incrementStep.setStatus(OmsStepStatus.MONITORING); incrementStep.setProgress(100); - incrementStep.setName(OmsStepName.INCR_TRANSFER); + incrementStep.setName(OscStepName.INCR_TRANSFER); projectSteps.add(incrementStep); } @@ -52,7 +52,7 @@ public void testDataApplyTaskReady() { response.setIncrSyncCheckpoint(System.currentTimeMillis() / 1000 - 10); ProjectStepResultChecker checker = new ProjectStepResultChecker(response, projectSteps, false, 1, Collections.emptyMap()); - Assert.assertTrue(checker.checkStepFinished(OmsStepName.INCR_TRANSFER)); + Assert.assertTrue(checker.checkStepFinished(OscStepName.INCR_TRANSFER)); } @Test @@ -61,6 +61,6 @@ public void testDataApplyTaskNotReady() { response.setIncrSyncCheckpoint(System.currentTimeMillis() / 1000 - 50); ProjectStepResultChecker checker = new ProjectStepResultChecker(response, projectSteps, false, 1, Collections.emptyMap()); - Assert.assertFalse(checker.checkStepFinished(OmsStepName.INCR_TRANSFER)); + Assert.assertFalse(checker.checkStepFinished(OscStepName.INCR_TRANSFER)); } } diff --git a/server/odc-service/src/test/java/com/oceanbase/odc/service/resource/k8s/K8sResourceOperatorTest.java b/server/odc-service/src/test/java/com/oceanbase/odc/service/resource/k8s/K8sResourceOperatorTest.java index 91f7d405d8..e3bb5ef7c1 100644 --- a/server/odc-service/src/test/java/com/oceanbase/odc/service/resource/k8s/K8sResourceOperatorTest.java +++ b/server/odc-service/src/test/java/com/oceanbase/odc/service/resource/k8s/K8sResourceOperatorTest.java @@ -158,7 +158,7 @@ private K8sPodResource buildByK8sContext(K8sResourceContext k8sResourceContext) AbstractK8sResourceOperatorBuilder.CLOUD_K8S_POD_TYPE, k8sResourceContext.resourceNamespace(), k8sResourceContext.getResourceName(), ResourceState.CREATING, - "localhost:8080", "8089", new Date(1024)); + "localhost:8080", "localhost:8080","8089", new Date(1024)); } } } diff --git a/server/odc-service/src/test/java/com/oceanbase/odc/service/resource/k8s/K8sResourceUtilTest.java b/server/odc-service/src/test/java/com/oceanbase/odc/service/resource/k8s/K8sResourceUtilTest.java new file mode 100644 index 0000000000..29cfbf35da --- /dev/null +++ b/server/odc-service/src/test/java/com/oceanbase/odc/service/resource/k8s/K8sResourceUtilTest.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2025 OceanBase. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.oceanbase.odc.service.resource.k8s; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.tuple.Pair; +import org.junit.Assert; +import org.junit.Test; + +import io.swagger.models.auth.In; + +/** + * @author longpeng.zlp + * @date 2025/4/2 16:47 + */ +public class K8sResourceUtilTest { + @Test(expected = RuntimeException.class) + public void testAllocateIPFailed() { + List ports = new ArrayList<>(); + for (int i = 0; i < 10240; ++i) { + ports.add(i); + } + K8sResourceUtil.buildRandomPortMapper(ports.toArray(new Integer[0])); + } + + @Test + public void testAllocateIPNormal() { + List ports = new ArrayList<>(); + for (int i = 0; i < 10; ++i) { + ports.add(i); + } + List> portMappers = K8sResourceUtil.buildRandomPortMapper(ports.toArray(new Integer[0])); + Assert.assertEquals(portMappers.size(), 10); + Set keys = portMappers.stream().map(p -> p.getLeft()).collect(Collectors.toSet()); + Set values = portMappers.stream().map(p -> p.getRight()).collect(Collectors.toSet()); + Assert.assertEquals(keys.size(), 10); + Assert.assertEquals(values.size(), 10); + values.forEach(v -> Assert.assertTrue(v >= K8sResourceUtil.MIN_PORT && v <= K8sResourceUtil.MAX_PORT)); + } +} From b53a6c69caa7fb4bdcc5a12e99d307d25e6e5d2f Mon Sep 17 00:00:00 2001 From: "longpeng.zlp" Date: Mon, 7 Apr 2025 11:50:50 +0800 Subject: [PATCH 2/4] introduce odc osc migrate logic --- .../oscfms/action/SwapTableAction.java | 4 +- .../action/odc/OdcCreateDataTaskAction.java | 10 +- .../action/odc/OdcMonitorDataTaskAction.java | 15 +- .../oscfms/action/odc/OdcSwapTableAction.java | 11 +- .../k8s/K8SResourceManageStrategy.java | 2 +- .../action/CleanResourcesActionBaseTest.java | 2 +- ...ava => MonitorDataTaskActionBaseTest.java} | 5 +- .../odc/OdcCreateDataTaskActionTest.java | 167 +++++++++++++++++- .../odc/OdcMonitorDataTaskActionTest.java | 122 +++++++++++++ .../action/odc/OdcSwapTableActionTest.java | 93 ++++++++++ .../oms/OmsCleanResourcesActionTest.java | 3 +- .../action/oms/OmsSwapTableActionTest.java | 10 +- .../resource/k8s/K8sResourceOperatorTest.java | 2 +- .../resource/k8s/K8sResourceUtilTest.java | 7 +- 14 files changed, 411 insertions(+), 42 deletions(-) rename server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/{OmsMonitorDataTaskActionTest.java => MonitorDataTaskActionBaseTest.java} (98%) create mode 100644 server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcMonitorDataTaskActionTest.java create mode 100644 server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcSwapTableActionTest.java diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/SwapTableAction.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/SwapTableAction.java index c65284f596..7e6ab693df 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/SwapTableAction.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/SwapTableAction.java @@ -19,6 +19,7 @@ import com.oceanbase.odc.service.onlineschemachange.configuration.OnlineSchemaChangeProperties; import com.oceanbase.odc.service.onlineschemachange.oms.openapi.OmsProjectOpenApiService; +import com.oceanbase.odc.service.onlineschemachange.oscfms.action.odc.OdcMonitorDataTaskAction; import com.oceanbase.odc.service.onlineschemachange.oscfms.action.odc.OdcSwapTableAction; import com.oceanbase.odc.service.onlineschemachange.oscfms.action.oms.OmsSwapTableAction; import com.oceanbase.odc.service.session.DBSessionManageFacade; @@ -35,7 +36,8 @@ public static SwapTableAction ofOMSSwapTableAction(@NotNull DBSessionManageFacad SwapTableAction ret = new SwapTableAction(); ret.omsAction = new OmsSwapTableAction(dbSessionManageFacade, projectOpenApiService, onlineSchemaChangeProperties); - ret.odcAction = new OdcSwapTableAction(dbSessionManageFacade, onlineSchemaChangeProperties); + ret.odcAction = new OdcSwapTableAction(dbSessionManageFacade, onlineSchemaChangeProperties, + new OdcMonitorDataTaskAction(onlineSchemaChangeProperties)); return ret; } } diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcCreateDataTaskAction.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcCreateDataTaskAction.java index 40494a2981..bf6ae210e5 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcCreateDataTaskAction.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcCreateDataTaskAction.java @@ -98,7 +98,7 @@ public OscActionResult execute(OscActionContext context) throws Exception { return new OscActionResult(OscStates.CREATE_DATA_TASK.name(), null, OscStates.MONITOR_DATA_TASK.name()); } - public void startTask(OscActionContext context, OnlineSchemaChangeScheduleTaskParameters parameters) + protected void startTask(OscActionContext context, OnlineSchemaChangeScheduleTaskParameters parameters) throws Exception { log.info("ODCCreateDataTaskAction: start migrate for taskID={}", context.getScheduleTask().getId()); Map startConfigs = new LinkedHashMap<>(); @@ -123,7 +123,7 @@ protected void fillOscTableRelatedConfigs(OscActionContext context, ConnectionCo configs.put("sourceTableName", parameters.getOriginTableName()); configs.put("targetTableName", parameters.getNewTableName()); // add col mapper - List columns = getValidColumns(context, connectionConfig); + List columns = getValidColumns(context); Map targetToSrcColumnMapper = new LinkedHashMap<>(); for (String c : columns) { targetToSrcColumnMapper.put(c, c); @@ -135,7 +135,7 @@ protected void fillOscTableRelatedConfigs(OscActionContext context, ConnectionCo } } - protected List getValidColumns(OscActionContext context, ConnectionConfig connectionConfig) { + protected List getValidColumns(OscActionContext context) { OnlineSchemaChangeScheduleTaskParameters taskParam = context.getTaskParameter(); // target missed columns, use target columns if (CollectionUtils.isNotEmpty(taskParam.getFilterColumns())) { @@ -263,7 +263,7 @@ protected String tryChooseUrl(K8sPodResource resource, Integer mapperPort) { String hostIp = resource.getHostIpAddress(); candidateUrl = buildUrlPrefix(hostIp, mapperPort); if (StringUtils.isNotEmpty(hostIp) && !StringUtils.equalsIgnoreCase(hostIp, Constants.RESOURCE_NULL_HOST) - && mapperPort > 0 && OscCommandUtil.isOSCMigrateSupervisorAlive(candidateUrl)) { + && null != mapperPort && mapperPort > 0 && OscCommandUtil.isOSCMigrateSupervisorAlive(candidateUrl)) { log.info("ODCCreateDataTaskAction: k8s ready, hostIP reached, candidateUrl = {} returned", candidateUrl); return candidateUrl; } @@ -281,7 +281,7 @@ protected String getK8sImplType() { protected boolean getK8sEnablePortMapper() { List enablePortMapperConfig = - systemConfigService.queryByKeyPrefix("odc.task-framework.enable-k8s-port-mapper"); + systemConfigService.queryByKeyPrefix("odc.task-framework.enable-k8s-port-mapper"); if (CollectionUtils.isEmpty(enablePortMapperConfig)) { return false; } diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcMonitorDataTaskAction.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcMonitorDataTaskAction.java index 506aa3a6d6..1ba1bb98ff 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcMonitorDataTaskAction.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcMonitorDataTaskAction.java @@ -56,12 +56,12 @@ public OdcMonitorDataTaskAction(@NotNull OnlineSchemaChangeProperties onlineSche */ protected ProjectStepResult getProjectStepResult(OnlineSchemaChangeScheduleTaskParameters taskParameter, OnlineSchemaChangeScheduleTaskResult lastResult) { - return getProjectStepResultInner(taskParameter); + return getProjectStepResultInner(taskParameter.getOdcCommandURl()); } - protected ProjectStepResult getProjectStepResultInner(OnlineSchemaChangeScheduleTaskParameters taskParameter) { + protected ProjectStepResult getProjectStepResultInner(String commandURL) { ProjectStepResult projectStepResult = new ProjectStepResult(); - SupervisorResponse supervisorResponse = OscCommandUtil.monitorTask(taskParameter.getOdcCommandURl()); + SupervisorResponse supervisorResponse = OscCommandUtil.monitorTask(commandURL); if (null == supervisorResponse || !supervisorResponse.isSuccess()) { log.info("OdcMonitorDataTaskAction: supervisor response failed, response = {}", supervisorResponse); return null; @@ -91,16 +91,15 @@ protected ProjectStepResult getProjectStepResultInner(OnlineSchemaChangeSchedule projectStepResult.setPreCheckResult(PrecheckResult.FINISHED); projectStepResult.setTaskStatus(TaskStatus.RUNNING); projectStepResult.setFullVerificationResult(FullVerificationResult.UNCHECK); + projectStepResult.setFullVerificationResultDescription("unchecked"); + projectStepResult.setFullVerificationProgressPercentage(0.0); // adapt steps try { - projectStepResult.setFullTransferEstimatedCount(getAndCheckValue(dataMap, "tableTotalRows", Long::valueOf)); + projectStepResult.setFullTransferFinishedCount(getAndCheckValue(dataMap, "tableTotalRows", Long::valueOf)); projectStepResult - .setFullTransferFinishedCount(getAndCheckValue(dataMap, "estimateMigrateRows", Long::valueOf)); + .setFullTransferEstimatedCount(getAndCheckValue(dataMap, "estimateMigrateRows", Long::valueOf)); projectStepResult.setFullTransferProgressPercentage( getAndCheckValue(dataMap, "fullMigratorProgress", Double::valueOf)); - projectStepResult.setFullVerificationResult(FullVerificationResult.UNCHECK); - projectStepResult.setFullVerificationResultDescription("unchecked"); - projectStepResult.setFullVerificationProgressPercentage(0.0); boolean fullMigrateDone = getAndCheckValue(dataMap, "fullMigratorDone", Boolean::valueOf); if (fullMigrateDone) { projectStepResult.setCurrentStep(OscStepName.TRANSFER_APP_SWITCH.name()); diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcSwapTableAction.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcSwapTableAction.java index dfc839fa14..279768cc78 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcSwapTableAction.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcSwapTableAction.java @@ -44,15 +44,17 @@ public class OdcSwapTableAction extends SwapTableActionBase { private final OdcMonitorDataTaskAction odcMonitorDataTaskAction; public OdcSwapTableAction(@NotNull DBSessionManageFacade dbSessionManageFacade, - @NotNull OnlineSchemaChangeProperties onlineSchemaChangeProperties) { + @NotNull OnlineSchemaChangeProperties onlineSchemaChangeProperties, + @NotNull OdcMonitorDataTaskAction odcMonitorDataTaskAction) { super(dbSessionManageFacade, onlineSchemaChangeProperties); - this.odcMonitorDataTaskAction = new OdcMonitorDataTaskAction(onlineSchemaChangeProperties); + this.odcMonitorDataTaskAction = odcMonitorDataTaskAction; } protected boolean checkOSCProjectReady(OscActionContext context) { OnlineSchemaChangeScheduleTaskParameters taskParameter = context.getTaskParameter(); // get oms step result - ProjectStepResult projectStepResult = odcMonitorDataTaskAction.getProjectStepResultInner(taskParameter); + ProjectStepResult projectStepResult = + odcMonitorDataTaskAction.getProjectStepResultInner(taskParameter.getOdcCommandURl()); if (null == projectStepResult) { return false; } @@ -71,7 +73,8 @@ protected boolean isIncrementDataAppliedDone(OnlineSchemaChangeProperties online // max check 25s long checkTimeoutMs = System.currentTimeMillis() + timeOutMS; while (true) { - ProjectStepResult projectStepResult = odcMonitorDataTaskAction.getProjectStepResultInner(parameters); + ProjectStepResult projectStepResult = + odcMonitorDataTaskAction.getProjectStepResultInner(parameters.getOdcCommandURl()); log.info("Osc check increment checkpoint, expect greater than {}, current = {}", safeDataCheckpoint, projectStepResult.getIncrementCheckpoint()); if (odcMonitorDataTaskAction.isMigrateTaskReady(projectStepResult) diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/task/resource/manager/strategy/k8s/K8SResourceManageStrategy.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/task/resource/manager/strategy/k8s/K8SResourceManageStrategy.java index b9e3e2128b..001855b24b 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/task/resource/manager/strategy/k8s/K8SResourceManageStrategy.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/task/resource/manager/strategy/k8s/K8SResourceManageStrategy.java @@ -98,7 +98,7 @@ public SupervisorEndpointEntity handleNoResourceAvailable(ResourceAllocateInfoEn List> portMapper = new ArrayList<>(); if (enableK8sPortMapper) { portMapper = K8sResourceUtil.buildRandomPortMapper(supervisorListenPort, - k8sProperties.getExecutorListenPort()); + k8sProperties.getExecutorListenPort()); } k8sPodResource = K8sResourceUtil.createK8sPodResource(resourceManager, resourceLocation, k8sImplType, imageName, k8sProperties, diff --git a/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/CleanResourcesActionBaseTest.java b/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/CleanResourcesActionBaseTest.java index cb9a336ea4..342cc9b869 100644 --- a/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/CleanResourcesActionBaseTest.java +++ b/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/CleanResourcesActionBaseTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 OceanBase. + * Copyright (c) 2023 OceanBase. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/OmsMonitorDataTaskActionTest.java b/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/MonitorDataTaskActionBaseTest.java similarity index 98% rename from server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/OmsMonitorDataTaskActionTest.java rename to server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/MonitorDataTaskActionBaseTest.java index da4622e971..a2ea85dd3d 100644 --- a/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/OmsMonitorDataTaskActionTest.java +++ b/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/MonitorDataTaskActionBaseTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 OceanBase. + * Copyright (c) 2023 OceanBase. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,7 +34,6 @@ import com.oceanbase.odc.service.onlineschemachange.oscfms.OscActionContext; import com.oceanbase.odc.service.onlineschemachange.oscfms.OscActionResult; import com.oceanbase.odc.service.onlineschemachange.oscfms.OscTestUtil; -import com.oceanbase.odc.service.onlineschemachange.oscfms.action.ProjectStepResult; import com.oceanbase.odc.service.onlineschemachange.oscfms.action.oms.OmsMonitorDataTaskAction; import com.oceanbase.odc.service.onlineschemachange.oscfms.state.OscStates; @@ -43,7 +42,7 @@ * @date 2024/7/30 14:31 * @since 4.3.1 */ -public class OmsMonitorDataTaskActionTest { +public class MonitorDataTaskActionBaseTest { private OmsProjectOpenApiService projectOpenApiService; private OnlineSchemaChangeProperties onlineSchemaChangeProperties; private OscActionContext context; diff --git a/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcCreateDataTaskActionTest.java b/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcCreateDataTaskActionTest.java index 6be8cef838..56d8badafa 100644 --- a/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcCreateDataTaskActionTest.java +++ b/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcCreateDataTaskActionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 OceanBase. + * Copyright (c) 2023 OceanBase. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,26 +13,41 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.oceanbase.odc.service.onlineschemachange.oscfms.action.odc; import java.sql.Date; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatchers; import org.mockito.MockedStatic; import org.mockito.Mockito; +import com.oceanbase.odc.core.session.ConnectionSession; +import com.oceanbase.odc.metadb.schedule.ScheduleTaskEntity; +import com.oceanbase.odc.metadb.schedule.ScheduleTaskRepository; import com.oceanbase.odc.service.config.SystemConfigService; +import com.oceanbase.odc.service.config.model.Configuration; +import com.oceanbase.odc.service.connection.model.ConnectionConfig; import com.oceanbase.odc.service.onlineschemachange.configuration.OnlineSchemaChangeProperties; import com.oceanbase.odc.service.onlineschemachange.configuration.OnlineSchemaChangeProperties.OmsProperties; +import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeScheduleTaskParameters; +import com.oceanbase.odc.service.onlineschemachange.model.RateLimiterConfig; +import com.oceanbase.odc.service.onlineschemachange.oms.request.CreateOceanBaseDataSourceRequest; +import com.oceanbase.odc.service.onlineschemachange.oscfms.OscActionContext; +import com.oceanbase.odc.service.onlineschemachange.oscfms.action.ConnectionProvider; import com.oceanbase.odc.service.onlineschemachange.oscfms.action.oms.OmsRequestUtil; import com.oceanbase.odc.service.resource.ResourceManager; import com.oceanbase.odc.service.resource.ResourceState; +import com.oceanbase.odc.service.resource.k8s.K8sResourceUtil; +import com.oceanbase.odc.service.task.config.K8sProperties; import com.oceanbase.odc.service.task.resource.K8sPodResource; -import com.sun.jna.platform.win32.OaIdl.DATE; /** * @author longpeng.zlp @@ -44,6 +59,11 @@ public class OdcCreateDataTaskActionTest { private ResourceManager resourceManager; private OnlineSchemaChangeProperties onlineSchemaChangeProperties; private K8sPodResource podResource; + private ConnectionConfig connectionConfig; + private ScheduleTaskRepository repository; + private OnlineSchemaChangeScheduleTaskParameters parameters; + private OscActionContext context; + private ScheduleTaskEntity scheduleTask; @Before public void init() { @@ -55,10 +75,58 @@ public void init() { omsProperties.setAuthorization("auth"); onlineSchemaChangeProperties.setOms(omsProperties); systemConfigService = Mockito.mock(SystemConfigService.class); + List kubeConfigUrls = + Arrays.asList(new Configuration("odc.task-framework.k8s-properties.kube-config", "xxx")); + Mockito.when(systemConfigService.queryByKeyPrefix("odc.task-framework.k8s-properties.")).thenReturn( + kubeConfigUrls); + List oscConfigUrls = + Arrays.asList(new Configuration("odc.osc.k8s-properties.pod-pending-timeout-seconds", "600")); + Mockito.when(systemConfigService.queryByKeyPrefix("odc.osc.k8s-properties.")).thenReturn( + oscConfigUrls); + + Mockito.when(systemConfigService.queryByKeyPrefix("odc.osc.k8s-properties.")).thenReturn( + kubeConfigUrls); resourceManager = Mockito.mock(ResourceManager.class); - createDataTaskAction = new OdcCreateDataTaskAction(systemConfigService, resourceManager, onlineSchemaChangeProperties); + createDataTaskAction = + new OdcCreateDataTaskAction(systemConfigService, resourceManager, onlineSchemaChangeProperties); podResource = new K8sPodResource("local", "local", "nativeK8s", "namespace", - "arn", ResourceState.CREATING, null, null, null, new Date(System.currentTimeMillis() / 1000)); + "arn", ResourceState.CREATING, null, null, null, new Date(System.currentTimeMillis() / 1000)); + connectionConfig = new ConnectionConfig(); + connectionConfig.setRegion("region"); + connectionConfig.setAttributes(Collections.singletonMap("cloudProvider", "ll")); + connectionConfig.setUsername("user"); + connectionConfig.setPassword("pwd"); + connectionConfig.setTenantName("tenant"); + connectionConfig.setClusterName("cluster"); + repository = Mockito.mock(ScheduleTaskRepository.class); + Mockito.when(repository.updateTaskParameters(ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(1); + parameters = new OnlineSchemaChangeScheduleTaskParameters(); + parameters.setResourceID(1L); + parameters.setDatabaseName("dbName"); + parameters.setOriginTableName("srcTable"); + parameters.setNewTableName("ghost_srcTable"); + parameters.setFilterColumns(Arrays.asList("c1", "c2")); + RateLimiterConfig config = new RateLimiterConfig(); + config.setRowLimit(1024); + parameters.setRateLimitConfig(config); + context = new OscActionContext(); + scheduleTask = new ScheduleTaskEntity(); + scheduleTask.setId(2L); + ConnectionProvider connectionProvider = new ConnectionProvider() { + @Override + public ConnectionConfig connectionConfig() { + return connectionConfig; + } + + @Override + public ConnectionSession createConnectionSession() { + return Mockito.mock(ConnectionSession.class); + } + }; + context.setScheduleTaskRepository(repository); + context.setTaskParameter(parameters); + context.setScheduleTask(scheduleTask); + context.setConnectionProvider(connectionProvider); } @Test @@ -70,7 +138,8 @@ public void testChooseUrlNotReadyWithNoIpReturned() { public void testChooseUrlNotReadyWithIpNotReached() { try (MockedStatic commandUtil = Mockito.mockStatic(OscCommandUtil.class);) { podResource.setPodIpAddress("127.0.0.1"); - commandUtil.when(() -> OscCommandUtil.isOSCMigrateSupervisorAlive(ArgumentMatchers.any())).thenReturn(false); + commandUtil.when(() -> OscCommandUtil.isOSCMigrateSupervisorAlive(ArgumentMatchers.any())) + .thenReturn(false); Assert.assertNull(createDataTaskAction.tryChooseUrl(podResource, -1)); podResource.setHostIpAddress("127.0.0.1"); Assert.assertNull(createDataTaskAction.tryChooseUrl(podResource, -1)); @@ -90,4 +159,90 @@ public void testChooseUrlReady() { } } + @Test + public void testBuildK8sProperties() { + K8sProperties k8sProperties = createDataTaskAction.buildK8sProperties(connectionConfig); + Assert.assertEquals(k8sProperties.getKubeConfig(), "xxx"); + Assert.assertEquals(k8sProperties.getPodPendingTimeoutSeconds().longValue(), 600L); + Assert.assertNull(k8sProperties.getExecutorListenPort()); + Assert.assertEquals(k8sProperties.getSupervisorListenPort().intValue(), 18001); + Assert.assertEquals(k8sProperties.getNodeMemInMB().longValue(), 16384); + } + + @Test + public void testWaitMigrateNodeNotReady() throws Exception { + podResource.setPodIpAddress("host"); + podResource.setHostIpAddress("host"); + try (MockedStatic commandUtil = Mockito.mockStatic(OscCommandUtil.class); + MockedStatic k8sUtil = Mockito.mockStatic(K8sResourceUtil.class)) { + k8sUtil.when(() -> K8sResourceUtil.queryIpAndAddress(ArgumentMatchers.any(), ArgumentMatchers.anyLong())) + .thenReturn(podResource); + commandUtil.when(() -> OscCommandUtil.isOSCMigrateSupervisorAlive(ArgumentMatchers.any())) + .thenReturn(false); + Assert.assertFalse(createDataTaskAction.waitMigrateNodeReady(context, parameters)); + } + } + + @Test + public void testWaitMigrateNodeReady() throws Exception { + podResource.setPodIpAddress("host1"); + podResource.setHostIpAddress("host2"); + parameters.setK8sMapperPort(1024); + try (MockedStatic commandUtil = Mockito.mockStatic(OscCommandUtil.class); + MockedStatic k8sUtil = Mockito.mockStatic(K8sResourceUtil.class)) { + k8sUtil.when(() -> K8sResourceUtil.queryIpAndAddress(ArgumentMatchers.any(), ArgumentMatchers.anyLong())) + .thenReturn(podResource); + commandUtil.when(() -> OscCommandUtil.isOSCMigrateSupervisorAlive("http://host1:18001")).thenReturn(false); + commandUtil.when(() -> OscCommandUtil.isOSCMigrateSupervisorAlive("http://host2:1024")).thenReturn(true); + Assert.assertTrue(createDataTaskAction.waitMigrateNodeReady(context, parameters)); + Assert.assertEquals("http://host2:1024", parameters.getOdcCommandURl()); + } + } + + @Test + public void testStartTask() throws Exception { + CreateOceanBaseDataSourceRequest dataSourceRequest = new CreateOceanBaseDataSourceRequest(); + String ip = "ip"; + Integer port = 123; + String configUrl = "configUrl"; + String drcUser = "drcUser"; + String drcPwd = "drcPwd"; + String drcCluster = "cluster"; + dataSourceRequest.setIp(ip); + dataSourceRequest.setPort(port); + dataSourceRequest.setPassword("pwd"); + dataSourceRequest.setConfigUrl(configUrl); + dataSourceRequest.setDrcUserName(drcUser); + dataSourceRequest.setDrcPassword(drcPwd); + dataSourceRequest.setCluster(drcCluster); + + try (MockedStatic commandUtil = Mockito.mockStatic(OscCommandUtil.class); + MockedStatic omsRequestUtil = Mockito.mockStatic(OmsRequestUtil.class)) { + commandUtil.when(() -> OscCommandUtil.startTask(ArgumentMatchers.any(), ArgumentMatchers.any())) + .thenReturn(new SupervisorResponse(true, null, null)); + omsRequestUtil.when(() -> OmsRequestUtil.getCreateDataSourceRequest(ArgumentMatchers.any(), + ArgumentMatchers.any(), ArgumentMatchers.any(), + ArgumentMatchers.any())).thenReturn(dataSourceRequest); + createDataTaskAction.startTask(context, parameters); + ArgumentCaptor> startParametersCapture = ArgumentCaptor.forClass(Map.class); + commandUtil + .verify(() -> OscCommandUtil.startTask(ArgumentMatchers.any(), startParametersCapture.capture())); + Map startParameters = startParametersCapture.getValue(); + // fill datasource + Assert.assertEquals(startParameters.get("databaseUrl"), "ip:123"); + Assert.assertEquals(startParameters.get("databaseUser"), "user@tenant#cluster"); + Assert.assertEquals(startParameters.get("databasePassword"), "pwd"); + Assert.assertEquals(startParameters.get("crawlerClusterURL"), "configUrl"); + Assert.assertEquals(startParameters.get("crawlerClusterUser"), "drcUser"); + Assert.assertEquals(startParameters.get("crawlerClusterPassword"), "drcPwd"); + Assert.assertEquals(startParameters.get("crawlerClusterAppName"), "cluster"); + // fill osc table configs + Assert.assertEquals(startParameters.get("dbname"), "dbName"); + Assert.assertEquals(startParameters.get("tenantName"), "tenant"); + Assert.assertEquals(startParameters.get("sourceTableName"), "srcTable"); + Assert.assertEquals(startParameters.get("targetTableName"), "ghost_srcTable"); + Assert.assertEquals(startParameters.get("targetToSrcColMapper"), "{\"c1\":\"c1\",\"c2\":\"c2\"}"); + Assert.assertEquals(startParameters.get("throttleRps"), "1024"); + } + } } diff --git a/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcMonitorDataTaskActionTest.java b/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcMonitorDataTaskActionTest.java new file mode 100644 index 0000000000..8a0d0df0b2 --- /dev/null +++ b/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcMonitorDataTaskActionTest.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2023 OceanBase. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.oceanbase.odc.service.onlineschemachange.oscfms.action.odc; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentMatchers; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + +import com.oceanbase.odc.common.json.JsonUtils; +import com.oceanbase.odc.core.shared.constant.TaskStatus; +import com.oceanbase.odc.service.onlineschemachange.configuration.OnlineSchemaChangeProperties; +import com.oceanbase.odc.service.onlineschemachange.configuration.OnlineSchemaChangeProperties.OmsProperties; +import com.oceanbase.odc.service.onlineschemachange.model.FullVerificationResult; +import com.oceanbase.odc.service.onlineschemachange.model.PrecheckResult; +import com.oceanbase.odc.service.onlineschemachange.oms.enums.OmsStepStatus; +import com.oceanbase.odc.service.onlineschemachange.oms.enums.OscStepName; +import com.oceanbase.odc.service.onlineschemachange.oscfms.action.ProjectStepResult; + +/** + * @author longpeng.zlp + * @date 2025/4/7 10:28 + */ +public class OdcMonitorDataTaskActionTest { + private OnlineSchemaChangeProperties onlineSchemaChangeProperties; + private OdcMonitorDataTaskAction dataTaskAction; + + + @Before + public void init() { + onlineSchemaChangeProperties = new OnlineSchemaChangeProperties(); + OmsProperties omsProperties = new OmsProperties(); + omsProperties.setUrl("127.0.0.1:8089"); + omsProperties.setRegion("default"); + omsProperties.setAuthorization("auth"); + onlineSchemaChangeProperties.setOms(omsProperties); + dataTaskAction = new OdcMonitorDataTaskAction(onlineSchemaChangeProperties); + } + + @Test + public void testParseResponse1() { + String response = "{\"errorMessage\":null," + + "\"responseData\":{" + + "\"data\":\"{" + + "\\\"checkpoint\\\":\\\"1743994588\\\"," + + "\\\"enableIncrementMigrator\\\":\\\"true\\\"," + + "\\\"estimateMigrateRows\\\":\\\"380\\\"," + + "\\\"enableFullMigrator\\\":\\\"true\\\"," + + "\\\"fullMigratorProgress\\\":\\\"100\\\"," + + "\\\"fullMigratorDone\\\":\\\"true\\\"," + + "\\\"tableTotalRows\\\":\\\"370\\\"" + + "}\"" + + "}," + + "\"success\":true}"; + SupervisorResponse supervisorResponse = JsonUtils.fromJson(response, SupervisorResponse.class); + try (MockedStatic commandUtil = Mockito.mockStatic(OscCommandUtil.class)) { + commandUtil.when(() -> OscCommandUtil.monitorTask(ArgumentMatchers.any())).thenReturn(supervisorResponse); + ProjectStepResult result = dataTaskAction.getProjectStepResultInner("url"); + Assert.assertEquals(result.getPreCheckResult(), PrecheckResult.FINISHED); + Assert.assertEquals(result.getTaskStatus(), TaskStatus.RUNNING); + Assert.assertEquals(result.getFullVerificationResult(), FullVerificationResult.UNCHECK); + Assert.assertEquals(result.getFullTransferEstimatedCount().longValue(), 380); + Assert.assertEquals(result.getFullTransferFinishedCount().longValue(), 370); + Assert.assertEquals(Double.valueOf(result.getFullTransferProgressPercentage()).intValue(), 100); + Assert.assertEquals(result.getCurrentStepStatus(), OmsStepStatus.RUNNING.name()); + Assert.assertEquals(result.getIncrementCheckpoint().longValue(), 1743994588); + Assert.assertEquals(Double.valueOf(result.getTaskPercentage()).intValue(), 95); + Assert.assertEquals(result.getCurrentStep(), OscStepName.TRANSFER_APP_SWITCH.name()); + } + } + + @Test + public void testParseResponse2() { + String response = "{\"errorMessage\":null," + + "\"responseData\":{" + + "\"data\":\"{" + + "\\\"checkpoint\\\":\\\"1743994588\\\"," + + "\\\"enableIncrementMigrator\\\":\\\"true\\\"," + + "\\\"estimateMigrateRows\\\":\\\"380\\\"," + + "\\\"enableFullMigrator\\\":\\\"true\\\"," + + "\\\"fullMigratorProgress\\\":\\\"100\\\"," + + "\\\"fullMigratorDone\\\":\\\"false\\\"," + + "\\\"tableTotalRows\\\":\\\"370\\\"" + + "}\"" + + "}," + + "\"success\":true}"; + SupervisorResponse supervisorResponse = JsonUtils.fromJson(response, SupervisorResponse.class); + try (MockedStatic commandUtil = Mockito.mockStatic(OscCommandUtil.class)) { + commandUtil.when(() -> OscCommandUtil.monitorTask(ArgumentMatchers.any())).thenReturn(supervisorResponse); + ProjectStepResult result = dataTaskAction.getProjectStepResultInner("url"); + Assert.assertEquals(result.getCurrentStep(), OscStepName.FULL_TRANSFER.name()); + } + } + + @Test + public void testProjectReady() { + ProjectStepResult result = new ProjectStepResult(); + result.setCurrentStep(OscStepName.TRANSFER_APP_SWITCH.name()); + result.setIncrementCheckpoint(System.currentTimeMillis() / 1000); + Assert.assertTrue(dataTaskAction.isMigrateTaskReady(result)); + result.setIncrementCheckpoint(System.currentTimeMillis() / 1000 - 100000); + Assert.assertFalse(dataTaskAction.isMigrateTaskReady(result)); + result.setCurrentStep(OscStepName.FULL_TRANSFER.name()); + result.setIncrementCheckpoint(System.currentTimeMillis() / 1000); + Assert.assertFalse(dataTaskAction.isMigrateTaskReady(result)); + } +} diff --git a/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcSwapTableActionTest.java b/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcSwapTableActionTest.java new file mode 100644 index 0000000000..75fea25a13 --- /dev/null +++ b/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcSwapTableActionTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2023 OceanBase. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.oceanbase.odc.service.onlineschemachange.oscfms.action.odc; + +import java.util.Collections; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentMatchers; +import org.mockito.Mockito; + +import com.oceanbase.odc.core.shared.constant.DialectType; +import com.oceanbase.odc.core.shared.constant.TaskStatus; +import com.oceanbase.odc.service.onlineschemachange.configuration.OnlineSchemaChangeProperties; +import com.oceanbase.odc.service.onlineschemachange.configuration.OnlineSchemaChangeProperties.OmsProperties; +import com.oceanbase.odc.service.onlineschemachange.model.OnlineSchemaChangeScheduleTaskParameters; +import com.oceanbase.odc.service.onlineschemachange.oscfms.OscActionContext; +import com.oceanbase.odc.service.onlineschemachange.oscfms.OscActionResult; +import com.oceanbase.odc.service.onlineschemachange.oscfms.OscTestUtil; +import com.oceanbase.odc.service.onlineschemachange.oscfms.action.ProjectStepResult; +import com.oceanbase.odc.service.onlineschemachange.oscfms.state.OscStates; +import com.oceanbase.odc.service.session.DBSessionManageFacade; + +/** + * @author longpeng.zlp + * @date 2024/7/30 16:34 + * @since 4.3.1 + */ +public class OdcSwapTableActionTest { + private DBSessionManageFacade dbSessionManageFacade; + private OnlineSchemaChangeProperties onlineSchemaChangeProperties; + private OnlineSchemaChangeScheduleTaskParameters onlineSchemaChangeScheduleTaskParameters; + private OdcSwapTableAction odcSwapTableAction; + private OdcMonitorDataTaskAction odcMonitorDataTaskAction; + private ProjectStepResult projectStepResult; + + @Before + public void init() { + dbSessionManageFacade = Mockito.mock(DBSessionManageFacade.class); + onlineSchemaChangeProperties = new OnlineSchemaChangeProperties(); + onlineSchemaChangeProperties.setEnableFullVerify(false); + OmsProperties omsProperties = new OmsProperties(); + omsProperties.setUrl("127.0.0.1:8089"); + omsProperties.setRegion("default"); + omsProperties.setAuthorization("auth"); + onlineSchemaChangeProperties.setOms(omsProperties); + onlineSchemaChangeScheduleTaskParameters = new OnlineSchemaChangeScheduleTaskParameters(); + onlineSchemaChangeScheduleTaskParameters.setUid("uid"); + onlineSchemaChangeScheduleTaskParameters.setOmsProjectId("projectID"); + onlineSchemaChangeScheduleTaskParameters.setDatabaseName("db"); + odcMonitorDataTaskAction = Mockito.mock(OdcMonitorDataTaskAction.class); + odcSwapTableAction = + new OdcSwapTableAction(dbSessionManageFacade, onlineSchemaChangeProperties, odcMonitorDataTaskAction); + projectStepResult = new ProjectStepResult(); + projectStepResult.setIncrementCheckpoint(System.currentTimeMillis() / 1000 + 10); + } + + @Test + public void testCheckOdcProjectNotReady() throws Exception { + OscActionContext context = OscTestUtil.createOcsActionContext(DialectType.OB_MYSQL, + OscStates.SWAP_TABLE.getState(), TaskStatus.RUNNING); + Mockito.when(odcMonitorDataTaskAction.getProjectStepResultInner(ArgumentMatchers.any())) + .thenReturn(projectStepResult); + Mockito.when(odcMonitorDataTaskAction.isMigrateTaskReady(ArgumentMatchers.any())).thenReturn(false); + OscActionResult result = odcSwapTableAction.execute(context); + Assert.assertEquals(result.getNextState(), OscStates.SWAP_TABLE.getState()); + Assert.assertFalse(odcSwapTableAction.isIncrementDataAppliedDone(null, onlineSchemaChangeScheduleTaskParameters, + Collections.emptyMap(), 1000)); + } + + @Test + public void testSwapTableReady() { + Mockito.when(odcMonitorDataTaskAction.getProjectStepResultInner(ArgumentMatchers.any())) + .thenReturn(projectStepResult); + Mockito.when(odcMonitorDataTaskAction.isMigrateTaskReady(ArgumentMatchers.any())).thenReturn(true); + Assert.assertTrue(odcSwapTableAction.isIncrementDataAppliedDone(null, onlineSchemaChangeScheduleTaskParameters, + Collections.emptyMap(), 1000)); + } +} diff --git a/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsCleanResourcesActionTest.java b/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsCleanResourcesActionTest.java index 9e0e098b82..a3f5532d77 100644 --- a/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsCleanResourcesActionTest.java +++ b/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsCleanResourcesActionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 OceanBase. + * Copyright (c) 2023 OceanBase. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.oceanbase.odc.service.onlineschemachange.oscfms.action.oms; import org.junit.Assert; diff --git a/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsSwapTableActionTest.java b/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsSwapTableActionTest.java index 1c8c8bb60a..43e48a90c7 100644 --- a/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsSwapTableActionTest.java +++ b/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/oms/OmsSwapTableActionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 OceanBase. + * Copyright (c) 2023 OceanBase. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -49,8 +49,6 @@ import com.oceanbase.odc.service.onlineschemachange.oscfms.OscTestUtil; import com.oceanbase.odc.service.onlineschemachange.oscfms.action.ConnectionProvider; import com.oceanbase.odc.service.onlineschemachange.oscfms.action.ProjectStepResult; -import com.oceanbase.odc.service.onlineschemachange.oscfms.action.oms.OmsRequestUtil; -import com.oceanbase.odc.service.onlineschemachange.oscfms.action.oms.OmsSwapTableAction; import com.oceanbase.odc.service.onlineschemachange.oscfms.state.OscStates; import com.oceanbase.odc.service.onlineschemachange.rename.DefaultRenameTableInvoker; import com.oceanbase.odc.service.onlineschemachange.rename.LockTableSupportDecider; @@ -178,7 +176,8 @@ public void testSwapTableReady() { OmsSwapTableAction swapTableAction = new OmsSwapTableAction(dbSessionManageFacade, omsProjectOpenApiService, onlineSchemaChangeProperties); Assert.assertTrue( - swapTableAction.isIncrementDataAppliedDone(onlineSchemaChangeProperties, onlineSchemaChangeScheduleTaskParameters, Collections.emptyMap(), 1000)); + swapTableAction.isIncrementDataAppliedDone(onlineSchemaChangeProperties, + onlineSchemaChangeScheduleTaskParameters, Collections.emptyMap(), 1000)); } } @@ -198,7 +197,8 @@ public void testSwapTableNotReady() { onlineSchemaChangeProperties); long currentTimeMS = System.currentTimeMillis(); Assert.assertFalse( - swapTableAction.isIncrementDataAppliedDone(onlineSchemaChangeProperties, onlineSchemaChangeScheduleTaskParameters, Collections.emptyMap(), 3000)); + swapTableAction.isIncrementDataAppliedDone(onlineSchemaChangeProperties, + onlineSchemaChangeScheduleTaskParameters, Collections.emptyMap(), 3000)); long endTimeMS = System.currentTimeMillis(); // test retry Assert.assertTrue(endTimeMS - currentTimeMS > 2000); diff --git a/server/odc-service/src/test/java/com/oceanbase/odc/service/resource/k8s/K8sResourceOperatorTest.java b/server/odc-service/src/test/java/com/oceanbase/odc/service/resource/k8s/K8sResourceOperatorTest.java index e3bb5ef7c1..2c3c9e22e6 100644 --- a/server/odc-service/src/test/java/com/oceanbase/odc/service/resource/k8s/K8sResourceOperatorTest.java +++ b/server/odc-service/src/test/java/com/oceanbase/odc/service/resource/k8s/K8sResourceOperatorTest.java @@ -158,7 +158,7 @@ private K8sPodResource buildByK8sContext(K8sResourceContext k8sResourceContext) AbstractK8sResourceOperatorBuilder.CLOUD_K8S_POD_TYPE, k8sResourceContext.resourceNamespace(), k8sResourceContext.getResourceName(), ResourceState.CREATING, - "localhost:8080", "localhost:8080","8089", new Date(1024)); + "localhost:8080", "localhost:8080", "8089", new Date(1024)); } } } diff --git a/server/odc-service/src/test/java/com/oceanbase/odc/service/resource/k8s/K8sResourceUtilTest.java b/server/odc-service/src/test/java/com/oceanbase/odc/service/resource/k8s/K8sResourceUtilTest.java index 29cfbf35da..2f800ec20d 100644 --- a/server/odc-service/src/test/java/com/oceanbase/odc/service/resource/k8s/K8sResourceUtilTest.java +++ b/server/odc-service/src/test/java/com/oceanbase/odc/service/resource/k8s/K8sResourceUtilTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 OceanBase. + * Copyright (c) 2023 OceanBase. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.oceanbase.odc.service.resource.k8s; import java.util.ArrayList; @@ -25,14 +24,12 @@ import org.junit.Assert; import org.junit.Test; -import io.swagger.models.auth.In; - /** * @author longpeng.zlp * @date 2025/4/2 16:47 */ public class K8sResourceUtilTest { - @Test(expected = RuntimeException.class) + @Test(expected = RuntimeException.class) public void testAllocateIPFailed() { List ports = new ArrayList<>(); for (int i = 0; i < 10240; ++i) { From e44e83827bdc181b31f3f8d3e2dc9a9f226decb9 Mon Sep 17 00:00:00 2001 From: "longpeng.zlp" Date: Mon, 7 Apr 2025 14:17:16 +0800 Subject: [PATCH 3/4] introduce odc osc migrate logic --- .../src/main/resources/i18n/ErrorMessages.properties | 4 ++-- .../main/resources/i18n/ErrorMessages_zh_TW.properties | 4 ++-- .../oscfms/action/odc/OdcCreateDataTaskActionTest.java | 8 ++++++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/server/odc-core/src/main/resources/i18n/ErrorMessages.properties b/server/odc-core/src/main/resources/i18n/ErrorMessages.properties index efa3710609..f0c5cda4fc 100644 --- a/server/odc-core/src/main/resources/i18n/ErrorMessages.properties +++ b/server/odc-core/src/main/resources/i18n/ErrorMessages.properties @@ -170,8 +170,8 @@ com.oceanbase.odc.ErrorCodes.OmsDataCheckInconsistent=Data inconsistencies found com.oceanbase.odc.ErrorCodes.OmsGhanaOperateFailed=OceanBase Migration Service (OMS) Ghana operation error. com.oceanbase.odc.ErrorCodes.OmsParamError=Parameter error of OceanBase Migration Service (OMS). com.oceanbase.odc.ErrorCodes.OmsConnectivityTestFailed=The connectivity test of OceanBase Migration Service (OMS) failed. -com.oceanbase.odc.ErrorCodes.OmsPreCheckFailed=The pre-check of OceanBase Migration Service (OMS) failed. -com.oceanbase.odc.ErrorCodes.OmsProjectExecutingFailed=OceanBase Migration Service (OMS) runtime failed +com.oceanbase.odc.ErrorCodes.OscPreCheckFailed=The pre-check of Online Schema Change failed. +com.oceanbase.odc.ErrorCodes.OscProjectExecutingFailed=Online Schema Change runtime failed com.oceanbase.odc.ErrorCodes.BuiltInResourceOperateNotAllowed=The operation {0} is not allowed for the resource type {1}. com.oceanbase.odc.ErrorCodes.BuiltInResourceNotAvailable=The built-in resource {0} has been disabled. com.oceanbase.odc.ErrorCodes.SqlInterceptApprovalRequired=Approval is required for this SQL statement before the execution. diff --git a/server/odc-core/src/main/resources/i18n/ErrorMessages_zh_TW.properties b/server/odc-core/src/main/resources/i18n/ErrorMessages_zh_TW.properties index 6a60a49714..275a13e309 100644 --- a/server/odc-core/src/main/resources/i18n/ErrorMessages_zh_TW.properties +++ b/server/odc-core/src/main/resources/i18n/ErrorMessages_zh_TW.properties @@ -168,8 +168,8 @@ com.oceanbase.odc.ErrorCodes.OmsDataCheckInconsistent=OceanBase Migration Servic com.oceanbase.odc.ErrorCodes.OmsGhanaOperateFailed=OceanBase Migration Service (OMS) 管控操作失敗 com.oceanbase.odc.ErrorCodes.OmsParamError=OceanBase Migration Service (OMS) 參數錯誤 com.oceanbase.odc.ErrorCodes.OmsConnectivityTestFailed=OceanBase Migration Service (OMS) 連接測試失敗 -com.oceanbase.odc.ErrorCodes.OmsPreCheckFailed=OmsOceanBase Migration Service (OMS) 預檢查失敗 -com.oceanbase.odc.ErrorCodes.OmsProjectExecutingFailed=OceanBase Migration Service (OMS) 執行失敗 +com.oceanbase.odc.ErrorCodes.OmsPreCheckFailed=Online Schema Change 預檢查失敗 +com.oceanbase.odc.ErrorCodes.OmsProjectExecutingFailed=Online Schema Change 執行失敗 com.oceanbase.odc.ErrorCodes.BuiltInResourceOperateNotAllowed=不支持對資源類型 {1} 進行 {0} 操作 com.oceanbase.odc.ErrorCodes.BuiltInResourceNotAvailable=內置資源: {0} 已被停用 com.oceanbase.odc.ErrorCodes.SqlInterceptApprovalRequired=無法直接執行此 SQL,需要發起審批流程 diff --git a/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcCreateDataTaskActionTest.java b/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcCreateDataTaskActionTest.java index 56d8badafa..c48ec4d6fc 100644 --- a/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcCreateDataTaskActionTest.java +++ b/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcCreateDataTaskActionTest.java @@ -80,11 +80,13 @@ public void init() { Mockito.when(systemConfigService.queryByKeyPrefix("odc.task-framework.k8s-properties.")).thenReturn( kubeConfigUrls); List oscConfigUrls = - Arrays.asList(new Configuration("odc.osc.k8s-properties.pod-pending-timeout-seconds", "600")); + Arrays.asList(new Configuration("odc.osc.k8s-properties.pod-pending-timeout-seconds", "600"), + new Configuration("odc.osc.k8s-properties.namespace", "namespace"), + new Configuration("odc.osc.k8s-properties.pod-image-name", "imageName")); Mockito.when(systemConfigService.queryByKeyPrefix("odc.osc.k8s-properties.")).thenReturn( oscConfigUrls); - Mockito.when(systemConfigService.queryByKeyPrefix("odc.osc.k8s-properties.")).thenReturn( + Mockito.when(systemConfigService.queryByKeyPrefix("odc.task-framework.k8s-properties.")).thenReturn( kubeConfigUrls); resourceManager = Mockito.mock(ResourceManager.class); createDataTaskAction = @@ -163,6 +165,8 @@ public void testChooseUrlReady() { public void testBuildK8sProperties() { K8sProperties k8sProperties = createDataTaskAction.buildK8sProperties(connectionConfig); Assert.assertEquals(k8sProperties.getKubeConfig(), "xxx"); + Assert.assertEquals(k8sProperties.getNamespace(), "namespace"); + Assert.assertEquals(k8sProperties.getPodImageName(), "imageName"); Assert.assertEquals(k8sProperties.getPodPendingTimeoutSeconds().longValue(), 600L); Assert.assertNull(k8sProperties.getExecutorListenPort()); Assert.assertEquals(k8sProperties.getSupervisorListenPort().intValue(), 18001); From c1422ed3eda4907c22ec0b449471031c3e8ab564 Mon Sep 17 00:00:00 2001 From: "longpeng.zlp" Date: Mon, 7 Apr 2025 14:56:28 +0800 Subject: [PATCH 4/4] introduce odc osc migrate logic --- .../oscfms/action/odc/OdcCreateDataTaskActionTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcCreateDataTaskActionTest.java b/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcCreateDataTaskActionTest.java index c48ec4d6fc..26c49deeb5 100644 --- a/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcCreateDataTaskActionTest.java +++ b/server/odc-service/src/test/java/com/oceanbase/odc/service/onlineschemachange/oscfms/action/odc/OdcCreateDataTaskActionTest.java @@ -81,8 +81,8 @@ public void init() { kubeConfigUrls); List oscConfigUrls = Arrays.asList(new Configuration("odc.osc.k8s-properties.pod-pending-timeout-seconds", "600"), - new Configuration("odc.osc.k8s-properties.namespace", "namespace"), - new Configuration("odc.osc.k8s-properties.pod-image-name", "imageName")); + new Configuration("odc.osc.k8s-properties.namespace", "namespace"), + new Configuration("odc.osc.k8s-properties.pod-image-name", "imageName")); Mockito.when(systemConfigService.queryByKeyPrefix("odc.osc.k8s-properties.")).thenReturn( oscConfigUrls);