content: /*
   * Copyright 1999-2018 Alibaba Group Holding Ltd.
   *
   * 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.alibaba.nacos.config.server.service.dump;
  
  import com.alibaba.nacos.config.server.manager.AbstractTask;
  import com.alibaba.nacos.config.server.manager.TaskProcessor;
  import com.alibaba.nacos.config.server.model.ConfigInfo;
  import com.alibaba.nacos.config.server.model.ConfigInfo4Beta;
  import com.alibaba.nacos.config.server.model.ConfigInfo4Tag;
  import com.alibaba.nacos.config.server.model.Page;
  import com.alibaba.nacos.config.server.service.*;
  import com.alibaba.nacos.config.server.service.PersistService.ConfigInfoBetaWrapper;
  import com.alibaba.nacos.config.server.service.PersistService.ConfigInfoTagWrapper;
  import com.alibaba.nacos.config.server.service.PersistService.ConfigInfoWrapper;
  import com.alibaba.nacos.config.server.service.trace.ConfigTraceService;
  import com.alibaba.nacos.config.server.utils.GroupKey2;
  import com.alibaba.nacos.config.server.utils.LogUtil;
  import com.alibaba.nacos.config.server.utils.MD5;
  import com.alibaba.nacos.config.server.utils.StringUtils;
  
  import java.sql.Timestamp;
  import java.util.List;
  
  import static com.alibaba.nacos.config.server.utils.LogUtil.defaultLog;
  
  /**
   * Dump data task
   *
   * @author Nacos
   */
  public class DumpTask extends AbstractTask {
  
      public DumpTask(String groupKey, long lastModified, String handleIp) {
          this.groupKey = groupKey;
          this.lastModified = lastModified;
          this.handleIp = handleIp;
          this.isBeta = false;
          this.tag = null;
          /**
           * retry interval: 1s
           */
          setTaskInterval(1000L);
      }
  
      public DumpTask(String groupKey, long lastModified, String handleIp, boolean isBeta) {
          this.groupKey = groupKey;
          this.lastModified = lastModified;
          this.handleIp = handleIp;
          this.isBeta = isBeta;
          this.tag = null;
          /**
           *  retry interval: 1s
           */
          setTaskInterval(1000L);
      }
  
      public DumpTask(String groupKey, String tag, long lastModified, String handleIp, boolean isBeta) {
          this.groupKey = groupKey;
          this.lastModified = lastModified;
          this.handleIp = handleIp;
          this.isBeta = isBeta;
          this.tag = tag;
          /**
           * retry interval: 1s
           */
          setTaskInterval(1000L);
      }
  
      @Override
      public void merge(AbstractTask task) {
      }
  
      final String groupKey;
      final long lastModified;
      final String handleIp;
      final boolean isBeta;
      final String tag;
  }
  
  class DumpAllTask extends AbstractTask {
      @Override
      public void merge(AbstractTask task) {
      }
  
      static final String TASK_ID = "dumpAllConfigTask";
  }
  
  class DumpAllBetaTask extends AbstractTask {
      @Override
      public void merge(AbstractTask task) {
      }
  
      static final String TASK_ID = "dumpAllBetaConfigTask";
  }
  
  class DumpAllTagTask extends AbstractTask {
      @Override
      public void merge(AbstractTask task) {
      }
  
      static final String TASK_ID = "dumpAllTagConfigTask";
  }
  
  class DumpChangeTask extends AbstractTask {
      @Override
      public void merge(AbstractTask task) {
      }
  
      static final String TASK_ID = "dumpChangeConfigTask";
  }
  
  class DumpProcessor implements TaskProcessor {
  
      DumpProcessor(DumpService dumpService) {
          this.dumpService = dumpService;
      }
  
      @Override
      public boolean process(String taskType, AbstractTask task) {
          DumpTask dumpTask = (DumpTask)task;
          String[] pair = GroupKey2.parseKey(dumpTask.groupKey);
          String dataId = pair[0];
          String group = pair[1];
          String tenant = pair[2];
          long lastModified = dumpTask.lastModified;
          String handleIp = dumpTask.handleIp;
          boolean isBeta = dumpTask.isBeta;
          String tag = dumpTask.tag;
          if (isBeta) {
              // beta发布，则dump数据，更新beta缓存
              ConfigInfo4Beta cf = dumpService.persistService.findConfigInfo4Beta(dataId, group, tenant);
              boolean result;
              if (null != cf) {
                  result = ConfigService.dumpBeta(dataId, group, tenant, cf.getContent(), lastModified, cf.getBetaIps());
                  if (result) {
                      ConfigTraceService.logDumpEvent(dataId, group, tenant, null, lastModified, handleIp,
                          ConfigTraceService.DUMP_EVENT_OK, System.currentTimeMillis() - lastModified,
                          cf.getContent().length());
                  }
              } else {
                  result = ConfigService.removeBeta(dataId, group, tenant);
                  if (result) {
                      ConfigTraceService.logDumpEvent(dataId, group, tenant, null, lastModified, handleIp,
                          ConfigTraceService.DUMP_EVENT_REMOVE_OK, System.currentTimeMillis() - lastModified, 0);
                  }
              }
              return result;
          } else {
              if (StringUtils.isBlank(tag)) {
                  ConfigInfo cf = dumpService.persistService.findConfigInfo(dataId, group, tenant);
                  if (dataId.equals(AggrWhitelist.AGGRIDS_METADATA)) {
                      if (null != cf) {
                          AggrWhitelist.load(cf.getContent());
                      } else {
                          AggrWhitelist.load(null);
                      }
                  }
  
                  if (dataId.equals(ClientIpWhiteList.CLIENT_IP_WHITELIST_METADATA)) {
                      if (null != cf) {
                          ClientIpWhiteList.load(cf.getContent());
                      } else {
                          ClientIpWhiteList.load(null);
                      }
                  }
  
                  if (dataId.equals(SwitchService.SWITCH_META_DATAID)) {
                      if (null != cf) {
                          SwitchService.load(cf.getContent());
                      } else {
                          SwitchService.load(null);
                      }
                  }
  
                  boolean result;
                  if (null != cf) {
                      result = ConfigService.dump(dataId, group, tenant, cf.getContent(), lastModified);
  
                      if (result) {
                          ConfigTraceService.logDumpEvent(dataId, group, tenant, null, lastModified, handleIp,
                              ConfigTraceService.DUMP_EVENT_OK, System.currentTimeMillis() - lastModified,
                              cf.getContent().length());
                      }
                  } else {
                      result = ConfigService.remove(dataId, group, tenant);
  
                      if (result) {
                          ConfigTraceService.logDumpEvent(dataId, group, tenant, null, lastModified, handleIp,
                              ConfigTraceService.DUMP_EVENT_REMOVE_OK, System.currentTimeMillis() - lastModified, 0);
                      }
                  }
                  return result;
              } else {
                  ConfigInfo4Tag cf = dumpService.persistService.findConfigInfo4Tag(dataId, group, tenant, tag);
                  //
                  boolean result;
                  if (null != cf) {
                      result = ConfigService.dumpTag(dataId, group, tenant, tag, cf.getContent(), lastModified);
                      if (result) {
                          ConfigTraceService.logDumpEvent(dataId, group, tenant, null, lastModified, handleIp,
                              ConfigTraceService.DUMP_EVENT_OK, System.currentTimeMillis() - lastModified,
                              cf.getContent().length());
                      }
                  } else {
                      result = ConfigService.removeTag(dataId, group, tenant, tag);
                      if (result) {
                          ConfigTraceService.logDumpEvent(dataId, group, tenant, null, lastModified, handleIp,
                              ConfigTraceService.DUMP_EVENT_REMOVE_OK, System.currentTimeMillis() - lastModified, 0);
                      }
                  }
                  return result;
              }
          }
  
      }
  
      final DumpService dumpService;
  }
  
  class DumpAllProcessor implements TaskProcessor {
  
      DumpAllProcessor(DumpService dumpService) {
          this.dumpService = dumpService;
          this.persistService = dumpService.persistService;
      }
  
      @Override
      public boolean process(String taskType, AbstractTask task) {
          long currentMaxId = persistService.findConfigMaxId();
          long lastMaxId = 0;
          while (lastMaxId < currentMaxId) {
              Page<PersistService.ConfigInfoWrapper> page = persistService.findAllConfigInfoFragment(lastMaxId,
                  PAGE_SIZE);
              if (page != null && page.getPageItems() != null) {
                  for (PersistService.ConfigInfoWrapper cf : page.getPageItems()) {
                      long id = cf.getId();
                      lastMaxId = id > lastMaxId ? id : lastMaxId;
                      if (cf.getDataId().equals(AggrWhitelist.AGGRIDS_METADATA)) {
                          AggrWhitelist.load(cf.getContent());
                      }
  
                      if (cf.getDataId().equals(ClientIpWhiteList.CLIENT_IP_WHITELIST_METADATA)) {
                          ClientIpWhiteList.load(cf.getContent());
                      }
  
                      if (cf.getDataId().equals(SwitchService.SWITCH_META_DATAID)) {
                          SwitchService.load(cf.getContent());
                      }
  
                      boolean result = ConfigService.dump(cf.getDataId(), cf.getGroup(), cf.getTenant(), cf.getContent(),
                          cf.getLastModified());
  
                      final String content = cf.getContent();
                      final String md5 = MD5.getInstance().getMD5String(content);
                      LogUtil.dumpLog.info("[dump-all-ok] {}, {}, length={}, md5={}",
                          GroupKey2.getKey(cf.getDataId(), cf.getGroup()), cf.getLastModified(), content.length(), md5);
                  }
                  defaultLog.info("[all-dump] {} / {}", lastMaxId, currentMaxId);
              } else {
                  lastMaxId += PAGE_SIZE;
              }
          }
          return true;
      }
  
      static final int PAGE_SIZE = 1000;
  
      final DumpService dumpService;
      final PersistService persistService;
  }
  
  class DumpAllBetaProcessor implements TaskProcessor {
  
      DumpAllBetaProcessor(DumpService dumpService) {
          this.dumpService = dumpService;
          this.persistService = dumpService.persistService;
      }
  
      @Override
      public boolean process(String taskType, AbstractTask task) {
          int rowCount = persistService.configInfoBetaCount();
          int pageCount = (int)Math.ceil(rowCount * 1.0 / PAGE_SIZE);
  
          int actualRowCount = 0;
          for (int pageNo = 1; pageNo <= pageCount; pageNo++) {
              Page<ConfigInfoBetaWrapper> page = persistService.findAllConfigInfoBetaForDumpAll(pageNo, PAGE_SIZE);
              if (page != null) {
                  for (ConfigInfoBetaWrapper cf : page.getPageItems()) {
                      boolean result = ConfigService.dumpBeta(cf.getDataId(), cf.getGroup(), cf.getTenant(),
                          cf.getContent(), cf.getLastModified(), cf.getBetaIps());
                      LogUtil.dumpLog.info("[dump-all-beta-ok] result={}, {}, {}, length={}, md5={}", result,
                          GroupKey2.getKey(cf.getDataId(), cf.getGroup()), cf.getLastModified(), cf.getContent()
                              .length(), cf.getMd5());
                  }
  
                  actualRowCount += page.getPageItems().size();
                  defaultLog.info("[all-dump-beta] {} / {}", actualRowCount, rowCount);
              }
          }
          return true;
      }
  
      static final int PAGE_SIZE = 1000;
  
      final DumpService dumpService;
      final PersistService persistService;
  }
  
  class DumpAllTagProcessor implements TaskProcessor {
  
      DumpAllTagProcessor(DumpService dumpService) {
          this.dumpService = dumpService;
          this.persistService = dumpService.persistService;
      }
  
      @Override
      public boolean process(String taskType, AbstractTask task) {
          int rowCount = persistService.configInfoTagCount();
          int pageCount = (int)Math.ceil(rowCount * 1.0 / PAGE_SIZE);
  
          int actualRowCount = 0;
          for (int pageNo = 1; pageNo <= pageCount; pageNo++) {
              Page<ConfigInfoTagWrapper> page = persistService.findAllConfigInfoTagForDumpAll(pageNo, PAGE_SIZE);
              if (page != null) {
                  for (ConfigInfoTagWrapper cf : page.getPageItems()) {
                      boolean result = ConfigService.dumpTag(cf.getDataId(), cf.getGroup(), cf.getTenant(), cf.getTag(),
                          cf.getContent(), cf.getLastModified());
                      LogUtil.dumpLog.info("[dump-all-Tag-ok] result={}, {}, {}, length={}, md5={}", result,
                          GroupKey2.getKey(cf.getDataId(), cf.getGroup()), cf.getLastModified(), cf.getContent()
                              .length(), cf.getMd5());
                  }
  
                  actualRowCount += page.getPageItems().size();
                  defaultLog.info("[all-dump-tag] {} / {}", actualRowCount, rowCount);
              }
          }
          return true;
      }
  
      static final int PAGE_SIZE = 1000;
  
      final DumpService dumpService;
      final PersistService persistService;
  }
  
  class DumpChangeProcessor implements TaskProcessor {
  
      DumpChangeProcessor(DumpService dumpService, Timestamp startTime,
                          Timestamp endTime) {
          this.dumpService = dumpService;
          this.persistService = dumpService.persistService;
          this.startTime = startTime;
          this.endTime = endTime;
      }
  
      @Override
      public boolean process(String taskType, AbstractTask task) {
          LogUtil.defaultLog.warn("quick start; startTime:{},endTime:{}",
              startTime, endTime);
          LogUtil.defaultLog.warn("updateMd5 start");
          long startUpdateMd5 = System.currentTimeMillis();
          List<ConfigInfoWrapper> updateMd5List = persistService
              .listAllGroupKeyMd5();
          LogUtil.defaultLog.warn("updateMd5 count:{}", updateMd5List.size());
          for (ConfigInfoWrapper config : updateMd5List) {
              final String groupKey = GroupKey2.getKey(config.getDataId(),
                  config.getGroup());
              ConfigService.updateMd5(groupKey, config.getMd5(),
                  config.getLastModified());
          }
          long endUpdateMd5 = System.currentTimeMillis();
          LogUtil.defaultLog.warn("updateMd5 done,cost:{}", endUpdateMd5
              - startUpdateMd5);
  
          LogUtil.defaultLog.warn("deletedConfig start");
          long startDeletedConfigTime = System.currentTimeMillis();
          List<ConfigInfo> configDeleted = persistService.findDeletedConfig(
              startTime, endTime);
          LogUtil.defaultLog.warn("deletedConfig count:{}", configDeleted.size());
          for (ConfigInfo configInfo : configDeleted) {
              if (persistService.findConfigInfo(configInfo.getDataId(), configInfo.getGroup(),
                  configInfo.getTenant()) == null) {
                  ConfigService.remove(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant());
              }
          }
          long endDeletedConfigTime = System.currentTimeMillis();
          LogUtil.defaultLog.warn("deletedConfig done,cost:{}",
              endDeletedConfigTime - startDeletedConfigTime);
  
          LogUtil.defaultLog.warn("changeConfig start");
          long startChangeConfigTime = System.currentTimeMillis();
          List<PersistService.ConfigInfoWrapper> changeConfigs = persistService
              .findChangeConfig(startTime, endTime);
          LogUtil.defaultLog.warn("changeConfig count:{}", changeConfigs.size());
          for (PersistService.ConfigInfoWrapper cf : changeConfigs) {
              boolean result = ConfigService.dumpChange(cf.getDataId(), cf.getGroup(), cf.getTenant(),
                  cf.getContent(), cf.getLastModified());
              final String content = cf.getContent();
              final String md5 = MD5.getInstance().getMD5String(content);
              LogUtil.defaultLog.info(
                  "[dump-change-ok] {}, {}, length={}, md5={}",
                  new Object[] {
                      GroupKey2.getKey(cf.getDataId(), cf.getGroup()),
                      cf.getLastModified(), content.length(), md5});
          }
          ConfigService.reloadConfig();
          long endChangeConfigTime = System.currentTimeMillis();
          LogUtil.defaultLog.warn("changeConfig done,cost:{}",
              endChangeConfigTime - startChangeConfigTime);
          return true;
      }
  
      // =====================
  
      final DumpService dumpService;
      final PersistService persistService;
      final Timestamp startTime;
      final Timestamp endTime;
  }
  