use of org.apache.ignite.internal.processors.cache.distributed.dht.PartitionUpdateCountersMessage in project ignite by apache.
the class IgniteTxLocalAdapter method calculatePartitionUpdateCounters.
/**
* Calculates partition update counters for current transaction. Each partition will be supplied with
* pair (init, delta) values, where init - initial update counter, and delta - updates count made
* by current transaction for a given partition.
*/
public void calculatePartitionUpdateCounters() throws IgniteTxRollbackCheckedException {
TxCounters counters = txCounters(false);
if (counters != null && F.isEmpty(counters.updateCounters())) {
List<PartitionUpdateCountersMessage> cntrMsgs = new ArrayList<>();
for (Map.Entry<Integer, Map<Integer, AtomicLong>> record : counters.accumulatedUpdateCounters().entrySet()) {
int cacheId = record.getKey();
Map<Integer, AtomicLong> partToCntrs = record.getValue();
assert partToCntrs != null;
if (F.isEmpty(partToCntrs))
continue;
PartitionUpdateCountersMessage msg = new PartitionUpdateCountersMessage(cacheId, partToCntrs.size());
GridCacheContext ctx0 = cctx.cacheContext(cacheId);
GridDhtPartitionTopology top = ctx0.topology();
assert top != null;
for (Map.Entry<Integer, AtomicLong> e : partToCntrs.entrySet()) {
AtomicLong acc = e.getValue();
assert acc != null;
long cntr = acc.get();
assert cntr >= 0;
if (cntr != 0) {
int p = e.getKey();
GridDhtLocalPartition part = top.localPartition(p);
// Verify primary tx mapping.
// LOST state is possible if tx is started over LOST partition.
boolean valid = part != null && (part.state() == OWNING || part.state() == LOST) && part.primary(top.readyTopologyVersion());
if (!valid) {
// Local node is no longer primary for the partition, need to rollback a transaction.
if (part != null && !part.primary(top.readyTopologyVersion())) {
log.warning("Failed to prepare a transaction on outdated topology, rolling back " + "[tx=" + CU.txString(this) + ", readyTopVer=" + top.readyTopologyVersion() + ", lostParts=" + top.lostPartitions() + ", part=" + part.toString() + ']');
throw new IgniteTxRollbackCheckedException("Failed to prepare a transaction on outdated " + "topology, please try again [timeout=" + timeout() + ", tx=" + CU.txString(this) + ']');
}
// Trigger error.
throw new AssertionError("Invalid primary mapping [tx=" + CU.txString(this) + ", readyTopVer=" + top.readyTopologyVersion() + ", lostParts=" + top.lostPartitions() + ", part=" + (part == null ? "NULL" : part.toString()) + ']');
}
msg.add(p, part.getAndIncrementUpdateCounter(cntr), cntr);
}
}
if (msg.size() > 0)
cntrMsgs.add(msg);
}
counters.updateCounters(cntrMsgs);
}
}
use of org.apache.ignite.internal.processors.cache.distributed.dht.PartitionUpdateCountersMessage in project ignite by apache.
the class IgniteTxHandler method filterUpdateCountersForBackupNode.
/**
* @param tx Transaction.
* @param node Backup node.
* @return Partition counters for the given backup node.
*/
@Nullable
public List<PartitionUpdateCountersMessage> filterUpdateCountersForBackupNode(IgniteInternalTx tx, ClusterNode node) {
TxCounters txCntrs = tx.txCounters(false);
Collection<PartitionUpdateCountersMessage> updCntrs;
if (txCntrs == null || F.isEmpty(updCntrs = txCntrs.updateCounters()))
return null;
List<PartitionUpdateCountersMessage> res = new ArrayList<>(updCntrs.size());
AffinityTopologyVersion top = tx.topologyVersionSnapshot();
for (PartitionUpdateCountersMessage partCntrs : updCntrs) {
GridDhtPartitionTopology topology = ctx.cacheContext(partCntrs.cacheId()).topology();
PartitionUpdateCountersMessage resCntrs = new PartitionUpdateCountersMessage(partCntrs.cacheId(), partCntrs.size());
for (int i = 0; i < partCntrs.size(); i++) {
int part = partCntrs.partition(i);
if (topology.nodes(part, top).indexOf(node) > 0)
resCntrs.add(part, partCntrs.initialCounter(i), partCntrs.updatesCount(i));
}
if (resCntrs.size() > 0)
res.add(resCntrs);
}
return res;
}
use of org.apache.ignite.internal.processors.cache.distributed.dht.PartitionUpdateCountersMessage in project ignite by apache.
the class IgniteTxHandler method applyPartitionsUpdatesCounters.
/**
* Applies partition counter updates for transactions.
* <p>
* Called after entries are written to WAL on commit or during rollback to close gaps in update counter sequence.
* <p>
* On rollback counters should be applied on the primary only after backup nodes, otherwise if the primary fail
* before sending rollback requests to backups remote transactions can be committed by recovery protocol and
* partition consistency will not be restored when primary returns to the grid because RollbackRecord was written
* (actual for persistent mode only).
*
* @param counters Counter values to be updated.
* @param rollback {@code True} if applied during rollbacks.
* @param rollbackOnPrimary {@code True} if rollback happens on primary node. Passed to CQ engine.
*/
public void applyPartitionsUpdatesCounters(Iterable<PartitionUpdateCountersMessage> counters, boolean rollback, boolean rollbackOnPrimary) throws IgniteCheckedException {
if (counters == null)
return;
WALPointer ptr = null;
try {
for (PartitionUpdateCountersMessage counter : counters) {
GridCacheContext ctx0 = ctx.cacheContext(counter.cacheId());
GridDhtPartitionTopology top = ctx0.topology();
AffinityTopologyVersion topVer = top.readyTopologyVersion();
assert top != null;
for (int i = 0; i < counter.size(); i++) {
boolean invalid = false;
try {
GridDhtLocalPartition part = top.localPartition(counter.partition(i));
if (part != null && part.reserve()) {
try {
if (part.state() != RENTING) {
// Check is actual only for backup node.
long start = counter.initialCounter(i);
long delta = counter.updatesCount(i);
boolean updated = part.updateCounter(start, delta);
// Need to log rolled back range for logical recovery.
if (updated && rollback) {
CacheGroupContext grpCtx = part.group();
if (grpCtx.persistenceEnabled() && grpCtx.walEnabled() && !grpCtx.mvccEnabled()) {
RollbackRecord rec = new RollbackRecord(grpCtx.groupId(), part.id(), start, delta);
ptr = ctx.wal().log(rec);
}
for (int cntr = 1; cntr <= delta; cntr++) {
ctx0.continuousQueries().skipUpdateCounter(null, part.id(), start + cntr, topVer, rollbackOnPrimary);
}
}
} else
invalid = true;
} finally {
part.release();
}
} else
invalid = true;
} catch (GridDhtInvalidPartitionException e) {
invalid = true;
}
if (log.isDebugEnabled() && invalid) {
log.debug("Received partition update counters message for invalid partition, ignoring: " + "[cacheId=" + counter.cacheId() + ", part=" + counter.partition(i) + ']');
}
}
}
} finally {
if (ptr != null)
ctx.wal().flush(ptr, false);
}
}
use of org.apache.ignite.internal.processors.cache.distributed.dht.PartitionUpdateCountersMessage in project ignite by apache.
the class MvccCachingManager method onTxFinished.
/**
* @param tx Transaction.
* @param commit {@code True} if commit.
*/
public void onTxFinished(IgniteInternalTx tx, boolean commit) throws IgniteCheckedException {
if (log.isDebugEnabled())
log.debug("Transaction finished: [commit=" + commit + ", tx=" + tx + ']');
if (tx.system() || tx.internal() || tx.mvccSnapshot() == null)
return;
cntrs.remove(new TxKey(tx.mvccSnapshot().coordinatorVersion(), tx.mvccSnapshot().counter()));
EnlistBuffer buf = enlistCache.remove(tx.xidVersion());
Map<Integer, Map<KeyCacheObject, MvccTxEntry>> allCached = buf == null ? null : buf.getCached();
TxCounters txCntrs = tx.txCounters(false);
Collection<PartitionUpdateCountersMessage> cntrsColl = txCntrs == null ? null : txCntrs.updateCounters();
if (txCntrs == null || F.isEmpty(cntrsColl))
return;
GridIntList cacheIds = tx.txState().cacheIds();
assert cacheIds != null;
for (int i = 0; i < cacheIds.size(); i++) {
int cacheId = cacheIds.get(i);
GridCacheContext ctx0 = cctx.cacheContext(cacheId);
assert ctx0 != null;
ctx0.group().listenerLock().readLock().lock();
try {
boolean hasListeners = ctx0.hasContinuousQueryListeners(tx);
boolean drEnabled = ctx0.isDrEnabled();
if (!hasListeners && !drEnabled)
// There are no listeners to notify.
continue;
// Get cached entries for the given cache.
Map<KeyCacheObject, MvccTxEntry> cached = allCached == null ? null : allCached.get(cacheId);
Map<Integer, Map<Integer, T2<AtomicLong, Long>>> cntrsMap = countersPerPartition(cntrsColl);
Map<Integer, T2<AtomicLong, Long>> cntrPerCache = cntrsMap.get(cacheId);
if (F.isEmpty(cntrPerCache))
// No updates were made for this cache.
continue;
boolean fakeEntries = false;
if (F.isEmpty(cached)) {
if (log.isDebugEnabled())
log.debug("Transaction updates were not cached fully (this can happen when listener started" + " during the transaction execution). [tx=" + tx + ']');
if (hasListeners) {
// Create fake update entries if we have CQ listeners.
cached = createFakeCachedEntries(cntrPerCache, tx, cacheId);
fakeEntries = true;
} else
// Nothing to do further if tx is not cached entirely and there are no any CQ listeners.
continue;
}
if (F.isEmpty(cached))
continue;
// Feed CQ & DR with entries.
for (Map.Entry<KeyCacheObject, MvccTxEntry> entry : cached.entrySet()) {
MvccTxEntry e = entry.getValue();
assert e.key().partition() != -1;
assert cntrPerCache != null;
assert e.cacheId() == cacheId;
T2<AtomicLong, Long> cntr = cntrPerCache.get(e.key().partition());
long resCntr = cntr.getKey().incrementAndGet();
assert resCntr <= cntr.getValue();
e.updateCounter(resCntr);
if (ctx0.group().sharedGroup()) {
ctx0.group().onPartitionCounterUpdate(cacheId, e.key().partition(), resCntr, tx.topologyVersion(), tx.local());
}
if (log.isDebugEnabled())
log.debug("Process cached entry:" + e);
// DR
if (ctx0.isDrEnabled() && !fakeEntries) {
ctx0.dr().replicate(e.key(), e.value(), e.ttl(), e.expireTime(), e.version(), tx.local() ? DR_PRIMARY : DR_BACKUP, e.topologyVersion());
}
// CQ
CacheContinuousQueryManager contQryMgr = ctx0.continuousQueries();
if (ctx0.continuousQueries().notifyContinuousQueries(tx)) {
Map<UUID, CacheContinuousQueryListener> lsnrCol = continuousQueryListeners(ctx0, tx);
if (!F.isEmpty(lsnrCol)) {
contQryMgr.onEntryUpdated(lsnrCol, e.key(), // Force skip update counter if rolled back.
commit ? e.value() : null, // Force skip update counter if rolled back.
commit ? e.oldValue() : null, false, e.key().partition(), tx.local(), false, e.updateCounter(), null, e.topologyVersion());
}
}
}
} finally {
ctx0.group().listenerLock().readLock().unlock();
}
}
}
use of org.apache.ignite.internal.processors.cache.distributed.dht.PartitionUpdateCountersMessage in project ignite by apache.
the class MvccCachingManager method countersPerPartition.
/**
* Calculates counters updates per cache and partition: cacheId -> partId -> initCntr -> cntr + delta.
*
* @param cntrsColl Counters collection.
* @return Counters updates per cache and partition.
*/
private Map<Integer, Map<Integer, T2<AtomicLong, Long>>> countersPerPartition(Collection<PartitionUpdateCountersMessage> cntrsColl) {
//
Map<Integer, Map<Integer, T2<AtomicLong, Long>>> cntrsMap = new HashMap<>();
for (PartitionUpdateCountersMessage msg : cntrsColl) {
for (int i = 0; i < msg.size(); i++) {
Map<Integer, T2<AtomicLong, Long>> cntrPerPart = cntrsMap.computeIfAbsent(msg.cacheId(), k -> new HashMap<>());
T2 prev = cntrPerPart.put(msg.partition(i), new T2<>(new AtomicLong(msg.initialCounter(i)), msg.initialCounter(i) + msg.updatesCount(i)));
assert prev == null;
}
}
return cntrsMap;
}
Aggregations