durable_rules 0.33.13 → 0.34.01

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/librb/engine.rb CHANGED
@@ -3,20 +3,25 @@ require "timers"
3
3
  require_relative "../src/rulesrb/rules"
4
4
 
5
5
  module Engine
6
-
6
+ @@timers = nil
7
7
  class Closure
8
- attr_reader :handle, :ruleset_name, :_timers, :_branches, :_messages, :_facts, :_retract
8
+ attr_reader :host, :handle, :ruleset_name, :_timers, :_cancelled_timers, :_branches, :_messages, :_queued_messages, :_facts, :_retract
9
9
  attr_accessor :s
10
10
 
11
- def initialize(state, message, handle, ruleset_name)
11
+ def initialize(host, state, message, handle, ruleset_name)
12
12
  @s = Content.new(state)
13
13
  @ruleset_name = ruleset_name
14
14
  @handle = handle
15
+ @host = host
15
16
  @_timers = {}
17
+ @_cancelled_timers = {}
16
18
  @_messages = {}
19
+ @_queued_messages = {}
17
20
  @_branches = {}
18
21
  @_facts = {}
19
22
  @_retract = {}
23
+ @_start_time = Time.now
24
+ @_completed = false
20
25
  if message.kind_of? Hash
21
26
  @m = message
22
27
  else
@@ -53,11 +58,47 @@ module Engine
53
58
  message_list << message
54
59
  end
55
60
 
56
- def start_timer(timer_name, duration)
57
- if @_timers.key? timer_name
58
- raise ArgumentError, "Timer with name #{timer_name} already added"
61
+ def queue(ruleset_name, message)
62
+ if message.kind_of? Content
63
+ message = message._d
64
+ end
65
+
66
+ if !(message.key? :sid) && !(message.key? "sid")
67
+ message[:sid] = @s.sid
68
+ end
69
+
70
+ message_list = []
71
+ if @_queued_messages.key? ruleset_name
72
+ message_list = @_queued_messages[ruleset_name]
59
73
  else
60
- @_timers[timer_name] = duration
74
+ @_queued_messages[ruleset_name] = message_list
75
+ end
76
+ message_list << message
77
+ end
78
+
79
+ def start_timer(timer_name, duration, timer_id = nil)
80
+ if !timer_id
81
+ timer_id = timer_name
82
+ end
83
+
84
+ if @_timers.key? timer_id
85
+ raise ArgumentError, "Timer with id #{timer_id} already added"
86
+ else
87
+ timer = {:sid => @s.sid, :id => timer_id, :$t => timer_name}
88
+ @_timers[timer_id] = [timer, duration]
89
+ end
90
+ end
91
+
92
+ def cancel_timer(timer_name, timer_id = nil)
93
+ if !timer_id
94
+ timer_id = timer_name
95
+ end
96
+
97
+ if @_cancelled_timers.key? timer_id
98
+ raise ArgumentError, "Timer with id #{timer_id} already cancelled"
99
+ else
100
+ timer = {:sid => @s.sid, :id => timer_id, :$t => timer_name}
101
+ @_cancelled_timers[timer_id] = timer
61
102
  end
62
103
  end
63
104
 
@@ -107,6 +148,22 @@ module Engine
107
148
  fact_list << fact
108
149
  end
109
150
 
151
+ def renew_action_lease()
152
+ if Time.now - @_start_time < 10000
153
+ @_start_time = Time.now
154
+ @host.renew_action_lease(@ruleset_name, @s.sid)
155
+ end
156
+ end
157
+
158
+ def has_completed()
159
+ if Time.now - @_start_time > 10000
160
+ @_completed = true
161
+ end
162
+ value = @_completed
163
+ @_completed = true
164
+ value
165
+ end
166
+
110
167
  private
111
168
 
112
169
  def handle_property(name, value=nil)
@@ -146,7 +203,11 @@ module Engine
146
203
  def handle_property(name, value=nil)
147
204
  name = name.to_s
148
205
  if name.end_with? '='
149
- @_d[name[0..-2]] = value
206
+ if value == nil
207
+ @_d.delete(name[0..-2])
208
+ else
209
+ @_d[name[0..-2]] = value
210
+ end
150
211
  nil
151
212
  elsif name.end_with? '?'
152
213
  @_d.key? name[0..-2]
@@ -167,12 +228,16 @@ module Engine
167
228
 
168
229
  class Promise
169
230
  attr_accessor :root
170
-
171
231
  def initialize(func)
172
232
  @func = func
173
233
  @next = nil
174
234
  @sync = true
175
235
  @root = self
236
+ @timers = Timers::Group.new
237
+
238
+ if func.arity > 1
239
+ @sync = false
240
+ end
176
241
  end
177
242
 
178
243
  def continue_with(next_func)
@@ -193,8 +258,9 @@ module Engine
193
258
  begin
194
259
  @func.call c
195
260
  rescue Exception => e
196
- complete.call e
197
- return
261
+ puts "unexpected error #{e}"
262
+ puts e.backtrace
263
+ c.s.exception = e.to_s
198
264
  end
199
265
 
200
266
  if @next
@@ -204,17 +270,37 @@ module Engine
204
270
  end
205
271
  else
206
272
  begin
207
- @func.call c, -> e {
273
+ time_left = @func.call c, -> e {
208
274
  if e
209
- complete.call e
275
+ c.s.exception = e.to_s
276
+ complete.call nil
210
277
  elsif @next
211
278
  @next.run c, complete
212
279
  else
213
280
  complete.call nil
214
281
  end
215
282
  }
283
+
284
+ if time_left && (time_left.kind_of? Integer)
285
+ max_time = Time.now + time_left
286
+ my_timer = @timers.every(5) {
287
+ if Time.now > max_time
288
+ my_timer.cancel
289
+ c.s.exception = "timeout expired"
290
+ complete.call nil
291
+ else
292
+ c.renew_action_lease()
293
+ end
294
+ }
295
+ Thread.new do
296
+ loop { @timers.wait }
297
+ end
298
+ end
216
299
  rescue Exception => e
217
- complete.call e
300
+ puts "unexpected error #{e}"
301
+ puts e.backtrace
302
+ c.s.exception = e.to_s
303
+ complete.call nil
218
304
  end
219
305
  end
220
306
  end
@@ -296,6 +382,10 @@ module Engine
296
382
  Rules.assert_event @handle, JSON.generate(message)
297
383
  end
298
384
 
385
+ def queue_event(sid, ruleset_name, message)
386
+ Rules.queue_event @handle, sid.to_s, ruleset_name.to_s, JSON.generate(message)
387
+ end
388
+
299
389
  def start_assert_event(message)
300
390
  return Rules.start_assert_event @handle, JSON.generate(message)
301
391
  end
@@ -308,11 +398,14 @@ module Engine
308
398
  return Rules.start_assert_events @handle, JSON.generate(messages)
309
399
  end
310
400
 
311
- def start_timer(sid, timer_name, timer_duration)
312
- timer = {:sid => sid, :id => rand(1000000000), :$t => timer_name}
401
+ def start_timer(sid, timer, timer_duration)
313
402
  Rules.start_timer @handle, sid.to_s, timer_duration, JSON.generate(timer)
314
403
  end
315
404
 
405
+ def cancel_timer(sid, timer)
406
+ Rules.cancel_timer @handle, sid.to_s, JSON.generate(timer)
407
+ end
408
+
316
409
  def assert_fact(fact)
317
410
  Rules.assert_fact @handle, JSON.generate(fact)
318
411
  end
@@ -350,9 +443,13 @@ module Engine
350
443
  end
351
444
 
352
445
  def get_state(sid)
353
- JSON.parse Rules.get_state(@handle, sid)
446
+ JSON.parse Rules.get_state(@handle, sid.to_s)
354
447
  end
355
448
 
449
+ def renew_action_lease(sid)
450
+ Rules.renew_action_lease @handle, sid.to_s
451
+ end
452
+
356
453
  def Ruleset.create_rulesets(parent_name, host, ruleset_definitions, state_cache_size)
357
454
  branches = {}
358
455
  for name, definition in ruleset_definitions do
@@ -385,22 +482,29 @@ module Engine
385
482
  complete.call nil
386
483
  end
387
484
 
388
- def dispatch(complete)
485
+ def dispatch(complete, async_result = nil)
389
486
  result_container = {}
390
487
  action_handle = nil
391
488
  action_binding = nil
392
489
  state = nil
393
- begin
394
- result = Rules.start_action @handle
395
- if result
396
- state = JSON.parse result[0]
397
- result_container = {:message => JSON.parse(result[1])}
398
- action_handle = result[2]
399
- action_binding = result[3]
490
+ if async_result
491
+ state = async_result[0]
492
+ result_container = {:message => JSON.parse(async_result[1])}
493
+ action_handle = async_result[2]
494
+ action_binding = async_result[3]
495
+ else
496
+ begin
497
+ result = Rules.start_action @handle
498
+ if result
499
+ state = JSON.parse result[0]
500
+ result_container = {:message => JSON.parse(result[1])}
501
+ action_handle = result[2]
502
+ action_binding = result[3]
503
+ end
504
+ rescue Exception => e
505
+ complete.call e
506
+ return
400
507
  end
401
- rescue Exception => e
402
- complete.call e
403
- return
404
508
  end
405
509
 
406
510
  while result_container.key? :message do
@@ -410,16 +514,36 @@ module Engine
410
514
  end
411
515
 
412
516
  result_container.delete :message
413
- c = Closure.new state, message, action_handle, @name
517
+ c = Closure.new @host, state, message, action_handle, @name
518
+
519
+ if result_container.key? :async
520
+ result_container.delete :async
521
+ end
522
+
414
523
  @actions[action_name].run c, -> e {
524
+ if c.has_completed
525
+ return
526
+ end
527
+
415
528
  if e
416
529
  Rules.abandon_action @handle, c.handle
417
530
  complete.call e
418
531
  else
419
532
  begin
420
- for timer_name, timer_duration in c._timers do
421
- start_timer c.s.sid, timer_name, timer_duration
533
+ for timer_id, timer in c._cancelled_timers do
534
+ cancel_timer c.s.sid, timer
535
+ end
536
+
537
+ for timer_id, timer_duration in c._timers do
538
+ start_timer c.s.sid, timer_duration[0], timer_duration[1]
422
539
  end
540
+
541
+ for ruleset_name, messages in c._queued_messages do
542
+ for message in messages do
543
+ queue_event c.s.sid, ruleset_name, message
544
+ end
545
+ end
546
+
423
547
  binding = 0
424
548
  replies = 0
425
549
  pending = {action_binding => 0}
@@ -465,6 +589,7 @@ module Engine
465
589
  else
466
590
  pending[binding] = replies
467
591
  end
592
+
468
593
  for binding, replies in pending do
469
594
  if binding != 0
470
595
  if binding != action_binding
@@ -472,17 +597,24 @@ module Engine
472
597
  else
473
598
  new_result = Rules.complete_and_start_action @handle, replies, c.handle
474
599
  if new_result
475
- result_container[:message] = JSON.parse new_result
600
+ if result_container.key? :async
601
+ dispatch -> e {}, [state, new_result, action_handle, action_binding]
602
+ else
603
+ result_container[:message] = JSON.parse new_result
604
+ end
476
605
  end
477
606
  end
478
607
  end
479
608
  end
480
609
  rescue Exception => e
481
610
  Rules.abandon_action @handle, c.handle
611
+ puts "internal error #{e}"
612
+ puts e.backtrace
482
613
  complete.call e
483
614
  end
484
615
  end
485
616
  }
617
+ result_container[:async] = true
486
618
  end
487
619
  complete.call nil
488
620
  end
@@ -533,8 +665,7 @@ module Engine
533
665
  if parent_triggers
534
666
  for parent_trigger_name, trigger in parent_triggers do
535
667
  parent_trigger_name = parent_trigger_name.to_s
536
- trigger_name = parent_trigger_name[parent_trigger_name.rindex('.')..-1]
537
- triggers["#{qualified_name}.#{trigger_name}"] = trigger
668
+ triggers["#{qualified_name}.#{parent_trigger_name}"] = trigger
538
669
  end
539
670
  end
540
671
 
@@ -954,6 +1085,10 @@ module Engine
954
1085
  get_ruleset(ruleset_name).assert_state state
955
1086
  end
956
1087
 
1088
+ def renew_action_lease(ruleset_name, sid)
1089
+ get_ruleset(ruleset_name).renew_action_lease sid
1090
+ end
1091
+
957
1092
  def register_rulesets(parent_name, ruleset_definitions)
958
1093
  rulesets = Ruleset.create_rulesets(parent_name, self, ruleset_definitions, @state_cache_size)
959
1094
  for ruleset_name, ruleset in rulesets do
@@ -976,8 +1111,7 @@ module Engine
976
1111
  dispatch_ruleset = -> c {
977
1112
 
978
1113
  callback = -> e {
979
- puts "internal error #{e}" if e
980
- if index % 10 == 0
1114
+ if index % 5 == 0
981
1115
  index += 1
982
1116
  timers.after 0.01, &dispatch_ruleset
983
1117
  else
@@ -987,9 +1121,8 @@ module Engine
987
1121
  }
988
1122
 
989
1123
  timers_callback = -> e {
990
- puts "internal error #{e}" if e
991
- if index % 10 == 0 && @ruleset_list.length > 0
992
- ruleset = @ruleset_list[(index / 10) % @ruleset_list.length]
1124
+ if index % 5 == 0 && @ruleset_list.length > 0
1125
+ ruleset = @ruleset_list[(index / 5) % @ruleset_list.length]
993
1126
  ruleset.dispatch_timers callback
994
1127
  else
995
1128
  callback.call e
@@ -1004,7 +1137,7 @@ module Engine
1004
1137
  end
1005
1138
  }
1006
1139
 
1007
- timers.after 0.001, &dispatch_ruleset
1140
+ timers.after 0.01, &dispatch_ruleset
1008
1141
  Thread.new do
1009
1142
  loop { timers.wait }
1010
1143
  end
data/src/rules/events.c CHANGED
@@ -793,7 +793,7 @@ static unsigned int isMatch(ruleset *tree,
793
793
  *propertyMatch = compareDouble(currentProperty->value.i, rightProperty->value.i, alphaOp);
794
794
  break;
795
795
  case OP_DOUBLE_DOUBLE:
796
- *propertyMatch = compareDouble(currentProperty->value.i, rightProperty->value.d, alphaOp);
796
+ *propertyMatch = compareDouble(currentProperty->value.d, rightProperty->value.d, alphaOp);
797
797
  break;
798
798
  case OP_DOUBLE_STRING:
799
799
  {
@@ -869,7 +869,6 @@ static unsigned int isMatch(ruleset *tree,
869
869
  if (releaseRightState) {
870
870
  free(rightState);
871
871
  }
872
-
873
872
  return result;
874
873
  }
875
874
 
@@ -1439,7 +1438,6 @@ static unsigned int handleTimers(void *handle,
1439
1438
 
1440
1439
  commands[*commandCount] = command;
1441
1440
  ++*commandCount;
1442
-
1443
1441
  result = handleMessage(handle,
1444
1442
  NULL,
1445
1443
  reply->element[i]->str,
@@ -1711,6 +1709,10 @@ unsigned int startAction(void *handle,
1711
1709
  *state = reply->element[1]->str;
1712
1710
  *messages = reply->element[2]->str;
1713
1711
  actionContext *context = malloc(sizeof(actionContext));
1712
+ if (!context) {
1713
+ return ERR_OUT_OF_MEMORY;
1714
+ }
1715
+
1714
1716
  context->reply = reply;
1715
1717
  context->rulesBinding = rulesBinding;
1716
1718
  *actionHandle = context;
@@ -1771,7 +1773,12 @@ unsigned int completeAction(void *handle,
1771
1773
  return result;
1772
1774
  }
1773
1775
 
1774
- result = executeBatch(rulesBinding, commands, commandCount);
1776
+ result = executeBatch(rulesBinding, commands, commandCount);
1777
+ if (result != RULES_OK) {
1778
+ //reply object should be freed by the app during abandonAction
1779
+ return result;
1780
+ }
1781
+
1775
1782
  freeReplyObject(reply);
1776
1783
  free(actionHandle);
1777
1784
  return result;
@@ -1840,6 +1847,16 @@ unsigned int abandonAction(void *handle, void *actionHandle) {
1840
1847
  return RULES_OK;
1841
1848
  }
1842
1849
 
1850
+ unsigned int queueMessage(void *handle, char *sid, char *destination, char *message) {
1851
+ void *rulesBinding;
1852
+ unsigned int result = resolveBinding(handle, sid, &rulesBinding);
1853
+ if (result != RULES_OK) {
1854
+ return result;
1855
+ }
1856
+
1857
+ return registerMessage(rulesBinding, destination, message);
1858
+ }
1859
+
1843
1860
  unsigned int startTimer(void *handle, char *sid, unsigned int duration, char *timer) {
1844
1861
  void *rulesBinding;
1845
1862
  unsigned int result = resolveBinding(handle, sid, &rulesBinding);
@@ -1849,3 +1866,24 @@ unsigned int startTimer(void *handle, char *sid, unsigned int duration, char *ti
1849
1866
 
1850
1867
  return registerTimer(rulesBinding, duration, timer);
1851
1868
  }
1869
+
1870
+ unsigned int cancelTimer(void *handle, char *sid, char *timer) {
1871
+ void *rulesBinding;
1872
+ unsigned int result = resolveBinding(handle, sid, &rulesBinding);
1873
+ if (result != RULES_OK) {
1874
+ return result;
1875
+ }
1876
+
1877
+ return removeTimer(rulesBinding, timer);
1878
+ }
1879
+
1880
+ unsigned int renewActionLease(void *handle, char *sid) {
1881
+ void *rulesBinding;
1882
+ unsigned int result = resolveBinding(handle, sid, &rulesBinding);
1883
+ if (result != RULES_OK) {
1884
+ return result;
1885
+ }
1886
+
1887
+ return updateAction(rulesBinding, sid);
1888
+ }
1889
+