Blog: http://blog.codenova.pl/
Twitter: @Kamil_Mrzyglod
GitHub: https://github.com/kamil-mrzyglod
StackOverflow: https://stackoverflow.com/users/1874991/kamo
LinkedIn: www.linkedin.com/in/kamil-mrzygłód-31470376
Account 1
Account 2
Database 1
Database 2
Distributed Transaction
Debit amount from Account A
Successful?
Credit amount to Account B
Successful?
Yes
No
Yes
No
Undo debit from Account A
Task Hub Worker
Task Orchestrations
Task Activity
Task Activity
Task Hub Client
Task Hub
Task Activity
public class EncodeVideoOrchestration : TaskOrchestration<string, string>
{
public override async Task<string> RunTask(OrchestrationContext context,
string input)
{
string encodedUrl =
await context.ScheduleTask<string>(typeof (EncodeActivity), input);
await context.ScheduleTask<object>(typeof (EmailActivity), input);
return encodedUrl;
}
}Orchestrations are single-threaded!
public class EncodeActivity : TaskActivity<string, string>
{
protected override string Execute(TaskContext context, string input)
{
Console.WriteLine("Encoding video " + input);
// TODO : actually encode the video to a destination
return "http://<azurebloblocation>/encoded_video.avi";
}
}
public class EmailActivity : TaskActivity<string, object>
{
protected override object Execute(TaskContext context, string input)
{
// TODO : actually send email to user
return null;
}
}Guaranteed to called at-least-once!
string serviceBusConnString
= "Endpoint=sb://<namespace>.servicebus.windows.net/;SharedSecretIssuer=[issuer];SharedSecretValue=[value]";
TaskHubWorker hubWorker = new TaskHubWorker("myvideohub", serviceBusConnString)
.AddTaskOrchestrations(typeof (EncodeVideoOrchestration))
.AddTaskActivities(typeof (EncodeActivity), typeof (EmailActivity))
.Start();string serviceBusConnString
= "Endpoint=sb://<namespace>.servicebus.windows.net/;SharedSecretIssuer=[issuer];SharedSecretValue=[value]";
TaskHubClient client = new TaskHubClient("myvideohub", serviceBusConnString);
client.CreateOrchestrationInstance(typeof (EncodeVideoOrchestration), "http://<azurebloblocation>/MyVideo.mpg");Task Orchestration
Activity 1
Activity 2
Activity 3
Replay
Replay
Replay
Execution History
public class DebitCreditOrchestration :
TaskOrchestration<object, DebitCreditOperation>
{
public override async Task<object> RunTask(OrchestrationContext context,
DebitCreditOperation operation)
{
bool failed = false;
bool debited = false;
try
{
await context.ScheduleTask<object>(typeof (DebitAccount),
new Tuple<string, float>(operation.SourceAccount, operation.Amount));
debited = true;
await context.ScheduleTask<object>(typeof(CreditAccount),
new Tuple<string, float>(operation.TargetAccount, operation.Amount));
}
catch (TaskFailedException exception)
{
failed = true;
}
if (failed)
{
if (debited)
{
// can build a try-catch around this as well, in which case the
// orchestration may either retry a few times or log the inconsistency for review
await context.ScheduleTask<object>(typeof(CreditAccount),
new Tuple<string, float>(operation.SourceAccount, operation.Amount));
}
}
return null;
}
}public class GetQuoteOrchestration : TaskOrchestration<string, string>
{
public override async Task<string> RunTask(OrchestrationContext context, string input)
{
// retry every 10 seconds upto 5 times before giving up and bubbling up the exception
RetryOptions retryOptions = new RetryOptions(TimeSpan.FromSeconds(10), 5);
await context.ScheduleWithRetry<object>(typeof (GetQuote), retryOptions, null);
return null;
}
}public class EncodeVideoOrchestration : TaskOrchestration<string, string>
{
public override async Task<string> RunTask(OrchestrationContext context, string input)
{
string encodedUrl = await context.ScheduleTask<string>(typeof (EncodeActivity), input);
await context.CreateTimer(context.CurrentUtcDateTime.Add(TimeSpan.FromDays(1)), "timer1");
await context.ScheduleTask<object>(typeof (EmailActivity), input);
return encodedUrl;
}
}public class BillingOrchestration : TaskOrchestration<string, string>
{
public override async Task<string> RunTask(OrchestrationContext context, string input)
{
for (int i = 0; i < 10; i++)
{
await context.CreateTimer(context.CurrentUtcDateTime.Add(TimeSpan.FromDays(1)), "timer1");
await context.ScheduleTask<object>(typeof (BillingActivity));
}
return null;
}
}public class GetQuoteOrchestration : TaskOrchestration<string, string>
{
public override async Task<string> RunTask(OrchestrationContext context, string input)
{
Task timer = context.CreateTimer(
context.CurrentUtcDateTime.Add(TimeSpan.FromSeconds(5)), "timer1");
Task getQuote = context.ScheduleTask<object>(typeof(GetQuote));
Task winner = Task.WhenAny(timer, getQuote);
if (timer.IsCompleted)
{
// request timed out, do some compensating action
}
else
{
// use getQuote task result
}
return null;
}
}public class CronOrchestration : TaskOrchestration<string, int>
{
public override async Task<string> RunTask(OrchestrationContext context, int intervalHours)
{
// bounded loop
for (int i = 0; i < 10; i++)
{
await context.CreateTimer<object>(
context.CurrentUtcDateTime.Add(TimeSpan.FromHours(intervalHours)), null);
// TODO : do something interesting
}
// create a new instance of self with the same input (or different if needed)
context.ContinueAsNew(intervalHours);
return null;
}
}public class GetQuoteOrchestration : TaskOrchestration<string, string>
{
TaskCompletionSource<object> getPermission = new TaskCompletionSource<object>();
public override async Task<string> RunTask(OrchestrationContext context, string input)
{
await getPermission.Task;
await context.ScheduleTask<object>(typeof (GetQuote), null);
return null;
}
public override void OnEvent(OrchestrationContext context, string name, string input)
{
getPermission.SetResult(null);
}
}TaskHubClient client = new TaskHubClient("test", serviceBusConnString);
OrchestrationInstance instance = client.CreateOrchestrationInstance(typeof (EncodeVideoOrchestration),
"http://<azurebloblocation>/MyVideo.mpg");
client.RaiseEvent(instance, "dummyEvent", "dummyData");public static async Task<List<string>> Run(DurableOrchestrationContext context)
{
var outputs = new List<string>();
outputs.Add(await context.CallFunctionAsync<string>("E1_SayHello", "Tokyo"));
outputs.Add(await context.CallFunctionAsync<string>("E1_SayHello", "Seattle"));
outputs.Add(await context.CallFunctionAsync<string>("E1_SayHello", "London"));
// returns ["Hello Tokyo!", "Hello Seattle!", "Hello London!"]
return outputs;
}