@Test public void testConnectedAgain() throws Exception { ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); ClientDescriptor client = new TestClientDescriptor(); activeEntity.connected(client); activeEntity.connected(client); Set<ClientDescriptor> connectedClients = activeEntity.getConnectedClients(); assertThat(connectedClients, hasSize(1)); assertThat(connectedClients, hasItem(client)); }
@Override public void loadExisting() { inflightInvalidations = new ArrayList<>(); if (!isStrong()) { LOGGER.debug("Preparing for handling inflight invalidations"); addInflightInvalidationsForEventualCaches(); } stateService.loadStore(storeIdentifier, configuration).setEvictionListener(this::invalidateHashAfterEviction); reconnectComplete.set(false); management.entityPromotionCompleted(); }
@Override public ReconnectHandler startReconnect() { return (clientDescriptor, bytes) -> { if (inflightInvalidations == null) { throw new AssertionError("Load existing was not invoked before handleReconnect"); } ClusterTierReconnectMessage reconnectMessage = reconnectMessageCodec.decode(bytes); ServerSideServerStore serverStore = stateService.getStore(storeIdentifier); addInflightInvalidationsForStrongCache(clientDescriptor, reconnectMessage, serverStore); lockManager.createLockStateAfterFailover(clientDescriptor, reconnectMessage.getLocksHeld()); LOGGER.info("Client '{}' successfully reconnected to newly promoted ACTIVE after failover.", clientDescriptor); connectedClients.put(clientDescriptor, Boolean.TRUE); }; }
private void invalidateAll(ClientDescriptor originatingClientDescriptor) { int invalidationId = invalidationIdGenerator.getAndIncrement(); Set<ClientDescriptor> clientsToInvalidate = new HashSet<>(getValidatedClients()); if (originatingClientDescriptor != null) { clientsToInvalidate.remove(originatingClientDescriptor); } InvalidationHolder invalidationHolder = new InvalidationHolder(originatingClientDescriptor, clientsToInvalidate); clientsWaitingForInvalidation.put(invalidationId, invalidationHolder); LOGGER.debug("SERVER: requesting {} client(s) invalidation of all in cache {} (ID {})", clientsToInvalidate.size(), storeIdentifier, invalidationId); for (ClientDescriptor clientDescriptorThatHasToInvalidate : clientsToInvalidate) { LOGGER.debug("SERVER: asking client {} to invalidate all from cache {} (ID {})", clientDescriptorThatHasToInvalidate, storeIdentifier, invalidationId); try { clientCommunicator.sendNoResponse(clientDescriptorThatHasToInvalidate, clientInvalidateAll(invalidationId)); } catch (MessageCodecException mce) { throw new AssertionError("Codec error", mce); } } if (clientsToInvalidate.isEmpty()) { clientInvalidated(invalidationHolder.clientDescriptorWaitingForInvalidation, invalidationId); } }
Set<Long> keys = store.getSegmentKeySets().get(segmentId); int remainingKeys = keys.size(); Map<Long, Chain> mappingsToSend = new HashMap<>(computeInitialMapCapacity(remainingKeys)); boolean capacityAdjusted = false; for (Long key : keys) { putMessage(messageQ, syncChannel, mappingsToSend); if (!capacityAdjusted && segmentId == 0) { capacityAdjusted = true; adjustInitialCapacity(numKeyGets); mappingsToSend = new HashMap<>(computeMapCapacity(remainingKeys, numKeyGets)); dataSize = 0; numKeyGets = 0; putMessage(messageQ, syncChannel, mappingsToSend); putMessage(messageQ, null, null); } catch (Throwable e) {
@Test public void testSyncToPassiveNoData() throws Exception { ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); activeEntity.createNew(); TestInvokeContext context = new TestInvokeContext(); activeEntity.connected(context.getClientDescriptor()); assertSuccess(activeEntity.invokeActive(context, new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration))); @SuppressWarnings("unchecked") PassiveSynchronizationChannel<EhcacheEntityMessage> syncChannel = mock(PassiveSynchronizationChannel.class); activeEntity.synchronizeKeyToPassive(syncChannel, 3); verifyZeroInteractions(syncChannel); }
/** * Ensures the disconnect of a connected client is properly tracked. */ @Test public void testDisconnected() throws Exception { ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); ClientDescriptor client1 = new TestClientDescriptor(); activeEntity.connected(client1); activeEntity.disconnected(client1); assertThat(activeEntity.getConnectedClients(), hasSize(0)); }
@Test public void testCreateDedicatedServerStoreExisting() throws Exception { ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); activeEntity.createNew(); ClusterTierActiveEntity otherEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); try { otherEntity.createNew(); fail("Duplicate creation should fail with an exception"); } catch (ConfigurationException e) { assertThat(e.getMessage(), containsString("already exists")); } }
@Test public void testInvalidMessageThrowsError() throws Exception { ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); TestInvokeContext context = new TestInvokeContext(); activeEntity.connected(context.getClientDescriptor()); try { activeEntity.invokeActive(context, new InvalidMessage()); fail("Invalid message should result in AssertionError"); } catch (AssertionError e) { assertThat(e.getMessage(), containsString("Unsupported")); } }
tmpInflightInvalidations.forEach(invalidationState -> { if (invalidationState.isClearInProgress()) { invalidateAll(invalidationState.getClientDescriptor()); .forEach(hashInvalidationToBeResent -> invalidateHashForClient(invalidationState.getClientDescriptor(), hashInvalidationToBeResent)); }); sendMessageToSelfAndDeferRetirement(activeInvokeContext, appendMessage, newChain); invalidateHashForClient(clientDescriptor, key); if (newChain.length() > chainCompactionLimit) { requestChainResolution(clientDescriptor, key, newChain); sendMessageToSelfAndDeferRetirement(activeInvokeContext, getAndAppendMessage, newChain); LOGGER.debug("Send invalidations for key {}", getAndAppendMessage.getKey()); invalidateHashForClient(clientDescriptor, getAndAppendMessage.getKey()); return getResponse(result); int invalidationId = clientInvalidationAck.getInvalidationId(); LOGGER.debug("SERVER: got notification of invalidation ack in cache {} from {} (ID {})", storeIdentifier, clientDescriptor, invalidationId); clientInvalidated(clientDescriptor, invalidationId); return success(); int invalidationId = clientInvalidationAllAck.getInvalidationId(); LOGGER.debug("SERVER: got notification of invalidation ack in cache {} from {} (ID {})", storeIdentifier, clientDescriptor, invalidationId); clientInvalidated(clientDescriptor, invalidationId); return success(); invalidateAll(clientDescriptor); return success();
@Test public void testLoadExistingRecoversInflightInvalidationsForEventualCache() throws Exception { ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); EhcacheStateServiceImpl ehcacheStateService = defaultRegistry.getStoreManagerService(); ehcacheStateService.createStore(defaultStoreName, defaultStoreConfiguration, false); //Passive would have done this before failover InvalidationTracker invalidationTracker = ehcacheStateService.getInvalidationTracker(defaultStoreName); Random random = new Random(); random.ints(0, 100).limit(10).forEach(invalidationTracker::trackHashInvalidation); activeEntity.loadExisting(); assertThat(activeEntity.getInflightInvalidations().isEmpty(), is(false)); }
@Test public void testDestroyServerStore() throws Exception { ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); activeEntity.createNew(); activeEntity.destroy(); assertThat(defaultRegistry.getResource(defaultResource).getUsed(), is(0L)); assertThat(defaultRegistry.getStoreManagerService().getStores(), empty()); assertThat(defaultRegistry.getStoreManagerService().getDedicatedResourcePoolIds(), empty()); }
public Set<ClientDescriptor> getConnectedClients() { return activeEntity.getConnectedClients(); }
@Test public void testDisconnectedNotConnected() throws Exception { ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); ClientDescriptor client1 = new TestClientDescriptor(); activeEntity.disconnected(client1); // Not expected to fail ... }
@Test public void testLoadExistingRegistersEvictionListener() throws Exception { EhcacheStateService stateService = mock(EhcacheStateService.class); ServerSideServerStore store = mock(ServerSideServerStore.class); when(stateService.loadStore(eq(defaultStoreName), any())).thenReturn(store); IEntityMessenger<EhcacheEntityMessage, EhcacheEntityResponse> entityMessenger = mock(IEntityMessenger.class); ServiceRegistry registry = getCustomMockedServiceRegistry(stateService, null, entityMessenger, null, null); ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(registry, defaultConfiguration, DEFAULT_MAPPER); activeEntity.loadExisting(); verify(store).setEvictionListener(any(ServerStoreEvictionListener.class)); }
@Override public ClusterTierActiveEntity createActiveEntity(ServiceRegistry registry, byte[] configuration) throws ConfigurationException { ClusterTierEntityConfiguration clusterTierEntityConfiguration = configCodec.decodeClusteredStoreConfiguration(configuration); return new ClusterTierActiveEntity(registry, clusterTierEntityConfiguration, DEFAULT_MAPPER); }
@Override public void disconnected(ClientDescriptor clientDescriptor) { // cleanup all invalidation requests waiting for a ack from this client Set<Integer> invalidationIds = clientsWaitingForInvalidation.keySet(); for (Integer invalidationId : invalidationIds) { clientInvalidated(clientDescriptor, invalidationId); } // cleanup all invalidation request this client was blocking on for(Iterator<Map.Entry<Integer, InvalidationHolder>> it = clientsWaitingForInvalidation.entrySet().iterator(); it.hasNext();) { Map.Entry<Integer, InvalidationHolder> next = it.next(); ClientDescriptor clientDescriptorWaitingForInvalidation = next.getValue().clientDescriptorWaitingForInvalidation; if (clientDescriptorWaitingForInvalidation != null && clientDescriptorWaitingForInvalidation.equals(clientDescriptor)) { it.remove(); } } lockManager.sweepLocksForClient(clientDescriptor, configuration.isWriteBehindConfigured() ? null : heldKeys -> heldKeys.forEach(stateService.getStore(storeIdentifier)::remove)); connectedClients.remove(clientDescriptor); }
private void prepareAndRunActiveEntityForPassiveSync(BiConsumer<ClusterTierActiveEntity, Integer> testConsumer) throws Exception { ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); activeEntity.createNew(); TestInvokeContext context = new TestInvokeContext(); activeEntity.connected(context.getClientDescriptor()); assertSuccess(activeEntity.invokeActive(context, new LifecycleMessage.ValidateServerStore(defaultStoreName, defaultStoreConfiguration))); ByteBuffer payload = ByteBuffer.allocate(512); // Put keys that maps to the same concurrency key ServerStoreOpMessage.AppendMessage testMessage = new ServerStoreOpMessage.AppendMessage(1L, payload); activeEntity.invokeActive(context, testMessage); activeEntity.invokeActive(context, new ServerStoreOpMessage.AppendMessage(-2L, payload)); activeEntity.invokeActive(context, new ServerStoreOpMessage.AppendMessage(17L, payload)); activeEntity.invokeActive(context, new ServerStoreOpMessage.AppendMessage(33L, payload)); ConcurrencyStrategies.DefaultConcurrencyStrategy concurrencyStrategy = new ConcurrencyStrategies.DefaultConcurrencyStrategy(DEFAULT_MAPPER); int concurrencyKey = concurrencyStrategy.concurrencyKey(testMessage); testConsumer.accept(activeEntity, concurrencyKey); }
/** * Ensures the disconnect of a connected client is properly tracked and does not affect others. */ @Test public void testDisconnectedSecond() throws Exception { ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); ClientDescriptor client1 = new TestClientDescriptor(); activeEntity.connected(client1); ClientDescriptor client2 = new TestClientDescriptor(); activeEntity.connected(client2); assertThat(activeEntity.getConnectedClients(), hasSize(2)); activeEntity.disconnected(client1); Set<ClientDescriptor> connectedClients = activeEntity.getConnectedClients(); assertThat(connectedClients, hasSize(1)); assertThat(connectedClients, hasItem(client2)); }
/** * Ensures shared pool and store (cache) name spaces are independent. * The cache alias is used as the name for a {@code ServerStore} instance; this name can be * the same as, but is independent of, the shared pool name. The */ @Test public void testSharedPoolCacheNameCollision() throws Exception { defaultRegistry.addSharedPool(defaultStoreName, MemoryUnit.MEGABYTES.toBytes(2), defaultResource); ClusterTierActiveEntity activeEntity = new ClusterTierActiveEntity(defaultRegistry, defaultConfiguration, DEFAULT_MAPPER); activeEntity.createNew(); assertThat(defaultRegistry.getStoreManagerService().getSharedResourcePoolIds(), contains(defaultStoreName)); assertThat(defaultRegistry.getStoreManagerService().getDedicatedResourcePoolIds(), contains(defaultStoreName)); assertThat(defaultRegistry.getStoreManagerService().getStores(), containsInAnyOrder(defaultStoreName)); }