thinking in rules

New Feature: Background Compilation

Mark Malen | 5/28/2015

We have added a new feature to the product that you should know about, because it can improve the overall performance of your .NET applications that are integrated with InRule!  This blog post will introduce the feature and then give a demonstration of it.

When a rule application is initially loaded for rule execution using irCatalog, the rule engine will retrieve the rule application from irCatalog, compile it, and cache it into memory (the AppDomain).  Subsequent requests for the rule application will pull it from the AppDomain cache, providing fast access to the rule application.  The rule engine will then perform a periodic, lightweight poll to irCatalog, based on a configurable time interval, to see if there is a newer revision of the rule application.  If it finds one, then it will retrieve it from irCatalog, compile it, and cache it into memory.  This architecture is often referred to as “hot-deployment”, and allows changes to rule applications to be deployed seamlessly to the .NET application.  However, a problem that has sometimes been encountered with this architecture, particularly for larger rule applications, is that there will be a delay while the newer revision of the rule application is retrieved, compiled, and cached.  This problem has now been solved with our new background compilation feature!  With this feature enabled, the rule engine will perform the retrieval, compilation, and caching using a background thread, while the main thread continues to use the previously cached revision of the rule application.  Let’s see how this works…

Demonstration of Background Compilation

To demonstrate this feature, I have created a simple console application that runs rules using a rule application stored in irCatalog, and outputs which revision of the rule application was loaded and how long it took.  I’ve put this code in a loop, so the program will run these rules over and over.  While the program is running, I will use irAuthor to check-out the rule application, make a change to it, and check it back in as a newer revision, so that we can see what affect this has.  We’ll first run this with background compilation disabled, and then repeat the exercise with background compilation enabled.

In this application, I will be enabling background compilation using a configuration setting, but this feature can also be enabled when instantiating the CatalogRuleApplicationReference object.
Let’s take a look at the code…

Console Application:

 … using System;
using System.Diagnostics;
using System.Configuration;
using InRule.Runtime;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Starting...");

            var uri = ConfigurationManager.AppSettings["uri"];
            var username = ConfigurationManager.AppSettings["username"];
            var password = ConfigurationManager.AppSettings["password"];
            var ruleapp = ConfigurationManager.AppSettings["ruleapp"];
            var entityName = ConfigurationManager.AppSettings["entityName"];

            var ruleAppRef = new CatalogRuleApplicationReference(uri, ruleapp, username, password);

            for (var i = 0; i < 200; i++)
            {

                using (var session = new RuleSession(ruleAppRef))
                {
                    var stopWatch = new Stopwatch();
                    stopWatch.Start();

                    var rev = session.GetRuleApplicationDef().Revision.ToString();

                    var topEntity = session.CreateEntity(entityName);
                    session.LoadState(@"C:\data\InputData.testscenario");
                    session.ApplyRules();

                    stopWatch.Stop();
                    // Get the elapsed time as a TimeSpan value.
                    var ts = stopWatch.Elapsed;

                    // Format and display the TimeSpan value. 
                    var elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
                        ts.Hours, ts.Minutes, ts.Seconds,
                        ts.Milliseconds / 10);
                    Console.WriteLine("Revision=" + rev + ", Elapsed Time=" + elapsedTime);

                }
            }
            Console.WriteLine();
            Console.WriteLine("Hit enter to exit");
            Console.ReadLine();
        }
    }
}
 … 

Configuration File:


            <configuration>
              <configSections>
                <section name="inrule.logging" type="InRule.Repository.Logging.Configuration.LoggingSectionHandler, InRule.Repository" />
                <section name="inrule.runtime" type="InRule.Runtime.Configuration.RuntimeConfigSectionHandler, InRule.Runtime" />
              </configSections>
              <startup>
                <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
              </startup>
              <inrule.logging>
                <group typeName="InRule.Repository.Logging.Loggers.LoggerGroup, InRule.Repository" level="Info">
                  <logger typeName="InRule.Repository.Logging.Loggers.EventLogLogger, InRule.Repository" />
                </group>
              </inrule.logging>
              <inrule.runtime>
                <catalogRuleApplication refreshInterval="00:00:10" enableBackgroundCompilation="false" />
              </inrule.runtime>
              <appSettings>
                <add key="ruleappfile" value="" />
                <add key="uri" value="http://localhost/inrulecatalogservice/service.svc" />
                <add key="username" value="Admin" />
                <add key="password" value="password" />
                <add key="ruleapp" value="PricingRules" />
                <add key="entityName" value="TopLevelEntity"/>
              </appSettings>
            </configuration>
     

Output When Background Compilation is Disabled

In the configuration setting highlighted above, I have disabled background compilation, which is the default behavior.  I will now run the program, and while it’s running, will use irAuthor to check-in a change to the rule application, and we’ll examine the elapsed time it took for each iteration of the loop.

Starting...

Revision=1, Elapsed Time=00:00:16.58

Revision=1, Elapsed Time=00:00:01.02

Revision=1, Elapsed Time=00:00:00.98

Revision=1, Elapsed Time=00:00:01.04

Revision=1, Elapsed Time=00:00:00.98

Revision=1, Elapsed Time=00:00:00.96

Revision=1, Elapsed Time=00:00:01.18

Revision=1, Elapsed Time=00:00:00.96

Revision=1, Elapsed Time=00:00:00.95

Revision=1, Elapsed Time=00:00:01.18

Revision=1, Elapsed Time=00:00:00.95

Revision=1, Elapsed Time=00:00:01.19

Revision=1, Elapsed Time=00:00:01.05

Revision=1, Elapsed Time=00:00:00.93

Revision=1, Elapsed Time=00:00:00.93

Revision=2, Elapsed Time=00:00:09.00

Revision=2, Elapsed Time=00:00:01.01

Revision=2, Elapsed Time=00:00:00.99

Revision=2, Elapsed Time=00:00:01.06

Revision=2, Elapsed Time=00:00:00.99

Revision=2, Elapsed Time=00:00:01.01

Revision=2, Elapsed Time=00:00:00.98

Revision=2, Elapsed Time=00:00:01.04

Revision=2, Elapsed Time=00:00:01.05

Revision=2, Elapsed Time=00:00:00.96

Revision=2, Elapsed Time=00:00:00.98

As you can highlighted in the above output, the first iteration took about 16 seconds, where it had to retrieve the rule application from irCatalog, compile it, and cache it into memory.  Subsequent iterations only took about 1 second each.  Once the rule engine polled irCatalog and found the newer revision (#2), it took about 9 seconds, where it had to retrieve it from irCatalog, compile it, and cache it into memory.

Next, I will change the configuration file to enable background compilation:


    <inrule.runtime>
    <catalogRuleApplication refreshInterval="00:00:10" enableBackgroundCompilation="true" />
  </inrule.runtime>

                 

I will run the program again, and while it’s running, will make another change to the rule application, which will create revision #3.

Output When Background Compilation is Enabled:

Starting...

Revision=2, Elapsed Time=00:00:14.76

Revision=2, Elapsed Time=00:00:01.04

Revision=2, Elapsed Time=00:00:00.95

Revision=2, Elapsed Time=00:00:00.94

Revision=2, Elapsed Time=00:00:01.05

Revision=2, Elapsed Time=00:00:01.11

Revision=2, Elapsed Time=00:00:01.73

Revision=2, Elapsed Time=00:00:01.08

Revision=2, Elapsed Time=00:00:01.89

Revision=2, Elapsed Time=00:00:01.43

Revision=2, Elapsed Time=00:00:01.07

Revision=2, Elapsed Time=00:00:00.98

Revision=2, Elapsed Time=00:00:01.15

Revision=2, Elapsed Time=00:00:01.10

Revision=2, Elapsed Time=00:00:01.26

Revision=2, Elapsed Time=00:00:01.08

Revision=2, Elapsed Time=00:00:01.00

Revision=3, Elapsed Time=00:00:01.11

Revision=3, Elapsed Time=00:00:01.10

Revision=3, Elapsed Time=00:00:00.98

Revision=3, Elapsed Time=00:00:00.97

Revision=3, Elapsed Time=00:00:00.96

Revision=3, Elapsed Time=00:00:01.25

Revision=3, Elapsed Time=00:00:00.95

Revision=3, Elapsed Time=00:00:00.95

Revision=3, Elapsed Time=00:00:00.97

Revision=3, Elapsed Time=00:00:01.09

Revision=3, Elapsed Time=00:00:00.98

Revision=3, Elapsed Time=00:00:00.93

As you can highlighted in the above output, the first iteration took about 14 seconds, where it had to retrieve the rule application from irCatalog, compile it, and cache it into memory, but all subsequent iterations took about 1 second, including the one that used the newer revision.  With background compilation enabled, there was no delay when this console application switched from using revision #2 to revision #3.

Summary

The use of this feature translates to no delay for your end users when you deploy a change to a rule application!  This feature is currently available for only irCatalog-based rule applications, but we will soon be adding this feature for file-based rule applications as well.


comments powered by Disqus