/** @param entry Entry. */ @SuppressWarnings({"SynchronizationOnLocalVariableOrMethodParameter"}) void recheck(@Nullable GridCacheEntryEx entry) { if (entry == null) return; if (exchLog.isDebugEnabled()) exchLog.debug("Rechecking entry for completion [entry=" + entry + ", finFut=" + this + ']'); Collection<GridCacheMvccCandidate> cands = pendingLocks.get(entry.txKey()); if (cands != null) { synchronized (cands) { for (Iterator<GridCacheMvccCandidate> it = cands.iterator(); it.hasNext(); ) { GridCacheMvccCandidate cand = it.next(); // Check exclude ID again, as key could have been reassigned. if (cand.removed()) it.remove(); } if (cands.isEmpty()) pendingLocks.remove(entry.txKey()); if (pendingLocks.isEmpty()) { onDone(); if (exchLog.isDebugEnabled()) exchLog.debug("Finish lock future is done: " + this); } } } }
/** {@inheritDoc} */ @Override public boolean onDone(GridCacheTx tx, Throwable err) { if ((initialized() || err != null) && super.onDone(tx, err)) { if (error() instanceof GridCacheTxHeuristicException) { long topVer = this.tx.topologyVersion(); for (GridCacheTxEntry<K, V> e : this.tx.writeMap().values()) { try { if (e.op() != NOOP && !cctx.affinity().localNode(e.key(), topVer)) { GridCacheEntryEx<K, V> cacheEntry = cctx.cache().peekEx(e.key()); if (cacheEntry != null) cacheEntry.invalidate(null, this.tx.xidVersion()); } } catch (Throwable t) { U.error(log, "Failed to invalidate entry.", t); if (t instanceof Error) throw (Error) t; } } } // Don't forget to clean up. cctx.mvcc().removeFuture(this); return true; } return false; }
/** * @param topVer Topology version. * @param entries Entries. */ FinishLockFuture(Iterable<GridDistributedCacheEntry> entries, AffinityTopologyVersion topVer) { assert topVer.compareTo(AffinityTopologyVersion.ZERO) > 0; this.topVer = topVer; for (GridCacheEntryEx entry : entries) { // Either local or near local candidates. try { Collection<GridCacheMvccCandidate> locs = entry.localCandidates(); if (!F.isEmpty(locs)) { Collection<GridCacheMvccCandidate> cands = new ConcurrentLinkedQueue<>(); cands.addAll(F.view(locs, versionFilter())); if (!F.isEmpty(cands)) pendingLocks.put(entry.txKey(), cands); } } catch (GridCacheEntryRemovedException ignored) { if (exchLog.isDebugEnabled()) exchLog.debug( "Got removed entry when adding it to finish lock future (will ignore): " + entry); } } if (exchLog.isDebugEnabled()) exchLog.debug("Pending lock set [topVer=" + topVer + ", locks=" + pendingLocks + ']'); }
/** @return Key. */ public K key() { GridCacheEntryEx<K, ?> parent0 = parent; if (parent0 == null) throw new IllegalStateException( "Parent entry was not initialized for MVCC candidate: " + this); return parent0.key(); }
/** {@inheritDoc} */ @SuppressWarnings({"unchecked"}) @Override public void onOwnerChanged( GridCacheEntryEx entry, GridCacheMvccCandidate prev, GridCacheMvccCandidate owner) { assert entry != null; assert owner != prev : "New and previous owner are identical instances: " + owner; assert owner == null || prev == null || !owner.version().equals(prev.version()) : "New and previous owners have identical versions [owner=" + owner + ", prev=" + prev + ']'; if (log.isDebugEnabled()) log.debug( "Received owner changed callback [" + entry.key() + ", owner=" + owner + ", prev=" + prev + ']'); if (owner != null && (owner.local() || owner.nearLocal())) { Collection<? extends GridCacheFuture> futCol = futs.get(owner.version()); if (futCol != null) { for (GridCacheFuture fut : futCol) { if (fut instanceof GridCacheMvccFuture && !fut.isDone()) { GridCacheMvccFuture<Boolean> mvccFut = (GridCacheMvccFuture<Boolean>) fut; // Since this method is called outside of entry synchronization, // we can safely invoke any method on the future. // Also note that we don't remove future here if it is done. // The removal is initiated from within future itself. if (mvccFut.onOwnerChanged(entry, owner)) return; } } } } if (log.isDebugEnabled()) log.debug( "Lock future not found for owner change callback (will try transaction futures) [owner=" + owner + ", prev=" + prev + ", entry=" + entry + ']'); // If no future was found, delegate to transaction manager. if (cctx.tm().onOwnerChanged(entry, owner)) { if (log.isDebugEnabled()) log.debug("Found transaction for changed owner: " + owner); } else if (log.isDebugEnabled()) log.debug("Failed to find transaction for changed owner: " + owner); for (FinishLockFuture f : finishFuts) f.recheck(entry); }
/** @return {@code True} if locks have been acquired. */ private boolean checkLocks() { if (!isDone() && initialized() && !hasPending()) { for (int i = 0; i < entries.size(); i++) { while (true) { GridCacheEntryEx<K, V> cached = entries.get(i); try { if (!locked(cached)) { if (log.isDebugEnabled()) log.debug( "Lock is still not acquired for entry (will keep waiting) [entry=" + cached + ", fut=" + this + ']'); return false; } break; } // Possible in concurrent cases, when owner is changed after locks // have been released or cancelled. catch (GridCacheEntryRemovedException ignore) { if (log.isDebugEnabled()) log.debug("Got removed entry in onOwnerChanged method (will retry): " + cached); // Replace old entry with new one. entries.set(i, (GridDistributedCacheEntry<K, V>) cctx.cache().entryEx(cached.key())); } } } if (log.isDebugEnabled()) log.debug("Local lock acquired for entries [fut=" + this + ", entries=" + entries + "]"); onComplete(true, true); return true; } return false; }
/** * Undoes all locks. * * @param dist If {@code true}, then remove locks from remote nodes as well. */ private void undoLocks(boolean dist) { // Transactions will undo during rollback. if (dist && tx == null) cctx.nearTx().removeLocks(lockVer, keys); else { if (tx != null) { if (tx.setRollbackOnly()) { if (log.isDebugEnabled()) log.debug( "Marked transaction as rollback only because locks could not be acquired: " + tx); } else if (log.isDebugEnabled()) log.debug( "Transaction was not marked rollback-only while locks were not acquired: " + tx); } for (GridCacheEntryEx<K, V> e : entriesCopy()) { try { e.removeLock(lockVer); } catch (GridCacheEntryRemovedException ignored) { while (true) { try { e = cctx.cache().peekEx(e.key()); if (e != null) e.removeLock(lockVer); break; } catch (GridCacheEntryRemovedException ignore) { if (log.isDebugEnabled()) log.debug( "Attempted to remove lock on removed entry (will retry) [ver=" + lockVer + ", entry=" + e + ']'); } } } } } cctx.mvcc().recheckPendingLocks(); }
/** * @param e Entry to evict if it qualifies for eviction. * @param obsoleteVer Obsolete version. * @return {@code True} if attempt was made to evict the entry. */ protected boolean evictNearEntry(GridCacheEntryEx<K, V> e, GridCacheVersion obsoleteVer) { assert e != null; assert obsoleteVer != null; if (isNearLocallyMapped(e)) { if (log.isDebugEnabled()) log.debug("Evicting dht-local entry from near cache [entry=" + e + ", tx=" + this + ']'); if (e.markObsolete(obsoleteVer, true)) return true; } return false; }
/** {@inheritDoc} */ @Override public String toString() { GridCacheMvccCandidate<?> prev = previous(); GridCacheMvccCandidate<?> next = next(); return S.toString( GridCacheMvccCandidate.class, this, "key", parent == null ? null : parent.key(), "masks", Mask.toString(flags()), "prevVer", (prev == null ? null : prev.version()), "nextVer", (next == null ? null : next.version())); }
/** * @param e Transaction entry. * @return {@code True} if entry is locally mapped as a primary or back up node. */ protected boolean isNearLocallyMapped(GridCacheEntryEx<K, V> e) { return F.contains(ctx.affinity(e.key(), CU.allNodes(ctx)), ctx.localNode()); }
/** * @param cached Entry. * @return {@code True} if locked. * @throws GridCacheEntryRemovedException If removed. */ private boolean locked(GridCacheEntryEx<K, V> cached) throws GridCacheEntryRemovedException { // Reentry-aware check (If filter failed, lock is failed). return cached.lockedLocallyByIdOrThread(lockVer, threadId) && filter(cached); }