Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

package com.function;

import com.microsoft.azure.functions.annotation.*;
import com.microsoft.azure.functions.*;

import java.util.*;

import com.microsoft.durabletask.*;
import com.microsoft.durabletask.azurefunctions.DurableActivityTrigger;
import com.microsoft.durabletask.azurefunctions.DurableOrchestrationTrigger;

public class IsReplayingChecks {

@FunctionName("IsReplayingEcho")
public String isReplayingEcho(
@DurableActivityTrigger(name = "value") String value,
final ExecutionContext context) {
// Bug: https://github.com/microsoft/durabletask-java/issues/235
if (value != null && value.length() >= 2 && value.startsWith("\"") && value.endsWith("\"")) {
value = value.substring(1, value.length() - 1);
}
return value;
}

@FunctionName("IsReplayingBasic")
public Map<String, Object> isReplayingBasic(
@DurableOrchestrationTrigger(name = "context") TaskOrchestrationContext ctx) {
boolean before = ctx.getIsReplaying();
String result = ctx.callActivity("IsReplayingEcho", "hello", String.class).await();
boolean after = ctx.getIsReplaying();

Map<String, Object> output = new LinkedHashMap<>();
output.put("before_activity", before);
output.put("after_activity", after);
output.put("activity_result", result);
return output;
}

@FunctionName("IsReplayingMultiActivity")
public Map<String, Object> isReplayingMultiActivity(
@DurableOrchestrationTrigger(name = "context") TaskOrchestrationContext ctx) {
List<Map<String, Object>> snapshots = new ArrayList<>();

Map<String, Object> s0 = new LinkedHashMap<>();
s0.put("step", 0);
s0.put("label", "start");
s0.put("is_replaying", ctx.getIsReplaying());
snapshots.add(s0);

String r1 = ctx.callActivity("IsReplayingEcho", "one", String.class).await();

Map<String, Object> s1 = new LinkedHashMap<>();
s1.put("step", 1);
s1.put("label", "after_first");
s1.put("is_replaying", ctx.getIsReplaying());
snapshots.add(s1);

String r2 = ctx.callActivity("IsReplayingEcho", "two", String.class).await();

Map<String, Object> s2 = new LinkedHashMap<>();
s2.put("step", 2);
s2.put("label", "after_second");
s2.put("is_replaying", ctx.getIsReplaying());
snapshots.add(s2);

String r3 = ctx.callActivity("IsReplayingEcho", "three", String.class).await();

Map<String, Object> s3 = new LinkedHashMap<>();
s3.put("step", 3);
s3.put("label", "after_third");
s3.put("is_replaying", ctx.getIsReplaying());
snapshots.add(s3);

Map<String, Object> output = new LinkedHashMap<>();
output.put("snapshots", snapshots);
output.put("activities", Arrays.asList(r1, r2, r3));
return output;
}

@FunctionName("IsReplayingConditionalLog")
public Map<String, Object> isReplayingConditionalLog(
@DurableOrchestrationTrigger(name = "context") TaskOrchestrationContext ctx,
final ExecutionContext executionContext) {
int liveLogCount = 0;

if (!ctx.getIsReplaying()) {
executionContext.getLogger().info("IsReplayingConditionalLog: LIVE before activity");
liveLogCount++;
} else {
executionContext.getLogger().info("IsReplayingConditionalLog: REPLAY before activity");
}

String result = ctx.callActivity("IsReplayingEcho", "logged", String.class).await();

if (!ctx.getIsReplaying()) {
executionContext.getLogger().info("IsReplayingConditionalLog: LIVE after activity");
liveLogCount++;
} else {
executionContext.getLogger().info("IsReplayingConditionalLog: REPLAY after activity");
}

Map<String, Object> output = new LinkedHashMap<>();
output.put("live_log_count", liveLogCount);
output.put("activity_result", result);
return output;
}

@FunctionName("IsReplayingCounter")
public Map<String, Object> isReplayingCounter(
@DurableOrchestrationTrigger(name = "context") TaskOrchestrationContext ctx) {
int nonReplayCount = 0;
int replayCount = 0;

if (ctx.getIsReplaying()) { replayCount++; } else { nonReplayCount++; }

String r1 = ctx.callActivity("IsReplayingEcho", "a", String.class).await();
if (ctx.getIsReplaying()) { replayCount++; } else { nonReplayCount++; }

String r2 = ctx.callActivity("IsReplayingEcho", "b", String.class).await();
if (ctx.getIsReplaying()) { replayCount++; } else { nonReplayCount++; }

String r3 = ctx.callActivity("IsReplayingEcho", "c", String.class).await();
if (ctx.getIsReplaying()) { replayCount++; } else { nonReplayCount++; }

Map<String, Object> output = new LinkedHashMap<>();
output.put("non_replay_count", nonReplayCount);
output.put("replay_count", replayCount);
output.put("total_checkpoints", nonReplayCount + replayCount);
output.put("activities", Arrays.asList(r1, r2, r3));
return output;
}

@FunctionName("IsReplayingFanOutFanIn")
public Map<String, Object> isReplayingFanOutFanIn(
@DurableOrchestrationTrigger(name = "context") TaskOrchestrationContext ctx) {
boolean before = ctx.getIsReplaying();

Task<String> t1 = ctx.callActivity("IsReplayingEcho", "alpha", String.class);
Task<String> t2 = ctx.callActivity("IsReplayingEcho", "beta", String.class);
Task<String> t3 = ctx.callActivity("IsReplayingEcho", "gamma", String.class);
ctx.allOf(Arrays.asList(t1, t2, t3)).await();

boolean after = ctx.getIsReplaying();

Map<String, Object> output = new LinkedHashMap<>();
output.put("before_fan_out", before);
output.put("after_fan_in", after);
output.put("activities", Arrays.asList(t1.await(), t2.await(), t3.await()));
return output;
}
}
147 changes: 147 additions & 0 deletions test/e2e/Apps/BasicNode/src/functions/IsReplayingChecks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

import * as df from 'durable-functions';
import { ActivityHandler, OrchestrationContext, OrchestrationHandler } from 'durable-functions';

// ---------------------------------------------------------------------------
// Activity used by the is_replaying orchestrators
// ---------------------------------------------------------------------------

const IsReplayingEcho: ActivityHandler = (input: string): string => {
return input;
};
df.app.activity('IsReplayingEcho', { handler: IsReplayingEcho });

// ---------------------------------------------------------------------------
// 1. IsReplayingBasic
// ---------------------------------------------------------------------------

const IsReplayingBasic: OrchestrationHandler = function* (context: OrchestrationContext) {
// Workaround: context.df.isReplaying is undefined before the first yield.
// See https://github.com/Azure/azure-functions-durable-js/issues/564
const before: boolean = context.df.isReplaying ?? true;
const result: string = yield context.df.callActivity('IsReplayingEcho', 'hello');
const after: boolean = context.df.isReplaying;
return {
before_activity: before,
after_activity: after,
activity_result: result,
};
};
df.app.orchestration('IsReplayingBasic', IsReplayingBasic);

// ---------------------------------------------------------------------------
// 2. IsReplayingMultiActivity
// ---------------------------------------------------------------------------

const IsReplayingMultiActivity: OrchestrationHandler = function* (context: OrchestrationContext) {
// Workaround: context.df.isReplaying is undefined before the first yield.
// See https://github.com/Azure/azure-functions-durable-js/issues/564
const snapshots: object[] = [];

snapshots.push({ step: 0, label: 'start', is_replaying: context.df.isReplaying ?? true });

const r1: string = yield context.df.callActivity('IsReplayingEcho', 'one');
snapshots.push({ step: 1, label: 'after_first', is_replaying: context.df.isReplaying });

const r2: string = yield context.df.callActivity('IsReplayingEcho', 'two');
snapshots.push({ step: 2, label: 'after_second', is_replaying: context.df.isReplaying });

const r3: string = yield context.df.callActivity('IsReplayingEcho', 'three');
snapshots.push({ step: 3, label: 'after_third', is_replaying: context.df.isReplaying });

return {
snapshots,
activities: [r1, r2, r3],
};
};
df.app.orchestration('IsReplayingMultiActivity', IsReplayingMultiActivity);

// ---------------------------------------------------------------------------
// 3. IsReplayingConditionalLog
// ---------------------------------------------------------------------------

const IsReplayingConditionalLog: OrchestrationHandler = function* (context: OrchestrationContext) {
// Workaround: context.df.isReplaying is undefined before the first yield.
// See https://github.com/Azure/azure-functions-durable-js/issues/564
let liveLogCount = 0;

if (!(context.df.isReplaying ?? true)) {
console.log('IsReplayingConditionalLog: LIVE before activity');
liveLogCount++;
} else {
console.log('IsReplayingConditionalLog: REPLAY before activity');
}

const result: string = yield context.df.callActivity('IsReplayingEcho', 'logged');

if (!context.df.isReplaying) {
console.log('IsReplayingConditionalLog: LIVE after activity');
liveLogCount++;
} else {
console.log('IsReplayingConditionalLog: REPLAY after activity');
}

return {
live_log_count: liveLogCount,
activity_result: result,
};
};
df.app.orchestration('IsReplayingConditionalLog', IsReplayingConditionalLog);

// ---------------------------------------------------------------------------
// 4. IsReplayingCounter
// ---------------------------------------------------------------------------

const IsReplayingCounter: OrchestrationHandler = function* (context: OrchestrationContext) {
// Workaround: context.df.isReplaying is undefined before the first yield.
// See https://github.com/Azure/azure-functions-durable-js/issues/564
let nonReplayCount = 0;
let replayCount = 0;

if (context.df.isReplaying ?? true) { replayCount++; } else { nonReplayCount++; }

const r1: string = yield context.df.callActivity('IsReplayingEcho', 'a');
if (context.df.isReplaying) { replayCount++; } else { nonReplayCount++; }

const r2: string = yield context.df.callActivity('IsReplayingEcho', 'b');
if (context.df.isReplaying) { replayCount++; } else { nonReplayCount++; }

const r3: string = yield context.df.callActivity('IsReplayingEcho', 'c');
if (context.df.isReplaying) { replayCount++; } else { nonReplayCount++; }

return {
non_replay_count: nonReplayCount,
replay_count: replayCount,
total_checkpoints: nonReplayCount + replayCount,
activities: [r1, r2, r3],
};
};
df.app.orchestration('IsReplayingCounter', IsReplayingCounter);

// ---------------------------------------------------------------------------
// 5. IsReplayingFanOutFanIn
// ---------------------------------------------------------------------------

const IsReplayingFanOutFanIn: OrchestrationHandler = function* (context: OrchestrationContext) {
// Workaround: context.df.isReplaying is undefined before the first yield.
// See https://github.com/Azure/azure-functions-durable-js/issues/564
const before: boolean = context.df.isReplaying ?? true;

const tasks = [
context.df.callActivity('IsReplayingEcho', 'alpha'),
context.df.callActivity('IsReplayingEcho', 'beta'),
context.df.callActivity('IsReplayingEcho', 'gamma'),
];
const results: string[] = yield context.df.Task.all(tasks);

const after: boolean = context.df.isReplaying;

return {
before_fan_out: before,
after_fan_in: after,
activities: results,
};
};
df.app.orchestration('IsReplayingFanOutFanIn', IsReplayingFanOutFanIn);
9 changes: 9 additions & 0 deletions test/e2e/Apps/BasicPowerShell/IsReplayingBasic/function.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"bindings": [
{
"name": "Context",
"type": "orchestrationTrigger",
"direction": "in"
}
]
}
11 changes: 11 additions & 0 deletions test/e2e/Apps/BasicPowerShell/IsReplayingBasic/run.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
param($Context)

$before = $Context.IsReplaying
$result = Invoke-DurableActivity -FunctionName 'IsReplayingEcho' -Input 'hello'
$after = $Context.IsReplaying

[ordered]@{
before_activity = $before
after_activity = $after
activity_result = $result
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"bindings": [
{
"name": "Context",
"type": "orchestrationTrigger",
"direction": "in"
}
]
}
24 changes: 24 additions & 0 deletions test/e2e/Apps/BasicPowerShell/IsReplayingConditionalLog/run.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
param($Context)

$liveLogCount = 0

if (-not $Context.IsReplaying) {
Write-Host "IsReplayingConditionalLog: LIVE before activity"
$liveLogCount++
} else {
Write-Host "IsReplayingConditionalLog: REPLAY before activity"
}

$result = Invoke-DurableActivity -FunctionName 'IsReplayingEcho' -Input 'logged'

if (-not $Context.IsReplaying) {
Write-Host "IsReplayingConditionalLog: LIVE after activity"
$liveLogCount++
} else {
Write-Host "IsReplayingConditionalLog: REPLAY after activity"
}

[ordered]@{
live_log_count = $liveLogCount
activity_result = $result
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"bindings": [
{
"name": "Context",
"type": "orchestrationTrigger",
"direction": "in"
}
]
}
22 changes: 22 additions & 0 deletions test/e2e/Apps/BasicPowerShell/IsReplayingCounter/run.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
param($Context)

$nonReplayCount = 0
$replayCount = 0

if ($Context.IsReplaying) { $replayCount++ } else { $nonReplayCount++ }

$r1 = Invoke-DurableActivity -FunctionName 'IsReplayingEcho' -Input 'a'
if ($Context.IsReplaying) { $replayCount++ } else { $nonReplayCount++ }

$r2 = Invoke-DurableActivity -FunctionName 'IsReplayingEcho' -Input 'b'
if ($Context.IsReplaying) { $replayCount++ } else { $nonReplayCount++ }

$r3 = Invoke-DurableActivity -FunctionName 'IsReplayingEcho' -Input 'c'
if ($Context.IsReplaying) { $replayCount++ } else { $nonReplayCount++ }

[ordered]@{
non_replay_count = $nonReplayCount
replay_count = $replayCount
total_checkpoints = $nonReplayCount + $replayCount
activities = @($r1, $r2, $r3)
}
Loading
Loading