private void vacuumTables(List<AnalyticsTable> tables) {
    ConcurrentLinkedQueue<AnalyticsTable> tableQ = new ConcurrentLinkedQueue<>(tables);

    List<Future<?>> futures = new ArrayList<>();

    for (int i = 0; i < getProcessNo(); i++) {
      tableManager.vacuumTablesAsync(tableQ);
    }

    ConcurrentUtils.waitForCompletion(futures);
  }
  private void populateTables(List<AnalyticsTable> tables) {
    int taskNo = Math.min(getProcessNo(), tables.size());

    log.info("Populate table task number: " + taskNo);

    ConcurrentLinkedQueue<AnalyticsTable> tableQ = new ConcurrentLinkedQueue<>(tables);

    List<Future<?>> futures = new ArrayList<>();

    for (int i = 0; i < taskNo; i++) {
      futures.add(tableManager.populateTableAsync(tableQ));
    }

    ConcurrentUtils.waitForCompletion(futures);
  }
  private void createIndexes(List<AnalyticsTable> tables) {
    ConcurrentLinkedQueue<AnalyticsIndex> indexes = new ConcurrentLinkedQueue<>();

    for (AnalyticsTable table : tables) {
      List<String[]> columns = table.getDimensionColumns();

      for (String[] column : columns) {
        indexes.add(new AnalyticsIndex(table.getTempTableName(), column[0]));
      }
    }

    log.info("No of analytics table indexes: " + indexes.size());

    List<Future<?>> futures = new ArrayList<>();

    for (int i = 0; i < getProcessNo(); i++) {
      futures.add(tableManager.createIndexesAsync(indexes));
    }

    ConcurrentUtils.waitForCompletion(futures);
  }
  private void applyAggregationLevels(List<AnalyticsTable> tables) {
    int maxLevels = organisationUnitService.getNumberOfOrganisationalLevels();

    boolean hasAggLevels = false;

    levelLoop:
    for (int i = 0; i < maxLevels; i++) {
      int level = maxLevels - i;

      Collection<String> dataElements =
          IdentifiableObjectUtils.getUids(
              dataElementService.getDataElementsByAggregationLevel(level));

      if (dataElements.isEmpty()) {
        continue levelLoop;
      }

      hasAggLevels = true;

      ConcurrentLinkedQueue<AnalyticsTable> tableQ = new ConcurrentLinkedQueue<>(tables);

      List<Future<?>> futures = new ArrayList<>();

      for (int j = 0; j < getProcessNo(); j++) {
        futures.add(tableManager.applyAggregationLevels(tableQ, dataElements, level));
      }

      ConcurrentUtils.waitForCompletion(futures);
    }

    if (hasAggLevels) {
      vacuumTables(tables);

      log.info("Vacuumed tables");
    }
  }