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