/* * Licensed to the Apache Software Foundation (ASF) under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional information regarding * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the License. You may obtain a * copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package org.apache.geode.internal.cache.wan.parallel; import org.apache.geode.DataSerializer; import org.apache.geode.cache.Cache; import org.apache.geode.distributed.DistributedMember; import org.apache.geode.distributed.internal.*; import org.apache.geode.distributed.internal.membership.InternalDistributedMember; import org.apache.geode.internal.cache.*; import org.apache.geode.internal.cache.wan.AbstractGatewaySender; import org.apache.geode.internal.cache.wan.WaitUntilGatewaySenderFlushedCoordinator; import org.apache.geode.internal.cache.wan.parallel.ConcurrentParallelGatewaySenderQueue; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.*; public class WaitUntilParallelGatewaySenderFlushedCoordinator extends WaitUntilGatewaySenderFlushedCoordinator { public WaitUntilParallelGatewaySenderFlushedCoordinator(AbstractGatewaySender sender, long timeout, TimeUnit unit, boolean initiator) { super(sender, timeout, unit, initiator); } public boolean waitUntilFlushed() throws Throwable { boolean remoteResult = true, localResult = true; Throwable exceptionToThrow = null; ConcurrentParallelGatewaySenderQueue prq = (ConcurrentParallelGatewaySenderQueue) this.sender.getQueue(); PartitionedRegion pr = (PartitionedRegion) prq.getRegion(); // Create callables for local buckets List<WaitUntilBucketRegionQueueFlushedCallable> callables = buildWaitUntilBucketRegionQueueFlushedCallables(pr); // Submit local callables for execution ExecutorService service = this.sender.getDistributionManager().getWaitingThreadPool(); List<Future<Boolean>> callableFutures = new ArrayList<>(); for (Callable<Boolean> callable : callables) { callableFutures.add(service.submit(callable)); } if (logger.isDebugEnabled()) { logger.debug("WaitUntilParallelGatewaySenderFlushedCoordinator: Created and submitted " + callables.size() + " callables=" + callables); } // Send message to remote buckets if (this.initiator) { remoteResult = false; try { remoteResult = waitUntilFlushedOnRemoteMembers(pr); } catch (Throwable t) { exceptionToThrow = t; } if (logger.isDebugEnabled()) { logger.debug("WaitUntilParallelGatewaySenderFlushedCoordinator: Processed remote result=" + remoteResult + "; exceptionToThrow=" + exceptionToThrow); } } // Process local future results for (Future<Boolean> future : callableFutures) { boolean singleBucketResult = false; try { singleBucketResult = future.get(); } catch (ExecutionException e) { exceptionToThrow = e.getCause(); } localResult = localResult && singleBucketResult; } if (logger.isDebugEnabled()) { logger.debug("WaitUntilParallelGatewaySenderFlushedCoordinator: Processed local result=" + localResult + "; exceptionToThrow=" + exceptionToThrow); } // Return the full result if (exceptionToThrow == null) { if (logger.isDebugEnabled()) { logger.debug("WaitUntilParallelGatewaySenderFlushedCoordinator: Returning full result=" + (remoteResult && localResult)); } return remoteResult && localResult; } else { throw exceptionToThrow; } } protected List<WaitUntilBucketRegionQueueFlushedCallable> buildWaitUntilBucketRegionQueueFlushedCallables( PartitionedRegion pr) { List<WaitUntilBucketRegionQueueFlushedCallable> callables = new ArrayList<>(); if (pr.isDataStore()) { for (BucketRegion br : pr.getDataStore().getAllLocalBucketRegions()) { callables.add(new WaitUntilBucketRegionQueueFlushedCallable((BucketRegionQueue) br, this.timeout, this.unit)); } } return callables; } protected boolean waitUntilFlushedOnRemoteMembers(PartitionedRegion pr) throws Throwable { boolean result = true; DM dm = this.sender.getDistributionManager(); Set<InternalDistributedMember> recipients = pr.getRegionAdvisor().adviseDataStore(); if (!recipients.isEmpty()) { if (logger.isDebugEnabled()) { logger.debug( "WaitUntilParallelGatewaySenderFlushedCoordinator: About to send message recipients=" + recipients); } WaitUntilGatewaySenderFlushedReplyProcessor processor = new WaitUntilGatewaySenderFlushedReplyProcessor(dm, recipients); WaitUntilGatewaySenderFlushedMessage message = new WaitUntilGatewaySenderFlushedMessage( recipients, processor.getProcessorId(), this.sender.getId(), this.timeout, this.unit); dm.putOutgoing(message); if (logger.isDebugEnabled()) { logger.debug("WaitUntilParallelGatewaySenderFlushedCoordinator: Sent message recipients=" + recipients); } try { processor.waitForReplies(); result = processor.getCombinedResult(); } catch (ReplyException e) { if (logger.isDebugEnabled()) { logger.debug("WaitUntilParallelGatewaySenderFlushedCoordinator: Caught e=" + e + "; cause=" + e.getCause()); } throw e.getCause(); } catch (InterruptedException e) { dm.getCancelCriterion().checkCancelInProgress(e); Thread.currentThread().interrupt(); result = false; } } return result; } public static class WaitUntilBucketRegionQueueFlushedCallable implements Callable<Boolean> { private BucketRegionQueue brq; private long timeout; private TimeUnit unit; public WaitUntilBucketRegionQueueFlushedCallable(BucketRegionQueue brq, long timeout, TimeUnit unit) { this.brq = brq; this.timeout = timeout; this.unit = unit; } @Override public Boolean call() throws Exception { return this.brq.waitUntilFlushed(this.timeout, this.unit); } @Override public String toString() { return new StringBuilder().append(getClass().getSimpleName()).append("[").append("brq=") .append(this.brq.getId()).append("]").toString(); } } public static class WaitUntilGatewaySenderFlushedReplyProcessor extends ReplyProcessor21 { private Map<DistributedMember, Boolean> responses; public WaitUntilGatewaySenderFlushedReplyProcessor(DM dm, Collection initMembers) { super(dm, initMembers); initializeResponses(); } private void initializeResponses() { this.responses = new ConcurrentHashMap<>(); for (InternalDistributedMember member : getMembers()) { this.responses.put(member, false); } } @Override public void process(DistributionMessage msg) { try { if (msg instanceof ReplyMessage) { ReplyMessage reply = (ReplyMessage) msg; if (logger.isDebugEnabled()) { logger .debug("WaitUntilGatewaySenderFlushedReplyProcessor: Processing reply from sender=" + reply.getSender() + "; returnValue=" + reply.getReturnValue() + "; exception=" + reply.getException()); } if (reply.getException() == null) { this.responses.put(reply.getSender(), (Boolean) reply.getReturnValue()); } else { reply.getException().printStackTrace(); } } } finally { super.process(msg); } } public boolean getCombinedResult() { boolean combinedResult = true; for (boolean singleMemberResult : this.responses.values()) { combinedResult = combinedResult && singleMemberResult; } if (logger.isDebugEnabled()) { logger.debug("WaitUntilGatewaySenderFlushedReplyProcessor: Returning combinedResult=" + combinedResult); } return combinedResult; } } public static class WaitUntilGatewaySenderFlushedMessage extends PooledDistributionMessage implements MessageWithReply { private int processorId; private String gatewaySenderId; private long timeout; private TimeUnit unit; /* For serialization */ public WaitUntilGatewaySenderFlushedMessage() {} protected WaitUntilGatewaySenderFlushedMessage(Collection recipients, int processorId, String gatewaySenderId, long timeout, TimeUnit unit) { super(); setRecipients(recipients); this.processorId = processorId; this.gatewaySenderId = gatewaySenderId; this.timeout = timeout; this.unit = unit; } @Override protected void process(DistributionManager dm) { boolean result = false; ReplyException replyException = null; try { if (logger.isDebugEnabled()) { logger.debug("WaitUntilGatewaySenderFlushedMessage: Processing gatewaySenderId=" + this.gatewaySenderId + "; timeout=" + this.timeout + "; unit=" + this.unit); } Cache cache = GemFireCacheImpl.getInstance(); if (cache != null) { AbstractGatewaySender sender = (AbstractGatewaySender) cache.getGatewaySender(this.gatewaySenderId); if (sender != null) { try { WaitUntilParallelGatewaySenderFlushedCoordinator coordinator = new WaitUntilParallelGatewaySenderFlushedCoordinator(sender, this.timeout, this.unit, false); result = coordinator.waitUntilFlushed(); } catch (Throwable e) { replyException = new ReplyException(e); } } } } finally { ReplyMessage replyMsg = new ReplyMessage(); replyMsg.setRecipient(getSender()); replyMsg.setProcessorId(this.processorId); if (replyException == null) { replyMsg.setReturnValue(result); } else { replyMsg.setException(replyException); } if (logger.isDebugEnabled()) { logger.debug("WaitUntilGatewaySenderFlushedMessage: Sending reply returnValue=" + replyMsg.getReturnValue() + "; exception=" + replyMsg.getException()); } dm.putOutgoing(replyMsg); } } @Override public int getDSFID() { return WAIT_UNTIL_GATEWAY_SENDER_FLUSHED_MESSAGE; } @Override public void toData(DataOutput out) throws IOException { super.toData(out); out.writeInt(this.processorId); DataSerializer.writeString(this.gatewaySenderId, out); out.writeLong(this.timeout); DataSerializer.writeEnum(this.unit, out); } @Override public void fromData(DataInput in) throws IOException, ClassNotFoundException { super.fromData(in); this.processorId = in.readInt(); this.gatewaySenderId = DataSerializer.readString(in); this.timeout = in.readLong(); this.unit = DataSerializer.readEnum(TimeUnit.class, in); } } }