private Throwable checkpoint(Throwable accumulate) { if (logger.isDebugEnabled()) logger.debug("Checkpointing update:{}, obsolete:{}", staged.update, staged.obsolete); if (staged.isEmpty()) return accumulate; Set<SSTableReader> toUpdate = toUpdate(); Set<SSTableReader> fresh = copyOf(fresh()); // check the current versions of the readers we're replacing haven't somehow been replaced by // someone else checkNotReplaced(filterIn(toUpdate, staged.update)); // ensure any new readers are in the compacting set, since we aren't done with them yet // and don't want anyone else messing with them // apply atomically along with updating the live set of readers tracker.apply( compose(updateCompacting(emptySet(), fresh), updateLiveSet(toUpdate, staged.update))); // log the staged changes and our newly marked readers marked.addAll(fresh); logged.log(staged); // setup our tracker, and mark our prior versions replaced, also releasing our references to // them // we do not replace/release obsoleted readers, since we may need to restore them on rollback accumulate = setReplaced(filterOut(toUpdate, staged.obsolete), accumulate); accumulate = release(selfRefs(filterOut(toUpdate, staged.obsolete)), accumulate); staged.clear(); return accumulate; }
/** * return the current version of the provided reader, whether or not it is visible or staged; i.e. * returns the first version present by testing staged, logged and originals in order. */ public SSTableReader current(SSTableReader reader) { Set<SSTableReader> container; if (staged.contains(reader)) container = staged.update.contains(reader) ? staged.update : staged.obsolete; else if (logged.contains(reader)) container = logged.update.contains(reader) ? logged.update : logged.obsolete; else if (originals.contains(reader)) container = originals; else throw new AssertionError(); return select(reader, container); }
/** remove the reader from the set we're modifying */ public void cancel(SSTableReader cancel) { logger.debug("Cancelling {} from transaction", cancel); assert originals.contains(cancel) : "may only cancel a reader in the 'original' set: " + cancel + " vs " + originals; assert !(staged.contains(cancel) || logged.contains(cancel)) : "may only cancel a reader that has not been updated or obsoleted in this transaction: " + cancel; originals.remove(cancel); marked.remove(cancel); maybeFail(unmarkCompacting(singleton(cancel), null)); }
/** undo all of the changes made by this transaction, resetting the state to its original form */ public Throwable doAbort(Throwable accumulate) { if (logger.isDebugEnabled()) logger.debug( "Aborting transaction over {}, with ({},{}) logged and ({},{}) staged", originals, logged.update, logged.obsolete, staged.update, staged.obsolete); if (logged.isEmpty() && staged.isEmpty()) return accumulate; // mark obsolete all readers that are not versions of those present in the original set Iterable<SSTableReader> obsolete = filterOut(concatUniq(staged.update, logged.update), originals); logger.debug("Obsoleting {}", obsolete); // we don't pass the tracker in for the obsoletion, since these readers have never been notified // externally // nor had their size accounting affected accumulate = markObsolete(null, obsolete, accumulate); // replace all updated readers with a version restored to its original state accumulate = tracker.apply(updateLiveSet(logged.update, restoreUpdatedOriginals()), accumulate); // setReplaced immediately preceding versions that have not been obsoleted accumulate = setReplaced(logged.update, accumulate); // we have replaced all of logged.update and never made visible staged.update, // and the files we have logged as obsolete we clone fresh versions of, so they are no longer // needed either // any _staged_ obsoletes should either be in staged.update already, and dealt with there, // or is still in its original form (so left as is); in either case no extra action is needed accumulate = release(selfRefs(concat(staged.update, logged.update, logged.obsolete)), accumulate); logged.clear(); staged.clear(); return accumulate; }
/** point of no return: commit all changes, but leave all readers marked as compacting */ public Throwable doCommit(Throwable accumulate) { assert staged.isEmpty() : "must be no actions introduced between prepareToCommit and a commit"; logger.debug("Committing update:{}, obsolete:{}", staged.update, staged.obsolete); // this is now the point of no return; we cannot safely rollback, so we ignore exceptions until // we're done // we restore state by obsoleting our obsolete files, releasing our references to them, and // updating our size // and notification status for the obsolete and new files accumulate = markObsolete(tracker, logged.obsolete, accumulate); accumulate = tracker.updateSizeTracking(logged.obsolete, logged.update, accumulate); accumulate = release(selfRefs(logged.obsolete), accumulate); accumulate = tracker.notifySSTablesChanged(originals, logged.update, operationType, accumulate); return accumulate; }
/** check this transaction has never been used */ private void checkUnused() { assert logged.isEmpty(); assert staged.isEmpty(); assert identities.size() == originals.size(); assert originals.size() == marked.size(); }