/** {@inheritDoc} */ @Override public GridFuture<GridCacheTxEx<K, V>> prepareAsync() { GridNearTxPrepareFuture<K, V> fut = prepFut.get(); if (fut == null) { // Future must be created before any exception can be thrown. if (!prepFut.compareAndSet(null, fut = new GridNearTxPrepareFuture<K, V>(cctx, this))) return prepFut.get(); } else // Prepare was called explicitly. return fut; if (!state(PREPARING)) { if (setRollbackOnly()) { if (timedOut()) fut.onError( new GridCacheTxTimeoutException( "Transaction timed out and was rolled back: " + this)); else fut.onError( new GridException( "Invalid transaction state for prepare [state=" + state() + ", tx=" + this + ']')); } else fut.onError( new GridCacheTxRollbackException( "Invalid transaction state for prepare [state=" + state() + ", tx=" + this + ']')); return fut; } // For pessimistic mode we don't distribute prepare request. if (pessimistic()) { try { userPrepare(); if (!state(PREPARED)) { setRollbackOnly(); fut.onError( new GridException( "Invalid transaction state for commit [state=" + state() + ", tx=" + this + ']')); return fut; } fut.complete(); return fut; } catch (GridException e) { fut.onError(e); return fut; } } try { cctx.topology().readLock(); try { topologyVersion(cctx.topology().topologyVersion()); userPrepare(); } finally { cctx.topology().readUnlock(); } // This will attempt to locally commit // EVENTUALLY CONSISTENT transactions. fut.onPreparedEC(); // Make sure to add future before calling prepare. cctx.mvcc().addFuture(fut); fut.prepare(); } catch (GridCacheTxTimeoutException e) { fut.onError(e); } catch (GridCacheTxOptimisticException e) { fut.onError(e); } catch (GridException e) { setRollbackOnly(); String msg = "Failed to prepare transaction (will attempt rollback): " + this; log.error(msg, e); try { rollback(); } catch (GridException e1) { U.error(log, "Failed to rollback transaction: " + this, e1); } fut.onError(new GridCacheTxRollbackException(msg, e)); } return fut; }
/** {@inheritDoc} */ @Override public void rollback() throws GridException { GridNearTxPrepareFuture<K, V> prepFut = this.prepFut.get(); GridNearTxFinishFuture<K, V> fut = rollbackFut.get(); if (fut == null && !rollbackFut.compareAndSet( null, fut = new GridNearTxFinishFuture<K, V>(cctx, this, false))) { rollbackFut.get(); return; } try { cctx.mvcc().addFuture(fut); if (prepFut == null) { finish(false); fut.finish(); } else { prepFut.listenAsync( new CI1<GridFuture<GridCacheTxEx<K, V>>>() { @Override public void apply(GridFuture<GridCacheTxEx<K, V>> f) { try { // Check for errors in prepare future. f.get(); } catch (GridException e) { if (log.isDebugEnabled()) log.debug("Got optimistic tx failure [tx=" + this + ", err=" + e + ']'); } try { finish(false); rollbackFut.get().finish(); } catch (GridException e) { U.error(log, "Failed to gracefully rollback transaction: " + this, e); rollbackFut.get().onError(e); } } }); } // TODO: Rollback Async? fut.get(); } catch (Error e) { U.addLastCause(e, commitErr.get()); throw e; } catch (RuntimeException e) { U.addLastCause(e, commitErr.get()); throw e; } catch (GridException e) { U.addLastCause(e, commitErr.get()); throw e; } finally { cctx.tm().txContextReset(); cctx.near().dht().context().tm().txContextReset(); } }
/** {@inheritDoc} */ @SuppressWarnings({"CatchGenericClass", "ThrowableInstanceNeverThrown"}) @Override public void finish(boolean commit) throws GridException { if (log.isDebugEnabled()) log.debug("Finishing near local tx [tx=" + this + ", commit=" + commit + "]"); if (commit) { if (!state(COMMITTING)) { GridCacheTxState state = state(); if (state != COMMITTING && state != COMMITTED) throw new GridException( "Invalid transaction state for commit [state=" + state() + ", tx=" + this + ']'); else { if (log.isDebugEnabled()) log.debug( "Invalid transaction state for commit (another thread is committing): " + this); return; } } } else { if (!state(ROLLING_BACK)) { if (log.isDebugEnabled()) log.debug( "Invalid transaction state for rollback [state=" + state() + ", tx=" + this + ']'); return; } } GridException err = null; // Commit to DB first. This way if there is a failure, transaction // won't be committed. try { if (commit && !isRollbackOnly()) userCommit(); else userRollback(); } catch (GridException e) { err = e; commit = false; // If heuristic error. if (!isRollbackOnly()) { invalidate = true; U.warn( log, "Set transaction invalidation flag to true due to error [tx=" + this + ", err=" + err + ']'); } } if (err != null) { state(UNKNOWN); throw err; } else { if (!state(commit ? COMMITTED : ROLLED_BACK)) { state(UNKNOWN); throw new GridException("Invalid transaction state for commit or rollback: " + this); } } }