use of org.apache.ignite.internal.processors.cache.PartitionUpdateCounter in project ignite by apache.
the class TxPartitionCounterStateConsistencyTest method testPartitionConsistencyDuringRebalanceAndConcurrentUpdates_LateAffinitySwitch.
/**
* Tests tx load concurrently with PME for switching late affinity.
* <p>
* Scenario: two keys tx mapped locally on late affinity topology and when mapped and prepared remotely on ideal
* topology, first key is mapped to non-moving partition, second is mapped on moving partition.
* <p>
* Success: key over moving partition is prepared on new owner (choosed after late affinity switch),
* otherwise it's possible txs are prepared on different primaries after late affinity switch.
*/
@Test
public void testPartitionConsistencyDuringRebalanceAndConcurrentUpdates_LateAffinitySwitch() throws Exception {
backups = 1;
customDiscoSpi = new BlockTcpDiscoverySpi().setIpFinder(IP_FINDER);
Field rndAddrsField = U.findField(BlockTcpDiscoverySpi.class, "skipAddrsRandomization");
assertNotNull(rndAddrsField);
rndAddrsField.set(customDiscoSpi, true);
// Start coordinator with custom discovery SPI.
IgniteEx crd = startGrid(0);
IgniteEx g1 = startGrid(1);
startGrid(2);
crd.cluster().baselineAutoAdjustEnabled(false);
crd.cluster().active(true);
// Same name pattern as in test configuration.
String consistentId = "node" + getTestIgniteInstanceName(3);
List<Integer> g1Keys = primaryKeys(g1.cache(DEFAULT_CACHE_NAME), 10);
List<Integer> movingFromG1 = movingKeysAfterJoin(g1, DEFAULT_CACHE_NAME, 10, null, consistentId);
// Retain only stable keys;
g1Keys.removeAll(movingFromG1);
// The key will move from grid0 to grid3.
Integer key = movingKeysAfterJoin(crd, DEFAULT_CACHE_NAME, 1, null, consistentId).get(0);
IgniteEx g3 = startGrid(3);
assertEquals(consistentId, g3.localNode().consistentId());
resetBaselineTopology();
awaitPartitionMapExchange();
assertTrue(crd.affinity(DEFAULT_CACHE_NAME).isPrimary(g1.localNode(), g1Keys.get(0)));
stopGrid(3);
Ignite client = startClientGrid(CLIENT_GRID_NAME);
IgniteCache<Object, Object> cache = client.cache(DEFAULT_CACHE_NAME);
IgniteCache<Object, Object> cache2 = client.getOrCreateCache(cacheConfiguration(DEFAULT_CACHE_NAME + "2"));
// Put one key per partition.
for (int k = 0; k < partitions(); k++) {
cache.put(k, 0);
cache2.put(k, 0);
}
CountDownLatch resumeDiscoSndLatch = new CountDownLatch(1);
BlockTcpDiscoverySpi crdDiscoSpi = (BlockTcpDiscoverySpi) grid(0).configuration().getDiscoverySpi();
CyclicBarrier sync = new CyclicBarrier(2);
crdDiscoSpi.setClosure((node, msg) -> {
if (msg instanceof CacheAffinityChangeMessage) {
U.awaitQuiet(sync);
U.awaitQuiet(resumeDiscoSndLatch);
}
return null;
});
// Locks mapped wait.
IgniteInternalFuture fut = GridTestUtils.runAsync(() -> {
try {
startGrid(SERVER_NODES);
awaitPartitionMapExchange();
} catch (Exception e) {
fail(X.getFullStackTrace(e));
}
});
sync.await();
TestRecordingCommunicationSpi clientSpi = TestRecordingCommunicationSpi.spi(client);
clientSpi.blockMessages((node, msg) -> msg instanceof GridNearLockRequest);
IgniteInternalFuture txFut = GridTestUtils.runAsync(() -> {
try (Transaction tx = client.transactions().txStart()) {
Map<Integer, Integer> map = new LinkedHashMap<>();
// clientFirst=true in lockAll mapped to stable part.
map.put(g1Keys.get(0), g1Keys.get(0));
// clientFirst=false in lockAll mapped to moving part.
map.put(key, key);
cache.putAll(map);
cache2.putAll(new LinkedHashMap<>(map));
// Will start preparing in the middle of PME.
tx.commit();
}
});
IgniteInternalFuture lockFut = GridTestUtils.runAsync(() -> {
try {
// Wait for first lock request sent on local (late) topology.
clientSpi.waitForBlocked();
// Continue late switch PME.
resumeDiscoSndLatch.countDown();
crdDiscoSpi.setClosure(null);
// Wait late affinity switch.
awaitPartitionMapExchange();
// Continue tx mapping and preparing.
clientSpi.stopBlock();
} catch (InterruptedException e) {
fail(X.getFullStackTrace(e));
}
});
fut.get();
txFut.get();
lockFut.get();
assertPartitionsSame(idleVerify(crd, DEFAULT_CACHE_NAME));
// TX must be prepared over new owner.
PartitionUpdateCounter cntr = counter(key, grid(3).name());
assertNotNull(cntr);
assertEquals(cntr.toString(), 2, cntr.reserved());
PartitionUpdateCounter cntr2 = counter(key, DEFAULT_CACHE_NAME + "2", grid(3).name());
assertNotNull(cntr2);
assertEquals(cntr2.toString(), 2, cntr2.reserved());
}
use of org.apache.ignite.internal.processors.cache.PartitionUpdateCounter in project ignite by apache.
the class TxPartitionCounterStateOnePrimaryTwoBackupsFailAllTest method doTestRestartAllOwnersAfterPartialCommit.
/**
* Test scenario:
* <p>
* 1. All txs is prepared.
* <p>
* 2. All txs are committed on primary, all txs until waitCommitIdx are committed.
* <p>
* 3. Tx processing is paused, all nodes are stopped.
* <p>
* 4. Start backup1 and backup2 in specified order, detect unrecoverable partition and trigger failure handler.
* <p>
* Pass condition: nodes are stopped by failure handler if applicable.
*
* @param skipCheckpoint Skip checkpoint on node stop.
* @param prepareOrder Prepare order.
* @param primCommitOrder Prim commit order.
* @param backup1CommitOrder Backup 1 commit order.
* @param backup2CommitOrder Backup 2 commit order.
* @param sizes Sizes.
* @param waitCommitIdx Wait commit index.
* @param backupsStartOrder Start order of backups (should work same for any order).
* @param expectAliveNodes Expected alive nodes.
* @param failNodesOnBadCntr {@code True} to trigger FH if consistency can't be recovered.
*/
private void doTestRestartAllOwnersAfterPartialCommit(boolean skipCheckpoint, int[] prepareOrder, int[] primCommitOrder, int[] backup1CommitOrder, int[] backup2CommitOrder, int[] sizes, int[] waitCommitIdx, int[] backupsStartOrder, int expectAliveNodes, boolean failNodesOnBadCntr) throws Exception {
Map<IgniteEx, int[]> commits = new HashMap<IgniteEx, int[]>();
Map<Integer, T2<Ignite, List<Ignite>>> txTop = runOnPartition(PARTITION_ID, null, BACKUPS, NODES_CNT, new IgniteClosure<Map<Integer, T2<Ignite, List<Ignite>>>, TxCallback>() {
@Override
public TxCallback apply(Map<Integer, T2<Ignite, List<Ignite>>> map) {
T2<Ignite, List<Ignite>> txTop = map.get(PARTITION_ID);
Map<IgniteEx, int[]> prepares = new HashMap<IgniteEx, int[]>();
prepares.put((IgniteEx) txTop.get1(), prepareOrder);
final Ignite backup1 = txTop.get2().get(0);
final Ignite backup2 = txTop.get2().get(1);
commits.put((IgniteEx) txTop.get1(), primCommitOrder);
commits.put((IgniteEx) backup1, backup1CommitOrder);
commits.put((IgniteEx) backup2, backup2CommitOrder);
CountDownLatch l = new CountDownLatch(2);
return new TwoPhaseCommitTxCallbackAdapter(prepares, commits, sizes.length) {
@Override
protected boolean onBackupCommitted(IgniteEx backup, int idx) {
super.onBackupCommitted(backup, idx);
if (idx == commits.get(backup)[waitCommitIdx[backup == backup1 ? 0 : 1]]) {
l.countDown();
// Wait until both backups are committed required transactions.
try {
assertTrue(U.await(l, 30_000, TimeUnit.MILLISECONDS));
} catch (IgniteInterruptedCheckedException e) {
fail(e.getMessage());
}
if (backup == backup1) {
blockRecovery();
stopGrid(skipCheckpoint, txTop.get2().get(0).name());
stopGrid(skipCheckpoint, txTop.get2().get(1).name());
// Stop all remaining nodes.
stopAllGrids();
}
return true;
}
return false;
}
};
}
}, sizes);
// All owners should be stopped.
waitForTopology(0);
if (failNodesOnBadCntr)
System.setProperty(IGNITE_FAIL_NODE_ON_UNRECOVERABLE_PARTITION_INCONSISTENCY, "true");
try {
// Start only backups in given order.
startGrid(txTop.get(PARTITION_ID).get2().get(backupsStartOrder[0]).name());
startGrid(txTop.get(PARTITION_ID).get2().get(backupsStartOrder[1]).name());
try {
grid(0).cluster().active(true);
} catch (Throwable t) {
// Nodes are expected to stop during activation due to irrecoverable partition consistency.
assertTrue(X.hasCause(t, NodeStoppingException.class));
}
waitForTopology(expectAliveNodes);
awaitPartitionMapExchange();
} finally {
System.clearProperty(IGNITE_FAIL_NODE_ON_UNRECOVERABLE_PARTITION_INCONSISTENCY);
}
// Alive node should not have missed updates.
if (expectAliveNodes == 1) {
IgniteEx node = (IgniteEx) G.allGrids().iterator().next();
PartitionUpdateCounter cntr = counter(PARTITION_ID, node.name());
assertTrue(cntr.sequential());
}
}
use of org.apache.ignite.internal.processors.cache.PartitionUpdateCounter in project ignite by apache.
the class TxPartitionCounterStateOnePrimaryTwoBackupsTest method testMissingUpdateBetweenMultipleCheckpoints.
/**
* Test scenario:
* <p>
* 1. Assign counters in order tx0, tx1
* <p>
* 2. Commit tx1.
* <p>
* 3. Delay tx0 commit on backup1.
* <p>
* 4. Put more keys in partition, trigger checkpoint, put more keys.
* <p>
* 5. Commit delayed tx closing gap.
* <p>
* 5. Restart backup1 without triggering checkpoint on stop.
* <p>
*
* Pass condition: backup1 after restart has sequential update counter. No rebalance is expected.
* The test states necessity of storing gaps between checkpoints.
*
* @throws Exception If failed.
*/
@Test
public void testMissingUpdateBetweenMultipleCheckpoints() throws Exception {
int[] sizes = new int[] { 3, 7 };
int[] assignOrder = new int[] { 0, 1 };
int delayBackupIdx = 0;
GridFutureAdapter<T2<Ignite, GridFutureAdapter>> fut = new GridFutureAdapter<>();
GridTestUtils.runAsync(new Runnable() {
@Override
public void run() {
try {
T2<Ignite, GridFutureAdapter> pair = fut.get(30, TimeUnit.SECONDS);
IgniteEx client = grid(CLIENT_GRID_NAME);
// Allow txs to work as usual.
for (Ignite node : G.allGrids()) TestRecordingCommunicationSpi.spi(node).stopBlock(false, null, true, false);
List<Integer> keys = partitionKeys(client.cache(DEFAULT_CACHE_NAME), PARTITION_ID, 10, sizes[0] + sizes[1] + PRELOAD_KEYS_CNT);
for (Integer key : keys) client.cache(DEFAULT_CACHE_NAME).put(key, key);
Ignite backup1 = pair.get1();
forceCheckpoint(backup1);
// Commit delayed tx.
pair.get2().onDone();
} catch (IgniteCheckedException e) {
fail(X.getFullStackTrace(e));
}
}
});
Map<Integer, T2<Ignite, List<Ignite>>> txTops = runOnPartition(PARTITION_ID, null, BACKUPS, SERVERS_CNT, map -> {
Ignite primary = map.get(PARTITION_ID).get1();
Ignite backup1 = map.get(PARTITION_ID).get2().get(delayBackupIdx);
return new TwoPhaseCommitTxCallbackAdapter(U.map((IgniteEx) primary, assignOrder), new HashMap<>(), new HashMap<>(), sizes.length) {
@Override
public boolean beforeBackupFinish(IgniteEx primary, IgniteEx backup, @Nullable IgniteInternalTx primaryTx, IgniteInternalTx backupTx, IgniteUuid nearXidVer, GridFutureAdapter<?> proceedFut) {
if (order(nearXidVer) == assignOrder[0] && backup == backup1) {
fut.onDone(new T2<>(backup1, proceedFut));
// Delay commit on backup.
return true;
}
return super.beforeBackupFinish(primary, backup, primaryTx, backupTx, nearXidVer, proceedFut);
}
};
}, sizes);
// At this point all txs are committed and no gaps are expected.
Ignite backup1 = txTops.get(PARTITION_ID).get2().get(delayBackupIdx);
PartitionUpdateCounter cntr;
assertNotNull(cntr = counter(PARTITION_ID, backup1.name()));
assertTrue(cntr.sequential());
stopGrid(true, backup1.name());
startGrid(backup1.name());
awaitPartitionMapExchange();
assertNotNull(cntr = counter(PARTITION_ID, backup1.name()));
assertTrue(cntr.sequential());
assertPartitionsSame(idleVerify(grid(CLIENT_GRID_NAME), DEFAULT_CACHE_NAME));
assertCountersSame(PARTITION_ID, true, DEFAULT_CACHE_NAME);
}
use of org.apache.ignite.internal.processors.cache.PartitionUpdateCounter in project ignite by apache.
the class PartitionUpdateCounterTest method testMixedModeMultithreaded.
/**
* Test multithreaded updates of pc in various modes.
*
* @throws Exception If failed.
*/
@Test
public void testMixedModeMultithreaded() throws Exception {
PartitionUpdateCounter pc = new PartitionUpdateCounterTrackingImpl(null);
AtomicBoolean stop = new AtomicBoolean();
Queue<long[]> reservations = new ConcurrentLinkedQueue<>();
LongAdder reserveCntr = new LongAdder();
IgniteInternalFuture<?> fut = multithreadedAsync(() -> {
while (!stop.get() || !reservations.isEmpty()) {
if (!stop.get() && ThreadLocalRandom.current().nextBoolean()) {
int size = ThreadLocalRandom.current().nextInt(9) + 1;
// Only update if stop flag is set.
reservations.add(new long[] { pc.reserve(size), size });
reserveCntr.add(size);
} else {
long[] reserved = reservations.poll();
if (reserved == null)
continue;
pc.update(reserved[0], reserved[1]);
}
}
}, Runtime.getRuntime().availableProcessors() * 2, "updater-thread");
doSleep(10_000);
stop.set(true);
fut.get();
assertTrue(reservations.isEmpty());
log.info("counter=" + pc.toString() + ", reserveCntrLocal=" + reserveCntr.sum());
assertTrue(pc.sequential());
assertTrue(pc.get() == pc.reserved());
assertEquals(reserveCntr.sum(), pc.get());
}
use of org.apache.ignite.internal.processors.cache.PartitionUpdateCounter in project ignite by apache.
the class PartitionUpdateCounterTest method testStaleUpdate.
/**
* Test if pc correctly reports stale (before current counter) updates.
* This information is used for logging rollback records only once.
*/
@Test
public void testStaleUpdate() {
PartitionUpdateCounter pc = new PartitionUpdateCounterTrackingImpl(null);
assertTrue(pc.update(0, 1));
assertFalse(pc.update(0, 1));
assertTrue(pc.update(2, 1));
assertFalse(pc.update(2, 1));
assertTrue(pc.update(1, 1));
assertFalse(pc.update(1, 1));
}
Aggregations