/** {@inheritDoc} */ @SuppressWarnings({"unchecked", "ThrowableInstanceNeverThrown"}) @Override protected GridFuture<Boolean> lockAllAsync( Collection<? extends K> keys, long timeout, GridCacheTxLocalEx<K, V> tx, boolean isInvalidate, boolean isRead, boolean retval, GridCacheTxIsolation isolation, GridPredicate<? super GridCacheEntry<K, V>>[] filter) { if (keys.isEmpty()) return new GridFinishedFuture<Boolean>(ctx.kernalContext(), true); Collection<GridRichNode> nodes = ctx.remoteNodes(keys); final GridReplicatedLockFuture<K, V> fut = new GridReplicatedLockFuture<K, V>(ctx, keys, tx, this, nodes, timeout, filter); GridDistributedLockRequest<K, V> req = new GridDistributedLockRequest<K, V>( locNodeId, Thread.currentThread().getId(), fut.futureId(), fut.version(), tx != null, isRead, isolation, isInvalidate, timeout, keys.size()); try { // Must add future before redying locks. if (!ctx.mvcc().addFuture(fut)) throw new IllegalStateException("Duplicate future ID: " + fut); boolean distribute = false; for (K key : keys) { while (true) { GridDistributedCacheEntry<K, V> entry = null; try { entry = entryexx(key); if (!ctx.isAll(entry.wrap(false), filter)) { if (log.isDebugEnabled()) log.debug("Entry being locked did not pass filter (will not lock): " + entry); fut.onDone(false); return fut; } // Removed exception may be thrown here. GridCacheMvccCandidate<K> cand = fut.addEntry(entry); if (cand != null) { req.addKeyBytes( key, cand.reentry() ? null : entry.getOrMarshalKeyBytes(), retval, entry.localCandidates(fut.version()), ctx); req.completedVersions( ctx.tm().committedVersions(fut.version()), ctx.tm().rolledbackVersions(fut.version())); distribute = !cand.reentry(); } else if (fut.isDone()) return fut; break; } catch (GridCacheEntryRemovedException ignored) { if (log.isDebugEnabled()) log.debug("Got removed entry in lockAsync(..) method (will retry): " + entry); } } } // If nothing to distribute at this point, // then all locks are reentries. if (!distribute) fut.complete(true); if (nodes.isEmpty()) fut.readyLocks(); // No reason to send request if all locks are locally re-entered, // or if timeout is negative and local locks could not be acquired. if (fut.isDone()) return fut; try { ctx.io() .safeSend( fut.nodes(), req, new P1<GridNode>() { @Override public boolean apply(GridNode node) { fut.onNodeLeft(node.id()); return !fut.isDone(); } }); } catch (GridException e) { U.error( log, "Failed to send lock request to node [nodes=" + U.toShortString(nodes) + ", req=" + req + ']', e); fut.onError(e); } return fut; } catch (GridException e) { Throwable err = new GridException("Failed to acquire asynchronous lock for keys: " + keys, e); // Clean-up. fut.onError(err); ctx.mvcc().removeFuture(fut); return fut; } }
/** * Processes lock request. * * @param nodeId Sender node ID. * @param msg Lock request. */ @SuppressWarnings({"unchecked", "ThrowableInstanceNeverThrown"}) private void processLockRequest(UUID nodeId, GridDistributedLockRequest<K, V> msg) { assert !nodeId.equals(locNodeId); List<byte[]> keys = msg.keyBytes(); int cnt = keys.size(); GridReplicatedTxRemote<K, V> tx = null; GridDistributedLockResponse res; ClassLoader ldr = null; try { ldr = ctx.deploy().globalLoader(); if (ldr != null) { res = new GridDistributedLockResponse(msg.version(), msg.futureId(), cnt); for (int i = 0; i < keys.size(); i++) { byte[] bytes = keys.get(i); K key = msg.keys().get(i); Collection<GridCacheMvccCandidate<K>> cands = msg.candidatesByIndex(i); if (bytes == null) continue; if (log.isDebugEnabled()) log.debug("Unmarshalled key: " + key); GridDistributedCacheEntry<K, V> entry = null; while (true) { try { entry = entryexx(key); // Handle implicit locks for pessimistic transactions. if (msg.inTx()) { tx = ctx.tm().tx(msg.version()); if (tx != null) { if (msg.txRead()) tx.addRead(key, bytes); else tx.addWrite(key, bytes); } else { tx = new GridReplicatedTxRemote<K, V>( nodeId, msg.threadId(), msg.version(), null, PESSIMISTIC, msg.isolation(), msg.isInvalidate(), msg.timeout(), key, bytes, msg.txRead(), ctx); tx = ctx.tm().onCreated(tx); if (tx == null || !ctx.tm().onStarted(tx)) throw new GridCacheTxRollbackException( "Failed to acquire lock " + "(transaction has been completed): " + msg.version()); } } // Add remote candidate before reordering. entry.addRemote( msg.nodeId(), null, msg.threadId(), msg.version(), msg.timeout(), tx != null && tx.ec(), tx != null, tx != null && tx.implicitSingle()); // Remote candidates for ordered lock queuing. entry.addRemoteCandidates( cands, msg.version(), msg.committedVersions(), msg.rolledbackVersions()); // Double-check in case if sender node left the grid. if (ctx.discovery().node(msg.nodeId()) == null) { if (log.isDebugEnabled()) log.debug( "Node requesting lock left grid (lock request will be ignored): " + msg); if (tx != null) tx.rollback(); return; } res.setCandidates( i, entry.localCandidates(), ctx.tm().committedVersions(msg.version()), ctx.tm().rolledbackVersions(msg.version())); res.addValueBytes( entry.rawGet(), msg.returnValue(i) ? entry.valueBytes(null) : null, ctx); // Entry is legit. break; } catch (GridCacheEntryRemovedException ignored) { assert entry.obsoleteVersion() != null : "Obsolete flag not set on removed entry: " + entry; if (log.isDebugEnabled()) log.debug( "Received entry removed exception (will retry on renewed entry): " + entry); if (tx != null) { tx.clearEntry(entry.key()); if (log.isDebugEnabled()) log.debug( "Cleared removed entry from remote transaction (will retry) [entry=" + entry + ", tx=" + tx + ']'); } } } } } else { String err = "Failed to acquire deployment class for message: " + msg; U.warn(log, err); res = new GridDistributedLockResponse(msg.version(), msg.futureId(), new GridException(err)); } } catch (GridCacheTxRollbackException e) { if (log.isDebugEnabled()) log.debug("Received lock request for completed transaction (will ignore): " + e); res = new GridDistributedLockResponse(msg.version(), msg.futureId(), e); } catch (GridException e) { String err = "Failed to unmarshal at least one of the keys for lock request message: " + msg; log.error(err, e); res = new GridDistributedLockResponse(msg.version(), msg.futureId(), new GridException(err, e)); if (tx != null) tx.rollback(); } catch (GridDistributedLockCancelledException ignored) { // Received lock request for cancelled lock. if (log.isDebugEnabled()) log.debug("Received lock request for canceled lock (will ignore): " + msg); if (tx != null) tx.rollback(); // Don't send response back. return; } GridNode node = ctx.discovery().node(msg.nodeId()); boolean releaseAll = false; if (node != null) { try { // Reply back to sender. ctx.io().send(node, res); } catch (GridException e) { U.error(log, "Failed to send message to node (did the node leave grid?): " + node.id(), e); releaseAll = ldr != null; } } // If sender left grid, release all locks acquired so far. else releaseAll = ldr != null; // Release all locks because sender node left grid. if (releaseAll) { for (K key : msg.keys()) { while (true) { GridDistributedCacheEntry<K, V> entry = peekexx(key); try { if (entry != null) entry.removeExplicitNodeLocks(msg.nodeId()); break; } catch (GridCacheEntryRemovedException ignore) { if (log.isDebugEnabled()) log.debug( "Attempted to remove lock on removed entity during failure " + "of replicated lock request handling (will retry): " + entry); } } } U.warn( log, "Sender node left grid in the midst of lock acquisition (locks will be released)."); } }