diff --git a/core/src/main/java/com/ctrip/xpipe/cluster/ClusterType.java b/core/src/main/java/com/ctrip/xpipe/cluster/ClusterType.java index e3d5f471d..62c8ef39c 100644 --- a/core/src/main/java/com/ctrip/xpipe/cluster/ClusterType.java +++ b/core/src/main/java/com/ctrip/xpipe/cluster/ClusterType.java @@ -100,6 +100,13 @@ public static boolean supportUpgradeAzGroup(String type) { || isSameClusterType(type, SINGLE_DC); } + public static boolean supportConvert(String type) { + if (StringUtil.isEmpty(type)) { + return false; + } + return isSameClusterType(type, ONE_WAY) || isSameClusterType(type, HETERO); + } + public static ClusterType lookup(String name) { if (StringUtil.isEmpty(name)) throw new IllegalArgumentException("no ClusterType for name " + name); return valueOf(name.toUpperCase()); diff --git a/redis/redis-console/src/main/java/com/ctrip/xpipe/redis/console/controller/api/data/ClusterUpdateController.java b/redis/redis-console/src/main/java/com/ctrip/xpipe/redis/console/controller/api/data/ClusterUpdateController.java index 7148115c3..2758a485f 100644 --- a/redis/redis-console/src/main/java/com/ctrip/xpipe/redis/console/controller/api/data/ClusterUpdateController.java +++ b/redis/redis-console/src/main/java/com/ctrip/xpipe/redis/console/controller/api/data/ClusterUpdateController.java @@ -277,6 +277,12 @@ public RetMessage upgradeAzGroup(@PathVariable String clusterName) { return RetMessage.createSuccessMessage(); } + @DeleteMapping(value = "/clusters/" + CLUSTER_NAME_PATH_VARIABLE + "/azGroup") + public RetMessage downgradeAzGroup(@PathVariable String clusterName) { + clusterService.downgradeAzGroup(clusterName); + return RetMessage.createSuccessMessage(); + } + @PostMapping(value = "/clusters/" + CLUSTER_NAME_PATH_VARIABLE + "/dcs/{dcName}") public RetMessage bindDc(@PathVariable String clusterName, @PathVariable String dcName, @RequestBody(required = false) DcDetailInfo dcDetailInfo) { diff --git a/redis/redis-console/src/main/java/com/ctrip/xpipe/redis/console/entity/AzGroupClusterEntity.java b/redis/redis-console/src/main/java/com/ctrip/xpipe/redis/console/entity/AzGroupClusterEntity.java index 36604612f..b815e300b 100644 --- a/redis/redis-console/src/main/java/com/ctrip/xpipe/redis/console/entity/AzGroupClusterEntity.java +++ b/redis/redis-console/src/main/java/com/ctrip/xpipe/redis/console/entity/AzGroupClusterEntity.java @@ -14,7 +14,7 @@ *

* * @author mybatis-generator - * @since 2023-07-10 + * @since 2023-11-14 */ @TableName("az_group_cluster_tbl") public class AzGroupClusterEntity extends BaseEntity { @@ -57,6 +57,12 @@ public class AzGroupClusterEntity extends BaseEntity { @TableField(value = "create_time", fill = FieldFill.INSERT) private Date createTime; + /** + * 逻辑删除时间 + */ + @TableField("delete_time") + private Date deleteTime; + public Long getId() { return id; } @@ -111,6 +117,15 @@ public AzGroupClusterEntity setCreateTime(Date createTime) { return this; } + public Date getDeleteTime() { + return deleteTime; + } + + public AzGroupClusterEntity setDeleteTime(Date deleteTime) { + this.deleteTime = deleteTime; + return this; + } + public static final String ID = "id"; public static final String CLUSTER_ID = "cluster_id"; @@ -123,10 +138,12 @@ public AzGroupClusterEntity setCreateTime(Date createTime) { public static final String CREATE_TIME = "create_time"; + public static final String DELETE_TIME = "delete_time"; + @Override public String toString() { return "AzGroupClusterEntity{" + "id = " + id + ", clusterId = " + clusterId + ", azGroupId = " + azGroupId + ", activeAzId = " + activeAzId + ", azGroupClusterType = " + azGroupClusterType + ", createTime = " - + createTime + "}"; + + createTime + ", deleteTime = " + deleteTime + "}"; } } diff --git a/redis/redis-console/src/main/java/com/ctrip/xpipe/redis/console/repository/AzGroupClusterRepository.java b/redis/redis-console/src/main/java/com/ctrip/xpipe/redis/console/repository/AzGroupClusterRepository.java index c63e3f883..353df7f8c 100644 --- a/redis/redis-console/src/main/java/com/ctrip/xpipe/redis/console/repository/AzGroupClusterRepository.java +++ b/redis/redis-console/src/main/java/com/ctrip/xpipe/redis/console/repository/AzGroupClusterRepository.java @@ -13,6 +13,7 @@ import javax.annotation.Resource; import java.util.Collections; +import java.util.Date; import java.util.List; @Repository @@ -148,17 +149,24 @@ public void deleteByClusterId(Long clusterId) { if (clusterId == null) { return; } - QueryWrapper wrapper = new QueryWrapper<>(); + UpdateWrapper wrapper = new UpdateWrapper<>(); + wrapper.set(AzGroupClusterEntity.DELETED, 1); + wrapper.set(AzGroupClusterEntity.DELETE_TIME, new Date()); + wrapper.eq(AzGroupClusterEntity.CLUSTER_ID, clusterId); - azGroupClusterMapper.delete(wrapper); + azGroupClusterMapper.update(null, wrapper); } public void deleteByClusterIdAndAzGroupId(Long clusterId, Long azGroupId) { if (clusterId == null || azGroupId == null) { return; } - QueryWrapper wrapper = new QueryWrapper<>(); - wrapper.eq(AzGroupClusterEntity.CLUSTER_ID, clusterId).eq(AzGroupClusterEntity.AZ_GROUP_ID, azGroupId); - azGroupClusterMapper.delete(wrapper); + UpdateWrapper wrapper = new UpdateWrapper<>(); + wrapper.set(AzGroupClusterEntity.DELETED, 1); + wrapper.set(AzGroupClusterEntity.DELETE_TIME, new Date()); + + wrapper.eq(AzGroupClusterEntity.CLUSTER_ID, clusterId); + wrapper.eq(AzGroupClusterEntity.AZ_GROUP_ID, azGroupId); + azGroupClusterMapper.update(null, wrapper); } } diff --git a/redis/redis-console/src/main/java/com/ctrip/xpipe/redis/console/service/ClusterService.java b/redis/redis-console/src/main/java/com/ctrip/xpipe/redis/console/service/ClusterService.java index 03c60805e..d15760374 100644 --- a/redis/redis-console/src/main/java/com/ctrip/xpipe/redis/console/service/ClusterService.java +++ b/redis/redis-console/src/main/java/com/ctrip/xpipe/redis/console/service/ClusterService.java @@ -55,6 +55,7 @@ public interface ClusterService { void exchangeName(Long formerClusterId, String formerClusterName, Long latterClusterId, String latterClusterName); void exchangeRegion(Long formerClusterId, String formerClusterName, Long latterClusterId, String latterClusterName, String regionName); void upgradeAzGroup(String clusterName); + void downgradeAzGroup(String clusterName); void bindRegionAz(String clusterName, String regionName, String azName); Set findMigratingClusterNames(); diff --git a/redis/redis-console/src/main/java/com/ctrip/xpipe/redis/console/service/impl/ClusterServiceImpl.java b/redis/redis-console/src/main/java/com/ctrip/xpipe/redis/console/service/impl/ClusterServiceImpl.java index 3d213aafc..e4004398d 100644 --- a/redis/redis-console/src/main/java/com/ctrip/xpipe/redis/console/service/impl/ClusterServiceImpl.java +++ b/redis/redis-console/src/main/java/com/ctrip/xpipe/redis/console/service/impl/ClusterServiceImpl.java @@ -654,49 +654,47 @@ private List setOrgNullIfNoOrgIdExsits(List clusterTblLi return clusterTblList; } - @Override - public String updateCluster(ClusterUpdateDTO clusterUpdateDTO) { - String clusterName = clusterUpdateDTO.getClusterName(); - ClusterTbl clusterTbl = clusterDao.findClusterByClusterName(clusterName); - if (clusterTbl == null) { - throw new BadRequestException("Not find cluster: " + clusterName); - } + @Override + public String updateCluster(ClusterUpdateDTO clusterUpdateDTO) { + String clusterName = clusterUpdateDTO.getClusterName(); + ClusterTbl clusterTbl = clusterDao.findClusterByClusterName(clusterName); + if (clusterTbl == null) { + throw new BadRequestException("Not find cluster: " + clusterName); + } boolean needUpdate = false; - ClusterType clusterType = StringUtils.isEmpty(clusterUpdateDTO.getClusterType()) - ? null : ClusterType.lookup(clusterUpdateDTO.getClusterType()); - if (clusterType != null) { + String clusterType = clusterUpdateDTO.getClusterType(); + if (!StringUtil.isEmpty(clusterType)) { String oldClusterType = clusterTbl.getClusterType(); - if (!ClusterType.isSameClusterType(oldClusterType, clusterType)) { - if (ClusterType.isSameClusterType(oldClusterType, ClusterType.ONE_WAY) - && clusterType == ClusterType.HETERO) { + if (!clusterType.equalsIgnoreCase(oldClusterType)) { + if (ClusterType.supportConvert(oldClusterType) && ClusterType.supportConvert(clusterType)) { needUpdate = true; - clusterTbl.setClusterType(clusterType.toString()); + clusterTbl.setClusterType(clusterType.toUpperCase()); } else { - // 仅允许单向同步集群改为异构集群 - throw new BadRequestException("Only ONE_WAY cluster can change type to HETERO"); + // 仅允许单向同步、异构集群之间互相转换 + throw new BadRequestException("Only ONE_WAY/HETERO cluster can be converted to each other"); } } } Long orgId = clusterUpdateDTO.getOrgId(); - if (!Objects.equals(orgId, clusterTbl.getClusterOrgId())) { + if (orgId != null && !Objects.equals(orgId, clusterTbl.getClusterOrgId())) { needUpdate = true; clusterTbl.setClusterOrgId(orgId); } String adminEmails = clusterUpdateDTO.getAdminEmails(); - if (!Objects.equals(adminEmails, clusterTbl.getClusterAdminEmails())) { + if (adminEmails != null && !Objects.equals(adminEmails, clusterTbl.getClusterAdminEmails())) { needUpdate = true; clusterTbl.setClusterAdminEmails(adminEmails); } - if (needUpdate) { - clusterDao.updateCluster(clusterTbl); - return RetMessage.SUCCESS; - } else { - return String.format("No field changes for cluster: %s", clusterName); - } - } + if (needUpdate) { + clusterDao.updateCluster(clusterTbl); + return RetMessage.SUCCESS; + } else { + return String.format("No field changes for cluster: %s", clusterName); + } + } @Override @DalTransaction @@ -1064,7 +1062,7 @@ public Integer doQuery() throws DalException { @Override @Transactional public void upgradeAzGroup(String clusterName) { - ClusterTbl cluster = clusterDao.findClusterByClusterName(clusterName); + ClusterEntity cluster = clusterRepository.selectByClusterName(clusterName); if (cluster == null) { throw new BadRequestException(String.format("Cluster: %s not exist", clusterName)); } @@ -1126,6 +1124,34 @@ public void upgradeAzGroup(String clusterName) { } } + @Override + @Transactional + public void downgradeAzGroup(String clusterName) { + ClusterEntity cluster = clusterRepository.selectByClusterName(clusterName); + if (cluster == null) { + throw new BadRequestException(String.format("Cluster: %s not exist", clusterName)); + } + Long clusterId = cluster.getId(); + List azGroupClusters = azGroupClusterRepository.selectByClusterId(clusterId); + if (CollectionUtils.isEmpty(azGroupClusters)) { + logger.warn("Cluster: {} not in AzGroup mode", clusterName); + return; + } + if (azGroupClusters.size() > 1) { + throw new BadRequestException( + String.format("Cluster: %s contains multi az group, cannot downgrade", clusterName)); + } + azGroupClusterRepository.deleteByClusterId(clusterId); + + List dcClusters = dcClusterRepository.selectByClusterId(clusterId); + List dcClusterIds = dcClusters.stream().map(DcClusterEntity::getDcClusterId).collect(Collectors.toList()); + dcClusterRepository.batchUpdateAzGroupClusterId(dcClusterIds, 0L); + + List shards = shardRepository.selectByClusterId(clusterId); + List shardIds = shards.stream().map(ShardEntity::getId).collect(Collectors.toList()); + shardRepository.batchUpdateAzGroupClusterId(shardIds, 0L); + } + @Override @DalTransaction public void exchangeName(Long formerClusterId, String formerClusterName, Long latterClusterId, String latterClusterName) { diff --git a/redis/redis-console/src/main/resources/sql/h2/xpipedemodbtables.sql b/redis/redis-console/src/main/resources/sql/h2/xpipedemodbtables.sql index deaf05aea..56bde1add 100644 --- a/redis/redis-console/src/main/resources/sql/h2/xpipedemodbtables.sql +++ b/redis/redis-console/src/main/resources/sql/h2/xpipedemodbtables.sql @@ -112,6 +112,7 @@ CREATE TABLE `AZ_GROUP_CLUSTER_TBL` ( `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'create time', `DataChange_LastTime` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'last modify time', `deleted` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT 'logic delete', + `delete_time` timestamp NOT NULL DEFAULT '2003-12-09 09:30:00' COMMENT 'logic delete time', PRIMARY KEY (`id`), UNIQUE KEY `uk_cluster_az_group` (`cluster_id`,`az_group_id`) ); diff --git a/redis/redis-console/src/test/java/com/ctrip/xpipe/redis/console/MybatisGeneratorTest.java b/redis/redis-console/src/test/java/com/ctrip/xpipe/redis/console/MybatisGeneratorTest.java index 75a1d8c4b..65547388f 100644 --- a/redis/redis-console/src/test/java/com/ctrip/xpipe/redis/console/MybatisGeneratorTest.java +++ b/redis/redis-console/src/test/java/com/ctrip/xpipe/redis/console/MybatisGeneratorTest.java @@ -52,7 +52,7 @@ protected static PackageConfig packageConfig() { */ protected static StrategyConfig strategyConfig() { return new StrategyConfig.Builder() - .addInclude("cluster_tbl", "shard_tbl") + .addInclude("az_group_cluster_tbl") // 删除tbl后缀 .addTableSuffix("_tbl") .entityBuilder() diff --git a/redis/redis-console/src/test/java/com/ctrip/xpipe/redis/console/controller/api/data/ClusterUpdateControllerTest.java b/redis/redis-console/src/test/java/com/ctrip/xpipe/redis/console/controller/api/data/ClusterUpdateControllerTest.java index 50066d813..fa8f6b1f0 100644 --- a/redis/redis-console/src/test/java/com/ctrip/xpipe/redis/console/controller/api/data/ClusterUpdateControllerTest.java +++ b/redis/redis-console/src/test/java/com/ctrip/xpipe/redis/console/controller/api/data/ClusterUpdateControllerTest.java @@ -203,19 +203,28 @@ public void updateCluster() { clusterInfo.setClusterName(clusterName); clusterInfo.setClusterType(ClusterType.HETERO.toString()); clusterInfo.setDesc(desc); - clusterInfo.setOrganizationId(orgId); - clusterInfo.setClusterAdminEmails(adminEmails); RetMessage retMessage = clusterController.updateCluster(clusterInfo); logger.info("{}", retMessage.getMessage()); Assert.assertEquals(RetMessage.SUCCESS_STATE, retMessage.getState()); ClusterTbl cluster = clusterDao.findClusterAndOrgByName(clusterName); - Assert.assertEquals(orgId, cluster.getOrganizationInfo().getOrgId()); + Assert.assertEquals(ClusterType.HETERO.toString(), cluster.getClusterType()); // 目前api接口不更新desc Assert.assertEquals("", cluster.getClusterDescription()); + Assert.assertEquals(0L, cluster.getClusterOrgId()); + Assert.assertNull(cluster.getClusterAdminEmails()); + + clusterInfo.setOrganizationId(orgId); + clusterController.updateCluster(clusterInfo); + cluster = clusterDao.findClusterAndOrgByName(clusterName); + Assert.assertEquals(orgId, cluster.getOrganizationInfo().getOrgId()); + Assert.assertNull(cluster.getClusterAdminEmails()); + + clusterInfo.setClusterAdminEmails(adminEmails); + clusterController.updateCluster(clusterInfo); + cluster = clusterDao.findClusterAndOrgByName(clusterName); Assert.assertEquals(adminEmails, cluster.getClusterAdminEmails()); - Assert.assertEquals(ClusterType.HETERO.toString(), cluster.getClusterType()); } @Test @@ -404,6 +413,17 @@ public void testUpgradeBiClusterToAzGroup() { Assert.assertEquals(Collections.singletonList("oy"), region2.getAzs()); } + @Test + public void testDowngradeAzGroupToOnewayCluster() { + this.createCluster(null, "HETERO", Arrays.asList("jq", "oy", "fra"), Arrays.asList(region1, region2)); + clusterController.unbindDc("cluster-name", "fra"); + + clusterController.downgradeAzGroup("cluster-name"); + ClusterCreateInfo cluster = clusterController.getCluster("cluster-name"); + Assert.assertEquals(0, cluster.getRegions().size()); + Assert.assertEquals(Arrays.asList("jq", "oy"), cluster.getDcs()); + } + @Test public void testBindDc() { this.createCluster(null, null, null, null);