use of voldemort.VoldemortException in project voldemort by voldemort.
the class AdminStoreSwapper method invokeFetch.
public Map<Node, Response> invokeFetch(final String storeName, final String basePath, final long pushVersion) {
// do fetch
final Map<Integer, Future<String>> fetchDirs = new HashMap<Integer, Future<String>>();
for (final Node node : cluster.getNodes()) {
fetchDirs.put(node.getId(), executor.submit(new Callable<String>() {
public String call() throws Exception {
String response = null;
if (buildPrimaryReplicasOnly) {
// Then we give the root directory to the server and let it decide what to fetch
response = fetch(basePath);
} else {
// Old behavior: fetch the node directory only
String storeDir = basePath + "/" + ReadOnlyUtils.NODE_DIRECTORY_PREFIX + node.getId();
response = fetch(storeDir);
}
if (response == null)
throw new VoldemortException("Fetch request on " + node.briefToString() + " failed");
logger.info("Fetch succeeded on " + node.briefToString());
return response.trim();
}
private String fetch(String hadoopStoreDirToFetch) {
// We need to keep the AdminClient instance separate in each Callable, so that a refresh of
// the client in one callable does not refresh the AdminClient used by another callable.
AdminClient currentAdminClient = AdminStoreSwapper.this.adminClient;
int attempt = 1;
while (attempt <= MAX_FETCH_ATTEMPTS) {
if (attempt > 1) {
logger.info("Fetch attempt " + attempt + "/" + MAX_FETCH_ATTEMPTS + " for " + node.briefToString() + ". Will wait " + WAIT_TIME_BETWEEN_FETCH_ATTEMPTS + " ms before going ahead.");
try {
Thread.sleep(WAIT_TIME_BETWEEN_FETCH_ATTEMPTS);
} catch (InterruptedException e) {
throw new VoldemortException(e);
}
}
logger.info("Invoking fetch for " + node.briefToString() + " for " + hadoopStoreDirToFetch);
try {
return currentAdminClient.readonlyOps.fetchStore(node.getId(), storeName, hadoopStoreDirToFetch, pushVersion, timeoutMs);
} catch (AsyncOperationTimeoutException e) {
throw e;
} catch (VoldemortException ve) {
if (attempt >= MAX_FETCH_ATTEMPTS) {
throw ve;
}
if (ExceptionUtils.recursiveClassEquals(ve, ExceptionUtils.BNP_SOFT_ERRORS)) {
String logMessage = "Got a " + ve.getClass().getSimpleName() + " from " + node.briefToString() + " while trying to fetch store '" + storeName + "'" + " (attempt " + attempt + "/" + MAX_FETCH_ATTEMPTS + ").";
if (currentAdminClient.isClusterModified()) {
logMessage += " It seems like the cluster.xml state has changed since this" + " AdminClient was constructed. Therefore, we will attempt constructing" + " a fresh AdminClient and retrying the fetch operation.";
currentAdminClient = currentAdminClient.getFreshClient();
} else {
logMessage += " The cluster.xml is up to date. We will retry with the same AdminClient.";
}
logger.info(logMessage);
attempt++;
} else {
throw ve;
}
}
}
// Defensive coding
throw new IllegalStateException("Code should never reach here!");
}
}));
}
Map<Node, Response> fetchResponseMap = Maps.newTreeMap();
boolean fetchErrors = false;
/*
* We wait for all fetches to complete successfully or throw any
* Exception. We don't handle QuotaException in a special way here. The
* idea is to protect the disk. It is okay to let the Bnp job run to
* completion. We still want to delete data of a failed fetch in all
* nodes that successfully fetched the data. After deleting the
* failedFetch data, we bubble up the Quota Exception as needed.
*
* The alternate is to cancel all future tasks as soon as we detect a
* QuotaExceededException. This will save time (fail faster) and protect
* the disk usage. But does not guarantee a clean state in all nodes wrt
* to data from failed fetch. Someone manually needs to clean up all the
* data from failedFetches. Instead we try to cleanup the data as much
* as we can before we fail the job.
*
* In iteration 2 we can try to improve this to fail faster, by adding
* either/both:
*
* 1. Client side checks 2. Server side takes care of failing fast as
* soon as it detect QuotaExceededException in one of the servers. Note
* that this needs careful decision on how to handle those fetches that
* already started in other nodes and how & when to clean them up.
*/
ArrayList<Node> failedNodes = new ArrayList<Node>();
for (final Node node : cluster.getNodes()) {
Future<String> val = fetchDirs.get(node.getId());
try {
String response = val.get();
fetchResponseMap.put(node, new Response(response));
} catch (Exception e) {
if (e.getCause() instanceof UnauthorizedStoreException) {
throw (UnauthorizedStoreException) e.getCause();
} else {
fetchErrors = true;
fetchResponseMap.put(node, new Response(e));
failedNodes.add(node);
}
}
}
if (fetchErrors) {
// Log All the errors for the user
for (Map.Entry<Node, Response> entry : fetchResponseMap.entrySet()) {
if (!entry.getValue().isSuccessful()) {
logger.error("Error on " + entry.getKey().briefToString() + " during push : ", entry.getValue().getException());
}
}
Iterator<FailedFetchStrategy> strategyIterator = failedFetchStrategyList.iterator();
boolean swapIsPossible = false;
FailedFetchStrategy strategy = null;
while (strategyIterator.hasNext() && !swapIsPossible) {
strategy = strategyIterator.next();
try {
logger.info("About to attempt: " + strategy.toString());
swapIsPossible = strategy.dealWithIt(storeName, pushVersion, fetchResponseMap);
logger.info("Finished executing: " + strategy.toString() + "; swapIsPossible: " + swapIsPossible);
} catch (Exception e) {
if (strategyIterator.hasNext()) {
logger.error("Got an exception while trying to execute: " + strategy.toString() + ". Continuing with next strategy.", e);
} else {
logger.error("Got an exception while trying to execute the last remaining strategy: " + strategy.toString() + ". Swap will be aborted.", e);
}
}
}
if (!swapIsPossible) {
throw new VoldemortException("Exception during push. Swap will be aborted", fetchResponseMap.get(failedNodes.get(0)).getException());
}
}
return fetchResponseMap;
}
use of voldemort.VoldemortException in project voldemort by voldemort.
the class AdminStoreSwapper method invokeRollback.
public void invokeRollback(final String storeName, final long pushVersion) {
Exception exception = null;
for (Node node : cluster.getNodes()) {
try {
logger.info("Attempting rollback for " + node.briefToString() + ", storeName = " + storeName);
adminClient.readonlyOps.rollbackStore(node.getId(), storeName, pushVersion);
logger.info("Rollback succeeded for " + node.briefToString());
} catch (Exception e) {
exception = e;
logger.error("Exception thrown during rollback operation on " + node.briefToString(), e);
}
}
if (exception != null)
throw new VoldemortException(exception);
}
use of voldemort.VoldemortException in project voldemort by voldemort.
the class ExternalSorter method sorted.
/**
* Produce an iterator over the input values in sorted order. Sorting will
* occur in the fixed space configured in the constructor, data will be
* dumped to disk as necessary.
*
* @param input An iterator over the input values
* @return An iterator over the values
*/
public Iterable<V> sorted(Iterator<V> input) {
ExecutorService executor = new ThreadPoolExecutor(this.numThreads, this.numThreads, 1000L, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>(), new CallerRunsPolicy());
final AtomicInteger count = new AtomicInteger(0);
final List<File> tempFiles = Collections.synchronizedList(new ArrayList<File>());
while (input.hasNext()) {
final int segmentId = count.getAndIncrement();
final long segmentStartMs = System.currentTimeMillis();
logger.info("Segment " + segmentId + ": filling sort buffer for segment...");
@SuppressWarnings("unchecked") final V[] buffer = (V[]) new Object[internalSortSize];
int segmentSizeIter = 0;
for (; segmentSizeIter < internalSortSize && input.hasNext(); segmentSizeIter++) buffer[segmentSizeIter] = input.next();
final int segmentSize = segmentSizeIter;
logger.info("Segment " + segmentId + ": sort buffer filled...adding to sort queue.");
// sort and write out asynchronously
executor.execute(new Runnable() {
public void run() {
logger.info("Segment " + segmentId + ": sorting buffer.");
long start = System.currentTimeMillis();
Arrays.sort(buffer, 0, segmentSize, comparator);
long elapsed = System.currentTimeMillis() - start;
logger.info("Segment " + segmentId + ": sort completed in " + elapsed + " ms, writing to temp file.");
// write out values to a temp file
try {
File tempFile = File.createTempFile("segment-", ".dat", tempDir);
tempFile.deleteOnExit();
tempFiles.add(tempFile);
OutputStream os = new BufferedOutputStream(new FileOutputStream(tempFile), bufferSize);
if (gzip)
os = new GZIPOutputStream(os);
DataOutputStream output = new DataOutputStream(os);
for (int i = 0; i < segmentSize; i++) writeValue(output, buffer[i]);
output.close();
} catch (IOException e) {
throw new VoldemortException(e);
}
long segmentElapsed = System.currentTimeMillis() - segmentStartMs;
logger.info("Segment " + segmentId + ": completed processing of segment in " + segmentElapsed + " ms.");
}
});
}
// wait for all sorting to complete
executor.shutdown();
try {
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
// create iterator over sorted values
return new DefaultIterable<V>(new ExternalSorterIterator(tempFiles, bufferSize / tempFiles.size()));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
use of voldemort.VoldemortException in project voldemort by voldemort.
the class ExternalSorter method readValue.
private V readValue(DataInputStream stream) throws EOFException {
try {
int size = stream.readInt();
byte[] bytes = new byte[size];
ByteUtils.read(stream, bytes);
return serializer.toObject(bytes);
} catch (EOFException e) {
throw e;
} catch (IOException e) {
throw new VoldemortException(e);
}
}
use of voldemort.VoldemortException in project voldemort by voldemort.
the class ReadOnlyStorageEngine method swapFiles.
/**
* Swap the current version folder for a new one
*
* @param newStoreDirectory The path to the new version directory
*/
@JmxOperation(description = "swapFiles changes this store to use the new data directory")
public void swapFiles(String newStoreDirectory) {
logger.info("Swapping files for store '" + getName() + "' to " + newStoreDirectory);
File newVersionDir = new File(newStoreDirectory);
if (!newVersionDir.exists())
throw new VoldemortException("File " + newVersionDir.getAbsolutePath() + " does not exist.");
if (!(newVersionDir.getParentFile().compareTo(storeDir.getAbsoluteFile()) == 0 && ReadOnlyUtils.checkVersionDirName(newVersionDir)))
throw new VoldemortException("Invalid version folder name '" + newVersionDir + "'. Either parent directory is incorrect or format(version-n) is incorrect");
// retrieve previous version for (a) check if last write is winning
// (b) if failure, rollback use
File previousVersionDir = ReadOnlyUtils.getCurrentVersion(storeDir);
if (previousVersionDir == null)
throw new VoldemortException("Could not find any latest directory to swap with in store '" + getName() + "'");
long newVersionId = ReadOnlyUtils.getVersionId(newVersionDir);
long previousVersionId = ReadOnlyUtils.getVersionId(previousVersionDir);
if (newVersionId == -1 || previousVersionId == -1)
throw new VoldemortException("Unable to parse folder names (" + newVersionDir.getName() + "," + previousVersionDir.getName() + ") since format(version-n) is incorrect");
// check if we're greater than latest since we want last write to win
if (previousVersionId > newVersionId) {
logger.info("No swap required since current latest version " + previousVersionId + " is greater than swap version " + newVersionId);
deleteBackups();
return;
}
logger.info("Acquiring write lock on '" + getName() + "':");
fileModificationLock.writeLock().lock();
boolean success = false;
try {
close();
logger.info("Opening primary files for store '" + getName() + "' at " + newStoreDirectory);
// open the latest store
open(newVersionDir);
success = true;
} finally {
try {
// we failed to do the swap, attempt a rollback to last version
if (!success)
rollback(previousVersionDir);
} finally {
fileModificationLock.writeLock().unlock();
if (success)
logger.info("Swap operation completed successfully on store " + getName() + ", releasing lock.");
else
logger.error("Swap operation failed.");
}
}
// okay we have released the lock and the store is now open again, it is
// safe to do a potentially slow delete if we have one too many backups
deleteBackups();
}
Aggregations