Orquestaciones eternas

Las orquestaciones eternas son funciones de orquestador que se ejecutan indefinidamente al restablecer periódicamente su propio historial mediante la continue-as-new API. Son útiles para los agregadores, los trabajos en segundo plano periódicos y cualquier escenario de Durable Functions que requiere un bucle infinito sin crecimiento de historial ilimitado.

Sin continue-as-new, un orquestador que ejecuta bucles eternamente acumule historial de orquestaciones con cada tarea programada, lo cual causaría finalmente problemas de rendimiento y uso excesivo de memoria. El patrón de orquestación eterna resuelve esto restableciendo el historial en cada iteración.

Nota:

Los ejemplos de código de orquestación eterna están disponibles para C#, JavaScript, Python y Java. PowerShell no admite continue-as-new.

En este artículo:

Las orquestaciones eternas son orquestaciones que se ejecutan indefinidamente restableciendo periódicamente su propio historial mediante la continue-as-new API. Son útiles para agregadores, trabajos en segundo plano periódicos y cualquier escenario que requiera un bucle infinito sin crecimiento de historial ilimitado.

Sin continue-as-new, una orquestación que se repita para siempre acumularía historial con cada tarea programada, lo que eventualmente causaría problemas de rendimiento y un uso excesivo de memoria. El patrón de orquestación eterna resuelve esto restableciendo el historial en cada iteración.

Importante

Actualmente, el SDK de Durable Task de PowerShell no está disponible.

En este artículo:

Cómo funciona "continue-as-new"

En lugar de usar bucles infinitos, las funciones del orquestador restablecen su estado llamando al método continue-as-new del enlace del desencadenador de orquestación. Este método toma un parámetro serializable json que se convierte en la nueva entrada para la próxima generación de funciones de orquestador.

Cuando se llama a continue-as-new, la instancia de orquestación se reinicia con el nuevo valor de entrada. Se mantiene el mismo identificador de instancia, pero se restablece el historial de la función de orquestador.

En lugar de usar bucles infinitos, las orquestaciones restablecen su estado llamando al continue-as-new método en el contexto de orquestación. Este método toma un parámetro serializable json que se convierte en la nueva entrada para la próxima generación de orquestación.

Cuando se llama a continue-as-new, la instancia de orquestación se reinicia con el nuevo valor de entrada. Se mantiene el mismo identificador de instancia, pero el historial de la orquestación se restablece.

Consideraciones sobre orquestación eterna

Tenga en cuenta estas consideraciones al usar el continue-as-new método en una orquestación:

  • Cuando se restablece una función de orquestador mediante el continue-as-new método , Durable Task Framework mantiene el mismo identificador de instancia, pero crea internamente y usa un nuevo identificador de ejecución en el futuro. Este ID de ejecución no se expone externamente, pero es útil al depurar la ejecución de la orquestación.

  • Cuando se produce una excepción no controlada durante la ejecución, la orquestación entra en un estado de error y finaliza la ejecución. Una llamada a continue-as-new desde un finally bloque no reinicia la orquestación después de una excepción no detectada.

  • Los resultados de cualquier tarea incompleta se descartan cuando una orquestación llama a continue-as-new. Por ejemplo, si se programa un temporizador y se llama a continue-as-new antes de que se active el temporizador, se descarta el evento del temporizador.

  • Opcionalmente, puede conservar eventos externos sin procesar en los reinicios de continue-as-new. En C#, ContinueAsNew conserva los eventos sin procesar de forma predeterminada. En Java, continueAsNew también conserva los eventos de forma predeterminada. En Python, continue_as_new no conserva los eventos a menos que save_events=True. En JavaScript, continueAsNew requiere un saveEvents parámetro (true o false) para controlar este comportamiento.

Tenga en cuenta estas consideraciones al usar el continue-as-new método en una orquestación:

  • Cuando se restablece una orquestación mediante el continue-as-new método , los SDK de Durable Task mantienen el mismo identificador de instancia, pero crean internamente y usan un nuevo identificador de ejecución en el futuro. Este identificador de ejecución no se expone externamente, pero puede ser útil al depurar la ejecución de la orquestación.

  • Cuando se produce una excepción no controlada durante la ejecución, la orquestación entra en un estado de error y finaliza la ejecución. Una llamada a continue-as-new desde un bloque finallyno reinicia la orquestación tras una excepción no detectada.

  • Los resultados de cualquier tarea incompleta se descartan cuando una orquestación llama a continue-as-new. Por ejemplo, si se programa un temporizador y se llama a continue-as-new antes de que se active el temporizador, se descarta el evento del temporizador.

  • Opcionalmente, puede conservar eventos externos sin procesar en los reinicios de continue-as-new. En .NET y Java, continue-as-new conserva los eventos sin procesar de forma predeterminada. En Python, continue_as_new no conserva los eventos a menos que save_events=True. En JavaScript, continueAsNew requiere un saveEvents parámetro (true o false) para controlar este comportamiento. En todos los casos, los eventos sin procesar se entregan cuando la orquestación realiza la siguiente llamada a waitForExternalEvent o wait_for_external_event.

Ejemplo de trabajo periódico

Un caso de uso común para las orquestaciones permanentes es el trabajo periódico en segundo plano, como los trabajos de limpieza.

¿Por qué no usar un activador de temporizador? Un disparador de temporizador basado en CRON se ejecuta en momentos fijos, independientemente de si la ejecución anterior ha finalizado. Una orquestación eterna espera a que se complete el trabajo antes de programar la siguiente iteración, por lo que las ejecuciones nunca se superponen.

Approach Programación (intervalo de 1 hora, trabajo de 30 minutos) Riesgo de superposición
Desencadenador de temporizador (CRON) 1:00, 2:00, 3:00 Sí: si el trabajo supera el intervalo
Orquestación eterna 1:00, 2:30, 4:00 No: la próxima ejecución espera la finalización
[FunctionName("Periodic_Cleanup_Loop")]
public static async Task Run(
    [OrchestrationTrigger] IDurableOrchestrationContext context)
{
    await context.CallActivityAsync("DoCleanup", null);

    // sleep for one hour between cleanups
    DateTime nextCleanup = context.CurrentUtcDateTime.AddHours(1);
    await context.CreateTimer(nextCleanup, CancellationToken.None);

    context.ContinueAsNew(null);
}
public class PeriodicCleanupLoop : TaskOrchestrator<object?, object?>
{
    public override async Task<object?> RunAsync(TaskOrchestrationContext context, object? input)
    {
        await context.CallActivityAsync("DoCleanup");

        // sleep for one hour between cleanups
        await context.CreateTimer(TimeSpan.FromHours(1), CancellationToken.None);

        context.ContinueAsNew(null);
        return null;
    }
}

Iniciar una orquestación eterna

Use el método de cliente durable start-new o schedule-new para iniciar una orquestación eterna, al igual que cualquier otra función de orquestación. Para asegurarse de que solo se ejecuta una instancia a la vez, use un identificador de instancia fijo. Para obtener más información, consulte Orquestaciones singleton.

[FunctionName("Trigger_Eternal_Orchestration")]
public static async Task<HttpResponseMessage> OrchestrationTrigger(
    [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequestMessage request,
    [DurableClient] IDurableOrchestrationClient client)
{
    string instanceId = "StaticId";

    await client.StartNewAsync("Periodic_Cleanup_Loop", instanceId); 
    return client.CreateCheckStatusResponse(request, instanceId);
}

Use el método de cliente schedule-new para iniciar una orquestación eterna, al igual que cualquier otra orquestación. Para asegurarse de que solo se ejecuta una instancia a la vez, use un identificador de instancia fijo. Para obtener más información, consulte Orquestaciones Singleton.

string instanceId = "StaticId";
await client.ScheduleNewOrchestrationInstanceAsync(
    "PeriodicCleanupLoop",
    null,
    new StartOrchestrationOptions { InstanceId = instanceId });

Salir de una orquestación eterna

Si una función de orquestador necesita finalmente completarse, no llame a continue-as-new y permita que la función termine.

Si una función de orquestador está en un bucle infinito y debe detenerse, use la API de finalización del enlace de cliente de orquestación para detenerla.

await client.TerminateAsync(instanceId, "Cleanup no longer needed");

Para obtener más información, consulte Administración de instancias.

Si una orquestación necesita finalmente completarse, no llame a continue-as-new y permita que la orquestación termine.

Si una orquestación está en un bucle infinito y debe detenerse, use la API de finalización en el cliente de tareas duradero para detenerla.

await client.TerminateInstanceAsync(instanceId, "Cleanup no longer needed");

Pasos siguientes