Menyimulasikan kegagalan selama beban kerja layanan

Skenario uji coba di Azure Service Fabric memungkinkan pengembang untuk tidak khawatir menangani kesalahan individu. Namun, ada skenario di mana penyelingan eksplisit beban kerja klien dan kegagalan mungkin diperlukan. Penyelingan beban kerja klien dan kesalahan memastikan bahwa layanan benar-benar melakukan beberapa tindakan ketika kegagalan terjadi. Mengingat tingkat kontrol yang diberikan pengujian, ini bisa berada di titik-titik yang tepat dari eksekusi beban kerja. Induksi kesalahan tersebut di berbagai status bagian dalam aplikasi dapat menemukan bug dan meningkatkan kualitas.

Sampel skenario kustom

Tes ini menunjukkan skenario yang mengintervensi beban kerja bisnis dengan kegagalan lancar dan tidak lancar. Kesalahan harus diinduksi di tengah operasi layanan atau komputasi untuk hasil terbaik.

Mari membahas mengenai contoh layanan yang memaparkan empat beban kerja: A, B, C, dan D. Masing-masing sesuai dengan set alur kerja dan bisa menjadi komputasi, penyimpanan, atau campuran. Sederhananya, kami akan mengabstraksi beban kerja dalam contoh-contoh kami. Kesalahan yang berbeda yang dijalankan dalam contoh ini adalah:

  • RestartNode: Kesalahan tidak lancar untuk mensimulasikan penghidupan ulang komputer.
  • RestartDeployedCodePackage: Kesalahan tidak lancar untuk mensimulasikan crash proses host layanan.
  • RemoveReplica: Kesalahan lancar untuk mensimulasikan penghapusan replika.
  • MovePrimary: Kesalahan lancar untuk mensimulasikan gerakan replika yang dipicu oleh load balancer Service Fabric.
// Add a reference to System.Fabric.Testability.dll and System.Fabric.dll.

using System;
using System.Fabric;
using System.Fabric.Testability.Scenario;
using System.Threading;
using System.Threading.Tasks;

class Test
{
    public static int Main(string[] args)
    {
        // Replace these strings with the actual version for your cluster and application.
        string clusterConnection = "localhost:19000";
        Uri applicationName = new Uri("fabric:/samples/PersistentToDoListApp");
        Uri serviceName = new Uri("fabric:/samples/PersistentToDoListApp/PersistentToDoListService");

        Console.WriteLine("Starting Workload Test...");
        try
        {
            RunTestAsync(clusterConnection, applicationName, serviceName).Wait();
        }
        catch (AggregateException ae)
        {
            Console.WriteLine("Workload Test failed: ");
            foreach (Exception ex in ae.InnerExceptions)
            {
                if (ex is FabricException)
                {
                    Console.WriteLine("HResult: {0} Message: {1}", ex.HResult, ex.Message);
                }
            }
            return -1;
        }

        Console.WriteLine("Workload Test completed successfully.");
        return 0;
    }

    public enum ServiceWorkloads
    {
        A,
        B,
        C,
        D
    }

    public enum ServiceFabricFaults
    {
        RestartNode,
        RestartCodePackage,
        RemoveReplica,
        MovePrimary,
    }

    public static async Task RunTestAsync(string clusterConnection, Uri applicationName, Uri serviceName)
    {
        // Create FabricClient with connection and security information here.
        FabricClient fabricClient = new FabricClient(clusterConnection);
        // Maximum time to wait for a service to stabilize.
        TimeSpan maxServiceStabilizationTime = TimeSpan.FromSeconds(120);

        // How many loops of faults you want to execute.
        uint testLoopCount = 20;
        Random random = new Random();

        for (var i = 0; i < testLoopCount; ++i)
        {
            var workload = SelectRandomValue<ServiceWorkloads>(random);
            // Start the workload.
            var workloadTask = RunWorkloadAsync(workload);

            // While the task is running, induce faults into the service. They can be ungraceful faults like
            // RestartNode and RestartDeployedCodePackage or graceful faults like RemoveReplica or MovePrimary.
            var fault = SelectRandomValue<ServiceFabricFaults>(random);

            // Create a replica selector, which will select a primary replica from the given service to test.
            var replicaSelector = ReplicaSelector.PrimaryOf(PartitionSelector.RandomOf(serviceName));
            // Run the selected random fault.
            await RunFaultAsync(applicationName, fault, replicaSelector, fabricClient);
            // Validate the health and stability of the service.
            await fabricClient.TestManager.ValidateServiceAsync(serviceName, maxServiceStabilizationTime);

            // Wait for the workload to finish successfully.
            await workloadTask;
        }
    }

    private static async Task RunFaultAsync(Uri applicationName, ServiceFabricFaults fault, ReplicaSelector selector, FabricClient client)
    {
        switch (fault)
        {
            case ServiceFabricFaults.RestartNode:
                await client.FaultManager.RestartNodeAsync(selector, CompletionMode.Verify);
                break;
            case ServiceFabricFaults.RestartCodePackage:
                await client.FaultManager.RestartDeployedCodePackageAsync(applicationName, selector, CompletionMode.Verify);
                break;
            case ServiceFabricFaults.RemoveReplica:
                await client.FaultManager.RemoveReplicaAsync(selector, CompletionMode.Verify, false);
                break;
            case ServiceFabricFaults.MovePrimary:
                await client.FaultManager.MovePrimaryAsync(selector.PartitionSelector);
                break;
        }
    }

    private static Task RunWorkloadAsync(ServiceWorkloads workload)
    {
        throw new NotImplementedException();
        // This is where you trigger and complete your service workload.
        // Note that the faults induced while your service workload is running will
        // fault the primary service. Hence, you will need to reconnect to complete or check
        // the status of the workload.
    }

    private static T SelectRandomValue<T>(Random random)
    {
        Array values = Enum.GetValues(typeof(T));
        T workload = (T)values.GetValue(random.Next(values.Length));
        return workload;
    }
}