LifecycleTransaction(
     Tracker tracker, OperationType operationType, Iterable<SSTableReader> readers) {
   this.tracker = tracker;
   this.operationType = operationType;
   for (SSTableReader reader : readers) {
     originals.add(reader);
     marked.add(reader);
     identities.add(reader.instanceId);
   }
 }
 /** 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));
 }
  /**
   * remove the provided readers from this Transaction, and return a new Transaction to manage them
   * only permitted to be called if the current Transaction has never been used
   */
  public LifecycleTransaction split(Collection<SSTableReader> readers) {
    logger.debug("Splitting {} into new transaction", readers);
    checkUnused();
    for (SSTableReader reader : readers)
      assert identities.contains(reader.instanceId)
          : "may only split the same reader instance the transaction was opened with: " + reader;

    for (SSTableReader reader : readers) {
      identities.remove(reader.instanceId);
      originals.remove(reader);
      marked.remove(reader);
    }
    return new LifecycleTransaction(tracker, operationType, readers);
  }
 /**
  * update a reader: if !original, this is a reader that is being introduced by this transaction;
  * otherwise it must be in the originals() set, i.e. a reader guarded by this transaction
  */
 public void update(SSTableReader reader, boolean original) {
   assert !staged.update.contains(reader)
       : "each reader may only be updated once per checkpoint: " + reader;
   assert !identities.contains(reader.instanceId)
       : "each reader instance may only be provided as an update once: " + reader;
   // check it isn't obsolete, and that it matches the original flag
   assert !(logged.obsolete.contains(reader) || staged.obsolete.contains(reader))
       : "may not update a reader that has been obsoleted";
   assert original == originals.contains(reader)
       : String.format(
           "the 'original' indicator was incorrect (%s provided): %s", original, reader);
   staged.update.add(reader);
   identities.add(reader.instanceId);
   if (!isOffline()) reader.setupKeyCache();
 }
 /**
  * mark this reader as for obsoletion. this does not actually obsolete the reader until commit()
  * is called, but on checkpoint() the reader will be removed from the live set
  */
 public void obsolete(SSTableReader reader) {
   logger.debug("Staging for obsolescence {}", reader);
   // check this is: a reader guarded by the transaction, an instance we have already worked with
   // and that we haven't already obsoleted it, nor do we have other changes staged for it
   assert identities.contains(reader.instanceId)
       : "only reader instances that have previously been provided may be obsoleted: " + reader;
   assert originals.contains(reader)
       : "only readers in the 'original' set may be obsoleted: " + reader + " vs " + originals;
   assert !(logged.obsolete.contains(reader) || staged.obsolete.contains(reader))
       : "may not obsolete a reader that has already been obsoleted: " + reader;
   assert !staged.update.contains(reader)
       : "may not obsolete a reader that has a staged update (must checkpoint first): " + reader;
   assert current(reader) == reader
       : "may only obsolete the latest version of the reader: " + reader;
   staged.obsolete.add(reader);
 }
  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);
 }
 @VisibleForTesting
 public ReaderState state(SSTableReader reader) {
   SSTableReader currentlyVisible =
       ReaderState.visible(reader, in(logged.obsolete), logged.update, originals);
   SSTableReader nextVisible =
       ReaderState.visible(
           reader,
           orIn(staged.obsolete, logged.obsolete),
           staged.update,
           logged.update,
           originals);
   return new ReaderState(
       ReaderState.Action.get(logged.update.contains(reader), logged.obsolete.contains(reader)),
       ReaderState.Action.get(staged.update.contains(reader), staged.obsolete.contains(reader)),
       currentlyVisible,
       nextVisible,
       originals.contains(reader));
 }
 void clear() {
   update.clear();
   obsolete.clear();
 }
 boolean isEmpty() {
   return update.isEmpty() && obsolete.isEmpty();
 }
 boolean contains(SSTableReader reader) {
   return update.contains(reader) || obsolete.contains(reader);
 }
 void log(State staged) {
   update.removeAll(staged.obsolete);
   update.removeAll(staged.update);
   update.addAll(staged.update);
   obsolete.addAll(staged.obsolete);
 }
 public String toString() {
   return originals.toString();
 }
 // convenience method for callers that know only one sstable is involved in the transaction
 public SSTableReader onlyOne() {
   assert originals.size() == 1;
   return getFirst(originals, null);
 }
 /** 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();
 }