Condividi tramite


Controllo delle versioni dell'orchestrazione in Durable Functions (Funzioni di Azure) - Anteprima pubblica

Il versionamento dell'orchestrazione affronta la sfida principale di distribuire le modifiche alle funzioni dell'orchestratore, mantenendo al contempo il modello di esecuzione deterministico richiesto da Durable Functions. Senza questa funzionalità, le modifiche di rilievo apportate alla logica dell'agente di orchestrazione o alle firme delle funzioni di attività causano un errore delle istanze di orchestrazione in anteprima durante la riproduzione perché interrompono il requisito deterministico che garantisce l'esecuzione affidabile dell'orchestrazione. Questa funzionalità predefinita fornisce l'isolamento automatico della versione con configurazione minima. È indipendente dal back-end, quindi può essere usato dalle app che sfruttano uno dei provider di archiviazione di Durable Functions, incluso lo Scheduler durevole delle attività.

Note

Per gli utenti di Durable Task Scheduler, se si usano gli SDK di Durable Task invece di Durable functions, fare riferimento all'articolo Controllo delle versioni degli SDK di Durable Task.

Terminology

Questo articolo usa due termini correlati ma distinti:

  • Funzione dell'agente di orchestrazione (o semplicemente "agente di orchestrazione"): fa riferimento al codice della funzione che definisce la logica del flusso di lavoro, ovvero il modello o il progetto per la modalità di esecuzione di un flusso di lavoro.
  • Istanza di orchestrazione (o semplicemente "orchestrazione"): fa riferimento a un'esecuzione specifica di una funzione dell'agente di orchestrazione, con il proprio stato, l'ID istanza e gli input. Più istanze di orchestrazione possono essere eseguite simultaneamente dalla stessa funzione di orchestrazione.

Comprendere questa distinzione è fondamentale per il controllo delle versioni dell'orchestrazione, in cui il codice della funzione dell'agente di orchestrazione contiene la logica in grado di essere compatibile con la versione, mentre le istanze di orchestrazione sono associate in modo permanente a una versione specifica al momento della creazione.

Come funziona

La funzionalità di controllo delle versioni dell'orchestrazione opera sui principi di base seguenti:

  • Associazione di versione: Quando un'istanza di orchestrazione viene creata, le viene permanentemente associata una versione.

  • Esecuzione compatibile con la versione: il codice della funzione di Orchestrator può esaminare il valore della versione associato all'istanza di orchestrazione corrente e all'esecuzione del ramo di conseguenza.

  • Compatibilità con le versioni precedenti: i worker che eseguono versioni più recenti dell'orchestratore possono continuare a eseguire istanze di orchestrazione create dalle versioni precedenti dell'orchestratore.

  • Protezione Avanzata: il runtime impedisce automaticamente agli agenti che eseguono versioni meno recenti dell'orchestratore di eseguire orchestrazioni iniziate da orchestratori con versioni più recenti.

Important

Il versionamento dell'orchestrazione è attualmente disponibile in anteprima pubblica.

Prerequisiti

Prima di usare il versionamento dell'orchestrazione, verificare di avere le versioni necessarie dei pacchetti per il linguaggio di programmazione.

Se si usa un linguaggio di non-.NET (JavaScript, Python, PowerShell o Java) con bundle di estensioni, l'app per le funzioni deve fare riferimento a Extension Bundle versione 4.26.0 o successiva. Configurare l'intervallo extensionBundle in host.json in modo che la versione minima sia almeno 4.26.0, ad esempio:

{
    "version": "2.0",
    "extensionBundle": {
        "id": "Microsoft.Azure.Functions.ExtensionBundle",
        "version": "[4.26.0, 5.0.0)"
    }
}

Per informazioni dettagliate sulla scelta e sull'aggiornamento delle versioni del bundle, vedere la documentazione relativa alla configurazione del bundle di estensione .

Usare Microsoft.Azure.Functions.Worker.Extensions.DurableTaskla versione 1.5.0 o successiva.

Utilizzo di base

Il caso d'uso più comune per il controllo delle versioni dell'orchestrazione è quando è necessario apportare modifiche di rilievo alla logica dell'agente di orchestrazione mantenendo le istanze di orchestrazione in anteprima esistenti in esecuzione con la versione originale. È sufficiente aggiornare defaultVersion nel tuo host.json e modificare il codice dell'orchestratore per controllare la versione dell'orchestrazione e quindi suddividere l'esecuzione di conseguenza. Verranno ora illustrati i passaggi necessari.

Note

Il comportamento descritto in questa sezione è destinato alle situazioni più comuni ed è ciò che fornisce la configurazione predefinita. Tuttavia, può essere modificato se necessario (vedere Utilizzo avanzato per informazioni dettagliate).

Passaggio 1: configurazione di defaultVersion

Per configurare la versione predefinita per le orchestrazioni, è necessario aggiungere o aggiornare l'impostazione defaultVersion nel file del progetto Azure Functions:

{
  "extensions": {
    "durableTask": {
      "defaultVersion": "<version>"
    }
  }
}

La stringa di versione può seguire qualsiasi formato adatto alla strategia di controllo delle versioni:

  • Versionamento multi-parte: "1.0.0", "2.1.0"
  • Numerazione semplice: "1", "2"
  • Basato su data: "2025-01-01"
  • Formato personalizzato: "v1.0-release"

Dopo aver impostato , defaultVersiontutte le nuove istanze di orchestrazione verranno associate in modo permanente a tale versione.

Regole di confronto delle versioni

Quando viene selezionata la strategia Strict o CurrentOrOlder (vedere Corrispondenza della versione), il runtime confronta la versione dell'istanza di orchestrazione con il defaultVersion valore del lavoratore usando le regole seguenti:

  • Le versioni vuote o Null vengono considerate uguali.
  • Una versione vuota o null è considerata precedente a qualsiasi versione definita.
  • Se entrambe le versioni possono essere analizzate come System.Version, viene usato il CompareTo metodo .
  • In caso contrario, viene eseguito il confronto tra stringhe senza distinzione tra maiuscole e minuscole.

Passaggio 2: Logica della funzione di Orchestrator

Per implementare la logica compatibile con la versione nella funzione dell'agente di orchestrazione, è possibile usare il parametro di contesto passato all'agente di orchestrazione per accedere alla versione dell'istanza di orchestrazione corrente, che consente di diramare la logica dell'agente di orchestrazione in base alla versione.

Important

Quando si implementa la logica compatibile con la versione, è fondamentale mantenere la logica esatta dell'agente di orchestrazione per le versioni precedenti. Eventuali modifiche alla sequenza, all'ordine o alla firma delle chiamate di attività per le versioni esistenti possono interrompere la riproduzione deterministica e causare errori o risultati non corretti. I percorsi di codice della versione precedente devono rimanere invariati dopo la distribuzione.

[Function("MyOrchestrator")]
public static async Task<string> RunOrchestrator(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    if (context.Version == "1.0")
    {
        // Original logic for version 1.0
        ...
    }
    else if (context.Version == "2.0")
    {
        // New logic for version 2.0
        ...
    }
    ...
}

Note

La context.Version proprietà è di sola lettura e riflette la versione associata in modo permanente all'istanza di orchestrazione al momento della creazione. Non è possibile modificare questo valore durante l'esecuzione dell'orchestrazione. Se si vuole specificare una versione tramite mezzi diversi host.jsonda , è possibile farlo quando si avvia un'istanza di orchestrazione con le API client di orchestrazione (vedere Avvio di nuove orchestrazioni e sotto orchestrazioni con versioni specifiche).

Tip

Se si sta iniziando a usare il controllo delle versioni di orchestrazione e si hanno già orchestrazioni in corso create prima di specificare un defaultVersion, ora è comunque possibile aggiungere l'impostazione defaultVersion al tuo host.json. Per tutte le orchestrazioni create in precedenza, context.Version restituisce null (o un valore dipendente dal linguaggio equivalente), in modo da poter strutturare la logica dell'orchestratore per gestire sia le orchestrazioni legacy (versione nulla) sia le nuove orchestrazioni con controllo delle versioni di conseguenza. Di seguito sono riportati i valori dipendenti dalla lingua per verificare la presenza del caso legacy:

  • C#: context.Version == null o context.Version is null
  • JavaScript: context.df.version == null
  • Python: context.version is None
  • PowerShell: $null -eq $Context.Version
  • Java: context.getVersion() == null nota anche che specificare "defaultVersion": null in host.json equivale a non specificare affatto.

Tip

A seconda della situazione, è possibile preferire la diramazione su livelli diversi. È possibile apportare una modifica locale esattamente in cui è necessaria questa modifica, come illustrato nell'esempio. In alternativa, è possibile creare un ramo a un livello superiore, anche a livello di implementazione dell'agente di orchestrazione, che introduce una duplicazione del codice, ma può mantenere il flusso di esecuzione chiaro. È consigliabile scegliere l'approccio più adatto allo scenario e allo stile di codifica.

Cosa accade dopo la distribuzione

Ecco cosa aspettarsi dopo aver distribuito la funzione dell'agente di orchestrazione aggiornata con la nuova logica della versione:

  • Coesistenza del ruolo di lavoro: i ruoli di lavoro che contengono il nuovo codice della funzione dell'agente di orchestrazione verranno avviati, mentre alcuni ruoli di lavoro con il codice precedente sono potenzialmente ancora attivi.

  • Assegnazione di versione per le nuove istanze: tutte le nuove orchestrazioni e sotto-orchestrazioni create dai nuovi processi di lavoro riceveranno la versione da defaultVersion assegnata loro.

  • Nuova compatibilità dei lavoratori: i nuovi lavoratori potranno elaborare sia le orchestrazioni appena create che quelle esistenti in precedenza, perché le modifiche eseguite nel passaggio 2 della sezione precedente garantiscono la compatibilità con le versioni precedenti attraverso una logica di diramazione consapevole della versione.

  • Restrizioni dei lavoratori precedenti: i lavoratori precedenti potranno elaborare solo le orchestrazioni con una versione uguale o inferiore a quella specificata nel proprio defaultVersion in host.json, perché non ci si aspetta che il codice dell'orchestratore sia compatibile con le versioni più recenti. Questa restrizione impedisce errori di esecuzione e comportamenti imprevisti.

Note

Il controllo delle versioni dell'orchestrazione non influisce sul ciclo di vita del ruolo di lavoro. La piattaforma Funzioni di Azure gestisce il provisioning dei ruoli di lavoro e la rimozione delle autorizzazioni in base alle regole regolari a seconda delle opzioni di hosting.

Esempio: Sostituzione di un'attività nella sequenza

Questo esempio illustra come sostituire un'attività con un'attività diversa al centro di una sequenza usando il versionamento dell'orchestrazione.

Versione 1.0

host.json configurazione:

{
  "extensions": {
    "durableTask": {
      "defaultVersion": "1.0"
    }
  }
}

Funzione dell'agente di orchestrazione:

[Function("ProcessOrderOrchestrator")]
public static async Task<string> ProcessOrder(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    var orderId = context.GetInput<string>();
    
    await context.CallActivityAsync("ValidateOrder", orderId);
    await context.CallActivityAsync("ProcessPayment", orderId);
    await context.CallActivityAsync("ShipOrder", orderId);
    
    return "Order processed successfully";
}

Versione 2.0 con elaborazione dello sconto

host.json configurazione:

{
  "extensions": {
    "durableTask": {
      "defaultVersion": "2.0"
    }
  }
}

Funzione dell'agente di orchestrazione:

using DurableTask.Core.Settings;

[Function("ProcessOrderOrchestrator")]
public static async Task<string> ProcessOrder(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    var orderId = context.GetInput<string>();

    await context.CallActivityAsync("ValidateOrder", orderId);

    if (VersioningSettings.CompareVersions(context.Version, "1.0") <= 0)
    {
        // Preserve original logic for existing instances
        await context.CallActivityAsync("ProcessPayment", orderId);
    }
    else // a higher version (including 2.0)
    {
        // New logic with discount processing (replaces payment processing)
        await context.CallActivityAsync("ApplyDiscount", orderId);
        await context.CallActivityAsync("ProcessPaymentWithDiscount", orderId);
    }
    
    await context.CallActivityAsync("ShipOrder", orderId);

    return "Order processed successfully";
}

Utilizzo avanzato

Per scenari di controllo delle versioni più sofisticati, è possibile configurare altre impostazioni per controllare il modo in cui il runtime gestisce le corrispondenze della versione e le mancate corrispondenze.

Tip

Usare la configurazione predefinita (CurrentOrOlder con Reject) per la maggior parte degli scenari per abilitare distribuzioni in sequenza sicure mantenendo lo stato di orchestrazione durante le transizioni di versione. È consigliabile procedere con la configurazione avanzata solo se sono presenti requisiti specifici che non possono essere soddisfatti con il comportamento predefinito.

Corrispondenza delle versioni

L'impostazione versionMatchStrategy determina il modo in cui il runtime corrisponde alle versioni di orchestrazione durante il caricamento delle funzioni dell'agente di orchestrazione. Controlla le istanze di orchestrazione che un'unità di lavoro può elaborare in base alla compatibilità tra versioni.

Configuration

{
  "extensions": {
    "durableTask": {
      "defaultVersion": "<version>",
      "versionMatchStrategy": "CurrentOrOlder"
    }
  }
}

Strategie disponibili

  • None (non consigliato): Ignorare completamente la versione di orchestrazione. Tutto il lavoro ricevuto viene elaborato indipendentemente dalla versione. Questa strategia disabilita efficacemente il controllo delle versioni e consente a qualsiasi ruolo di lavoro di elaborare qualsiasi istanza di orchestrazione.

  • Strict: elaborare solo le attività dalle orchestrazioni con la stessa versione esatta specificata da defaultVersion nel host.json del lavoratore. Questa strategia fornisce il massimo livello di isolamento della versione, ma richiede un'attenta coordinamento della distribuzione per evitare orchestrazioni orfane. Le conseguenze della mancata corrispondenza della versione sono descritte nella sezione Gestione della mancata corrispondenza della versione .

  • CurrentOrOlder (impostazione predefinita): elaborare le attività dalle orchestrazioni la cui versione è minore o uguale alla versione specificata da defaultVersion in host.json del lavoratore. Questa strategia consente la retrocompatibilità, permettendo ai lavoratori più recenti di gestire le orchestrazioni avviate da versioni precedenti dell'orchestratore, impedendo al contempo ai lavoratori meno recenti di elaborare orchestrazioni più recenti. Le conseguenze della mancata corrispondenza della versione sono descritte nella sezione Gestione della mancata corrispondenza della versione .

Gestione del disallineamento della versione

L'impostazione versionFailureStrategy determina cosa accade quando una versione dell'istanza di orchestrazione non corrisponde all'oggetto corrente defaultVersion.

Configuration:

{
  "extensions": {
    "durableTask": {
      "defaultVersion": "<version>",
      "versionFailureStrategy": "Reject"
    }
  }
}

Strategie disponibili:

  • Reject (impostazione predefinita): non elaborare l'orchestrazione. L'istanza di orchestrazione rimane nello stato corrente e può essere ritentata in un secondo momento quando un ruolo di lavoro compatibile diventa disponibile. Questa strategia è l'opzione più sicura in quanto mantiene lo stato di orchestrazione.

  • Fail: non eseguire l'orchestrazione. Questa strategia termina immediatamente l'istanza di orchestrazione con uno stato di errore, che può essere appropriata negli scenari in cui le mancate corrispondenze della versione indicano problemi di distribuzione gravi.

Avvio di nuove orchestrazioni e sotto orchestrazioni con versioni specifiche

Per impostazione predefinita, tutte le nuove istanze di orchestrazione vengono create con l'oggetto corrente defaultVersion specificato nella configurazione host.json. Tuttavia, potrebbero essere presenti scenari in cui è necessario creare orchestrazioni con una versione specifica, anche se differisce dall'impostazione predefinita corrente.

Quando usare versioni specifiche:

  • Migrazione graduale: si vuole continuare a creare orchestrazioni con una versione precedente anche dopo la distribuzione di una versione più recente.
  • Scenari di test: è necessario testare il comportamento della versione specifico nell'ambiente di produzione.
  • Situazioni di rollback: è necessario ripristinare temporaneamente la creazione di istanze con una versione precedente.
  • Flussi di lavoro specifici della versione: i diversi processi aziendali richiedono versioni di orchestrazione diverse.

È possibile eseguire l'override della versione predefinita specificando un valore di versione specifico durante la creazione di nuove istanze di orchestrazione usando le API client di orchestrazione. In questo modo è possibile esercitare un controllo dettagliato su quale versione utilizza ciascuna nuova istanza di orchestrazione.

[Function("HttpStart")]
public static async Task<HttpResponseData> HttpStart(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
    [DurableClient] DurableTaskClient client,
    FunctionContext executionContext)
{
    var options = new StartOrchestrationOptions
    {
        Version = "1.0"
    };
    
    string instanceId = await client.ScheduleNewOrchestrationInstanceAsync("ProcessOrderOrchestrator", orderId, options);

    // ...
}

È anche possibile avviare sotto-orchestrazioni con versioni specifiche dall'interno di una funzione dell'agente di orchestrazione:

[Function("MainOrchestrator")]
public static async Task<string> RunMainOrchestrator(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    var subOptions = new SubOrchestratorOptions
    {
        Version = "1.0"
    };
    
    var result = await context.CallSubOrchestratorAsync<string>("ProcessPaymentOrchestrator", orderId, subOptions);
    
    // ...
}

Rimozione dei percorsi di codice legacy

Nel corso del tempo, è possibile rimuovere percorsi di codice legacy dalle funzioni dell'agente di orchestrazione per semplificare la manutenzione e ridurre il debito tecnico. Tuttavia, la rimozione del codice deve essere eseguita con attenzione per evitare l'interruzione delle istanze di orchestrazione esistenti.

Quando è sicuro rimuovere il codice legacy:

  • Tutte le istanze di orchestrazione che usano la versione vecchia sono state completate (riuscite, fallite o terminate)
  • Non verranno create nuove istanze di orchestrazione con la versione precedente
  • Hai verificato attraverso il monitoraggio o le query che nessuna istanza è in esecuzione con la versione legacy
  • È trascorso un periodo di tempo sufficiente dall'ultima distribuzione della versione precedente (considerando i requisiti di continuità aziendale)

Procedure consigliate per la rimozione:

  • Monitorare attivamente le istanze in esecuzione: usare le API di gestione di Durable Functions per eseguire query per le istanze usando versioni specifiche.
  • Impostare i criteri di conservazione: definire per quanto tempo si intende mantenere la compatibilità con le versioni precedenti per ogni versione.
  • Rimuovi in modo incrementale: è consigliabile rimuovere una versione alla volta anziché più versioni contemporaneamente.
  • Rimozione dei documenti: mantieni record chiari di quando le versioni sono state rimosse e perché.

Warning

La rimozione dei percorsi di codice legacy mentre le istanze di orchestrazione sono ancora in esecuzione queste versioni possono causare errori di riproduzione deterministici o un comportamento imprevisto. Verificare sempre che nessuna istanza usi la versione legacy prima di rimuovere il codice.

Procedure consigliate

Gestione delle versioni

  • Usare il controllo delle versioni in più parti: adottare uno schema di controllo delle versioni coerente, ad esempio major.minor.patch.
  • Modifiche rilevanti per il documento: documentare chiaramente quali cambiamenti richiedono una nuova versione.
  • Pianificare il ciclo di vita della versione: definire quando rimuovere i percorsi di codice legacy.

Organizzazione del codice

  • Logica di versione separata: utilizzare una ramificazione chiara o metodi separati per le diverse versioni.
  • Mantieni determinismo: evitare di modificare la logica della versione esistente dopo la distribuzione. Se le modifiche sono assolutamente necessarie (ad esempio correzioni di bug critiche), assicurarsi di mantenere un comportamento deterministico e non modificare la sequenza di operazioni o prevedere che le versioni più recenti dell'agente di orchestrazione abbiano esito negativo durante l'elaborazione di orchestrazioni meno recenti.
  • Test approfondito: testare tutti i percorsi delle versioni, in particolare durante le transizioni.

Monitoraggio e osservabilità

  • Informazioni sulla versione del log: includere la versione nella registrazione per semplificare il debug.
  • Monitorare la distribuzione delle versioni: tenere traccia delle versioni in esecuzione attivamente.
  • Configurare gli avvisi: monitorare eventuali errori correlati alla versione.

Troubleshooting

Problemi comuni

  • Problema: le istanze di orchestrazione create con la versione 1.0 hanno esito negativo dopo la distribuzione della versione 2.0

    • Soluzione: assicurarsi che il percorso del codice della versione 1.0 nell'agente di orchestrazione rimanga esattamente lo stesso. Eventuali modifiche apportate alla sequenza di esecuzione possono interrompere la riproduzione deterministica.
  • Problema: I lavoratori che eseguono versioni precedenti dell'orchestratore non possono eseguire nuove orchestrazioni

    • Soluzione: si tratta di un comportamento previsto. Il runtime impedisce intenzionalmente ai lavoratori meno recenti di eseguire orchestrazioni con versioni più recenti per mantenere la sicurezza. Assicurarsi che tutti i ruoli di lavoro vengano aggiornati alla versione più recente dell'agente di orchestrazione e che l'impostazione defaultVersion n host.json sia aggiornata di conseguenza. Se necessario, è possibile modificare questo comportamento usando le opzioni di configurazione avanzate (vedere Utilizzo avanzato per informazioni dettagliate).
  • Problema: le informazioni sulla versione non sono disponibili nell'agente di orchestrazione (context.Version o context.getVersion() è null, indipendentemente dall'impostazione defaultVersion )

    • Soluzione: controllare la sezione Prerequisiti per assicurarsi che l'ambiente soddisfi tutti i requisiti per il controllo delle versioni dell'orchestrazione.
  • Problema: le orchestrazioni di una versione più recente stanno facendo progressi molto lenti o sono completamente bloccate

    • Soluzione: il problema può avere cause radice diverse:
      1. Lavoratori più recenti insufficienti: assicurarsi che un numero sufficiente di lavoratori contenenti una versione uguale o superiore a defaultVersion sia distribuito e attivo nei sistemi per gestire le orchestrazioni più recenti.
      2. Interferenza del routing dell'orchestrazione da parte dei lavoratori più anziani: i lavoratori più anziani possono interferire con il meccanismo di routing dell'orchestrazione, rendendo più difficile per i nuovi lavoratori cogliere orchestrazioni per l'elaborazione. Ciò può essere particolarmente evidente quando si usano determinati provider di archiviazione (Archiviazione di Azure o MSSQL). In genere, la piattaforma Funzioni di Azure garantisce che i vecchi ruoli di lavoro vengano eliminati subito dopo una distribuzione, quindi qualsiasi ritardo in genere non è significativo. Tuttavia, se si usa una configurazione che consente di controllare il ciclo di vita dei lavoratori meno recenti, assicurarsi che i lavoratori meno recenti vengano arrestati. In alternativa, considera l'utilizzo del Scheduler per Attività durevoli, perché fornisce un meccanismo di routing migliorato che è meno incline a questo problema.

Passaggi successivi