content: /*
   * Copyright (C) 2016-2023 ActionTech.
   * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher.
   */
  
  package com.actiontech.dble.statistic.sql;
  
  import com.actiontech.dble.config.model.SystemConfig;
  import com.actiontech.dble.services.manager.information.tables.statistic.AssociateTablesByEntryByUser;
  import com.actiontech.dble.services.manager.information.tables.statistic.FrontendByBackendByEntryByUser;
  import com.actiontech.dble.services.manager.information.tables.statistic.SqlLog;
  import com.actiontech.dble.services.manager.information.tables.statistic.TableByUserByEntry;
  import com.actiontech.dble.statistic.sql.entry.StatisticEntry;
  import com.actiontech.dble.statistic.sql.handler.*;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.Map;
  import java.util.Timer;
  import java.util.concurrent.ConcurrentLinkedQueue;
  import java.util.concurrent.locks.ReentrantReadWriteLock;
  
  public final class StatisticManager {
      private static final Logger LOGGER = LoggerFactory.getLogger(StatisticManager.class);
      private static final StatisticManager INSTANCE = new StatisticManager();
      private StatisticDisruptor disruptor;
      private static Map<String, StatisticDataHandler> statisticDataHandlers = new HashMap<>(8);
  
      private static ConcurrentLinkedQueue<UsageDataBlock> usageData = new ConcurrentLinkedQueue<>();
      private boolean isStart = false;
      private Timer queueMonitor;
      private static final ReentrantReadWriteLock MONITO_RLOCK = new ReentrantReadWriteLock();
  
      // variable
      private volatile boolean enable = SystemConfig.getInstance().getEnableStatistic() == 1;
      private volatile int associateTablesByEntryByUserTableSize = SystemConfig.getInstance().getAssociateTablesByEntryByUserTableSize();
      private volatile int frontendByBackendByEntryByUserTableSize = SystemConfig.getInstance().getFrontendByBackendByEntryByUserTableSize();
      private volatile int tableByUserByEntryTableSize = SystemConfig.getInstance().getTableByUserByEntryTableSize();
      private int statisticQueueSize = SystemConfig.getInstance().getStatisticQueueSize();
  
      // sampling
      private volatile int sqlLogSize = SystemConfig.getInstance().getSqlLogTableSize();
      private volatile int samplingRate = SystemConfig.getInstance().getSamplingRate();
  
      private volatile boolean enableAnalysis = SystemConfig.getInstance().getEnableStatisticAnalysis() == 1;
  
      private StatisticManager() {
          Runtime.getRuntime().addShutdownHook(new Thread(this::close));
      }
  
      public static StatisticManager getInstance() {
          return INSTANCE;
      }
  
      static {
          statisticDataHandlers.put(FrontendByBackendByEntryByUser.TABLE_NAME, new FrontendByBackendByEntryByUserCalcHandler());
          statisticDataHandlers.put(TableByUserByEntry.TABLE_NAME, new TableByUserByEntryCalcHandler());
          statisticDataHandlers.put(AssociateTablesByEntryByUser.TABLE_NAME, new AssociateTablesByEntryByUserCalcHandler());
          // sampling
          statisticDataHandlers.put(SqlLog.TABLE_NAME, new SqlStatisticHandler());
          statisticDataHandlers.put("analysis", new AnalysisHandler());
      }
  
      // start
      public void start() {
          statisticDataHandlers.values().forEach(StatisticDataHandler::clear);
          ArrayList list = new ArrayList<>(statisticDataHandlers.values());
          disruptor = new StatisticDisruptor(statisticQueueSize, (StatisticDataHandler[]) list.toArray(new StatisticDataHandler[list.size()]));
          isStart = true;
          if (LOGGER.isDebugEnabled()) {
              LOGGER.debug("start sql statistic success");
          }
      }
  
      // stop
      public void stop() {
          if (disruptor != null) {
              disruptor.stop();
              disruptor = null;
          }
          isStart = false;
          cancelMonitoring();
          if (LOGGER.isDebugEnabled()) {
              LOGGER.debug("stop sql statistic success");
          }
      }
  
      public void close() {
          stop();
          statisticDataHandlers.values().forEach(StatisticDataHandler::clear);
      }
  
      // push
      public void push(final StatisticEntry entry) {
          disruptor.push(entry);
      }
  
      public boolean isMonitoring() {
          MONITO_RLOCK.readLock().lock();
          try {
              return queueMonitor != null;
          } finally {
              MONITO_RLOCK.readLock().unlock();
          }
      }
  
      public Timer getQueueMonitor() {
          MONITO_RLOCK.writeLock().lock();
          try {
              if (disruptor == null)
                  return null;
              if (queueMonitor == null)
                  queueMonitor = new Timer("monitorStatisticQueue");
              return queueMonitor;
          } finally {
              MONITO_RLOCK.writeLock().unlock();
          }
      }
  
      public void cancelMonitoring() {
          MONITO_RLOCK.writeLock().lock();
          try {
              if (null != queueMonitor) {
                  queueMonitor.cancel();
                  queueMonitor = null;
                  if (LOGGER.isDebugEnabled())
                      LOGGER.debug("cancel queue monitor");
              }
          } finally {
              MONITO_RLOCK.writeLock().unlock();
          }
      }
  
      public ConcurrentLinkedQueue<UsageDataBlock> getUsageData() {
          return usageData;
      }
  
      public void resetUsageData() {
          usageData.clear();
      }
  
      public boolean mainSwitch() {
          return enable || samplingRate > 0 || enableAnalysis;
      }
  
      public boolean isEnable() {
          return enable;
      }
  
      public void setEnable(boolean enable) {
          this.enable = enable;
          if (enable && !isStart) {
              start();
              return;
          }
          if (!enable && (isStart && samplingRate == 0 && !enableAnalysis)) {
              stop();
          }
      }
  
      public int getAssociateTablesByEntryByUserTableSize() {
          return associateTablesByEntryByUserTableSize;
      }
  
      public void setAssociateTablesByEntryByUserTableSize(int associateTablesByEntryByUserTableSize) {
          this.associateTablesByEntryByUserTableSize = associateTablesByEntryByUserTableSize;
      }
  
      public int getFrontendByBackendByEntryByUserTableSize() {
          return frontendByBackendByEntryByUserTableSize;
      }
  
      public void setFrontendByBackendByEntryByUserTableSize(int frontendByBackendByEntryByUserTableSize) {
          this.frontendByBackendByEntryByUserTableSize = frontendByBackendByEntryByUserTableSize;
      }
  
      public int getTableByUserByEntryTableSize() {
          return tableByUserByEntryTableSize;
      }
  
      public void setTableByUserByEntryTableSize(int tableByUserByEntryTableSize) {
          this.tableByUserByEntryTableSize = tableByUserByEntryTableSize;
      }
  
      public int getSqlLogSize() {
          return sqlLogSize;
      }
  
      public void setSqlLogSize(int sqlLogSize) {
          this.sqlLogSize = sqlLogSize;
      }
  
      public void setSamplingRate(int samplingRate) {
          this.samplingRate = samplingRate;
          if (samplingRate > 0) {
              final SqlStatisticHandler handler = ((SqlStatisticHandler) statisticDataHandlers.get(SqlLog.TABLE_NAME));
              handler.setSampleDecisions(samplingRate);
              if (!isStart) {
                  start();
              }
              return;
          }
          if (samplingRate == 0 && (isStart && !enable && !enableAnalysis)) {
              stop();
          }
      }
  
      public boolean isPureRecordSql() {
          return !enable && (samplingRate > 0 || enableAnalysis);
      }
  
      public boolean isEnableAnalysis() {
          return enableAnalysis;
      }
  
      public void setEnableAnalysis(boolean enableAnalysis) {
          this.enableAnalysis = enableAnalysis;
          if (enableAnalysis && !isStart) {
              start();
              return;
          }
          if (!enableAnalysis && (isStart && samplingRate == 0 && !enable)) {
              stop();
          }
      }
  
      public int getSamplingRate() {
          return samplingRate;
      }
  
      public int getStatisticQueueSize() {
          return statisticQueueSize;
      }
  
      public long getDisruptorRemaining() {
          return disruptor.getDisruptor().getRingBuffer().remainingCapacity();
      }
  
      public StatisticDataHandler getHandler(String key) {
          return statisticDataHandlers.get(key);
      }
  }
  