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