All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.solr.handler.CdcrReplicatorState Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.apache.solr.handler;

import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.solr.client.solrj.impl.CloudSolrClient;
import org.apache.solr.update.CdcrUpdateLog;
import org.apache.solr.update.UpdateLog;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The state of the replication with a target cluster.
 */
class CdcrReplicatorState {

  private final String targetCollection;
  private final String zkHost;
  private final CloudSolrClient targetClient;

  private CdcrUpdateLog.CdcrLogReader logReader;

  private long consecutiveErrors = 0;
  private final Map errorCounters = new HashMap<>();
  private final FixedQueue errorsQueue = new FixedQueue<>(100); // keep the last 100 errors

  private BenchmarkTimer benchmarkTimer;

  private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

  private final AtomicBoolean bootstrapInProgress = new AtomicBoolean(false);
  private final AtomicInteger numBootstraps = new AtomicInteger();

  CdcrReplicatorState(final String targetCollection, final String zkHost, final CloudSolrClient targetClient) {
    this.targetCollection = targetCollection;
    this.targetClient = targetClient;
    this.zkHost = zkHost;
    this.benchmarkTimer = new BenchmarkTimer();
  }

  /**
   * Initialise the replicator state with a {@link org.apache.solr.update.CdcrUpdateLog.CdcrLogReader}
   * that is positioned at the last target cluster checkpoint.
   */
  void init(final CdcrUpdateLog.CdcrLogReader logReader) {
    this.logReader = logReader;
  }

  void closeLogReader() {
    if (logReader != null) {
      logReader.close();
      logReader = null;
    }
  }

  CdcrUpdateLog.CdcrLogReader getLogReader() {
    return logReader;
  }

  String getTargetCollection() {
    return targetCollection;
  }

  String getZkHost() {
    return zkHost;
  }

  CloudSolrClient getClient() {
    return targetClient;
  }

  void shutdown() {
    try {
      targetClient.close();
    } catch (IOException ioe) {
      log.warn("Caught exception trying to close server: ", ioe.getMessage());
    }
    this.closeLogReader();
  }

  void reportError(ErrorType error) {
    if (!errorCounters.containsKey(error)) {
      errorCounters.put(error, 0l);
    }
    errorCounters.put(error, errorCounters.get(error) + 1);
    errorsQueue.add(new ErrorQueueEntry(error, new Date()));
    consecutiveErrors++;
  }

  void resetConsecutiveErrors() {
    consecutiveErrors = 0;
  }

  /**
   * Returns the number of consecutive errors encountered while trying to forward updates to the target.
   */
  long getConsecutiveErrors() {
    return consecutiveErrors;
  }

  /**
   * Gets the number of errors of a particular type.
   */
  long getErrorCount(ErrorType type) {
    if (errorCounters.containsKey(type)) {
      return errorCounters.get(type);
    } else {
      return 0;
    }
  }

  /**
   * Gets the last errors ordered by timestamp (most recent first)
   */
  List getLastErrors() {
    List lastErrors = new ArrayList<>();
    synchronized (errorsQueue) {
      Iterator it = errorsQueue.iterator();
      while (it.hasNext()) {
        ErrorQueueEntry entry = it.next();
        lastErrors.add(new String[]{entry.timestamp.toInstant().toString(), entry.type.toLower()});
      }
    }
    return lastErrors;
  }

  /**
   * Return the timestamp of the last processed operations
   */
  String getTimestampOfLastProcessedOperation() {
    if (logReader != null && logReader.getLastVersion() != -1) {
      // Shift back to the right by 20 bits the version number - See VersionInfo#getNewClock
      return Instant.ofEpochMilli(logReader.getLastVersion() >> 20).toString();
    }
    return "";
  }

  /**
   * Gets the benchmark timer.
   */
  BenchmarkTimer getBenchmarkTimer() {
    return this.benchmarkTimer;
  }

  /**
   * @return true if a bootstrap operation is in progress, false otherwise
   */
  boolean isBootstrapInProgress() {
    return bootstrapInProgress.get();
  }

  void setBootstrapInProgress(boolean inProgress) {
    if (bootstrapInProgress.compareAndSet(true, false)) {
      numBootstraps.incrementAndGet();
    }
    bootstrapInProgress.set(inProgress);
  }

  public int getNumBootstraps() {
    return numBootstraps.get();
  }

  enum ErrorType {
    INTERNAL,
    BAD_REQUEST;

    public String toLower() {
      return toString().toLowerCase(Locale.ROOT);
    }

  }

  static class BenchmarkTimer {

    private long startTime;
    private long runTime = 0;
    private Map opCounters = new HashMap<>();

    /**
     * Start recording time.
     */
    void start() {
      startTime = System.nanoTime();
    }

    /**
     * Stop recording time.
     */
    void stop() {
      runTime += System.nanoTime() - startTime;
      startTime = -1;
    }

    void incrementCounter(final int operationType) {
      switch (operationType) {
        case UpdateLog.ADD:
        case UpdateLog.DELETE:
        case UpdateLog.DELETE_BY_QUERY: {
          if (!opCounters.containsKey(operationType)) {
            opCounters.put(operationType, 0l);
          }
          opCounters.put(operationType, opCounters.get(operationType) + 1);
          return;
        }

        default:
      }
    }

    long getRunTime() {
      long totalRunTime = runTime;
      if (startTime != -1) { // we are currently recording the time
        totalRunTime += System.nanoTime() - startTime;
      }
      return totalRunTime;
    }

    double getOperationsPerSecond() {
      long total = 0;
      for (long counter : opCounters.values()) {
        total += counter;
      }
      double elapsedTimeInSeconds = ((double) this.getRunTime() / 1E9);
      return total / elapsedTimeInSeconds;
    }

    double getAddsPerSecond() {
      long total = opCounters.get(UpdateLog.ADD) != null ? opCounters.get(UpdateLog.ADD) : 0;
      double elapsedTimeInSeconds = ((double) this.getRunTime() / 1E9);
      return total / elapsedTimeInSeconds;
    }

    double getDeletesPerSecond() {
      long total = opCounters.get(UpdateLog.DELETE) != null ? opCounters.get(UpdateLog.DELETE) : 0;
      total += opCounters.get(UpdateLog.DELETE_BY_QUERY) != null ? opCounters.get(UpdateLog.DELETE_BY_QUERY) : 0;
      double elapsedTimeInSeconds = ((double) this.getRunTime() / 1E9);
      return total / elapsedTimeInSeconds;
    }

  }

  private static class ErrorQueueEntry {

    private ErrorType type;
    private Date timestamp;

    private ErrorQueueEntry(ErrorType type, Date timestamp) {
      this.type = type;
      this.timestamp = timestamp;
    }
  }

  private static class FixedQueue extends LinkedList {

    private int maxSize;

    public FixedQueue(int maxSize) {
      this.maxSize = maxSize;
    }

    @Override
    public synchronized boolean add(E e) {
      super.addFirst(e);
      if (size() > maxSize) {
        removeLast();
      }
      return true;
    }
  }

}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy