Controllo delle versioni in Funzioni permanenti (Funzioni di Azure)
Nella durata di un'applicazione è comune che vengano aggiunte, rimosse e modificate funzioni. Funzioni permanenti consente di concatenare diverse funzioni in modi non possibili in precedenza, influenzando in tal modo la gestione del controllo delle versioni.
Come gestire le modifiche di rilievo
Sono disponibili alcuni esempi di modifiche di rilievo che è necessario tenere presenti. Questo articolo illustra i più comuni, basati tutti sul fatto che tutte le funzioni di orchestrazione, sia nuove che esistenti, sono interessate dalle modifiche apportate al codice di funzione.
Modifica delle firme delle funzioni di attività o entità
Una modifica della firma fa riferimento a una modifica di nome, input oppure output di una funzione. Se questo tipo di modifica viene apportata a un'attività o a una funzione di entità, potrebbe interrompere qualsiasi funzione dell'agente di orchestrazione che dipende da essa. Questo 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 funzione dell'agente di orchestrazione seguente.
[FunctionName("FooBar")]
public static Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
bool result = await context.CallActivityAsync<bool>("Foo");
await context.CallActivityAsync("Bar", result);
}
Questa semplice funzione usa i risultati di Foo e li 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 corso. 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 String, causando un comportamento imprevisto o anche un'eccezione di runtime per i linguaggi indipendenti dai tipi.
Questo esempio è solo uno dei diversi modi in cui una modifica della firma di funzione può interrompere le istanze esistenti. In generale, se è necessario modificare il modo in cui un agente di orchestrazione chiama una funzione, la modifica può diventare un problema.
Modifica della logica dell'agente di orchestrazione
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 anteprima.
Si consideri la funzione dell'agente di orchestrazione 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 chiamata di funzione a SendNotification tra Foo e Bar. Non sono presenti modifiche della firma. Il problema si verifica quando un'istanza esistente riprende dopo la chiamata a Bar. Durante la riproduzione, se la chiamata originale a Foo ha restituitotrue
, 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 deterministico perché ha rilevato una chiamata a SendNotification quando si prevede di visualizzare una chiamata a Barra. 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
Di seguito vengono indicate alcune strategie per affrontare le problematiche di controllo delle versioni.
- Non eseguire alcuna operazione (non consigliata)
- Arrestare tutte le istanze in corso
- Eseguire distribuzioni side-by-side
Non eseguire alcuna operazione
L'approccio ingenuo al controllo delle versioni è quello di non eseguire alcuna operazione e lasciare che le istanze di orchestrazione in anteprima non riescano. A seconda del tipo di modifica, possono verificarsi i tipi di errori seguenti.
- Le orchestrazioni potrebbero non riuscire con un errore di orchestrazione non deterministico .
- Le orchestrazioni possono rimanere bloccate per un periodo illimitato, segnalando uno
Running
stato. - Se una funzione viene rimossa, qualsiasi funzione che tenta di chiamarla potrebbe non riuscire con un errore.
- Se una funzione viene rimossa dopo che è stata pianificata l'esecuzione, 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 di "non eseguire alcuna operazione" non è consigliata.
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, è possibile arrestare tutte le istanze cancellando il contenuto delle code di accodamento dei controlli interni e delle code di workitem.If you're using the default Azure Storage provider for Durable Functions, stopping all instances can be done by clearing the contents of the internal control-queue and workitem-queue queues. In alternativa, è possibile arrestare l'app per le funzioni, eliminare queste code e riavviare di nuovo l'app. Le code verranno ricreate automaticamente dopo il riavvio dell'app. Le istanze di orchestrazione precedenti possono rimanere nello stato "In esecuzione" per un periodo illimitato, ma non ingombrano i log con messaggi di errore o causano danni all'app. Questo approccio è ideale per lo sviluppo rapido di prototipi, incluso lo sviluppo locale.
Nota
Questo approccio richiede l'accesso diretto alle risorse di archiviazione sottostanti e non è appropriato per tutti i provider di archiviazione supportati da Durable Functions.
Eseguire distribuzioni side-by-side
Il modo migliore per garantire che le modifiche di rilievo vengano distribuite in modo sicuro è quello di eseguire una distribuzione side-by-side con le versioni precedenti. Questa operazione può essere eseguita tramite una delle tecniche seguenti:
- Distribuire tutti gli aggiornamenti come funzioni completamente nuove, lasciando così come sono le funzioni esistenti. Ciò in genere non è consigliato a causa della complessità interessata dall'aggiornamento ricorsivo dei chiamanti delle nuove versioni delle nuove funzioni.
- Distribuire tutti gli aggiornamenti come una 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.
Nota
Queste linee guida per la strategia di distribuzione side-by-side usano termini specifici di Archiviazione di Azure, ma si applicano in genere a tutti i provider di archiviazione supportati Durable Functions.
Slot di distribuzione
Quando si eseguono distribuzioni side-by-side in Funzioni di Azure o 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.
Nota
Questa strategia funziona meglio quando si usano trigger HTTP e di 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 scambio.