use of com.hazelcast.spi.impl.operationservice.Operation in project hazelcast by hazelcast.
the class JobProxy method doExportSnapshot.
private JobStateSnapshot doExportSnapshot(String name, boolean cancelJob) {
checkNotLightJob("export snapshot");
JetServiceBackend jetServiceBackend = container().getService(JetServiceBackend.SERVICE_NAME);
try {
Operation operation = jetServiceBackend.createExportSnapshotOperation(getId(), name, cancelJob);
invokeOp(operation).get();
} catch (Exception e) {
throw rethrow(e);
}
return jetServiceBackend.getJet().getJobStateSnapshot(name);
}
use of com.hazelcast.spi.impl.operationservice.Operation in project hazelcast by hazelcast.
the class MasterContext method invokeOnParticipants.
/**
* @param completionCallback a consumer that will receive a collection
* of member-response pairs, one for each
* member, after all have been received. The
* response value will be either the response
* (including a null response) or an
* exception thrown from the operation (the
* pairs themselves will never be null); size
* will be equal to participant count
* @param individualCallback A callback that will be called after each
* individual participant completes
* @param retryOnTimeoutException if true, operations that threw {@link
* com.hazelcast.core.OperationTimeoutException}
* will be retried
*/
void invokeOnParticipants(Function<ExecutionPlan, Operation> operationCtor, @Nullable Consumer<Collection<Map.Entry<MemberInfo, Object>>> completionCallback, @Nullable BiConsumer<Address, Object> individualCallback, boolean retryOnTimeoutException) {
ConcurrentMap<MemberInfo, Object> responses = new ConcurrentHashMap<>();
AtomicInteger remainingCount = new AtomicInteger(executionPlanMap.size());
for (Entry<MemberInfo, ExecutionPlan> entry : executionPlanMap.entrySet()) {
MemberInfo memberInfo = entry.getKey();
Supplier<Operation> opSupplier = () -> operationCtor.apply(entry.getValue());
invokeOnParticipant(memberInfo, opSupplier, completionCallback, individualCallback, retryOnTimeoutException, responses, remainingCount);
}
}
use of com.hazelcast.spi.impl.operationservice.Operation in project hazelcast by hazelcast.
the class MasterContext method invokeOnParticipant.
private void invokeOnParticipant(MemberInfo memberInfo, Supplier<Operation> operationSupplier, @Nullable Consumer<Collection<Map.Entry<MemberInfo, Object>>> completionCallback, @Nullable BiConsumer<Address, Object> individualCallback, boolean retryOnTimeoutException, ConcurrentMap<MemberInfo, Object> collectedResponses, AtomicInteger remainingCount) {
Operation operation = operationSupplier.get();
InternalCompletableFuture<Object> future = nodeEngine.getOperationService().createInvocationBuilder(JetServiceBackend.SERVICE_NAME, operation, memberInfo.getAddress()).invoke();
future.whenCompleteAsync(withTryCatch(logger, (r, throwable) -> {
Object response = r != null ? r : throwable != null ? peel(throwable) : NULL_OBJECT;
if (retryOnTimeoutException && throwable instanceof OperationTimeoutException) {
logger.warning("Retrying " + operation.getClass().getName() + " that failed with " + OperationTimeoutException.class.getSimpleName() + " in " + jobIdString());
invokeOnParticipant(memberInfo, operationSupplier, completionCallback, individualCallback, retryOnTimeoutException, collectedResponses, remainingCount);
return;
}
if (individualCallback != null) {
individualCallback.accept(memberInfo.getAddress(), throwable != null ? peel(throwable) : r);
}
Object oldResponse = collectedResponses.put(memberInfo, response);
assert oldResponse == null : "Duplicate response for " + memberInfo.getAddress() + ". Old=" + oldResponse + ", new=" + response;
if (remainingCount.decrementAndGet() == 0 && completionCallback != null) {
completionCallback.accept(collectedResponses.entrySet().stream().map(e -> e.getValue() == NULL_OBJECT ? entry(e.getKey(), null) : e).collect(Collectors.toList()));
}
}));
}
use of com.hazelcast.spi.impl.operationservice.Operation in project hazelcast by hazelcast.
the class MasterSnapshotContext method tryBeginSnapshot.
void tryBeginSnapshot() {
mc.coordinationService().submitToCoordinatorThread(() -> {
boolean isTerminal;
String snapshotMapName;
CompletableFuture<Void> future;
mc.lock();
long localExecutionId;
try {
if (mc.jobStatus() != RUNNING) {
logger.fine("Not beginning snapshot, " + mc.jobIdString() + " is not RUNNING, but " + mc.jobStatus());
return;
}
if (snapshotInProgress) {
logger.fine("Not beginning snapshot since one is already in progress " + mc.jobIdString());
return;
}
if (terminalSnapshotFuture.isDone()) {
logger.fine("Not beginning snapshot since terminal snapshot is already completed " + mc.jobIdString());
return;
}
Tuple3<String, Boolean, CompletableFuture<Void>> requestedSnapshot = snapshotQueue.poll();
if (requestedSnapshot == null) {
return;
}
snapshotInProgress = true;
snapshotMapName = requestedSnapshot.f0();
assert requestedSnapshot.f1() != null;
isTerminal = requestedSnapshot.f1();
future = requestedSnapshot.f2();
mc.jobExecutionRecord().startNewSnapshot(snapshotMapName);
localExecutionId = mc.executionId();
} finally {
mc.unlock();
}
mc.writeJobExecutionRecord(false);
long newSnapshotId = mc.jobExecutionRecord().ongoingSnapshotId();
boolean isExport = snapshotMapName != null;
int snapshotFlags = SnapshotFlags.create(isTerminal, isExport);
String finalMapName = isExport ? exportedSnapshotMapName(snapshotMapName) : snapshotDataMapName(mc.jobId(), mc.jobExecutionRecord().ongoingDataMapIndex());
mc.nodeEngine().getHazelcastInstance().getMap(finalMapName).clear();
logFine(logger, "Starting snapshot %d for %s, flags: %s, writing to: %s", newSnapshotId, jobNameAndExecutionId(mc.jobName(), localExecutionId), SnapshotFlags.toString(snapshotFlags), snapshotMapName);
Function<ExecutionPlan, Operation> factory = plan -> new SnapshotPhase1Operation(mc.jobId(), localExecutionId, newSnapshotId, finalMapName, snapshotFlags);
// Need to take a copy of executionId: we don't cancel the scheduled task when the execution
// finalizes. If a new execution is started in the meantime, we'll use the execution ID to detect it.
mc.invokeOnParticipants(factory, responses -> onSnapshotPhase1Complete(responses, localExecutionId, newSnapshotId, finalMapName, snapshotFlags, future), null, true);
});
}
use of com.hazelcast.spi.impl.operationservice.Operation in project hazelcast by hazelcast.
the class MasterSnapshotContext method onSnapshotPhase1Complete.
/**
* @param responses collected responses from the members
* @param snapshotMapName the IMap name to which the snapshot is written
* @param snapshotFlags flags of the snapshot
* @param future a future to be completed when the phase-2 is fully completed
*/
private void onSnapshotPhase1Complete(Collection<Map.Entry<MemberInfo, Object>> responses, long executionId, long snapshotId, String snapshotMapName, int snapshotFlags, @Nullable CompletableFuture<Void> future) {
mc.coordinationService().submitToCoordinatorThread(() -> {
SnapshotPhase1Result mergedResult = new SnapshotPhase1Result();
List<CompletableFuture<Void>> missingResponses = new ArrayList<>();
for (Map.Entry<MemberInfo, Object> entry : responses) {
// the response is either SnapshotOperationResult or an exception, see #invokeOnParticipants() method
Object response = entry.getValue();
if (response instanceof Throwable) {
// all the responses to an array, and we'll wait for them later.
if (response instanceof ExecutionNotFoundException) {
missingResponses.add(mc.startOperationResponses().get(entry.getKey().getAddress()));
continue;
}
response = new SnapshotPhase1Result(0, 0, 0, (Throwable) response);
}
mergedResult.merge((SnapshotPhase1Result) response);
}
if (!missingResponses.isEmpty()) {
LoggingUtil.logFine(logger, "%s will wait for %d responses to StartExecutionOperation in " + "onSnapshotPhase1Complete()", mc.jobIdString(), missingResponses.size());
}
// In a typical case `missingResponses` will be empty. It will be non-empty if some member completed
// its execution and some other did not, or near the completion of a job, e.g. after a failure.
// `allOf` for an empty array returns a completed future immediately.
// Another edge case is that we'll be waiting for a response to start operation from a next execution,
// which can happen much later - we could handle it, but we ignore it: when it arrives, we'll find a
// changed executionId and ignore the response. It also doesn't occupy a thread - we're using a future.
CompletableFuture.allOf(missingResponses.toArray(new CompletableFuture[0])).whenComplete(withTryCatch(logger, (r, t) -> onSnapshotPhase1CompleteWithStartResponses(responses, executionId, snapshotId, snapshotMapName, snapshotFlags, future, mergedResult, missingResponses)));
});
}
Aggregations