/** * For all remote candidates standing behind the candidate being salvaged marks their transactions * as system invalidate and marks these candidates as owned and used. * * @param ver Version to salvage. */ public void salvageRemote(GridCacheVersion ver) { assert ver != null; GridCacheMvccCandidate cand = candidate(rmts, ver); if (cand != null) { assert rmts != null; assert !rmts.isEmpty(); for (Iterator<GridCacheMvccCandidate> iter = rmts.iterator(); iter.hasNext(); ) { GridCacheMvccCandidate rmt = iter.next(); // For salvaged candidate doneRemote will be called explicitly. if (rmt == cand) break; // Only Near and DHT remote candidates should be released. assert !rmt.nearLocal(); IgniteInternalTx tx = cctx.tm().tx(rmt.version()); if (tx != null) { tx.systemInvalidate(true); rmt.setOwner(); rmt.setUsed(); } else iter.remove(); } } }
/** * Sets remote candidate to done. * * @param ver Version. * @param pending Pending versions. * @param committed Committed versions. * @param rolledback Rolledback versions. * @return Lock owner. */ @Nullable public CacheLockCandidates doneRemote( GridCacheVersion ver, Collection<GridCacheVersion> pending, Collection<GridCacheVersion> committed, Collection<GridCacheVersion> rolledback) { assert ver != null; if (log.isDebugEnabled()) log.debug("Setting remote candidate to done [mvcc=" + this + ", ver=" + ver + "]"); // Check remote candidate. GridCacheMvccCandidate cand = candidate(rmts, ver); if (cand != null) { assert rmts != null; assert !rmts.isEmpty(); assert !cand.local() : "Remote candidate is marked as local: " + cand; assert !cand.nearLocal() : "Remote candidate is marked as near local: " + cand; cand.setOwner(); cand.setUsed(); List<GridCacheMvccCandidate> mvAfter = null; for (ListIterator<GridCacheMvccCandidate> it = rmts.listIterator(); it.hasNext(); ) { GridCacheMvccCandidate c = it.next(); assert !c.nearLocal() : "Remote candidate marked as near local: " + c; if (c == cand) { if (mvAfter != null) for (GridCacheMvccCandidate mv : mvAfter) it.add(mv); break; } else if (!committed.contains(c.version()) && !rolledback.contains(c.version()) && pending.contains(c.version())) { it.remove(); if (mvAfter == null) mvAfter = new LinkedList<>(); mvAfter.add(c); } } } return allOwners(); }
/** Assigns local lock. */ private void reassign() { GridCacheMvccCandidate firstRmt = null; if (rmts != null) { for (GridCacheMvccCandidate cand : rmts) { if (firstRmt == null) firstRmt = cand; // If there is a remote owner, then local cannot be an owner, // so no reassignment happens. if (cand.owner()) return; } } if (locs != null) { boolean first = true; ListIterator<GridCacheMvccCandidate> it = locs.listIterator(); while (it.hasNext()) { GridCacheMvccCandidate cand = it.next(); if (first) { if (cand.read()) { if (cand.ready() && !cand.owner()) cand.setOwner(); while (it.hasNext()) { cand = it.next(); if (!cand.read()) break; if (cand.ready() && !cand.owner()) cand.setOwner(); } return; } else if (cand.serializable()) { if (cand.owner() || !cand.ready()) return; cand.setOwner(); return; } first = false; } if (cand.owner()) return; if (cand.ready()) { GridCacheMvccCandidate prev = nonRollbackPrevious(cand); // If previous has not been acquired, this candidate cannot acquire lock either, // so we move on to the next one. if (prev != null && !prev.owner()) continue; boolean assigned = false; if (!cctx.isNear() && firstRmt != null && cand.version().isGreater(firstRmt.version())) { // Check previous candidates for 2 cases: // 1. If this candidate is waiting for a smaller remote version, // then we must check if previous candidate is the owner and // has the same remote candidate version. In that case, we can // safely set this candidate to owner as well. // 2. If this candidate is waiting for a smaller remote version, // then we must check if previous candidate is the owner and // any of the local candidates with versions smaller than first // remote version have the same key as the previous owner. In // that case, we can safely set this candidate to owner as well. while (prev != null && prev.owner()) { for (GridCacheMvccCandidate c : prev.parent().remoteMvccSnapshot()) { if (c.version().equals(firstRmt.version())) { cand.setOwner(); assigned = true; break; // For. } } if (!assigned) { for (GridCacheMvccCandidate c : locs) { if (c == cand || c.version().isGreater(firstRmt.version())) break; for (GridCacheMvccCandidate p = c.previous(); p != null; p = p.previous()) { if (p.key().equals(prev.key())) { cand.setOwner(); assigned = true; break; // For. } } if (assigned) break; // For. } } if (assigned) break; // While. prev = prev.previous(); } } if (!assigned) { if (!cctx.isNear() && firstRmt != null) { if (cand.version().isLess(firstRmt.version())) { assert !cand.nearLocal(); cand.setOwner(); assigned = true; } } else { cand.setOwner(); assigned = true; } } if (assigned) { assert !cand.serializable() : cand; it.remove(); // Owner must be first in the list. locs.addFirst(cand); } return; } } } }
/** * Marks near-local candidate as ready and makes locks reassignment. Following reorderings are * performed when candidate is marked ready: * * <ul> * <li/>All candidates preceding ready one are moved right after it. * <li/>Near local candidate is assigned a mapped dht version. All remote non-pending candidates * with version less then mapped dht version are marked as owned. * </ul> * * @param ver Version to mark as ready. * @param mappedVer Mapped dht version. * @param committedVers Committed versions. * @param rolledBackVers Rolled back versions. * @param pending Pending dht versions that are not owned and which version is less then mapped. * @return Lock owner after reassignment. */ @Nullable public CacheLockCandidates readyNearLocal( GridCacheVersion ver, GridCacheVersion mappedVer, Collection<GridCacheVersion> committedVers, Collection<GridCacheVersion> rolledBackVers, Collection<GridCacheVersion> pending) { GridCacheMvccCandidate cand = candidate(locs, ver); if (cand != null) { assert cand.nearLocal() : "Near local candidate is not marked as near local: " + cand; cand.setReady(); boolean setMapped = cand.otherVersion(mappedVer); assert setMapped : "Failed to set mapped dht version for near local candidate [mappedVer=" + mappedVer + ", cand=" + cand + ']'; // For near locals we move all not owned candidates after this one. List<GridCacheMvccCandidate> mvAfter = null; for (ListIterator<GridCacheMvccCandidate> it = locs.listIterator(); it.hasNext(); ) { GridCacheMvccCandidate c = it.next(); assert c.nearLocal() : "Near local candidate is not marked as near local: " + c; if (c == cand) { if (mvAfter != null) for (GridCacheMvccCandidate mv : mvAfter) it.add(mv); break; } else { if (c.owner()) continue; assert !c.ready() || (c.read() && cand.read()) : "Cannot have more then one ready near-local candidate [c=" + c + ", cand=" + cand + ", mvcc=" + this + ']'; it.remove(); if (mvAfter == null) mvAfter = new LinkedList<>(); mvAfter.add(c); } } // Mark all remote candidates with less version as owner unless it is pending. if (rmts != null) { for (GridCacheMvccCandidate rmt : rmts) { GridCacheVersion rmtVer = rmt.version(); if (rmtVer.isLess(mappedVer)) { if (!pending.contains(rmtVer) && !mappedVer.equals(rmt.ownerVersion())) rmt.setOwner(); } else { // Remote version is greater, so need to check if it was committed or rolled back. if (committedVers.contains(rmtVer) || rolledBackVers.contains(rmtVer)) rmt.setOwner(); } } } reassign(); } return allOwners(); }
/** * Moves completed candidates right before the base one. Note that if base is not found, then * nothing happens and {@code false} is returned. * * @param baseVer Base version. * @param committedVers Committed versions relative to base. * @param rolledbackVers Rolled back versions relative to base. */ public void orderCompleted( GridCacheVersion baseVer, Collection<GridCacheVersion> committedVers, Collection<GridCacheVersion> rolledbackVers) { assert baseVer != null; if (rmts != null && !F.isEmpty(committedVers)) { Deque<GridCacheMvccCandidate> mvAfter = null; int maxIdx = -1; for (ListIterator<GridCacheMvccCandidate> it = rmts.listIterator(rmts.size()); it.hasPrevious(); ) { GridCacheMvccCandidate cur = it.previous(); if (!cur.version().equals(baseVer) && committedVers.contains(cur.version())) { cur.setOwner(); assert localOwners() == null || localOwner().nearLocal() : "Cannot not have local owner and " + "remote completed transactions at the same time [baseVer=" + baseVer + ", committedVers=" + committedVers + ", rolledbackVers=" + rolledbackVers + ", localOwner=" + localOwner() + ", locs=" + locs + ", rmts=" + rmts + ']'; if (maxIdx < 0) maxIdx = it.nextIndex(); } else if (maxIdx >= 0 && cur.version().isGreaterEqual(baseVer)) { if (--maxIdx >= 0) { if (mvAfter == null) mvAfter = new LinkedList<>(); it.remove(); mvAfter.addFirst(cur); } } // If base is completed, then set it to owner too. if (!cur.owner() && cur.version().equals(baseVer) && committedVers.contains(cur.version())) cur.setOwner(); } if (maxIdx >= 0 && mvAfter != null) { ListIterator<GridCacheMvccCandidate> it = rmts.listIterator(maxIdx + 1); for (GridCacheMvccCandidate cand : mvAfter) it.add(cand); } // Remove rolled back versions. if (!F.isEmpty(rolledbackVers)) { for (Iterator<GridCacheMvccCandidate> it = rmts.iterator(); it.hasNext(); ) { GridCacheMvccCandidate cand = it.next(); if (rolledbackVers.contains(cand.version())) { cand.setUsed(); // Mark as used to be consistent, even though we are about to remove it. it.remove(); } } if (rmts.isEmpty()) rmts = null; } } }
/** * @param cand Candidate to add. * @return {@code False} if failed to add candidate and transaction should be cancelled. */ private boolean add0(GridCacheMvccCandidate cand) { assert cand != null; // Local. if (cand.local()) { if (locs == null) locs = new LinkedList<>(); if (!cand.nearLocal()) { if (!locs.isEmpty()) { if (cand.serializable()) { Iterator<GridCacheMvccCandidate> it = locs.descendingIterator(); if (cand.read()) { while (it.hasNext()) { GridCacheMvccCandidate c = it.next(); if (!c.serializable()) return false; if (!c.read()) { if (compareSerializableVersion(c, cand)) break; else return false; } } } else { while (it.hasNext()) { GridCacheMvccCandidate c = it.next(); if (!c.serializable() || !compareSerializableVersion(c, cand)) return false; if (!c.read()) break; } } locs.addLast(cand); return true; } GridCacheMvccCandidate first = locs.getFirst(); if (first.owner()) { // If reentry, add at the beginning. Note that // no reentry happens for DHT-local candidates. if (!cand.dhtLocal() && first.threadId() == cand.threadId()) { assert !first.serializable(); cand.setOwner(); cand.setReady(); cand.setReentry(); locs.addFirst(cand); return true; } } // Iterate in reverse order. for (ListIterator<GridCacheMvccCandidate> it = locs.listIterator(locs.size()); it.hasPrevious(); ) { GridCacheMvccCandidate c = it.previous(); assert !c.version().equals(cand.version()) : "Versions can't match [existing=" + c + ", new=" + cand + ']'; // Add after the owner or serializable tx. if (c.owner() || c.serializable()) { // Threads are checked above. assert cand.dhtLocal() || c.threadId() != cand.threadId(); // Reposition. it.next(); it.add(cand); return true; } // If not the owner, add after the lesser version. if (c.version().isLess(cand.version())) { // Reposition. it.next(); it.add(cand); return true; } } } // Either list is empty or candidate is first. locs.addFirst(cand); } else // For near local candidates just add it to the end of list. locs.add(cand); } // Remote. else { assert !cand.serializable() && !cand.read() : cand; if (rmts == null) rmts = new LinkedList<>(); assert !cand.owner() || localOwners() == null : "Cannot have local and remote owners " + "at the same time [cand=" + cand + ", locs=" + locs + ", rmts=" + rmts + ']'; GridCacheMvccCandidate cur = candidate(rmts, cand.version()); // For existing candidates, we only care about owners and keys. if (cur != null) { if (cand.owner()) cur.setOwner(); return true; } // Either list is empty or candidate is last. rmts.add(cand); } return true; }