Friday, August 7, 2015

Observer desgin pattern in Service Fabric Actor


First of all I want to thank Service Fabric Team to help me overcome and understand the actor logic with clarity.

In this blog I aim to create an Observer design Pattern of a real world sample using Service Fabric as a backbone. Apart from reliable service creation Service fabric also allows an author to create actor instances. Details are at here. These actors can be imagines as entities with specific business logic. These have proven to be fast and highly reliable in gaming solutions where response is the prime factor.

Today we are going to discuss of a widely used design pattern - Observer pattern in Service Fabric Actor to accomplish a threat analyzer for our test framework.

Problem Statement

Every company big or small emphasizes on testing frameworks. Usually these are typical dashboard based web solution which have compute role scheduling and triggering tests, monitoring them while they execute and storing the result in database. Test Dashboard shows a report view of the stored data.

We have often come across statements like

1.       How many tests are failing due to infra issue?

2.       How many tests are failing due to real code issue?

3.       What are the Azure Infra Issue type? etc

In my case we have a parser which goes and reads the database and do the bucketing based on different regex expressions. The challenge was the service was not reliable. This include service failures, connectivity issues etc. for our analyzer system, so we thought to try out Service Fabric and create a reliable service which will not only help us bucket the issue types but also will help us maintain the state for later analysis through dashboard. This was the time when we started going through service fabric actor model.

We heavily rely on Observer design pattern for our service. The intent was all the different issue types will be listening to a subject. The subject will at an interval of 30 min or so will query the database and push the list to all the observers. Based on issue type observers will add the object to itself.

When we tried to visualize this with Service Fabric, we had a picture which is described below:


In the above picture there is a stateless Fabric Actor which is our subject which will talk to the database at an interval of 30 min, and will push the objects to the observers. Observers will add the data based on its exception type.

Stateless Actor will push the data to the stateful actors, which will maintain the state as a list of issues. (Refer to the diagram below)

 

Later during analysis the user can directly call the actors and get their corresponding data. (Refer to the pic below)

 

Coding Time


 

Now let’s deep dive into how an actor works. As per my understanding author should keep these following things in mind. I will map the pointers to my example for better clarification

1.       Create a Serializable class which the Actor interface can call.

In this case my class was

[DataContract]

       public class ThreatState

       {

        [DataMember]

        public string ExceptionMessage;

 

        [DataMember]

        public string ExceptionType;

 

        [DataMember]

        public string TestRunId;

 

        [DataMember]

        public string TestId;

 

}

 

2.       Define a Subject Interface. Following OOD for implement to interface instead of concrete implementation, here is my ISubject interface

 

public interface ISubject : IActor

       {

        //Register Observer to Subject

        Task<bool> RegisterObserver(IIssueObserver observer);

 

        //Remove Observer from Subject

        Task<bool> RemoveObserver(IIssueObserver observer);

      

        //Set the Observable class state

        Task<bool> SetState(ThreatState state);

}

3.       Define a Observer Interface

 

public interface IIssueObserver: IActor

       {

        //Update the Observer object

        Task<bool> Update(ThreatState t);

}

 

Note: In both these interfaces we have implemented IActor which makes them actor interface. One primary thing to keep in mind is all the actor interfaces should return Task results.

 

4.       The Stateless Observable class will implement Actor and ISubject

public class ThreatObservable : Actor, ISubject

{…}

 

5.       For Stateful Observers here is the only change we need to make from Actor point of view

[ActorService(Name="AzureIssue")]

class AzureInfraIssueObserver : Actor<ThreatState>, IIssueObserver

{…}

 

[ActorService(Name="MachineIssue")]

class MachineIssueObserver : Actor<ThreatState>, IIssueObserver

{…}

 

[ActorService(Name="ProductIssue")]

       class ProductIssueObserver : Actor<ThreatState>, IIssueObserver

{…}

 

[ActorService(Name = "TestIssue")]

       class TestIssueObserver : Actor<ThreatState>, IIssueObserver

{…}

 

6.       Now in your Service Code during initiation add the different observers to the subject

 

public static void Main(string[] args)

        {

            try

            {

                using (FabricRuntime fabricRuntime = FabricRuntime.Create())

                {

                    fabricRuntime.RegisterActor(typeof(ThreatObservable));

                    fabricRuntime.RegisterActor(typeof(AzureInfraIssueObserver));

                    fabricRuntime.RegisterActor(typeof(MachineIssueObserver));

                    fabricRuntime.RegisterActor(typeof(ProductIssueObserver));

                    fabricRuntime.RegisterActor(typeof(TestIssueObserver));

 

                    var machineIssue = new MachineIssueObserver();

                    var testIssue = new TestIssueObserver();

                    var codeIssue = new ProductIssueObserver();

                    var azureIssue = new AzureInfraIssueObserver();

 

                    ThreatObservable observable = new ThreatObservable();

                    var registerMachine = observable.RegisterObserver(machineIssue);

                    var registerTest = observable.RegisterObserver(testIssue);

                    var registerCode = observable.RegisterObserver(codeIssue);

                    var registerAzure = observable.RegisterObserver(azureIssue);

 

                    if (!registerMachine.Result || !registerAzure.Result || !registerTest.Result || !registerCode.Result)

                    {

                        Trace.WriteLine("Registeration failed");

                    }

                    Thread.Sleep(Timeout.Infinite);

                }

            }

            catch (Exception e)

            {

                ActorEventSource.Current.ActorHostInitializationFailed(e);

                throw;

            }

 }

 

7.       This is what needs to be done from Service point of view.

8.       I tested the service with the following client

 

public static void Main(string[] args)

        {

            

            var proxy = ActorProxy.Create<ISubject>(new ActorId("ThreatObservable"),new Uri("fabric:/ThreatObserverApplication/SubjectActorService"));

            var lstThreats = GetListOfThreats();

 

            var result1 = proxy.SetState(lstThreats[0]).Result;

            var result2 = proxy.SetState(lstThreats[1]).Result;

            var result3 = proxy.SetState(lstThreats[2]);

            var result4 = proxy.SetState(lstThreats[3]);

 

            //Assert all the results to true and verify

        }

 

        private static List<ThreatState> GetListOfThreats()

        {

            var listThreats = new List<ThreatState>();

            listThreats.Add(new ThreatState() {ExceptionMessage = "Azure Storage Exception", ExceptionType = "Azure", TestRunId = "1234",TestId = "2345"});

            listThreats.Add(new ThreatState() { ExceptionMessage = "Bad Machine Exception", ExceptionType = "Machine", TestRunId = "1234", TestId = "2345" });

            listThreats.Add(new ThreatState() { ExceptionMessage = "Test Exception", ExceptionType = "Test", TestRunId = "1234", TestId = "2345" });

            listThreats.Add(new ThreatState() { ExceptionMessage = "Code Exception", ExceptionType = "Code", TestRunId = "1234", TestId = "2345" });

 

            return listThreats;

        }

 

That is it!!

As I wanted to keep this short, I wanted to focus on major interactions of your code with service fabric actor model. If you want to know more I have pushed a sample app at https://github.com/Nabarun/ServiceFabricSample.git. Let me know if there are any feedback or suggestion.