0

I am using Nesper as the CEP engine to process the events in my application.

I am trying to model the following EPL statement:

  • The Value field of the events are averaged over limited window time.
  • If any of these averages matched 1 desired value, an event is generated.

I've modeled this as:

SELECT (
(AVG(ParameterEvent1.Value) = 1) OR 
(AVG(ParameterEvent2.Value) = 1)
...
(AVG(ParameterEvent50.Value) = 1)
) AS BooleanValue 
FROM 
ParameterEvent(Id = 1).win:time(3 sec) AS ParameterEvent1, 
ParameterEvent(Id = 2).win:time(3 sec) AS ParameterEvent2 
...
ParameterEvent(Id = 50).win:time(3 sec) AS ParameterEvent50

And in a separate thread, I am feeding the engine the parameter values with steady rate of 50 event/sec.

This setup cause huge CPU and RAM usage until a com.espertech.esper.client.EPException: ReaderWriterLock timeout expired Exception happens.

I wonder what is causing the issue. It is interesting that when I change the window from win:time(3 sec) to std:lastevent() this issue resolves. However, I need the window to make sure the value of parameters is 1 to 3 seconds before generating the event.

The complete demo code is in the following. You just need to install Nesper package to run it:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using com.espertech.esper.client;

namespace NesperTester
{
    class Program
    {
        public class ParameterEvent
        {
            public int Id { get; set; }
            public int Value { get; set; }
        }

        static EPServiceProvider engine;

        const int NParameters = 50;

        static void Main(string[] args)
        {
            engine = EPServiceProviderManager.GetDefaultProvider();

            engine.EPAdministrator.Configuration.AddEventType("ParameterEvent", typeof(ParameterEvent));

            CreateEPLStatement();

            Task.Factory.StartNew(() => SimulateParameterChange(), TaskCreationOptions.LongRunning);

            System.Threading.Thread.Sleep(int.MaxValue);
        }

        static void CreateEPLStatement()
        {
            const int windowLength = 3;

            var sb = new StringBuilder();

            for (int i = 1; i <= NParameters; i++)
            {
                sb.AppendFormat("(AVG(ParameterEvent{0}.Value) = 1) OR ", i);
            }

            var rule = sb.ToString();
            rule = rule.Remove(rule.Length - 4, 4);
            sb.Clear();

            for (int paramId = 1; paramId <= NParameters; paramId++)
            {
                sb.AppendFormat("ParameterEvent(Id = {0}).win:time({1} sec) AS ParameterEvent{0}, ", paramId, windowLength);
                //sb.AppendFormat("ParameterEvent(Id = {0}).std:lastevent() AS ParameterEvent{0}, ", paramId, windowLength);
            }

            var selectSources = sb.ToString();
            selectSources = selectSources.Remove(selectSources.Length - 2, 2);

            var eplStr = string.Format("SELECT ({0}) AS BooleanValue FROM {1}", rule, selectSources);

            var statementName = string.Format("statement1");

            var statement = engine.EPAdministrator.CreateEPL(eplStr, statementName, null);
            statement.Start();
            statement.Events += Statement_Events;
        }


        static bool PrevState = false;
        static void Statement_Events(object sender, UpdateEventArgs e)
        {
            var underlying = e.NewEvents[0].Underlying as Dictionary<string, object>;

            if (underlying.Last().Value == null)
            {
                Console.WriteLine("Statement value is unknown");
            }
            else
            {
                var statementValue = (bool)underlying.First().Value;

                if (statementValue != PrevState)
                {
                    PrevState = statementValue;
                    Console.WriteLine("Statement is {0}", statementValue);
                }
            }
        }

        static void SimulateParameterChange()
        {
            int counter = 0;

            while (true)
            {
                for (int i = 1; i <= NParameters; i++)
                {
                    var evt = new ParameterEvent();

                    evt.Id = i;

                    if (i == 1 && (counter/4) % 2 == 0)
                    {
                        evt.Value = 1;
                    }
                    else
                    {
                        evt.Value = 0;
                    }

                    engine.EPRuntime.SendEvent(evt);

                    System.Threading.Thread.Sleep(1000 / NParameters);
                }

                counter++;
            }
        }
    }
}
Isaac
  • 2,332
  • 6
  • 33
  • 59
  • The code is joining 50 streams? There is no where-clause? Do you know what you get if you join 50 tables in an SQL database without where-clause ---> possibly a very very large number of rows. I think what you want is 50 independent statements. Or two statements as is listed here, http://espertech.com/esper/solution_patterns.php#expiry-3 – user650839 Jun 23 '17 at 20:59
  • @user650839: I am actually doing the filtering using stream-level filters which based on [Nesper documentation](http://www.espertech.com/esper/release-5.5.0/esper-reference/html_single/index.html#perf-tips-5) is highly optimized compared to the WHERE cause. – Isaac Jun 23 '17 at 21:13
  • @user650839: The link you provided seems to be interesting. The problem is the statements in my application is more complex and I have provided a very simplified example here. I have to figure out whether there is a way to map those statements into basic EPL statement while employing `partitioned context` for filtering. – Isaac Jun 23 '17 at 21:15
  • @user650839: Using `partitioned context` can help in the above example, but not always. For instance, if the above statement was a chain of `AND`s, it was not possible to break them into simple statements. All of them had to be referenced in single EPL statement. – Isaac Jun 23 '17 at 21:25
  • Doubt that is true. I suggest to post the requirement and not some 50-stream join. – user650839 Jun 23 '17 at 22:53

1 Answers1

0

See http://espertech.com/esper/solution_patterns.php#expiry-3 The "having" clause for triggering.

user650839
  • 2,594
  • 1
  • 13
  • 9