Sostituzione dell'API di avvio del nodo e dell'API di arresto del nodo con l'API di transizione del nodoReplacing the Start Node and Stop node APIs with the Node Transition API

Come funzionano le API di avvio del nodo e di arresto del nodo?What do the Stop Node and Start Node APIs do?

L'API di avvio del nodo (gestita: StopNodeAsync(), PowerShell: Stop-ServiceFabricNode) arresta un nodo di Service Fabric.The Stop Node API (managed: StopNodeAsync(), PowerShell: Stop-ServiceFabricNode) stops a Service Fabric node. Un nodo di Service Fabric è un processo, non una VM o macchina; la VM o la macchina sarà comunque in esecuzione.A Service Fabric node is process, not a VM or machine – the VM or machine will still be running. Nel resto del documento, "nodo" indica il nodo di Service Fabric.For the rest of the document "node" will mean Service Fabric node. L'arresto di un nodo inserisce il nodo in uno stato arrestato, in cui non è un membro del cluster e non può ospitare servizi, simulando in questo modo un nodo inattivo.Stopping a node puts it into a stopped state where it is not a member of the cluster and cannot host services, thus simulating a down node. Questa condizione è utile per l'inserimento di errori nel sistema per testare l'applicazione.This is useful for injecting faults into the system to test your application. L'API di avvio del nodo (gestita: StartNodeAsync(), PowerShell: Start-ServiceFabricNode]) inverte l'API di arresto del nodo riportando il nodo a uno stato normale.The Start Node API (managed: StartNodeAsync(), PowerShell: Start-ServiceFabricNode]) reverses the Stop Node API, which brings the node back to a normal state.

Perché si sta procedendo alla relativa sostituzione?Why are we replacing these?

Come descritto in precedenza, un nodo arrestato di Service Fabric è un nodo di destinazione scelto intenzionalmente usando l'API di arresto del nodo.As described earlier, a stopped Service Fabric node is a node intentionally targeted using the Stop Node API. Un nodo inattivo è un nodo che non è attivo per qualsiasi altro motivo, ad esempio, la VM o la macchina è disattivata.A down node is a node that is down for any other reason (e.g. the VM or machine is off). Con l'API di arresto del nodo, il sistema non espone le informazioni per distinguere i nodi arrestati e i nodi inattivi.With the Stop Node API, the system does not expose information to differentiate between stopped nodes and down nodes.

Inoltre, alcuni errori restituiti da queste API non offrono il massimo livello di dettaglio possibile.In addition, some errors returned by these APIs are not as descriptive as they could be. Ad esempio, se si chiama l'API di arresto del nodo su un nodo già arrestato verrà restituito l'errore InvalidAddress.For example, invoking the Stop Node API on an already stopped node will return the error InvalidAddress. Questa esperienza poteva essere migliorata.This experience could be improved.

Inoltre, la durata per cui un nodo viene arrestato è "infinita" finché non viene richiamata l'API di avvio del nodo.Also, the duration a node is stopped for is “infinite” until the Start Node API is invoked. Questa condizione può causare problemi ed essere soggetta a errori.We’ve found this can cause problems and may be error-prone. Ad esempio, si sono verificati problemi in cui un utente ha richiama l'API di avvio del nodo su un nodo e successivamente ha dimenticato di avere eseguito questa operazione.For example, we’ve seen problems where a user invoked the Stop Node API on a node and then forgot about it. In seguito non è stato chiaro se il nodo era inattivo o arrestato.Later, it was unclear if the node was down or stopped.

Introduzione alle API di transizione del nodoIntroducing the Node Transition APIs

Questi problemi sono stati risolti in un nuovo set di API.We’ve addressed these issues above in a new set of APIs. La nuova API di transizione del nodo (gestito: StartNodeTransitionAsync()) può essere usata per la transizione di un nodo di Service Fabric a uno stato arrestato o per eseguire la transizione da uno stato arrestato a uno stato attivo normale.The new Node Transition API (managed: StartNodeTransitionAsync()) may be used to transition a Service Fabric node to a stopped state, or to transition it from a stopped state to a normal up state. Si noti che "Start" nel nome dell'API non fa riferimento all'avvio di un nodo.Please note that the “Start” in the name of the API does not refer to starting a node. Fa riferimento all'avvio di un'operazione asincrona che verrà eseguita dal sistema per la transizione del nodo nello stato arrestato o avviato.It refers to beginning an asynchronous operation that the system will execute to transition the node to either stopped or started state.

UtilizzoUsage

Se l'API di transizione del nodo non genera un'eccezione quando viene richiamata, il sistema ha accettato l'operazione asincrona e la eseguirà.If the Node Transition API does not throw an exception when invoked, then the system has accepted the asynchronous operation, and will execute it. Una chiamata eseguita correttamente non implica che l'operazione sia stata completata.A successful call does not imply the operation is finished yet. Per ottenere informazioni sullo stato corrente dell'operazione, chiamare l'API di avanzamento della transizione del nodo (gestito: GetNodeTransitionProgressAsync()) con il GUID usato per chiamare l'API di transizione di nodo per questa operazione.To get information about the current state of the operation, call the Node Transition Progress API (managed: GetNodeTransitionProgressAsync()) with the guid used when invoking Node Transition API for this operation. L'API di avanzamento della transizione del nodo restituisce un oggetto NodeTransitionProgress.The Node Transition Progress API returns an NodeTransitionProgress object. La proprietà State dell'oggetto specifica lo stato corrente dell'operazione.This object’s State property specifies the current state of the operation. Se lo stato è "in esecuzione", l'operazione è in corso.If the state is “Running” then the operation is executing. Se lo stato è completato, l'operazione è stata completata senza errori.If it is Completed, the operation finished without error. Se lo stato indica un errore, si è verificato un problema in fase di esecuzione.If it is Faulted, there was a problem executing the operation. La proprietà Exception della proprietà Result indicherà il problema riscontrato.The Result property’s Exception property will indicate what the issue was. Per altre informazioni sulla proprietà State e la sezione "Esempio di utilizzo" per esempi di codice, vedere https://docs.microsoft.com/dotnet/api/system.fabric.testcommandprogressstate.See https://docs.microsoft.com/dotnet/api/system.fabric.testcommandprogressstate for more information about the State property, and the “Sample Usage” section below for code examples.

Differenze tra un nodo arrestato e un nodo inattivo Se un nodo è stato arrestato usando l'API di transizione del nodo, l'output di una query del nodo (gestito: GetNodeListAsync(), PowerShell: Get-ServiceFabricNode) indicherà il valore true della proprietà IsStopped per questo nodo.Differentiating between a stopped node and a down node If a node is stopped using the Node Transition API, the output of a node query (managed: GetNodeListAsync(), PowerShell: Get-ServiceFabricNode) will show that this node has an IsStopped property value of true. Si noti che questo valore è diverso dal valore della proprietà NodeStatus che indicherà lo stato inattivo.Note this is different from the value of the NodeStatus property, which will say Down. Se la proprietà NodeStatus ha il valore inattivo, ma IsStopped restituisce false, il nodo non è stato arrestato usando l'API di transizione del nodo ed è inattivo per un altro motivo.If the NodeStatus property has a value of Down, but IsStopped is false, then the node was not stopped using the Node Transition API, and is Down due some other reason. Se il valore della proprietà IsStopped è true e la proprietà NodeStatus indica lo stato inattivo, il nodo è stato arrestato usando l'API di transizione del nodo.If the IsStopped property is true, and the NodeStatus property is Down, then it was stopped using the Node Transition API.

L'avvio di un nodo arrestato usando l'API di transizione del nodo ne ripristina il funzionamento come membro normale del cluster.Starting a stopped node using the Node Transition API will return it to function as a normal member of the cluster again. L'output dell'API di query del nodo indicherà IsStopped come false e NodeStatus indicherà un valore che non è inattivo, ad esempio, attivo.The output of the node query API will show IsStopped as false, and NodeStatus as something that is not Down (e.g. Up).

Durata limitata quando si usa l'API di transizione di nodo per arrestare un nodo, uno dei parametri obbligatori, stopNodeDurationInSeconds, rappresenta il tempo in secondi per cui il nodo viene mantenuto arrestato.Limited Duration When using the Node Transition API to stop a node, one of the required parameters, stopNodeDurationInSeconds, represents the amount of time in seconds to keep the node stopped. Questo valore deve essere compreso nell'intervallo consentito, che include un minimo di 600 secondi e un massimo di 14.400 secondi.This value must be in the allowed range, which has a minimum of 600, and a maximum of 14400. Al termine di questo periodo, il nodo verrà riavviato automaticamente in uno stato attivo.After this time expires, the node will restart itself into Up state automatically. Fare riferimento all'esempio 1 di seguito per un esempio di utilizzo.Refer to Sample 1 below for an example of usage.

Avviso

Non combinare le API di transizione del nodo e le API di avvio del nodo e di arresto del nodo.Avoid mixing Node Transition APIs and the Stop Node and Start Node APIs. È consigliabile usare solo l'API di transizione del nodo.The recommendation is to use the Node Transition API only. Se un nodo è già stato arrestato usando l'API di arresto del nodo, è necessario avviarlo usando l'API di avvio del nodo prima di usare le API di transizioni del nodo.> If a node has been already been stopped using the Stop Node API, it should be started using the Start Node API first before using the > Node Transition APIs.

Avviso

Non è possibile eseguire più chiamate API di transizione del nodo in parallelo nello stesso nodo.Multiple Node Transition APIs calls cannot be made on the same node in parallel. In questo caso, l'API di transizione del nodo genererà un FabricException con un valore NodeTransitionInProgress della proprietà ErrorCode.In such a situation, the Node Transition API will > throw a FabricException with an ErrorCode property value of NodeTransitionInProgress. Dopo avere avviato una transizione del nodo in un nodo specifico, è necessario attendere fino a quando l'operazione raggiunge uno stato finale (completato, con errore e annullamento forzato) prima di avviare una nuova transizione nello stesso nodo.Once a node transition on a specific node has > been started, you should wait until the operation reaches a terminal state (Completed, Faulted, or ForceCancelled) before starting a > new transition on the same node. Sono consentite chiamate a transizioni del nodo in parallelo su nodi diversi.Parallel node transition calls on different nodes are allowed.

Esempio di utilizzoSample Usage

Esempio 1: l'esempio seguente usa l'API di transizione del nodo per arrestare un nodo.Sample 1 - The following sample uses the Node Transition API to stop a node.

        // Helper function to get information about a node
        static Node GetNodeInfo(FabricClient fc, string node)
        {
            NodeList n = null;
            while (n == null)
            {
                n = fc.QueryManager.GetNodeListAsync(node).GetAwaiter().GetResult();
                Task.Delay(TimeSpan.FromSeconds(1)).GetAwaiter();
            };

            return n.FirstOrDefault();
        }

        static async Task WaitForStateAsync(FabricClient fc, Guid operationId, TestCommandProgressState targetState)
        {
            NodeTransitionProgress progress = null;

            do
            {
                bool exceptionObserved = false;
                try
                {
                    progress = await fc.TestManager.GetNodeTransitionProgressAsync(operationId, TimeSpan.FromMinutes(1), CancellationToken.None).ConfigureAwait(false);
                }
                catch (OperationCanceledException oce)
                {
                    Console.WriteLine("Caught exception '{0}'", oce);
                    exceptionObserved = true;
                }
                catch (FabricTransientException fte)
                {
                    Console.WriteLine("Caught exception '{0}'", fte);
                    exceptionObserved = true;
                }

                if (!exceptionObserved)
                {
                    Console.WriteLine("Current state of operation '{0}': {1}", operationId, progress.State);

                    if (progress.State == TestCommandProgressState.Faulted)
                    {
                        // Inspect the progress object's Result.Exception.HResult to get the error code.
                        Console.WriteLine("'{0}' failed with: {1}, HResult: {2}", operationId, progress.Result.Exception, progress.Result.Exception.HResult);

                        // ...additional logic as required
                    }

                    if (progress.State == targetState)
                    {
                        Console.WriteLine("Target state '{0}' has been reached", targetState);
                        break;
                    }
                }

                await Task.Delay(TimeSpan.FromSeconds(5)).ConfigureAwait(false);
            }
            while (true);
        }

        static async Task StopNodeAsync(FabricClient fc, string nodeName, int durationInSeconds)
        {
            // Uses the GetNodeListAsync() API to get information about the target node
            Node n = GetNodeInfo(fc, nodeName);

            // Create a Guid
            Guid guid = Guid.NewGuid();

            // Create a NodeStopDescription object, which will be used as a parameter into StartNodeTransition
            NodeStopDescription description = new NodeStopDescription(guid, n.NodeName, n.NodeInstanceId, durationInSeconds);

            bool wasSuccessful = false;

            do
            {
                try
                {
                    // Invoke StartNodeTransitionAsync with the NodeStopDescription from above, which will stop the target node.  Retry transient errors.
                    await fc.TestManager.StartNodeTransitionAsync(description, TimeSpan.FromMinutes(1), CancellationToken.None).ConfigureAwait(false);
                    wasSuccessful = true;
                }
                catch (OperationCanceledException oce)
                {
                    // This is retryable
                }
                catch (FabricTransientException fte)
                {
                    // This is retryable
                }

                // Backoff
                await Task.Delay(TimeSpan.FromSeconds(5)).ConfigureAwait(false);
            }
            while (!wasSuccessful);

            // Now call StartNodeTransitionProgressAsync() until hte desired state is reached.
            await WaitForStateAsync(fc, guid, TestCommandProgressState.Completed).ConfigureAwait(false);
        }

Esempio 2: l'esempio seguente avvia un nodo arrestato.Sample 2 - The following sample starts a stopped node. Vengono usati alcuni metodi di supporto dal primo esempio.It uses some helper methods from the first sample.

        static async Task StartNodeAsync(FabricClient fc, string nodeName)
        {
            // Uses the GetNodeListAsync() API to get information about the target node
            Node n = GetNodeInfo(fc, nodeName);

            Guid guid = Guid.NewGuid();
            BigInteger nodeInstanceId = n.NodeInstanceId;

            // Create a NodeStartDescription object, which will be used as a parameter into StartNodeTransition
            NodeStartDescription description = new NodeStartDescription(guid, n.NodeName, nodeInstanceId);

            bool wasSuccessful = false;

            do
            {
                try
                {
                    // Invoke StartNodeTransitionAsync with the NodeStartDescription from above, which will start the target stopped node.  Retry transient errors.
                    await fc.TestManager.StartNodeTransitionAsync(description, TimeSpan.FromMinutes(1), CancellationToken.None).ConfigureAwait(false);
                    wasSuccessful = true;
                }
                catch (OperationCanceledException oce)
                {
                    Console.WriteLine("Caught exception '{0}'", oce);
                }
                catch (FabricTransientException fte)
                {
                    Console.WriteLine("Caught exception '{0}'", fte);
                }

                await Task.Delay(TimeSpan.FromSeconds(5)).ConfigureAwait(false);

            }
            while (!wasSuccessful);

            // Now call StartNodeTransitionProgressAsync() until hte desired state is reached.
            await WaitForStateAsync(fc, guid, TestCommandProgressState.Completed).ConfigureAwait(false);
        }

Esempio 3: l'esempio seguente illustra un uso non corretto.Sample 3 - The following sample shows incorrect usage. Questo uso non è corretto perché il valore stopDurationInSeconds fornito è maggiore dell'intervallo consentito.This usage is incorrect because the stopDurationInSeconds it provides is greater than the allowed range. Poiché StartNodeTransitionAsync() genererà un errore irreversibile, l'operazione non è stata accettata e l'API di avanzamento non deve essere chiamata.Since StartNodeTransitionAsync() will fail with a fatal error, the operation was not accepted, and the progress API should not be called. Questo esempio usa alcuni metodi di supporto dal primo esempio.This sample uses some helper methods from the first sample.

        static async Task StopNodeWithOutOfRangeDurationAsync(FabricClient fc, string nodeName)
        {
            Node n = GetNodeInfo(fc, nodeName);

            Guid guid = Guid.NewGuid();

            // Use an out of range value for stopDurationInSeconds to demonstrate error
            NodeStopDescription description = new NodeStopDescription(guid, n.NodeName, n.NodeInstanceId, 99999);

            try
            {
                await fc.TestManager.StartNodeTransitionAsync(description, TimeSpan.FromMinutes(1), CancellationToken.None).ConfigureAwait(false);
            }

            catch (FabricException e)
            {
                Console.WriteLine("Caught {0}", e);
                Console.WriteLine("ErrorCode {0}", e.ErrorCode);
                // Output:
                // Caught System.Fabric.FabricException: System.Runtime.InteropServices.COMException (-2147017629)
                // StopDurationInSeconds is out of range ---> System.Runtime.InteropServices.COMException: Exception from HRESULT: 0x80071C63
                // << Parts of exception omitted>>
                //
                // ErrorCode InvalidDuration
            }
        }

Esempio 4: l'esempio seguente mostra le informazioni sull'errore che verranno restituite dall'API di avanzamento della transizione del nodo quando l'operazione avviata dall'API di transizione del nodo viene accettata, ma si verifica un errore successivamente in fase di esecuzione.Sample 4 - The following sample shows the error information that will be returned from the Node Transition Progress API when the operation initiated by the Node Transition API is accepted, but fails later while executing. In questo caso l'errore si verifica perché l'API di transizione del nodo tenta di avviare un nodo che non esiste.In the case, it fails because the Node Transition API attempts to start a node that does not exist. Questo esempio usa alcuni metodi di supporto dal primo esempio.This sample uses some helper methods from the first sample.

        static async Task StartNodeWithNonexistentNodeAsync(FabricClient fc)
        {
            Guid guid = Guid.NewGuid();
            BigInteger nodeInstanceId = 12345;

            // Intentionally use a nonexistent node
            NodeStartDescription description = new NodeStartDescription(guid, "NonexistentNode", nodeInstanceId);

            bool wasSuccessful = false;

            do
            {
                try
                {
                    // Invoke StartNodeTransitionAsync with the NodeStartDescription from above, which will start the target stopped node.  Retry transient errors.
                    await fc.TestManager.StartNodeTransitionAsync(description, TimeSpan.FromMinutes(1), CancellationToken.None).ConfigureAwait(false);
                    wasSuccessful = true;
                }
                catch (OperationCanceledException oce)
                {
                    Console.WriteLine("Caught exception '{0}'", oce);
                }
                catch (FabricTransientException fte)
                {
                    Console.WriteLine("Caught exception '{0}'", fte);
                }

                await Task.Delay(TimeSpan.FromSeconds(5)).ConfigureAwait(false);

            }
            while (!wasSuccessful);

            // Now call StartNodeTransitionProgressAsync() until the desired state is reached.  In this case, it will end up in the Faulted state since the node does not exist.
            // When StartNodeTransitionProgressAsync()'s returned progress object has a State if Faulted, inspect the progress object's Result.Exception.HResult to get the error code.
            // In this case, it will be NodeNotFound.
            await WaitForStateAsync(fc, guid, TestCommandProgressState.Faulted).ConfigureAwait(false);
        }