hakuban 0.6.2 → 0.6.3
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.
- checksums.yaml +4 -4
- data/lib/hakuban/async.rb +11 -24
- data/lib/hakuban/event-queue.rb +0 -2
- data/lib/hakuban/hakuban.rb +6 -1
- data/lib/hakuban/manager.rb +164 -42
- data/lib/hakuban/thread.rb +9 -22
- data/lib/hakuban/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e6822f983334fd24d18385b13f77352026609b85e2f386760b00bae8e60e53cb
|
|
4
|
+
data.tar.gz: e90301ced5b302e23fcc4c3da3d59c9ac32bf9e22d472272ff43bdb8ad78eadc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 22eb5c5b3223bd25d7b3dea31f52134f9a71a07a1fcdf06e4d6077dc27df689e57a9e92cb059919f283f8708bfbe26e9ed679cdaeee7631d1203eedc4254aa85
|
|
7
|
+
data.tar.gz: e39db8ea279d53883c61333768928aca255891e105dfb27e82e6ddc88f73a1128fe0b9972da59921074479272ed7baf32b0317dc933bebef1664568414469e8c
|
data/lib/hakuban/async.rb
CHANGED
|
@@ -5,41 +5,28 @@ require 'async'
|
|
|
5
5
|
module Hakuban
|
|
6
6
|
|
|
7
7
|
class AsyncObjectManager < ObjectManager
|
|
8
|
+
|
|
8
9
|
def async_run
|
|
9
10
|
Async { yield }
|
|
10
11
|
end
|
|
11
12
|
|
|
12
|
-
def async_join(
|
|
13
|
-
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class ObjectObserve
|
|
19
|
-
def async(&block)
|
|
20
|
-
AsyncObjectManager.new(self, ObservedObject, block)
|
|
13
|
+
def async_join(task)
|
|
14
|
+
task.wait
|
|
21
15
|
end
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
class ObjectExpose
|
|
26
|
-
def async(&block)
|
|
27
|
-
AsyncObjectManager.new(self, ExposedObject, block)
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
16
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
AsyncObjectManager.new(self, ObservedObject, block)
|
|
17
|
+
def async_stop(task)
|
|
18
|
+
task.stop
|
|
35
19
|
end
|
|
20
|
+
|
|
36
21
|
end
|
|
37
22
|
|
|
38
23
|
|
|
39
|
-
class
|
|
40
|
-
|
|
41
|
-
|
|
24
|
+
class ObjectManagerBuilder
|
|
25
|
+
|
|
26
|
+
def with_async(&block)
|
|
27
|
+
build(AsyncObjectManager, block)
|
|
42
28
|
end
|
|
29
|
+
|
|
43
30
|
end
|
|
44
31
|
|
|
45
32
|
end
|
data/lib/hakuban/event-queue.rb
CHANGED
data/lib/hakuban/hakuban.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
require 'set'
|
|
2
2
|
require 'ostruct'
|
|
3
3
|
|
|
4
|
-
require_relative './ffi.rb'
|
|
5
4
|
require_relative './event-queue.rb'
|
|
6
5
|
|
|
7
6
|
#TODO: explicit drops?
|
|
@@ -33,9 +32,14 @@ module Hakuban
|
|
|
33
32
|
end
|
|
34
33
|
|
|
35
34
|
|
|
35
|
+
def self.hakuban_initialize
|
|
36
|
+
require_relative './ffi.rb'
|
|
37
|
+
end
|
|
38
|
+
|
|
36
39
|
@@logger_initialized = false
|
|
37
40
|
|
|
38
41
|
def self.logger_initialize(default_level, skip_if_already_initialized = false)
|
|
42
|
+
::Hakuban::hakuban_initialize
|
|
39
43
|
if @@logger_initialized and !skip_if_already_initialized
|
|
40
44
|
raise "Logger already initialized. This can't be done more than once. Make sure logger_initialize is called before any LocalNode gets constructed."
|
|
41
45
|
end
|
|
@@ -53,6 +57,7 @@ module Hakuban
|
|
|
53
57
|
attr_reader :local_node_pointer #todo: hide
|
|
54
58
|
|
|
55
59
|
def initialize(name: nil)
|
|
60
|
+
::Hakuban::hakuban_initialize
|
|
56
61
|
@default_serializer = lambda { |data_type, data|
|
|
57
62
|
[["JSON"]+data_type, JSON.dump(data)]
|
|
58
63
|
}
|
data/lib/hakuban/manager.rb
CHANGED
|
@@ -6,17 +6,55 @@ require 'ostruct'
|
|
|
6
6
|
|
|
7
7
|
module Hakuban
|
|
8
8
|
|
|
9
|
+
class ObjectManagerBuilder
|
|
10
|
+
|
|
11
|
+
def initialize(contract)
|
|
12
|
+
@contract = contract
|
|
13
|
+
@on_exception_policy = :retry
|
|
14
|
+
@retry_backoff = [0.01, 0.1, 1.0, 10.0]
|
|
15
|
+
@terminate_on_deactivation = false
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def on_exception(policy)
|
|
20
|
+
@on_exception_policy = policy
|
|
21
|
+
self
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def terminate_on_deactivation
|
|
26
|
+
@terminate_on_deactivation = true
|
|
27
|
+
self
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def retry_backoff(*times)
|
|
32
|
+
@retry_backoff = times.flatten
|
|
33
|
+
self
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def build(manager_class, block)
|
|
38
|
+
raise "don't use build() directly, use .with_thread {} or .with_async {} instead" if not manager_class
|
|
39
|
+
manager_class.new(@contract, block, @retry_backoff, @terminate_on_deactivation, @on_exception_policy)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
|
|
9
45
|
class ObjectManager
|
|
10
46
|
|
|
11
47
|
attr_reader :contract
|
|
12
48
|
|
|
13
49
|
class Event < OpenStruct; end
|
|
50
|
+
class HandlerException < OpenStruct; end
|
|
14
51
|
|
|
15
|
-
def initialize(contract,
|
|
52
|
+
def initialize(contract, block, retry_backoff, terminate_on_deactivation, on_exception_policy)
|
|
16
53
|
@contract = contract
|
|
54
|
+
object_class = @contract.class.const_get(:ManagedObject)
|
|
17
55
|
@objects_mutex = Mutex.new
|
|
18
|
-
@
|
|
19
|
-
@
|
|
56
|
+
@existing_objects = {} #TODO: turn this into a WeakMap, to keep object if external code still holds reference to it
|
|
57
|
+
@active_objects = {}
|
|
20
58
|
@event_queue = Queue.new
|
|
21
59
|
|
|
22
60
|
# This callback gets called from a separate thread, with no async reactor running.
|
|
@@ -31,38 +69,82 @@ module Hakuban
|
|
|
31
69
|
while @event_queue and event = @event_queue.shift
|
|
32
70
|
@objects_mutex.synchronize {
|
|
33
71
|
case event.action
|
|
34
|
-
when :insert
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
72
|
+
when :insert, :change, :remove, :stopped, :timer
|
|
73
|
+
object = @existing_objects[event.descriptor] ||= object_class.new(@contract, event.descriptor)
|
|
74
|
+
if !object.running and object.async
|
|
75
|
+
error = async_join(object.async)
|
|
76
|
+
object.async = nil
|
|
77
|
+
if object.last_exception_at
|
|
78
|
+
interval_since_last_exception = Time.new - object.last_exception_at
|
|
79
|
+
heal_multiplier = 10.0
|
|
80
|
+
object.current_delay_index -= (interval_since_last_exception / (retry_backoff[-1]*heal_multiplier)).floor
|
|
81
|
+
object.current_delay_index = 0 if object.current_delay_index < 0
|
|
82
|
+
end
|
|
83
|
+
if error.kind_of? HandlerException
|
|
84
|
+
case on_exception_policy
|
|
85
|
+
when :raise
|
|
86
|
+
raise error.exception
|
|
87
|
+
when :retry
|
|
88
|
+
object.last_exception_at = Time.new
|
|
89
|
+
object.earliest_next_run = Time.new + retry_backoff[object.current_delay_index]
|
|
90
|
+
lambda_sleep_time = object.earliest_next_run - Time.new
|
|
91
|
+
lambda_descriptor = event.descriptor
|
|
92
|
+
async_run {
|
|
93
|
+
sleep lambda_sleep_time
|
|
94
|
+
@event_queue << Event.new(action: :timer, descriptor: lambda_descriptor) if @event_queue
|
|
95
|
+
} #TODO: do we have to join this?
|
|
96
|
+
object.current_delay_index += 1 if object.current_delay_index < retry_backoff.size - 1
|
|
97
|
+
#when :ignore_failing_descriptor
|
|
98
|
+
#when :drop_contract
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
if object.check_active
|
|
103
|
+
if block and !object.running and (!object.earliest_next_run or Time.new >= object.earliest_next_run)
|
|
104
|
+
descriptor_for_lambda = event.descriptor
|
|
105
|
+
object.running = true
|
|
106
|
+
object.async = async_run do
|
|
107
|
+
object.run(block)
|
|
108
|
+
rescue Async::Stop
|
|
109
|
+
nil
|
|
110
|
+
rescue Exception => e
|
|
111
|
+
$stderr.puts "Exception in hakuban manager lambda: #{e}"
|
|
112
|
+
$stderr.puts "Contract: "+@contract.inspect
|
|
113
|
+
$stderr.puts "Descriptor: "+descriptor_for_lambda.inspect
|
|
114
|
+
$stderr.puts "Backtrace:\n\t#{e.backtrace.join("\n\t")}"
|
|
115
|
+
HandlerException.new(exception: e) # exception has to wrapped here to stop async from throwing on join
|
|
116
|
+
ensure
|
|
117
|
+
object.running = false
|
|
118
|
+
@event_queue << Event.new(action: :stopped, descriptor: descriptor_for_lambda) if @event_queue
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
@active_objects[event.descriptor] ||= object
|
|
122
|
+
object.change
|
|
123
|
+
else
|
|
124
|
+
@active_objects.delete(event.descriptor)
|
|
125
|
+
if object.running
|
|
126
|
+
object.stop
|
|
127
|
+
async_stop(object.async) if terminate_on_deactivation
|
|
128
|
+
else
|
|
129
|
+
@existing_objects.delete(event.descriptor)
|
|
130
|
+
end
|
|
52
131
|
end
|
|
53
|
-
when :handler_finished
|
|
54
|
-
raise if not @running_handlers[event.descriptor]
|
|
55
|
-
@running_handlers.delete(event.descriptor)
|
|
56
|
-
@event_queue << Event.new(action: :handler_start, descriptor: event.descriptor) if @objects[event.descriptor]
|
|
57
132
|
when :drop
|
|
58
133
|
@ffi_events.callback_unregister
|
|
59
|
-
@
|
|
60
|
-
|
|
134
|
+
@active_objects.clear
|
|
135
|
+
@existing_objects.dup.each { |descriptor, object|
|
|
136
|
+
if object.running
|
|
137
|
+
object.stop
|
|
138
|
+
async_stop(object.async) if terminate_on_deactivation
|
|
139
|
+
else
|
|
140
|
+
@existing_objects.delete(descriptor)
|
|
141
|
+
end
|
|
142
|
+
}
|
|
143
|
+
while @existing_objects.size > 0
|
|
61
144
|
event = @event_queue.shift
|
|
62
|
-
@
|
|
145
|
+
@existing_objects.delete(event.descriptor) if event.action == :stopped
|
|
63
146
|
end
|
|
64
147
|
@event_queue.clear
|
|
65
|
-
@objects.clear
|
|
66
148
|
@contract.drop
|
|
67
149
|
@contract, @event_queue = nil, nil
|
|
68
150
|
break
|
|
@@ -75,14 +157,14 @@ module Hakuban
|
|
|
75
157
|
|
|
76
158
|
def objects
|
|
77
159
|
@objects_mutex.synchronize {
|
|
78
|
-
@
|
|
160
|
+
@active_objects.dup
|
|
79
161
|
}
|
|
80
162
|
end
|
|
81
163
|
|
|
82
164
|
|
|
83
165
|
def object
|
|
84
166
|
@objects_mutex.synchronize {
|
|
85
|
-
@
|
|
167
|
+
@active_objects.values.first
|
|
86
168
|
}
|
|
87
169
|
end
|
|
88
170
|
|
|
@@ -102,20 +184,20 @@ module Hakuban
|
|
|
102
184
|
|
|
103
185
|
|
|
104
186
|
|
|
105
|
-
class
|
|
187
|
+
class ManagedObjectBase
|
|
106
188
|
|
|
107
|
-
attr_reader :descriptor
|
|
189
|
+
attr_reader :descriptor
|
|
190
|
+
attr_accessor :running, :async, :last_exception_at, :current_delay_index, :earliest_next_run
|
|
108
191
|
|
|
109
192
|
|
|
110
193
|
def initialize(contract, descriptor)
|
|
111
194
|
@contract,@descriptor = contract, descriptor
|
|
112
195
|
@changes = Queue.new
|
|
113
|
-
@
|
|
196
|
+
@current_delay_index = 0
|
|
114
197
|
end
|
|
115
198
|
|
|
116
199
|
|
|
117
200
|
def run(handler)
|
|
118
|
-
@handler_already_run = true
|
|
119
201
|
handler.call(self)
|
|
120
202
|
end
|
|
121
203
|
|
|
@@ -125,23 +207,27 @@ module Hakuban
|
|
|
125
207
|
loop {
|
|
126
208
|
case @changes.shift
|
|
127
209
|
when :change then return true
|
|
128
|
-
when :
|
|
210
|
+
when :stop then return false
|
|
129
211
|
end
|
|
130
212
|
}
|
|
131
213
|
end
|
|
132
214
|
|
|
133
215
|
|
|
134
|
-
def
|
|
135
|
-
@changes.push(change)
|
|
216
|
+
def change
|
|
217
|
+
@changes.push(:change)
|
|
136
218
|
end
|
|
137
219
|
|
|
220
|
+
def stop
|
|
221
|
+
@changes.push(:stop)
|
|
222
|
+
end
|
|
223
|
+
|
|
138
224
|
end
|
|
139
225
|
|
|
140
226
|
|
|
141
227
|
|
|
142
228
|
class ObjectObserve
|
|
143
229
|
|
|
144
|
-
class
|
|
230
|
+
class ManagedObject < ManagedObjectBase
|
|
145
231
|
|
|
146
232
|
def state
|
|
147
233
|
@contract.object_state
|
|
@@ -151,6 +237,15 @@ module Hakuban
|
|
|
151
237
|
@contract.object_state&.data
|
|
152
238
|
end
|
|
153
239
|
|
|
240
|
+
def check_active
|
|
241
|
+
!!state
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def manage
|
|
248
|
+
ObjectManagerBuilder.new(self)
|
|
154
249
|
end
|
|
155
250
|
|
|
156
251
|
end
|
|
@@ -158,7 +253,7 @@ module Hakuban
|
|
|
158
253
|
|
|
159
254
|
class ObjectExpose
|
|
160
255
|
|
|
161
|
-
class
|
|
256
|
+
class ManagedObject < ManagedObjectBase
|
|
162
257
|
|
|
163
258
|
def initialize(contract, descriptor)
|
|
164
259
|
super(contract, descriptor)
|
|
@@ -201,6 +296,15 @@ module Hakuban
|
|
|
201
296
|
set_data(value)
|
|
202
297
|
end
|
|
203
298
|
|
|
299
|
+
def check_active
|
|
300
|
+
assigned?
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def manage
|
|
307
|
+
ObjectManagerBuilder.new(self)
|
|
204
308
|
end
|
|
205
309
|
|
|
206
310
|
end
|
|
@@ -208,7 +312,7 @@ module Hakuban
|
|
|
208
312
|
|
|
209
313
|
class TagObserve
|
|
210
314
|
|
|
211
|
-
class
|
|
315
|
+
class ManagedObject < ManagedObjectBase
|
|
212
316
|
|
|
213
317
|
def state
|
|
214
318
|
@contract.object_state(@descriptor)
|
|
@@ -218,6 +322,15 @@ module Hakuban
|
|
|
218
322
|
@contract.object_state(@descriptor)&.data
|
|
219
323
|
end
|
|
220
324
|
|
|
325
|
+
def check_active
|
|
326
|
+
!!state
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def manage
|
|
333
|
+
ObjectManagerBuilder.new(self)
|
|
221
334
|
end
|
|
222
335
|
|
|
223
336
|
end
|
|
@@ -225,7 +338,7 @@ module Hakuban
|
|
|
225
338
|
|
|
226
339
|
class TagExpose
|
|
227
340
|
|
|
228
|
-
class
|
|
341
|
+
class ManagedObject < ManagedObjectBase
|
|
229
342
|
|
|
230
343
|
def initialize(contract, descriptor)
|
|
231
344
|
super(contract, descriptor)
|
|
@@ -268,6 +381,15 @@ module Hakuban
|
|
|
268
381
|
set_data(value)
|
|
269
382
|
end
|
|
270
383
|
|
|
384
|
+
def check_active
|
|
385
|
+
assigned?
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
def manage
|
|
392
|
+
ObjectManagerBuilder.new(self)
|
|
271
393
|
end
|
|
272
394
|
|
|
273
395
|
end
|
data/lib/hakuban/thread.rb
CHANGED
|
@@ -4,41 +4,28 @@ require 'hakuban/manager'
|
|
|
4
4
|
module Hakuban
|
|
5
5
|
|
|
6
6
|
class ThreadObjectManager < ObjectManager
|
|
7
|
+
|
|
7
8
|
def async_run
|
|
8
9
|
Thread.new { yield }
|
|
9
10
|
end
|
|
10
11
|
|
|
11
12
|
def async_join(thread)
|
|
12
|
-
thread.
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class ObjectObserve
|
|
18
|
-
def thread(&block)
|
|
19
|
-
ThreadObjectManager.new(self, ObservedObject, block)
|
|
13
|
+
thread.value
|
|
20
14
|
end
|
|
21
|
-
end
|
|
22
|
-
|
|
23
15
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
ThreadObjectManager.new(self, ExposedObject, block)
|
|
16
|
+
def async_stop(thread)
|
|
17
|
+
thread.kill
|
|
27
18
|
end
|
|
19
|
+
|
|
28
20
|
end
|
|
29
21
|
|
|
30
22
|
|
|
31
|
-
class
|
|
32
|
-
|
|
33
|
-
|
|
23
|
+
class ObjectManagerBuilder
|
|
24
|
+
|
|
25
|
+
def with_thread(&block)
|
|
26
|
+
build(ThreadObjectManager, block)
|
|
34
27
|
end
|
|
35
|
-
end
|
|
36
28
|
|
|
37
|
-
|
|
38
|
-
class TagExpose
|
|
39
|
-
def thread(&block)
|
|
40
|
-
ThreadObjectManager.new(self, ExposedObject, block)
|
|
41
|
-
end
|
|
42
29
|
end
|
|
43
30
|
|
|
44
31
|
end
|
data/lib/hakuban/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: hakuban
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- yunta
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2022-
|
|
11
|
+
date: 2022-03-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rspec
|