Package org.apache.accumulo.fate

Source Code of org.apache.accumulo.fate.Fate

/*
* 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.accumulo.fate;

import java.util.EnumSet;
import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.accumulo.fate.ReadOnlyTStore.TStatus;
import org.apache.accumulo.fate.util.Daemon;
import org.apache.accumulo.fate.util.LoggingRunnable;
import org.apache.log4j.Logger;

/**
* Fault tolerant executor
*
*
*/

public class Fate<T> {
 
  private static final String DEBUG_PROP = "debug";
  private static final String AUTO_CLEAN_PROP = "autoClean";
  private static final String EXCEPTION_PROP = "exception";
  private static final String RETURN_PROP = "return";
 
  final private static Logger log = Logger.getLogger(Fate.class);
 
  private TStore<T> store;
  private T environment;
 
  private static final EnumSet<TStatus> FINISHED_STATES = EnumSet.of(TStatus.FAILED, TStatus.SUCCESSFUL, TStatus.UNKNOWN);
 
  private AtomicBoolean keepRunning = new AtomicBoolean(true);
 
  private class TransactionRunner implements Runnable {
   
    @Override
    public void run() {
      while (keepRunning.get()) {
        long deferTime = 0;
        long tid = store.reserve();
        try {
          TStatus status = store.getStatus(tid);
          Repo<T> op = store.top(tid);
          if (status == TStatus.FAILED_IN_PROGRESS) {
            processFailed(tid, op);
          } else {
            Repo<T> prevOp = null;
            try {
              deferTime = op.isReady(tid, environment);
              if (deferTime == 0) {
                prevOp = op;
                op = op.call(tid, environment);
              } else
                continue;
             
            } catch (Exception e) {
              transitionToFailed(tid, op, e);
              continue;
            }
           
            if (op == null) {
              // transaction is finished
              String ret = prevOp.getReturn();
              if (ret != null)
                store.setProperty(tid, RETURN_PROP, ret);
              store.setStatus(tid, TStatus.SUCCESSFUL);
              doCleanUp(tid);
            } else {
              try {
                store.push(tid, op);
              } catch (StackOverflowException e) {
                // the op that failed to push onto the stack was never executed, so no need to undo it
                // just transition to failed and undo the ops that executed
                transitionToFailed(tid, op, e);
                continue;
              }
            }
          }
        } finally {
          store.unreserve(tid, deferTime);
        }
       
      }
    }
   
    private void transitionToFailed(long tid, Repo<T> op, Exception e) {
      String tidStr = String.format("%016x", tid);
      log.warn("Failed to execute Repo, tid=" + tidStr, e);
      store.setProperty(tid, EXCEPTION_PROP, e);
      store.setStatus(tid, TStatus.FAILED_IN_PROGRESS);
      log.info("Updated status for Repo with tid=" + tidStr + " to FAILED_IN_PROGRESS");
    }
   
    private void processFailed(long tid, Repo<T> op) {
      while (op != null) {
        undo(tid, op);
       
        store.pop(tid);
        op = store.top(tid);
      }
     
      store.setStatus(tid, TStatus.FAILED);
      doCleanUp(tid);
    }
   
    private void doCleanUp(long tid) {
      Boolean autoClean = (Boolean) store.getProperty(tid, AUTO_CLEAN_PROP);
      if (autoClean != null && autoClean) {
        store.delete(tid);
      } else {
        // no longer need persisted operations, so delete them to save space in case
        // TX is never cleaned up...
        while (store.top(tid) != null)
          store.pop(tid);
      }
    }
   
    private void undo(long tid, Repo<T> op) {
      try {
        op.undo(tid, environment);
      } catch (Exception e) {
        log.warn("Failed to undo Repo, tid=" + String.format("%016x", tid), e);
      }
    }
   
  }
 
  // TODO: move thread creation to another method ACCUMULO-2136
  public Fate(T environment, TStore<T> store, int numThreads) {
    this.store = store;
    this.environment = environment;
   
    for (int i = 0; i < numThreads; i++) {
      // TODO: use an ExecutorService, maybe a utility to do these steps throughout the server packages - ACCUMULO-1311
      Thread thread = new Daemon(new LoggingRunnable(log, new TransactionRunner()), "Repo runner " + i);
      thread.start();
    }
  }
 
  // get a transaction id back to the requester before doing any work
  public long startTransaction() {
    long dir = store.create();
    return dir;
  }
 
  // start work in the transaction.. it is safe to call this
  // multiple times for a transaction... but it will only seed once
  public void seedTransaction(long tid, Repo<T> repo, boolean autoCleanUp) {
    store.reserve(tid);
    try {
      if (store.getStatus(tid) == TStatus.NEW) {
        if (store.top(tid) == null) {
          try {
            store.push(tid, repo);
          } catch (StackOverflowException e) {
            // this should not happen
            throw new RuntimeException(e);
          }
        }
       
        if (autoCleanUp)
          store.setProperty(tid, AUTO_CLEAN_PROP, Boolean.valueOf(autoCleanUp));
       
        store.setProperty(tid, DEBUG_PROP, repo.getDescription());
       
        store.setStatus(tid, TStatus.IN_PROGRESS);
      }
    } finally {
      store.unreserve(tid, 0);
    }
   
  }
 
  // check on the transaction
  public TStatus waitForCompletion(long tid) {
    return store.waitForStatusChange(tid, FINISHED_STATES);
  }
 
  // resource cleanup
  public void delete(long tid) {
    store.reserve(tid);
    try {
      switch (store.getStatus(tid)) {
        case NEW:
        case FAILED:
        case SUCCESSFUL:
          store.delete(tid);
          break;
        case FAILED_IN_PROGRESS:
        case IN_PROGRESS:
          throw new IllegalStateException("Can not delete in progress transaction " + String.format("%016x", tid));
        case UNKNOWN:
          // nothing to do, it does not exist
          break;
      }
    } finally {
      store.unreserve(tid, 0);
    }
  }
 
  public String getReturn(long tid) {
    store.reserve(tid);
    try {
      if (store.getStatus(tid) != TStatus.SUCCESSFUL)
        throw new IllegalStateException("Tried to get exception when transaction " + String.format("%016x", tid) + " not in successful state");
      return (String) store.getProperty(tid, RETURN_PROP);
    } finally {
      store.unreserve(tid, 0);
    }
  }
 
  // get reportable failures
  public Exception getException(long tid) {
    store.reserve(tid);
    try {
      if (store.getStatus(tid) != TStatus.FAILED)
        throw new IllegalStateException("Tried to get exception when transaction " + String.format("%016x", tid) + " not in failed state");
      return (Exception) store.getProperty(tid, EXCEPTION_PROP);
    } finally {
      store.unreserve(tid, 0);
    }
  }
 
  /**
   * Flags that FATE threadpool to clear out and end. Does not actively stop running FATE processes.
   */
  public void shutdown() {
    keepRunning.set(false);
  }

}
TOP

Related Classes of org.apache.accumulo.fate.Fate

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.