Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add suspend/resume tests #3028

Merged
merged 10 commits into from
Feb 6, 2025
108 changes: 53 additions & 55 deletions test/e2e/Apps/BasicDotNetIsolated/HelloCities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,74 +7,72 @@
using Microsoft.DurableTask.Client;
using Microsoft.Extensions.Logging;

namespace Microsoft.Azure.Durable.Tests.E2E
namespace Microsoft.Azure.Durable.Tests.E2E;
public static class HelloCities
{
public static class HelloCities
[Function(nameof(HelloCities))]
public static async Task<List<string>> RunOrchestrator(
[OrchestrationTrigger] TaskOrchestrationContext context)
{
[Function(nameof(HelloCities))]
public static async Task<List<string>> RunOrchestrator(
[OrchestrationTrigger] TaskOrchestrationContext context)
{
ILogger logger = context.CreateReplaySafeLogger(nameof(HelloCities));
logger.LogInformation("Saying hello.");
var outputs = new List<string>();
ILogger logger = context.CreateReplaySafeLogger(nameof(HelloCities));
logger.LogInformation("Saying hello.");
var outputs = new List<string>();

// Replace name and input with values relevant for your Durable Functions Activity
outputs.Add(await context.CallActivityAsync<string>(nameof(SayHello), "Tokyo"));
outputs.Add(await context.CallActivityAsync<string>(nameof(SayHello), "Seattle"));
outputs.Add(await context.CallActivityAsync<string>(nameof(SayHello), "London"));
// Replace name and input with values relevant for your Durable Functions Activity
outputs.Add(await context.CallActivityAsync<string>(nameof(SayHello), "Tokyo"));
outputs.Add(await context.CallActivityAsync<string>(nameof(SayHello), "Seattle"));
outputs.Add(await context.CallActivityAsync<string>(nameof(SayHello), "London"));

// returns ["Hello Tokyo!", "Hello Seattle!", "Hello London!"]
return outputs;
}
// returns ["Hello Tokyo!", "Hello Seattle!", "Hello London!"]
return outputs;
}

[Function(nameof(SayHello))]
public static string SayHello([ActivityTrigger] string name, FunctionContext executionContext)
{
ILogger logger = executionContext.GetLogger("SayHello");
logger.LogInformation("Saying hello to {name}.", name);
return $"Hello {name}!";
}
[Function(nameof(SayHello))]
public static string SayHello([ActivityTrigger] string name, FunctionContext executionContext)
{
ILogger logger = executionContext.GetLogger("SayHello");
logger.LogInformation("Saying hello to {name}.", name);
return $"Hello {name}!";
}

[Function("HelloCities_HttpStart")]
public static async Task<HttpResponseData> HttpStart(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
[DurableClient] DurableTaskClient client,
FunctionContext executionContext)
{
ILogger logger = executionContext.GetLogger("HelloCities_HttpStart");
[Function("HelloCities_HttpStart")]
public static async Task<HttpResponseData> HttpStart(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
[DurableClient] DurableTaskClient client,
FunctionContext executionContext)
{
ILogger logger = executionContext.GetLogger("HelloCities_HttpStart");

// Function input comes from the request content.
string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(
nameof(HelloCities));
// Function input comes from the request content.
string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(
nameof(HelloCities));

logger.LogInformation("Started orchestration with ID = '{instanceId}'.", instanceId);
logger.LogInformation("Started orchestration with ID = '{instanceId}'.", instanceId);

// Returns an HTTP 202 response with an instance management payload.
// See https://learn.microsoft.com/azure/azure-functions/durable/durable-functions-http-api#start-orchestration
return await client.CreateCheckStatusResponseAsync(req, instanceId);
}
// Returns an HTTP 202 response with an instance management payload.
// See https://learn.microsoft.com/azure/azure-functions/durable/durable-functions-http-api#start-orchestration
return await client.CreateCheckStatusResponseAsync(req, instanceId);
}

[Function("HelloCities_HttpStart_Scheduled")]
public static async Task<HttpResponseData> HttpStartScheduled(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
[DurableClient] DurableTaskClient client,
FunctionContext executionContext,
DateTime scheduledStartTime)
{
ILogger logger = executionContext.GetLogger("HelloCities_HttpStart");
[Function("HelloCities_HttpStart_Scheduled")]
public static async Task<HttpResponseData> HttpStartScheduled(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
[DurableClient] DurableTaskClient client,
FunctionContext executionContext,
DateTime scheduledStartTime)
{
ILogger logger = executionContext.GetLogger("HelloCities_HttpStart");

var startOptions = new StartOrchestrationOptions(StartAt: scheduledStartTime);
var startOptions = new StartOrchestrationOptions(StartAt: scheduledStartTime);

// Function input comes from the request content.
string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(
nameof(HelloCities), startOptions);
// Function input comes from the request content.
string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(
nameof(HelloCities), startOptions);

logger.LogInformation("Started orchestration with ID = '{instanceId}'.", instanceId);
logger.LogInformation("Started orchestration with ID = '{instanceId}'.", instanceId);

// Returns an HTTP 202 response with an instance management payload.
// See https://learn.microsoft.com/azure/azure-functions/durable/durable-functions-http-api#start-orchestration
return await client.CreateCheckStatusResponseAsync(req, instanceId);
}
// Returns an HTTP 202 response with an instance management payload.
// See https://learn.microsoft.com/azure/azure-functions/durable/durable-functions-http-api#start-orchestration
return await client.CreateCheckStatusResponseAsync(req, instanceId);
}
}
54 changes: 54 additions & 0 deletions test/e2e/Apps/BasicDotNetIsolated/SuspendResumeOrchestration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System.Net;
using Grpc.Core;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.DurableTask.Client;

namespace Microsoft.Azure.Durable.Tests.E2E;
public static class SuspendResumeOrchestration
{
[Function("SuspendInstance")]
public static async Task<HttpResponseData> Suspend(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
[DurableClient] DurableTaskClient client,
string instanceId)
{
string suspendReason = "Suspending the instance for test.";
try
{
await client.SuspendInstanceAsync(instanceId, suspendReason);
return req.CreateResponse(HttpStatusCode.OK);
}
catch (RpcException ex)
{
var response = req.CreateResponse(HttpStatusCode.BadRequest);
response.Headers.Add("Content-Type", "text/plain");
await response.WriteStringAsync(ex.Message);
return response;
}
}

[Function("ResumeInstance")]
public static async Task<HttpResponseData> Resume(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
[DurableClient] DurableTaskClient client,
string instanceId)
{
string resumeReason = "Resuming the instance for test.";
try
{
await client.ResumeInstanceAsync(instanceId, resumeReason);
return req.CreateResponse(HttpStatusCode.OK);
}
catch (RpcException ex)
{
var response = req.CreateResponse(HttpStatusCode.BadRequest);
response.Headers.Add("Content-Type", "text/plain");
await response.WriteStringAsync(ex.Message);
return response;
}
}
}
108 changes: 53 additions & 55 deletions test/e2e/Apps/BasicDotNetIsolated/TerminateOrchestration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,72 +9,70 @@
using Microsoft.DurableTask.Client;
using Microsoft.Extensions.Logging;

namespace Microsoft.Azure.Durable.Tests.E2E
namespace Microsoft.Azure.Durable.Tests.E2E;
public static class LongRunningOrchestration
{
public static class LongRunningOrchestration
[Function(nameof(LongRunningOrchestrator))]
public static async Task<List<string>> LongRunningOrchestrator(
[OrchestrationTrigger] TaskOrchestrationContext context)
{
[Function(nameof(LongRunningOrchestrator))]
public static async Task<List<string>> LongRunningOrchestrator(
[OrchestrationTrigger] TaskOrchestrationContext context)
ILogger logger = context.CreateReplaySafeLogger(nameof(HelloCities));
logger.LogInformation("Starting long-running orchestration.");
var outputs = new List<string>();

// Call our fake activity 100,000 times to simulate an orchestration that might run for >= 10,000s (2.7 hours)
for (int i = 0; i < 100000; i++)
{
ILogger logger = context.CreateReplaySafeLogger(nameof(HelloCities));
logger.LogInformation("Starting long-running orchestration.");
var outputs = new List<string>();
outputs.Add(await context.CallActivityAsync<string>(nameof(SimulatedWorkActivity), 100));
}

// Call our fake activity 100,000 times to simulate an orchestration that might run for >= 10,000s (2.7 hours)
for (int i = 0; i < 100000; i++)
{
outputs.Add(await context.CallActivityAsync<string>(nameof(SimulatedWorkActivity), 100));
}
return outputs;
}

return outputs;
}
[Function(nameof(SimulatedWorkActivity))]
public static string SimulatedWorkActivity([ActivityTrigger]int sleepMs, FunctionContext executionContext)
{
// Sleep the provided number of ms to simulate a long-running activity operation
ILogger logger = executionContext.GetLogger("SimulatedWorkActivity");
logger.LogInformation("Sleeping for {sleepMs}ms.", sleepMs);
Thread.Sleep(sleepMs);
return $"Slept for {sleepMs}ms.";
}

[Function(nameof(SimulatedWorkActivity))]
public static string SimulatedWorkActivity([ActivityTrigger]int sleepMs, FunctionContext executionContext)
{
// Sleep the provided number of ms to simulate a long-running activity operation
ILogger logger = executionContext.GetLogger("SimulatedWorkActivity");
logger.LogInformation("Sleeping for {sleepMs}ms.", sleepMs);
Thread.Sleep(sleepMs);
return $"Slept for {sleepMs}ms.";
}
[Function("LongOrchestrator_HttpStart")]
public static async Task<HttpResponseData> HttpStart(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
[DurableClient] DurableTaskClient client,
FunctionContext executionContext)
{
ILogger logger = executionContext.GetLogger("LongOrchestrator_HttpStart");

[Function("LongOrchestrator_HttpStart")]
public static async Task<HttpResponseData> HttpStart(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
[DurableClient] DurableTaskClient client,
FunctionContext executionContext)
{
ILogger logger = executionContext.GetLogger("LongOrchestrator_HttpStart");
string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(
nameof(LongRunningOrchestrator));

string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(
nameof(LongRunningOrchestrator));
logger.LogInformation("Started orchestration with ID = '{instanceId}'.", instanceId);

return await client.CreateCheckStatusResponseAsync(req, instanceId);
}

logger.LogInformation("Started orchestration with ID = '{instanceId}'.", instanceId);

return await client.CreateCheckStatusResponseAsync(req, instanceId);
[Function("TerminateInstance")]
public static async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
[DurableClient] DurableTaskClient client,
string instanceId)
{
string reason = "Long-running orchestration was terminated early.";
try
{
await client.TerminateInstanceAsync(instanceId, reason);
return req.CreateResponse(HttpStatusCode.OK);
}

[Function("TerminateInstance")]
public static async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
[DurableClient] DurableTaskClient client,
string instanceId)
catch (RpcException ex)
{
string reason = "Long-running orchestration was terminated early.";
try
{
await client.TerminateInstanceAsync(instanceId, reason);
return req.CreateResponse(HttpStatusCode.OK);
}
catch (RpcException ex)
{
var response = req.CreateResponse(HttpStatusCode.BadRequest);
response.Headers.Add("Content-Type", "text/plain");
await response.WriteStringAsync(ex.Message);
return response;
}
var response = req.CreateResponse(HttpStatusCode.BadRequest);
response.Headers.Add("Content-Type", "text/plain");
await response.WriteStringAsync(ex.Message);
return response;
}
}
}
Loading
Loading