public ClusterTierManagerPassiveEntity(ClusterTierManagerConfiguration config, EhcacheStateService ehcacheStateService, Management management) throws ConfigurationException { if (config == null) { throw new ConfigurationException("ClusterTierManagerConfiguration cannot be null"); } this.ehcacheStateService = ehcacheStateService; this.configuration = config; if (ehcacheStateService == null) { throw new AssertionError("Server failed to retrieve EhcacheStateService."); } try { ehcacheStateService.configure(); this.management = management; } catch (ConfigurationException e) { ehcacheStateService.destroy(); throw e; } }
@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); }
@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(); }
private EhcacheEntityResponse prepareForDestroy() { EhcacheEntityResponse.PrepareForDestroy response = EhcacheEntityResponse.prepareForDestroy(ehcacheStateService.getStores()); ehcacheStateService.prepareForDestroy(); return response; }
private void invokeServerStoreOperation(ServerStoreOpMessage message) throws ClusterException { ServerSideServerStore cacheStore = stateService.getStore(storeIdentifier); if (cacheStore == null) { // An operation on a non-existent store should never get out of the client throw new LifecycleException("cluster tier does not exist : '" + storeIdentifier + "'"); } switch (message.getMessageType()) { case REPLACE: { ServerStoreOpMessage.ReplaceAtHeadMessage replaceAtHeadMessage = (ServerStoreOpMessage.ReplaceAtHeadMessage)message; cacheStore.replaceAtHead(replaceAtHeadMessage.getKey(), replaceAtHeadMessage.getExpect(), replaceAtHeadMessage.getUpdate()); break; } case CLEAR: { LOGGER.info("Clearing cluster tier {}", storeIdentifier); try { cacheStore.clear(); } catch (TimeoutException e) { throw new AssertionError("Server side store is not expected to throw timeout exception"); } if (isEventual()) { stateService.getInvalidationTracker(storeIdentifier).setClearInProgress(true); } break; } default: throw new AssertionError("Unsupported ServerStore operation : " + message.getMessageType()); } }
@Override public void destroy() { ehcacheStateService.destroy(); management.close(); } }
/** * Handles the {@link ValidateStoreManager ValidateStoreManager} message. This message is used by a client to * connect to an established {@code ClusterTierManagerActiveEntity}. This method validates the client-provided configuration * against the existing configuration to ensure compatibility. * * @param clientDescriptor the client identifier requesting attachment to a configured store manager * @param message the {@code ValidateStoreManager} message carrying the client expected resource pool configuration */ private void validate(ClientDescriptor clientDescriptor, ValidateStoreManager message) throws ClusterException { ehcacheStateService.validate(message.getConfiguration()); } }
private void invokeSyncOperation(InvokeContext context, EhcacheSyncMessage message) { switch (message.getMessageType()) { case DATA: EhcacheDataSyncMessage dataSyncMessage = (EhcacheDataSyncMessage) message; ServerSideServerStore store = stateService.getStore(storeIdentifier); dataSyncMessage.getChainMap().forEach(store::put); break; case STATE_REPO: EhcacheStateRepoSyncMessage stateRepoSyncMessage = (EhcacheStateRepoSyncMessage) message; stateService.getStateRepositoryManager().processSyncMessage(stateRepoSyncMessage); break; case MESSAGE_TRACKER: EhcacheMessageTrackerMessage messageTrackerMessage = (EhcacheMessageTrackerMessage) message; messageTrackerMessage.getTrackedMessages().forEach((key, value) -> messageHandler.loadTrackedResponsesForSegment(messageTrackerMessage.getSegmentId(), context.makeClientSourceId(key), value)); break; default: throw new AssertionError("Unsupported Sync operation " + message.getMessageType()); } }
private void init() { ServerSideServerStore serverStore = ehcacheStateService.getStore(storeIdentifier); ServerStoreBinding serverStoreBinding = new ServerStoreBinding(storeIdentifier, serverStore); CompletableFuture<Void> r1 = managementRegistry.register(serverStoreBinding); ServerSideConfiguration.Pool pool = ehcacheStateService.getDedicatedResourcePool(storeIdentifier); CompletableFuture<Void> allOf; if (pool != null) { allOf = CompletableFuture.allOf(r1, managementRegistry.register(new PoolBinding(storeIdentifier, pool, PoolBinding.AllocationType.DEDICATED))); } else { allOf = r1; } allOf.thenRun(() -> { managementRegistry.refresh(); managementRegistry.pushServerEntityNotification(serverStoreBinding, EHCACHE_SERVER_STORE_CREATED.name()); }); }
private void invokeLifeCycleOperation(LifecycleMessage message) { switch (message.getMessageType()) { case PREPARE_FOR_DESTROY: ehcacheStateService.prepareForDestroy(); break; default: throw new AssertionError("Unsupported LifeCycle operation " + message.getMessageType()); } }
@SuppressWarnings("try") private EhcacheEntityResponse invokePassiveInternal(InvokeContext context, EhcacheEntityMessage message) { if (message instanceof EhcacheOperationMessage) { EhcacheOperationMessage operationMessage = (EhcacheOperationMessage) message; try (EhcacheStateContext ignored = stateService.beginProcessing(operationMessage, storeIdentifier)) { EhcacheMessageType messageType = operationMessage.getMessageType(); if (isStoreOperationMessage(messageType)) { invokeServerStoreOperation((ServerStoreOpMessage) message); } else if (isStateRepoOperationMessage(messageType)) { return stateService.getStateRepositoryManager().invoke((StateRepositoryOpMessage) message); } else if (isPassiveReplicationMessage(messageType)) { return invokeRetirementMessages((PassiveReplicationMessage) message); } else { throw new AssertionError("Unsupported EhcacheOperationMessage: " + operationMessage.getMessageType()); } } catch (ClusterException | OversizeMappingException e) { // The above operations are not critical enough to fail a passive, so just log the exception LOGGER.error("Unexpected exception raised during operation: " + message, e); } } else if (message instanceof EhcacheSyncMessage) { invokeSyncOperation(context, (EhcacheSyncMessage) message); } else { throw new AssertionError("Unsupported EhcacheEntityMessage: " + message.getClass()); } // Default response for messages not returning anything specific return success(); }
@Override protected StatisticRegistry getStatisticRegistry(PoolBinding managedObject) { if (managedObject == PoolBinding.ALL_SHARED) { return new StatisticRegistry(null, () -> getTimeSource().getTimestamp()); } String poolName = managedObject.getAlias(); PoolBinding.AllocationType allocationType = managedObject.getAllocationType(); if (allocationType == PoolBinding.AllocationType.DEDICATED) { return new StatisticRegistry(ehcacheStateService.getDedicatedResourcePageSource(poolName), () -> getTimeSource().getTimestamp()); } else { return new StatisticRegistry(ehcacheStateService.getSharedResourcePageSource(poolName), () -> getTimeSource().getTimestamp()); } }
@Override public void destroy() { LOGGER.info("Destroying cluster tier '{}'", storeIdentifier); try { stateService.destroyServerStore(storeIdentifier); } catch (ClusterException e) { LOGGER.error("Failed to destroy server store - does not exist", e); } management.close(); }
@Override public void createNew() throws ConfigurationException { stateService.createStore(storeIdentifier, configuration, false); management.entityCreated(); }
@SuppressWarnings("try") private EhcacheEntityResponse invokeActiveInternal(InvokeContext context, EhcacheEntityMessage message) { try { if (message instanceof EhcacheOperationMessage) { EhcacheOperationMessage operationMessage = (EhcacheOperationMessage) message; try (EhcacheStateContext ignored = stateService.beginProcessing(operationMessage, storeIdentifier)) { EhcacheMessageType messageType = operationMessage.getMessageType(); if (isStoreOperationMessage(messageType)) { return invokeServerStoreOperation(context, (ServerStoreOpMessage) message); } else if (isLifecycleMessage(messageType)) { return invokeLifeCycleOperation(context, (LifecycleMessage) message); } else if (isStateRepoOperationMessage(messageType)) { return invokeStateRepositoryOperation((StateRepositoryOpMessage) message); } } } throw new AssertionError("Unsupported message : " + message.getClass()); } catch (ClusterException e) { return failure(e); } catch (Exception e) { LOGGER.error("Unexpected exception raised during operation: " + message, e); return failure(new InvalidOperationException(e)); } }
ChainReplicationMessage retirementMessage = (ChainReplicationMessage) message; LOGGER.debug("Chain Replication message for transactionId {} & clientId {}", retirementMessage.getTransactionId(), retirementMessage.getClientId()); ServerSideServerStore cacheStore = stateService.getStore(storeIdentifier); if (cacheStore == null) { stateService.getInvalidationTracker(storeIdentifier).trackHashInvalidation(retirementMessage.getKey()); if (isEventual()) { InvalidationCompleteMessage invalidationCompleteMessage = (InvalidationCompleteMessage) message; stateService.getInvalidationTracker(storeIdentifier).untrackHashInvalidation(invalidationCompleteMessage.getKey()); stateService.getInvalidationTracker(storeIdentifier).setClearInProgress(false);
/** * {@inheritDoc} * <p> * This method is invoked in response to a call to a {@code com.tc.objectserver.api.ServerEntityRequest} * message for a {@code ServerEntityAction.DESTROY_ENTITY} request which is sent via a call to the * {@code ClusteringService.destroyAll} method. This method is expected to be called only when no * clients actively using this entity. */ @Override public void destroy() { ehcacheStateService.destroy(); management.close(); }
@Test public void testPrepareForDestroy() throws Exception { final OffHeapIdentifierRegistry registry = new OffHeapIdentifierRegistry(); registry.addResource("serverResource1", 8, MemoryUnit.MEGABYTES); EhcacheStateService ehcacheStateService = registry.getService(new EhcacheStateServiceConfig(blankConfiguration, registry, DEFAULT_MAPPER)); final ClusterTierManagerActiveEntity activeEntity = new ClusterTierManagerActiveEntity(blankConfiguration, ehcacheStateService, management); TestInvokeContext context = new TestInvokeContext(); activeEntity.connected(context.getClientDescriptor()); activeEntity.invokeActive(context, MESSAGE_FACTORY.prepareForDestroy()); try { ehcacheStateService.validate(null); fail("DestroyInProgressException expected"); } catch (DestroyInProgressException e) { assertThat(e.getMessage(), containsString("in progress for destroy")); } }
@Test public void testPrepareForDestroyInProgress() throws Exception { final OffHeapIdentifierRegistry registry = new OffHeapIdentifierRegistry(); registry.addResource("serverResource1", 8, MemoryUnit.MEGABYTES); EhcacheStateService ehcacheStateService = registry.getService(new EhcacheStateServiceConfig(blankConfiguration, registry, DEFAULT_MAPPER)); ehcacheStateService.prepareForDestroy(); final ClusterTierManagerActiveEntity activeEntity = new ClusterTierManagerActiveEntity(blankConfiguration, ehcacheStateService, management); TestInvokeContext context = new TestInvokeContext(); activeEntity.connected(context.getClientDescriptor()); assertFailure(activeEntity.invokeActive(context, MESSAGE_FACTORY.validateStoreManager(null)), DestroyInProgressException.class, "in progress for destroy"); }
@Override public void destroy() { LOGGER.info("Destroying cluster tier '{}'", storeIdentifier); try { stateService.destroyServerStore(storeIdentifier); } catch (ClusterException e) { throw new AssertionError(e); } management.close(); }