Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
È inevitabile che le funzioni vengano aggiunte, rimosse e modificate per tutta la durata di un'applicazione. Durable Functions consente di concatenare le funzioni in modi non possibili in precedenza e questo concatenamento influisce su come gestire il controllo delle versioni.
Tipi di modifiche di rilievo
Sono disponibili alcuni esempi di modifiche di rilievo che è necessario tenere presenti. Questo articolo illustra quelli più comuni. Il tema principale di tutte è che le orchestrazioni delle funzioni nuove ed esistenti sono influenzate dalle modifiche apportate al codice delle funzioni.
Modifica delle firme di funzioni per attività o entità
Una modifica della firma fa riferimento a una modifica del nome, dell'input o dell'output di una funzione. Se questo tipo di modifica viene apportata a un'attività o a una funzione di entità, potrebbe interrompere qualsiasi funzione di orchestrazione che dipende da essa. Ciò vale soprattutto per le lingue indipendenti dai tipi. Se si aggiorna la funzione dell'agente di orchestrazione per gestire questa modifica, si potrebbero interrompere le istanze esistenti in corso.
Si supponga, ad esempio, di avere la seguente funzione di orchestrazione.
[FunctionName("FooBar")]
public static Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
bool result = await context.CallActivityAsync<bool>("Foo");
await context.CallActivityAsync("Bar", result);
}
Questa funzione semplicistica accetta i risultati di Foo e la passa a Bar. Si supponga di dover modificare il valore restituito di Foo da un valore booleano a un valore String per supportare un'ampia gamma di valori dei risultati. Il risultato è simile al seguente:
[FunctionName("FooBar")]
public static Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
string result = await context.CallActivityAsync<string>("Foo");
await context.CallActivityAsync("Bar", result);
}
Questa modifica funziona correttamente per tutte le nuove istanze della funzione dell'agente di orchestrazione, ma può interrompere qualsiasi istanza in-flight. Si consideri ad esempio il caso in cui un'istanza di orchestrazione chiama una funzione denominata Foo, restituisce un valore booleano e quindi i checkpoint. Se la modifica della firma viene distribuita a questo punto, l'istanza per cui è stato applicato il checkpoint ha immediatamente esito negativo quando riprende e riesegue la chiamata a Foo. Questo errore si verifica perché il risultato nella tabella di cronologia è un valore booleano, ma il nuovo codice tenta di deserializzarlo in un valore stringa, causando un comportamento imprevisto o anche un'eccezione di runtime per i linguaggi con sicurezza dei tipi.
Questo esempio è solo uno dei molti modi diversi in cui una modifica della firma della funzione può interrompere le istanze esistenti. In generale, se un agente di orchestrazione deve modificare il modo in cui chiama una funzione, è probabile che la modifica sia problematica.
Modificando la logica dell'orchestratore
L'altra classe di problemi di controllo delle versioni deriva dalla modifica del codice della funzione dell'agente di orchestrazione in modo da modificare il percorso di esecuzione per le istanze in-flight.
Si consideri la funzione dell'orchestratore seguente:
[FunctionName("FooBar")]
public static Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
bool result = await context.CallActivityAsync<bool>("Foo");
await context.CallActivityAsync("Bar", result);
}
Si supponga ora di voler apportare una modifica per aggiungere una nuova chiamata di funzione tra le due chiamate di funzione esistenti.
[FunctionName("FooBar")]
public static Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
bool result = await context.CallActivityAsync<bool>("Foo");
if (result)
{
await context.CallActivityAsync("SendNotification");
}
await context.CallActivityAsync("Bar", result);
}
Questa modifica aggiunge una nuova chiamata di funzione a SendNotification tra Foo e Bar. Non sono state apportate modifiche alla firma. Il problema si verifica quando un'istanza esistente riprende dalla chiamata a Bar. Durante la riproduzione, se la chiamata originaria a Foo ha restituito true, la riproduzione dell'agente di orchestrazione chiamerà SendNotification, che non si trova nella cronologia di esecuzione. Il runtime rileva questa incoerenza e genera un errore di orchestrazione non deterministica perché ha rilevato una chiamata a SendNotification quando ci si aspettava di vedere una chiamata a Bar. Lo stesso tipo di problema può verificarsi quando si aggiungono chiamate API ad altre operazioni durevoli, ad esempio la creazione di timer durevoli, l'attesa di eventi esterni, la chiamata di sotto orchestrazioni e così via.
Strategie di mitigazione
Ecco alcune delle strategie per affrontare le sfide di controllo delle versioni:
- Non eseguire alcuna operazione (non consigliata)
- Versionamento dell'orchestrazione (consigliato nella maggior parte dei casi)
- Arrestare tutte le istanze in corso
- Distribuzioni affiancate
Non fare nulla
L'approccio ingenuo alla gestione delle versioni consiste nel non fare nulla e lasciare che le istanze di orchestrazione in corso falliscano. A seconda del tipo di modifica, possono verificarsi i tipi di errori seguenti.
- Le orchestrazioni potrebbero non riuscire con un errore di orchestrazione non deterministica.
- Le orchestrazioni possono rimanere bloccate a tempo indeterminato, segnalando uno stato
Running. - Se una funzione viene rimossa, qualsiasi funzione che tenta di chiamarla potrebbe non riuscire con un errore.
- Se una funzione viene rimossa dopo l'esecuzione pianificata, l'app potrebbe riscontrare errori di runtime di basso livello nel motore Durable Task Framework, causando potenzialmente una grave riduzione delle prestazioni.
A causa di questi potenziali errori, la strategia "non fare nulla" non è consigliata.
Versionamento dell'orchestrazione
Annotazioni
Il versionamento dell'orchestrazione è attualmente disponibile in anteprima pubblica.
La funzionalità di controllo delle versioni dell'orchestrazione consente a versioni diverse di orchestrazioni di coesistere ed eseguire simultaneamente senza conflitti e problemi non deterministici, rendendo possibile distribuire gli aggiornamenti, consentendo al tempo stesso il completamento delle istanze di orchestrazione in-flight senza intervento manuale.
Con il controllo delle versioni dell'orchestrazione:
- Ogni istanza di orchestrazione ottiene una versione associata in modo permanente al momento della creazione
- Le funzioni dell'agente di orchestrazione possono esaminare la relativa versione e l'esecuzione del ramo di conseguenza
- I ruoli di lavoro che eseguono versioni più recenti della funzione dell'agente di orchestrazione possono continuare a eseguire istanze di orchestrazione create dalle versioni precedenti
- Il runtime impedisce ai lavoratori che eseguono versioni precedenti della funzione orchestratore di avviare orchestrazioni di versioni più recenti.
Questa strategia è consigliata per le applicazioni che devono supportare modifiche di rilievo mantenendo distribuzioni senza tempi di inattività.
Per indicazioni dettagliate sulla configurazione e sull'implementazione, vedere Controllo delle versioni dell'orchestrazione in Durable Functions.
Arrestare tutte le istanze in corso
Un'altra opzione è quella di arrestare tutte le istanze in corso. Se si usa il provider di archiviazione di Azure predefinito per Durable Functions, l'arresto di tutte le istanze può essere eseguito cancellando il contenuto delle code control-queue e workitem-queue. In alternativa, è possibile arrestare l'app per le funzioni, eliminare queste code e riavviare nuovamente l'app. Le code verranno ricreate automaticamente al riavvio dell'app. Le istanze di orchestrazione precedenti potrebbero rimanere nello stato "In esecuzione" per un tempo illimitato, ma non infonderanno i log con messaggi di errore o causeranno danni all'app. Questo approccio è ideale per lo sviluppo rapido di prototipi, incluso lo sviluppo locale.
Annotazioni
Questo approccio richiede l'accesso diretto alle risorse di archiviazione sottostanti e potrebbe non essere appropriato per tutti i provider di archiviazione supportati da Durable Functions.
Distribuzioni affiancate
Il modo più sicuro per garantire che le modifiche significative vengano distribuite in modo sicuro è distribuirle affiancate alle versioni precedenti. Questa operazione può essere eseguita usando una delle tecniche seguenti:
- Distribuire tutti gli aggiornamenti come funzioni completamente nuove, lasciando le funzioni esistenti as-is. Questo in genere non è consigliato a causa della complessità dell'aggiornamento ricorsivo dei chiamanti delle nuove versioni delle funzioni.
- Distribuire tutti gli aggiornamenti come nuova app per le funzioni con un account di archiviazione diverso.
- Distribuire una nuova copia dell'app per le funzioni con lo stesso account di archiviazione ma con un nome aggiornato dell'hub attività. Ciò comporta la creazione di nuovi artefatti di archiviazione che possono essere usati dalla nuova versione dell'app. La versione precedente dell'app continuerà a essere eseguita usando il set precedente di artefatti di archiviazione.
La distribuzione side-by-side è la tecnica consigliata per la distribuzione di nuove versioni delle app per le funzioni.
Annotazioni
Queste linee guida per la strategia di distribuzione side-by-side usano termini specifici di Archiviazione di Azure, ma generalmente si applicano a tutti i provider archiviazione Durable Functions supportati.
Slot di distribuzione
Quando si eseguono distribuzioni side-by-side in Funzioni di Azure o nel servizio app di Azure, è consigliabile distribuire la nuova versione dell'app per le funzioni in un nuovo slot di distribuzione. Gli slot di distribuzione consentono di eseguire side-by-side più copie dell'app per le funzioni con un solo slot come slot di produzione attivo. Quando è possibile esporre la nuova logica di orchestrazione all'infrastruttura esistente, l'operazione può essere semplice come la sostituzione della nuova versione nello slot di produzione.
Annotazioni
Questa strategia funziona meglio quando si usano trigger HTTP e webhook per le funzioni dell'agente di orchestrazione. Per i trigger non HTTP, ad esempio code o Hub eventi, la definizione del trigger deve derivare da un'impostazione dell'app che viene aggiornata come parte dell'operazione di swapping.