RuES - Expression Evaluation as Service

RuES is a minimal JMES expression evaluation side-car, that uses JMESPath, and it can handle arbitrary JSON. Which effectively makes it general purpose logical expression evaluation engine, just like some Python libraries that used to evaluate logical expression. This in turn can allow you implement complex stuff like Rule engine, RBAC, or Policy engines etc.

Here is what makes RuES special:

Why?

A very obvious question to ask might be, why RuES and why not just use a library? RuES can be beneficial in large scale scenarios with following benefits:

Usage

Make sure you have rules.hjson in your current working directory when launching rues. Given following example rules:

hjson { example_one: "value == `2`" example_two: "a.b" }

Each rule is exposed under /eval/{rule_name} as POST endpoint, which in turn can be posted payload to evaluate the expression. Simple use curl to test:

```

curl -X POST http://localhost:8080/eval/exampleone -H 'Content-Type: application/json' -d '{"value": 2}' {"Success":{"expression":"value == 2","name":"exampleone","istruthy":true,"value":true}} curl -X POST http://localhost:8080/eval/exampletwo -H 'Content-Type: application/json' -d '{"a": {"b": "Hello"}}' {"Success":{"expression":"a.b","name":"exampletwo","istruthy":true,"value":"Hello"}} ```

Response object contains Success if evaluation was successful. e.g. json { "Success": { "name": "filter_active", "expression": "[?isActive] | length(@)", "is_truthy": true, "value": 2 } }

Response will have an Error if there was an error in expression or there was some violation while evaluating the expression (in which case reason will contain a reason):

json { "Error": { "name": "filter_registered", "expression": "[?matched('^201\\d', registered)] | length(@)", "reason": "Runtime error: Call to undefined function matched (line 0, column 9)\n[?matched('^201\\d', registered)] | length(@)\n ^\n" } }

Response will have a NotFound if the specified rule is not found:

json { "NotFound": { "name": "filter_register" } }

Batch Rules API

Many times you need evaluate a set of rules against a payload. RuES supports evaluating a context against multiple rules using batch API. Given the rules file:

hjson { example_one: "c == `2`" example_two: "a.b" }

One can invoke batch api by simply invoking /eval with POST data of: json { "context": { "c": 3, "a": { "b": true } }, "rules": ["example_one", "example_two", "example_three"] }

The rules will be evaluated in sequence of order they were passed in, and server will return an array response:

json [ {"Success":{"expression":"c == `2`","name":"example_one","is_truthy":false,"value":false}}, {"Success":{"expression":"a.b","name":"example_two","is_truthy":true,"value":true}}, {"NotFound":{"name":"example_three"}} ]

Additional functions

In addition to built-in functions of JMES, there additional are following additional functions:

Configuration variables

Benchmarks

My brief stress testing shows with a single CPU core (single worker), 3 rules, and payload size of 1.6 KB. Server was easily able to handle 10K RPS (even with sustained load) under 19 MB of RSS memory footprint, and a p99 of 4ms.

$ cat vegeta_attack.txt | vegeta attack -duration=10s -rate=10000 | vegeta report Requests [total, rate, throughput] 100000, 10000.20, 9999.80 Duration [total, attack, wait] 10s, 10s, 394.927µs Latencies [min, mean, 50, 90, 95, 99, max] 107.266µs, 811.954µs, 285.329µs, 2.128ms, 2.654ms, 4.517ms, 12.373ms Bytes In [total, mean] 9566673, 95.67 Bytes Out [total, mean] 166000000, 1660.00 Success [ratio] 100.00% Status Codes [code:count] 200:100000 Error Set:

With two CPU cores (two workers), the results were even better: $ cat vegeta_attack.txt | vegeta attack -duration=10s -rate=10000 | vegeta report Requests [total, rate, throughput] 100000, 10000.30, 10000.08 Duration [total, attack, wait] 10s, 10s, 217.653µs Latencies [min, mean, 50, 90, 95, 99, max] 111.479µs, 270.125µs, 219.274µs, 413.215µs, 564.181µs, 1.021ms, 8.184ms Bytes In [total, mean] 9566673, 95.67 Bytes Out [total, mean] 166000000, 1660.00 Success [ratio] 100.00% Status Codes [code:count] 200:100000 Error Set:

All the rules, and data has been shipped under stress_test. Feel free to share your results, and I will be more than happy to include your results.