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

com.torodb.mongodb.repl.oplogreplier.ReplSyncApplier Maven / Gradle / Ivy

There is a newer version: 0.50.3
Show newest version
/*
 * ToroDB
 * Copyright © 2014 8Kdata Technology (www.8kdata.com)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see .
 */

package com.torodb.mongodb.repl.oplogreplier;

import com.torodb.core.annotations.TorodbRunnableService;
import com.torodb.core.exceptions.user.UserException;
import com.torodb.core.logging.LoggerFactory;
import com.torodb.core.services.RunnableTorodbService;
import com.torodb.core.supervision.Supervisor;
import com.torodb.core.transaction.RollbackException;
import com.torodb.mongodb.core.ExclusiveWriteMongodTransaction;
import com.torodb.mongodb.core.MongodConnection;
import com.torodb.mongodb.core.MongodServer;
import com.torodb.mongodb.repl.OplogManager;
import com.torodb.mongodb.repl.OplogManager.OplogManagerPersistException;
import com.torodb.mongodb.repl.OplogManager.WriteOplogTransaction;
import com.torodb.mongodb.repl.oplogreplier.OplogOperationApplier.OplogApplyingException;
import com.torodb.mongowp.Status;
import com.torodb.mongowp.commands.oplog.OplogOperation;
import org.apache.logging.log4j.Logger;

import java.util.List;
import java.util.concurrent.ThreadFactory;

import javax.annotation.Nonnull;

class ReplSyncApplier extends RunnableTorodbService {

  private final Logger logger;
  private final SyncServiceView callback;
  private final OplogOperationApplier oplogOpApplier;
  private final OplogManager oplogManager;
  private final MongodConnection connection;

  private volatile Thread runThread;

  ReplSyncApplier(
      @TorodbRunnableService ThreadFactory threadFactory,
      @Nonnull OplogOperationApplier oplogOpApplier,
      @Nonnull MongodServer server,
      @Nonnull OplogManager oplogManager,
      @Nonnull SyncServiceView callback,
      @Nonnull LoggerFactory loggerFactory) {
    super(callback, threadFactory);
    this.logger = loggerFactory.apply(this.getClass());
    this.callback = callback;
    this.connection = server.openConnection();
    this.oplogOpApplier = oplogOpApplier;
    this.oplogManager = oplogManager;
  }

  @Override
  protected Logger getLogger() {
    return logger;
  }

  @Override
  protected String serviceName() {
    return "ToroDB Sync Applier";
  }

  @Override
  protected void triggerShutdown() {
    if (runThread != null) {
      runThread.interrupt();
    }
  }

  @Override
  protected void runProtected() {
    runThread = Thread.currentThread();
    /*
     * TODO: In general, the replication context can be set as not reaplying. But it is not frequent
     * but possible to stop the replication after some oplog ops have been apply but not marked as
     * executed on the oplog manager. For that reason, all oplog ops betwen the last operation that
     * have been marked as applyed and the current last operation on the remote oplog must be
     * executed as replying operations. As it is not possible to do that yet, we have to always
     * apply operations as replying to be safe.
     */
    ApplierContext applierContext = new ApplierContext.Builder()
        .setReapplying(true)
        .setUpdatesAsUpserts(true)
        .build();
    while (isRunning()) {
      OplogOperation lastOperation = null;
      ExclusiveWriteMongodTransaction trans = connection.openExclusiveWriteTransaction();
      try (ExclusiveWriteMongodTransaction transaction = trans) {
        try {
          for (OplogOperation opToApply : callback.takeOps()) {
            lastOperation = opToApply;
            logger.trace("Executing {}", opToApply);
            try {
              boolean done = false;
              while (!done) {
                try {
                  oplogOpApplier.apply(
                      opToApply,
                      transaction,
                      applierContext
                  );
                  transaction.commit();
                  done = true;
                } catch (RollbackException ex) {
                  logger.debug("Recived a rollback exception while applying an oplog op", ex);
                }
              }
            } catch (OplogApplyingException ex) {
              if (!callback.failedToApply(opToApply, ex)) {
                logger.error(serviceName() + " stopped because one operation "
                    + "cannot be executed", ex);
                break;
              }
            } catch (UserException ex) {
              if (callback.failedToApply(opToApply, ex)) {

                logger.error(serviceName() + " stopped because one operation "
                    + "cannot be executed", ex);
                break;
              }
            } catch (Throwable ex) {
              if (callback.failedToApply(opToApply, ex)) {
                logger.error(serviceName() + " stopped because "
                    + "an unknown error", ex);
                break;
              }
            }
            callback.markAsApplied(opToApply);
          }
        } catch (InterruptedException ex) {
          logger.debug("Interrupted applier thread while applying an operator");
        }
      }
      if (lastOperation != null) {
        try (WriteOplogTransaction oplogTransaction =
            oplogManager.createWriteTransaction()) {
          oplogTransaction.addOperation(lastOperation);
        } catch (OplogManagerPersistException ex) {
          if (callback.failedToApply(lastOperation, ex)) {
            logger.error(serviceName() + " stopped because "
                + "the last applied operation couldn't "
                + "be persisted", ex);
            break;
          }
        }
      }
    }
  }

  @Override
  protected void shutDown() throws Exception {
    connection.close();
  }

  public static interface SyncServiceView extends Supervisor {

    public List takeOps() throws InterruptedException;

    public void markAsApplied(OplogOperation oplogOperation);

    /**
     *
     * @param oplogOperation
     * @param status
     * @return false iff the applier loop should stop
     */
    public boolean failedToApply(OplogOperation oplogOperation, Status status);

    /**
     *
     * @param oplogOperation
     * @param t
     * @return false iff the applier loop should stop
     */
    public boolean failedToApply(OplogOperation oplogOperation, OplogManagerPersistException t);

    /**
     *
     * @param oplogOperation
     * @param t
     * @return false iff the applier loop should stop
     */
    public boolean failedToApply(OplogOperation oplogOperation, Throwable t);

  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy