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; }
/** 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(); }