use of com.uber.cadence.workflow.Promise in project cadence-client by uber-java.
the class DeterministicRunnerTest method workflowThreadsWillEvictCacheWhenMaxThreadCountIsHit.
@Test
public void workflowThreadsWillEvictCacheWhenMaxThreadCountIsHit() throws Throwable {
// Arrange
// Arrange
Map<String, String> tags = new ImmutableMap.Builder<String, String>(2).put(MetricsTag.DOMAIN, "domain").put(MetricsTag.TASK_LIST, "stickyTaskList").build();
StatsReporter reporter = mock(StatsReporter.class);
Scope scope = new RootScopeBuilder().reporter(reporter).reportEvery(com.uber.m3.util.Duration.ofMillis(300)).tagged(tags);
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 3, 1, TimeUnit.SECONDS, new SynchronousQueue<>());
AtomicReference<String> status = new AtomicReference<>();
DeciderCache cache = new DeciderCache(3, scope);
DecisionContext decisionContext = mock(DecisionContext.class);
when(decisionContext.getMetricsScope()).thenReturn(scope);
when(decisionContext.getDomain()).thenReturn("domain");
when(decisionContext.getWorkflowType()).thenReturn(new WorkflowType());
DeterministicRunnerImpl d = new DeterministicRunnerImpl(threadPool, new SyncDecisionContext(decisionContext, JsonDataConverter.getInstance(), null, next -> next, null), // clock override
() -> 0L, () -> {
Promise<Void> thread = Async.procedure(() -> {
status.set("started");
WorkflowThread.await("doing something", () -> false);
status.set("done");
});
thread.get();
}, cache);
Decider decider = new DetermisiticRunnerContainerDecider(d);
PollForDecisionTaskResponse response = HistoryUtils.generateDecisionTaskWithInitialHistory();
cache.getOrCreate(response, () -> decider);
cache.addToCache(response, decider);
d.runUntilAllBlocked();
assertEquals(2, threadPool.getActiveCount());
DeterministicRunnerImpl d2 = new DeterministicRunnerImpl(threadPool, new SyncDecisionContext(decisionContext, JsonDataConverter.getInstance(), null, next -> next, null), // clock override
() -> 0L, () -> {
Promise<Void> thread = Async.procedure(() -> {
status.set("started");
WorkflowThread.await("doing something else", () -> false);
status.set("done");
});
thread.get();
}, cache);
// Root thread added for d2 therefore we expect a total of 3 threads used
assertEquals(3, threadPool.getActiveCount());
assertEquals(1, cache.size());
// Act: This should kick out threads consumed by 'd'
d2.runUntilAllBlocked();
// Assert: Cache is evicted and only threads consumed by d2 remain.
assertEquals(2, threadPool.getActiveCount());
// cache was evicted
assertEquals(0, cache.size());
// Wait for reporter
Thread.sleep(600);
verify(reporter, atLeastOnce()).reportCounter(eq(MetricsType.STICKY_CACHE_THREAD_FORCED_EVICTION), eq(tags), anyInt());
}
use of com.uber.cadence.workflow.Promise in project cadence-client by uber-java.
the class WorkflowRetryerInternal method retryAsync.
private static <R> Promise<R> retryAsync(String retryId, RetryOptions options, Functions.Func<Promise<R>> func, long startTime, long attempt) {
options.validate();
RetryOptions retryOptions = WorkflowInternal.mutableSideEffect(retryId, RetryOptions.class, RetryOptions.class, Object::equals, () -> options);
CompletablePromise<R> funcResult = WorkflowInternal.newCompletablePromise();
try {
funcResult.completeFrom(func.apply());
} catch (RuntimeException e) {
funcResult.completeExceptionally(e);
}
return funcResult.handle((r, e) -> {
if (e == null) {
return WorkflowInternal.newPromise(r);
}
long elapsed = WorkflowInternal.currentTimeMillis() - startTime;
long sleepTime = retryOptions.calculateSleepTime(attempt);
if (retryOptions.shouldRethrow(e, attempt, elapsed, sleepTime)) {
throw e;
}
// recursion.
return WorkflowInternal.newTimer(Duration.ofMillis(sleepTime)).thenCompose((nil) -> retryAsync(retryId, retryOptions, func, startTime, attempt + 1));
}).thenCompose((r) -> r);
}
use of com.uber.cadence.workflow.Promise in project cadence-client by uber-java.
the class DeterministicRunnerImpl method close.
@Override
public void close() {
List<Future<?>> threadFutures = new ArrayList<>();
lock.lock();
if (closed) {
lock.unlock();
return;
}
// Do not close while runUntilAllBlocked executes.
// closeRequested tells it to call close() at the end.
closeRequested = true;
if (inRunUntilAllBlocked) {
lock.unlock();
return;
}
try {
for (WorkflowThread c : threadsToAdd) {
threads.addLast(c);
}
threadsToAdd.clear();
for (WorkflowThread c : threads) {
threadFutures.add(c.stopNow());
}
threads.clear();
// We cannot use an iterator to unregister failed Promises since f.get()
// will remove the promise directly from failedPromises. This causes an
// ConcurrentModificationException
// For this reason we will loop over a copy of failedPromises.
Set<Promise> failedPromisesLoop = new HashSet<>(failedPromises);
for (Promise f : failedPromisesLoop) {
if (!f.isCompleted()) {
throw new Error("expected failed");
}
try {
f.get();
throw new Error("unreachable");
} catch (RuntimeException e) {
log.warn("Promise that was completedExceptionally was never accessed. " + "The ignored exception:", CheckedExceptionWrapper.unwrap(e));
}
}
} finally {
closed = true;
lock.unlock();
}
// these tasks use the same lock to execute.
for (Future<?> future : threadFutures) {
try {
future.get();
} catch (InterruptedException e) {
throw new Error("Unexpected interrupt", e);
} catch (ExecutionException e) {
throw new Error("Unexpected failure stopping coroutine", e);
}
}
}
Aggregations