Azure / durabletask

Durable Task Framework allows users to write long running persistent workflows in C# using the async/await capabilities.
Apache License 2.0
1.52k stars 293 forks source link

Expose version information in the orchestration context #524

Open cgillum opened 3 years ago

cgillum commented 3 years ago

It would be convenient if orchestration instances could know their versions so that developers could write inline branch logic when versioning their orchestrations rather than mapping new versions to new classes, which is a bit more heavy-handed.

This is something supported by Temporal: https://docs.temporal.io/docs/go-versioning/. I think it would be useful to have in DTFx as well, though developers would need to use it carefully.

Example:

    class LongRunningOrchestration : TaskOrchestration<string, string>
    {
        public override async Task<string> RunTask(OrchestrationContext context, string input)
        {
            await context.ScheduleTask<string>(typeof(ActivityA), input);

            // At this point in the orchestration, a change may have been deployed

            if (context.Version == Version1)
            {
                await context.ScheduleTask<object>(typeof(ActivityB1), input);
            }
            else if (context.Version == Version2)
            {
                await context.ScheduleTask<object>(typeof(ActivityB2), input);
            }

            return $"Completed as version '{context.Version}'";
        }
    }
jviau commented 3 years ago

I solved this in our codebase by having Name and Version be properties on our base class OrchestrationBase. And then middleware sets it, as the name and version are available there via DispatchMiddlewareContext.GetProperty<OrchestrationRuntimeState>(). Did a similar approach for Activities - just a different property to get from the middleware context.

cgillum commented 3 years ago

@jviau thanks for letting me know how you solved it! It's good to know that someone's already using a similar approach for versioning successfully. :)

Any gotchas that users of something like this should be prepared for, based on your experience?

jviau commented 3 years ago

@cgillum no gotchas so far. It works pretty great! The middleware approach works great with a custom base class - all the info you need is there, and the middleware context holds the activity/orchestration that is going to be run, so easy access to it.

jviau commented 3 years ago

This does remind me of a different gotcha with orchestration versioning (and even adding new orchestrations) though. How do you handle the interim point during a deployment when some of your workers have the new orchestration / new version, and others don't? Do we need to worry about an older worker popping off new work that it does not support (yet)? Or has DTFx already solved that?

cgillum commented 3 years ago

@jviau I don't believe DTFx has a good solution for rolling, zero-downtime upgrades today. In Durable Functions where we have the luxury of serverless we tell people to do side-by-side deployments of their apps for this. More thinking needs to be done to figure out how to make these kinds of live upgrades more practical for vanilla DTFx users.

jviau commented 3 years ago

@cgillum thanks! I might open a different issue to discuss this problem and how DTFx could improve upon it.

joebowbeer commented 3 months ago

Links to prior work in Temporal:

  1. patched / deprecatePatch functions in workflow API

https://docs.temporal.io/develop/typescript/versioning#patching

https://docs.temporal.io/develop/typescript/versioning#typescript-sdk-patching-api

  1. Replay workflow execution in CI

https://docs.temporal.io/develop/typescript/testing-suite#replay

  1. Worker Versioning

Preview of built-in support for managing different versions of short-lived workers

https://github.com/temporalio/temporal/blob/main/docs/worker-versioning.md

joebowbeer commented 3 months ago

I think a user-supplied patch ID (string) approach, which can used to conditionalize a block of code (patch), is easier, more flexible, and more descriptive, than a monotonic version attached to the entire workflow.