cache(1).put("k1", "v1");
cache(2).put("k2", "v2");
cache(3).put("k3", "v3");
final StateTransferManager stm0 = advancedCache(0).getComponentRegistry().getStateTransferManager();
final int initialTopologyId = stm0.getCacheTopology().getTopologyId();
assertEquals(Arrays.asList(address(1), address(2), address(3)), stm0.getCacheTopology().getCurrentCH().locateOwners("k1"));
assertNull(stm0.getCacheTopology().getPendingCH());
// Block when cache 0 sends the first state request to cache 1
CommandMatcher segmentRequestMatcher = new CommandMatcher() {
@Override
public boolean accept(ReplicableCommand command) {
if (!(command instanceof StateRequestCommand))
return false;
StateRequestCommand stateRequestCommand = (StateRequestCommand) command;
if (stateRequestCommand.getType() != StateRequestCommand.Type.START_STATE_TRANSFER)
return false;
return stateRequestCommand.getTopologyId() == initialTopologyId + 1;
}
};
advanceOnOutboundRpc(sequencer, cache(0), segmentRequestMatcher)
.before("st:block_state_request", "st:resume_state_request");
// Cache 0 will become an owner and will request state from cache 1
consistentHashFactory.setOwnerIndexes(new int[]{0, 1, 2}, new int[]{0, 1, 2});
consistentHashFactory.triggerRebalance(cache(0));
sequencer.enter("st:simulate_old_response");
assertNotNull(stm0.getCacheTopology().getPendingCH());
assertEquals(Arrays.asList(address(0), address(1), address(2)), stm0.getCacheTopology().getPendingCH().locateOwners("k1"));
// Cache 0 didn't manage to request any segments yet, but it has registered all the inbound transfer tasks.
// We'll pretend it got a StateResponseCommand with an older topology id.
InboundInvocationHandler iih = TestingUtil.extractGlobalComponent(manager(0), InboundInvocationHandler.class);
StateChunk stateChunk0 = new StateChunk(0, Arrays.<InternalCacheEntry>asList(new ImmortalCacheEntry("k0", "v0")), true);
StateChunk stateChunk1 = new StateChunk(1, Arrays.<InternalCacheEntry>asList(new ImmortalCacheEntry("k0", "v0")), true);
StateResponseCommand stateResponseCommand = new StateResponseCommand(CacheContainer.DEFAULT_CACHE_NAME,
address(1), initialTopologyId, Arrays.asList(stateChunk0, stateChunk1));
// Call with preserveOrder = true to force the execution in the same thread
iih.handle(stateResponseCommand, address(3), null, true);
sequencer.exit("st:simulate_old_response");
waitForRehashToComplete(cache(0), cache(1), cache(2), cache(3));
// Check that state wasn't lost
assertTrue(stm0.getCacheTopology().getReadConsistentHash().isKeyLocalToNode(address(0), "k1"));
assertTrue(stm0.getCacheTopology().getReadConsistentHash().isKeyLocalToNode(address(0), "k2"));
assertTrue(stm0.getCacheTopology().getReadConsistentHash().isKeyLocalToNode(address(0), "k3"));
assertEquals("v1", cache(0).get("k1"));
assertEquals("v2", cache(0).get("k2"));
assertEquals("v3", cache(0).get("k3"));
// Check that the old state response was ignored
assertNull(cache(0).get("k0"));