My case is: If the sum of withdraw of my bank account is greater than 1000$ within any continues 10 mins, E.g., 0m-10m and then 0m1s-10m1s, then 0m2s-10m2s, which is a sliding time window, the bank system should send me a warning message.
So, can anyone help me writing the rule by Drools?
My initial idea is below:
when
Number( $total : intValue, intValue >= 1000)
from accumulate (Withdraw ($money : money)
over window:time( 10m )
from entry-point ATMEntry,
sum($money))
then
System.out.println("Warning! too more withdraw:"+$total);
However, it will just check the upcoming 10m for one time. After the first 10m, no matter how many withdraw object that I insert to ATMEntry are, I will not receive the warning message.
And if I fire above rule interval in different session, for example every 1m, it makes me confused about how to insert withdraw object to ATMEntry for different session.
So, is that possible to use Drools to in my case?
Thanks,
You have to trigger the evaluation by another Withdraw event:
when
Withdraw()
Number( $total : intValue >= 1000)
from accumulate (Withdraw ($money : money)
over window:time( 10m )
from entry-point ATMEntry,
sum($money))
then
System.out.println( "Warning! " + $total );
end
If you need the individual events it might be better to collect them into a list. This can also help to close one interval with excess withdrawal and open another one. It depends on the details of the spec: when to raise an alaram, and when to raise - or raise not - the next alarm.
Related
I am simulating an evacuation and would like to pick up people with busses. I would like to select "exact quantity(wait for)" because partially empty busses would be highly inefficient. However, the problem Im running into is that some people may be left behind if the last group is smaller than the specified bus capacity. The bus will then not leave, because it's not filled up.
Does anybody know a way of using conditioning to get around the problem? I can't just modify the total amount of waiting people to fill all the busses. This is because I have different groups of people entering different types of vehicles.
something like
exact quantity (wait for) - IF "waiting area" contains > 12 agents
quantity (if available) - IF "waiting area" contains ≤ 12 agents
Thanks
Keep your Pickup blocks with "exact quantity (wait for)"; the quantity is a dynamic property (re-evaluated every time an agent enters the Pickup block), so you can track the number remaining to pickup in a variable (set once you know how many are to pickup in total, and decremented for every pickup) and use a conditional statement (Java ternary expression) in your Pickup block quantity.
If your buses take 12 passengers as in your question, and your left-to-pickup is an int variable called leftToPickup, the expression would be
leftToPickup < 12 ? leftToPickup : 12
(read as 'if leftToPickup is less than 12, the quantity expression evaluates to leftToPickup, otherwise it evaluates to 12').
Screenshots of a 'minimal example' model to do this below.
My question : is it possible to have a trigger on that item that will be activated if there's a difference of xx% between the two last queries ?
Example :
Query at 01:00 -> 2000 users connected
Query at 01:10 -> 2100 users, difference is positive, we don't care
Query at 01:20 -> 2050 users, -50 users, around 2-3%, no big deal
Query at 01:30 -> 800 users, around 60% less connections, there's something wrong here
Is it possible to have a trigger that activates when the difference is, let's say, 20% negative ?
You can use the abschange function:
The amount of absolute difference between last and previous values
to alert for both positive and negative changes.
Or you can use the last function to get the latest values you need:
For example:
last() is always equal to last(#1)
last(#3) - third most recent value (not three latest values)
In both cases you need to compute the % value in your trigger with the usual proportion:
older_value:100 = newer_value:x
I want to create a Grafana 'singlestat' Panel that shows the Uptime or SLA 'percentage', based on the presence or absence of test failure metrics.
I already have the appropriate metric, e2e_tests_failure_count, for different test frameworks.
This means that the following query returns the sum of observed test failures:
sum(e2e_tests_failure_count{kubernetes_name=~"test-framework-1|test-framework-2|test-framework-3",kubernetes_namespace="platform-edge"})
I already managed to create a graph that is "1" if everything is ok and "0" if there are any test failures:
1 - clamp_max(sum(e2e_tests_failure_count{kubernetes_name=~"test-framework-1|test-framework-1|test-framework-1",kubernetes_namespace="platform-edge"}), 1)
I now want to have a single percentage value that shows the "uptime" (= amount of time the environment was 'helathy') over a period of time, e.g. the last 5 days. Something like "99.5%" or, more appropriate for the screenshot, "65%".
I tried something like this:
(1 - clamp_max(sum(e2e_tests_failure_count{kubernetes_name=~"service-cvi-e2e-tests|service-svhb-e2e-tests|service-svh-roundtrip-e2e-tests",kubernetes_namespace="platform-edge"}), 1))[5d]
but this only results in parser errors. Googling didn't really get me any further, so I'm hoping I can find help here :)
Just figured this out and I believe it is producing correct results. You have to use recording rules because you cannot create a range vector from the instance vector result of a function in a single query, as you have already discovered (you get a parse error). So we record the function result (which will be an instance vector) as a new time series and use that as the metric name in a different query, where you can then add the [5d] to select a range.
We run our tests multiple times per minute against all our services, and each service ("service" is a label where each service's name is the label value) has a different number of tests associated with it, but if any of the tests for a given service fails, we consider that a "down moment". (The number of test failures for a given service is captured in the metrics with the status="failure" label value.) We clamp the number of failures to 1 so we only have zeroes and ones for our values and can therefore convert a "failure values time series" into a "success values time series" instead, using an inequality operator and the bool modifier. (See this post for a discussion about the use of bool.) So the result of the first recorded metric is 1 for every service where all its tests succeeded during that scrape interval, and 0 where there was at least one test failure for that service.
If the number of failures for a service is > 0 for all the values returned for any given minute, we consider that service to be "down" for that minute. (So if we have both a failure and a success in a given minute, that does not count as downtime.) That is why we have the second recorded metric to produce the actual "up for this minute" boolean values. The second recorded metric builds on the first, which is OK since the Prometheus documentation says the recorded metrics are run in series within each group.
So "Uptime" for any given duration is the sum of "up for this minute" values (i.e. 1 for each minute up) divided by the total number of minutes in the duration, whatever that duration happens to be.
Since we have defined a recorded metric named "minute_up_bool", we can then create an uptime graph over whatever range we want. (BTW, recorded metrics are only generated for times after you first define them, so you won't get yesterday's time series data included in a recorded metric you define today.) Here's a query you can put in Grafana to show uptime % over a moving window of the last 5 days:
sum_over_time(minute_up_bool[5d]) * 100 / (5 * 24 * 60)
So this is our recording rule configuration:
groups:
- name: uptime
interval: 1m
# Each rule here builds on the previous one.
rules:
# Get test results as pass/fail => 1/0
# (label_replace() removes confusing status="failure" label value)
- record: test_success_bool
expr: label_replace(clamp_max(test_statuses_total{status="failure"}, 1), "status", "", "", "") != bool 1
# Get the uptime as 1 minute range where the sum of successes is not zero
- record: minute_up_bool
expr: clamp_max(sum_over_time(test_success_bool[1m]), 1)
You have to use recording rules because you cannot create a range
vector from the instance vector result of a function in a single
query
Actually you can, by using a subquery:
(...some complicated instant subexpression...)[5d:1m]
This gives the same results as if you'd used a recording rule with a 1 minute evaluation interval. The recording rule is still beneficial though, as it avoids recomputing the subexpression every time.
The problem I'm trying to solve
Get stock ticks
Always consider latest stock price
Each x second take a snapshot of ticks and send for processing
So I have an Observable source of stock ticks. It sends only the ticks for stocks I'm interested in. What I need to do is to receive these stock prices, and after each x seconds (for the sake of example let's say every 3 seconds) send a snapshot of prices for processing. If within 3 seconds I receive 2 ticks for the same stock, I only need the latest tick. This processing is compute heavy, so if possible I would like to avoid sending same stock price for processing twice.
To bring an example.
Let's say at the beginning of sequence I received 2 ticks -> MSFT:1$, GOOG:2$.
In the next 3 seconds I receive nothing, so MSFT & GOOG ticks should be sent for processing.
Now the next second I receive new ticks -> MSFT:1$, GOOG:3$, INTL:3$
Again let's assume within next 3 seconds nothing comes in.
Here, since MSFT price didn't change (it's still 1$), only GOOG & INTL should be sent for processing.
And this repeats throughout a day.
Now I think Rx helps to solve this kind of problems in easy & elegant way. But I'm having a problem to have the proper queries.
This is what I have so far, will try to explain what it does and what's the issue with it
var finalQuery =
from priceUpdate in **Observable<StockTick>**
group priceUpdate by priceUpdate.Stock into grouped
from combined in Observable.Interval(TimeSpan.FromSeconds(3))
.CombineLatest(grouped, (t, pei) => new { PEI = pei, Interval = t })
group combined by new { combined.Interval } into combined
select new
{
Interval = combined.Key.Interval,
PEI = combined.Select(c => new StockTick(c.PEI.Stock, c.PEI.Price))
};
finalQuery
.SelectMany(combined => combined.PEI)
.Distinct(pu => new { pu.Stock, pu.Price })
.Subscribe(priceUpdate =>
{
Process(priceUpdate);
});
public class StockTick
{
public StockTick(string stock, decimal price)
{
Stock = stock;
Price = price;
}
public string Stock {get;set;}
public decimal Price {get;set;}
}
So this gets the stock price, groups it by stock, then combines latest from this grouped sequence with Observable.Interval. This way I'm trying to ensure only latest ticks for a stock are processed and it fires up every 3 seconds.
Then again it groups it up by interval this time, as a result I have group of sequences for each 3 second intervals that passed.
And as a last step, I flatten this sequence to sequence of stock price updates using SelectMany and also I'm applying Distinct to ensure same price for the same stock is not processed twice.
There are 2 issues with this query I don't like. First is I don't really like double group by's - is there any way to avoid it ? Second - with this approach I have to process prices one by one, what I really would like to have is snapshots - that is within 3 seconds whatever I have I will bundle and send for processing, but can't figure out how to bundle.
I'll be happy for suggestions to solve this problem other way, but I would prefer to stay within Rx, unless there is really something much much better.
A couple things:
You'll want to take advantage of the Sample operator:
You probably want DistinctUntilChanged instead of Distinct. If you use Distinct, then if MSFT goes from $1, to $2 then back to $1, you won't get an event on the third tick.
I imagine your solution will look something like this:
IObservable<StockTick> source;
source
.GroupBy(st => st.Stock)
.Select(stockObservable => stockObservable
.Sample(TimeSpan.FromSeconds(3))
.DistinctUntilChanged(st => st.Price)
)
.Merge()
.Subscribe(st => Process(st));
EDIT (Distinct performance problems):
Each Distinct operator has to maintain within it, the full distinct history. If you have a high-priced stock, for example AMZN, which so far today has ranged from $958-$974, then you could end up with a lot of data. That's ~1600 possible data points that have to sit in memory until you unsubscribe from the Distinct. It also will eventually degrade performance, as each AMZN tick has to be compared to the 1600-ish present data points before going through. If this is for a long-running process (spanning multiple trading days), then you'll end up with even more data points.
Given N stocks, you have N Distinct operators that need to operate accordingly. Multiply that behavior by N stocks, and you have an ever-increasing problem.
All,
Am using Drools Workbench 6.2.0.Final to declaratively create Guided Rules...
My situation is something like this:
Have a Loan Data Object with the following attributes:
state - String
amount - double
interestRate - double
message - String
requirement - boolean
The auto-generated drl file is:
rule "Arizona"
when
loan : Loan( state == "Arizona", amount >= 1000 , amount <= 3000,
interestRate >= 0.15, interestRate <= 0.50 )
then
loan.setRequirement( true );
end
Question(s):
(1) How can I declaratively use the Guided Rules Editor to set the following when this rule fails:
loan.setMessage( "Allowed values for amount should be in the range of 1000 to 3000");
or
loan.setMessage( "Allowed values for interest rate values should be in the range of 15% to 50%" );
(2) Is there a way to declaratively customize the SOAP Response:
e.g.
<requirement>true</true>
or
<requirement>false</requirement>
<message>Allowed values for amount should be in the range of 1000 to 3000</message>
or
<requirement>false</requirement>
<message>Allowed values for interest rate values should be in the range of 15% to 50%</message>
Do not want to do this programmatically...
Thanks to all...
There is a fundamental misunderstanding in the way this question is formulated as there is no such state as "when this rule fails". A rule fires when its conditions match for a matched set of facts, which may happen any number of times for the current set of facts in Working Memory.
There are, of course, scenarios when a rule does not fire - but even with a simple rule such as "Arizona" there is more than one reason why it fails.
There may not be any Loan fact in WM.
There may be a Loan fact in WM, but with state "Texas" (or any of the other 48 possibilities).
The value of either or both of amount and interest rate aren't in the expected bracket.
There is no way a program could know that you are interested in #3 alone (to say nothing about the detailed analysis, i.e., is it just one or both values). But a program can be made to know: just implement all the rules detecting the reasons for failure, the ones you are interested in, which can be done (I think) with a couple of rules for #3.
It seems that your design of Loan foresees only a single message, but this can be fixed.
Further reading: a white paper on rule design patterns, section "Handling Failure to Match" which is too long as an answer.