hakuban 0.6.2 → 0.6.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|