hakuban 0.6.5 → 0.8.0
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/bin/{hakuban-thread-engine → hakuban-engine} +12 -11
- data/bin/hakuban-observe +61 -0
- data/lib/hakuban/contract.rb +148 -0
- data/lib/hakuban/descriptor.rb +85 -0
- data/lib/hakuban/engine.rb +73 -29
- data/lib/hakuban/exchange.rb +115 -0
- data/lib/hakuban/ffi-object.rb +123 -0
- data/lib/hakuban/ffi.rb +133 -150
- data/lib/hakuban/logger.rb +16 -0
- data/lib/hakuban/object_state.rb +105 -0
- data/lib/hakuban/object_state_sink.rb +56 -0
- data/lib/hakuban/object_state_stream.rb +39 -0
- data/lib/hakuban/refinements.rb +23 -0
- data/lib/hakuban/stream.rb +189 -0
- data/lib/hakuban/tokio-websocket-connector.rb +39 -0
- data/lib/hakuban/version.rb +1 -1
- data/lib/hakuban.rb +21 -6
- metadata +40 -20
- data/bin/hakuban-observer +0 -64
- data/lib/hakuban/async.rb +0 -38
- data/lib/hakuban/event-queue.rb +0 -75
- data/lib/hakuban/hakuban.rb +0 -545
- data/lib/hakuban/manager.rb +0 -398
- data/lib/hakuban/thread.rb +0 -35
data/lib/hakuban/manager.rb
DELETED
@@ -1,398 +0,0 @@
|
|
1
|
-
require 'hakuban/hakuban'
|
2
|
-
require 'ostruct'
|
3
|
-
|
4
|
-
#TODO: prevent usage of old object?
|
5
|
-
#TODO: destructors
|
6
|
-
|
7
|
-
module Hakuban
|
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
|
-
|
45
|
-
class ObjectManager
|
46
|
-
|
47
|
-
attr_reader :contract
|
48
|
-
|
49
|
-
class Event < OpenStruct; end
|
50
|
-
class HandlerException < OpenStruct; end
|
51
|
-
|
52
|
-
|
53
|
-
def initialize(contract, block, retry_backoff, terminate_on_deactivation, on_exception_policy)
|
54
|
-
@contract = contract
|
55
|
-
object_class = @contract.class.const_get(:ManagedObject)
|
56
|
-
@objects_mutex = Mutex.new
|
57
|
-
@existing_objects = {} #TODO: turn this into a WeakMap, to keep object if external code still holds reference to it
|
58
|
-
@active_objects = {}
|
59
|
-
@event_queue = Queue.new
|
60
|
-
|
61
|
-
# This callback gets called from a separate thread, with no async reactor running.
|
62
|
-
# So, we only use it to forward actions to the main thread.
|
63
|
-
@ffi_callback = proc { |descriptor, action|
|
64
|
-
@event_queue << Event.new(action: action, descriptor: descriptor)
|
65
|
-
}
|
66
|
-
@ffi_events = @contract.new_callback_event_queue
|
67
|
-
@ffi_events.callback_register(&@ffi_callback)
|
68
|
-
|
69
|
-
@async = async_run {
|
70
|
-
while @event_queue and event = @event_queue.shift
|
71
|
-
@objects_mutex.synchronize {
|
72
|
-
case event.action
|
73
|
-
when :insert, :change, :remove, :stopped, :timer
|
74
|
-
object = @existing_objects[event.descriptor] ||= object_class.new(@contract, event.descriptor)
|
75
|
-
if !object.running and object.async
|
76
|
-
error = async_join(object.async)
|
77
|
-
object.async = nil
|
78
|
-
if object.last_exception_at
|
79
|
-
interval_since_last_exception = Time.new - object.last_exception_at
|
80
|
-
heal_multiplier = 10.0
|
81
|
-
object.current_delay_index -= (interval_since_last_exception / (retry_backoff[-1]*heal_multiplier)).floor
|
82
|
-
object.current_delay_index = 0 if object.current_delay_index < 0
|
83
|
-
end
|
84
|
-
if error.kind_of? HandlerException
|
85
|
-
case on_exception_policy
|
86
|
-
when :raise
|
87
|
-
raise error.exception
|
88
|
-
when :retry
|
89
|
-
object.last_exception_at = Time.new
|
90
|
-
object.earliest_next_run = Time.new + retry_backoff[object.current_delay_index]
|
91
|
-
lambda_sleep_time = object.earliest_next_run - Time.new
|
92
|
-
lambda_descriptor = event.descriptor
|
93
|
-
async_run {
|
94
|
-
sleep lambda_sleep_time
|
95
|
-
@event_queue << Event.new(action: :timer, descriptor: lambda_descriptor) if @event_queue
|
96
|
-
} #TODO: do we have to join this?
|
97
|
-
object.current_delay_index += 1 if object.current_delay_index < retry_backoff.size - 1
|
98
|
-
#when :ignore_failing_descriptor
|
99
|
-
#when :drop_contract
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
if object.check_active
|
104
|
-
if block and !object.running and (!object.earliest_next_run or Time.new >= object.earliest_next_run)
|
105
|
-
descriptor_for_lambda = event.descriptor
|
106
|
-
object.running = true
|
107
|
-
object.async = async_run do
|
108
|
-
async_filter_out_stop_exception {
|
109
|
-
object.run(block)
|
110
|
-
}
|
111
|
-
rescue Exception => e
|
112
|
-
$stderr.puts "Exception in hakuban manager lambda: #{e}"
|
113
|
-
$stderr.puts "Contract: "+@contract.inspect
|
114
|
-
$stderr.puts "Descriptor: "+descriptor_for_lambda.inspect
|
115
|
-
$stderr.puts "Backtrace:\n\t#{e.backtrace.join("\n\t")}"
|
116
|
-
HandlerException.new(exception: e) # exception has to wrapped here to stop async from throwing on join
|
117
|
-
ensure
|
118
|
-
object.running = false
|
119
|
-
@event_queue << Event.new(action: :stopped, descriptor: descriptor_for_lambda) if @event_queue
|
120
|
-
end
|
121
|
-
end
|
122
|
-
@active_objects[event.descriptor] ||= object
|
123
|
-
object.change
|
124
|
-
else
|
125
|
-
@active_objects.delete(event.descriptor)
|
126
|
-
if object.running
|
127
|
-
object.stop
|
128
|
-
async_stop(object.async) if terminate_on_deactivation
|
129
|
-
else
|
130
|
-
@existing_objects.delete(event.descriptor)
|
131
|
-
end
|
132
|
-
end
|
133
|
-
when :drop
|
134
|
-
@ffi_events.callback_unregister
|
135
|
-
@active_objects.clear
|
136
|
-
@existing_objects.dup.each { |descriptor, object|
|
137
|
-
if object.running
|
138
|
-
object.stop
|
139
|
-
async_stop(object.async) if terminate_on_deactivation
|
140
|
-
else
|
141
|
-
@existing_objects.delete(descriptor)
|
142
|
-
end
|
143
|
-
}
|
144
|
-
while @existing_objects.size > 0
|
145
|
-
event = @event_queue.shift
|
146
|
-
@existing_objects.delete(event.descriptor) if event.action == :stopped
|
147
|
-
end
|
148
|
-
@event_queue.clear
|
149
|
-
@contract.drop
|
150
|
-
@contract, @event_queue = nil, nil
|
151
|
-
break
|
152
|
-
end
|
153
|
-
}
|
154
|
-
end
|
155
|
-
}
|
156
|
-
end
|
157
|
-
|
158
|
-
|
159
|
-
def objects
|
160
|
-
@objects_mutex.synchronize {
|
161
|
-
@active_objects.dup
|
162
|
-
}
|
163
|
-
end
|
164
|
-
|
165
|
-
|
166
|
-
def object
|
167
|
-
@objects_mutex.synchronize {
|
168
|
-
@active_objects.values.first
|
169
|
-
}
|
170
|
-
end
|
171
|
-
|
172
|
-
|
173
|
-
def drop
|
174
|
-
drop_nonblock
|
175
|
-
async_join(@async)
|
176
|
-
end
|
177
|
-
|
178
|
-
|
179
|
-
def drop_nonblock
|
180
|
-
if @contract
|
181
|
-
@event_queue << Event.new(action: :drop)
|
182
|
-
end
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
class ManagedObjectBase
|
189
|
-
|
190
|
-
attr_reader :descriptor
|
191
|
-
attr_accessor :running, :async, :last_exception_at, :current_delay_index, :earliest_next_run
|
192
|
-
|
193
|
-
|
194
|
-
def initialize(contract, descriptor)
|
195
|
-
@contract,@descriptor = contract, descriptor
|
196
|
-
@changes = Queue.new
|
197
|
-
@current_delay_index = 0
|
198
|
-
end
|
199
|
-
|
200
|
-
|
201
|
-
def run(handler)
|
202
|
-
handler.call(self)
|
203
|
-
end
|
204
|
-
|
205
|
-
|
206
|
-
def next_event; next_change; end
|
207
|
-
def next_change
|
208
|
-
loop {
|
209
|
-
case @changes.shift
|
210
|
-
when :change then return true
|
211
|
-
when :stop then return false
|
212
|
-
end
|
213
|
-
}
|
214
|
-
end
|
215
|
-
|
216
|
-
|
217
|
-
def change
|
218
|
-
@changes.push(:change)
|
219
|
-
end
|
220
|
-
|
221
|
-
def stop
|
222
|
-
@changes.push(:stop)
|
223
|
-
end
|
224
|
-
|
225
|
-
end
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
class ObjectObserve
|
230
|
-
|
231
|
-
class ManagedObject < ManagedObjectBase
|
232
|
-
|
233
|
-
def state
|
234
|
-
@contract.object_state
|
235
|
-
end
|
236
|
-
|
237
|
-
def data
|
238
|
-
@contract.object_state&.data
|
239
|
-
end
|
240
|
-
|
241
|
-
def check_active
|
242
|
-
!!state
|
243
|
-
end
|
244
|
-
|
245
|
-
end
|
246
|
-
|
247
|
-
|
248
|
-
def manage
|
249
|
-
ObjectManagerBuilder.new(self)
|
250
|
-
end
|
251
|
-
|
252
|
-
end
|
253
|
-
|
254
|
-
|
255
|
-
class ObjectExpose
|
256
|
-
|
257
|
-
class ManagedObject < ManagedObjectBase
|
258
|
-
|
259
|
-
def initialize(contract, descriptor)
|
260
|
-
super(contract, descriptor)
|
261
|
-
@assignment = contract.assignment
|
262
|
-
end
|
263
|
-
|
264
|
-
def run(handler)
|
265
|
-
super
|
266
|
-
@contract.desynchronize(@assignment)
|
267
|
-
end
|
268
|
-
|
269
|
-
def do_change(change)
|
270
|
-
@assignment = @contract.assignment
|
271
|
-
super(change)
|
272
|
-
end
|
273
|
-
|
274
|
-
def assignement
|
275
|
-
@assignment = @contract.assignment()
|
276
|
-
end
|
277
|
-
|
278
|
-
def assigned?
|
279
|
-
@contract.assigned?()
|
280
|
-
end
|
281
|
-
|
282
|
-
def set_state(*state, **kwargs)
|
283
|
-
kwargs[:assignment] ||= @assignment
|
284
|
-
@contract.set_object_state(*state, **kwargs)
|
285
|
-
end
|
286
|
-
|
287
|
-
def state=(state)
|
288
|
-
set_state(*state)
|
289
|
-
end
|
290
|
-
|
291
|
-
def set_data(value)
|
292
|
-
timestamp = Time.new.to_f
|
293
|
-
set_state([1, timestamp.floor, ((timestamp - timestamp.floor)*1000000000).floor, 0],value)
|
294
|
-
end
|
295
|
-
|
296
|
-
def data=(value)
|
297
|
-
set_data(value)
|
298
|
-
end
|
299
|
-
|
300
|
-
def check_active
|
301
|
-
assigned?
|
302
|
-
end
|
303
|
-
|
304
|
-
end
|
305
|
-
|
306
|
-
|
307
|
-
def manage
|
308
|
-
ObjectManagerBuilder.new(self)
|
309
|
-
end
|
310
|
-
|
311
|
-
end
|
312
|
-
|
313
|
-
|
314
|
-
class TagObserve
|
315
|
-
|
316
|
-
class ManagedObject < ManagedObjectBase
|
317
|
-
|
318
|
-
def state
|
319
|
-
@contract.object_state(@descriptor)
|
320
|
-
end
|
321
|
-
|
322
|
-
def data
|
323
|
-
@contract.object_state(@descriptor)&.data
|
324
|
-
end
|
325
|
-
|
326
|
-
def check_active
|
327
|
-
!!state
|
328
|
-
end
|
329
|
-
|
330
|
-
end
|
331
|
-
|
332
|
-
|
333
|
-
def manage
|
334
|
-
ObjectManagerBuilder.new(self)
|
335
|
-
end
|
336
|
-
|
337
|
-
end
|
338
|
-
|
339
|
-
|
340
|
-
class TagExpose
|
341
|
-
|
342
|
-
class ManagedObject < ManagedObjectBase
|
343
|
-
|
344
|
-
def initialize(contract, descriptor)
|
345
|
-
super(contract, descriptor)
|
346
|
-
@assignment = contract.assignment(@descriptor)
|
347
|
-
end
|
348
|
-
|
349
|
-
def run(handler)
|
350
|
-
super
|
351
|
-
@contract.desynchronize(@descriptor, @assignment)
|
352
|
-
end
|
353
|
-
|
354
|
-
def do_change(change)
|
355
|
-
@assignment = @contract.assignment(@descriptor)
|
356
|
-
super(change)
|
357
|
-
end
|
358
|
-
|
359
|
-
def assignement
|
360
|
-
@assignment = @contract.assignment(@descriptor)
|
361
|
-
end
|
362
|
-
|
363
|
-
def assigned?
|
364
|
-
@contract.assigned?(@descriptor)
|
365
|
-
end
|
366
|
-
|
367
|
-
def set_state(*args, **kwargs)
|
368
|
-
kwargs[:assignment] ||= @assignment
|
369
|
-
@contract.set_object_state(@descriptor, *args, **kwargs)
|
370
|
-
end
|
371
|
-
|
372
|
-
def state=(state)
|
373
|
-
set_state(*state)
|
374
|
-
end
|
375
|
-
|
376
|
-
def set_data(value)
|
377
|
-
timestamp = Time.new.to_f
|
378
|
-
set_state([1, timestamp.floor, ((timestamp - timestamp.floor)*1000000000).floor, 0],value)
|
379
|
-
end
|
380
|
-
|
381
|
-
def data=(value)
|
382
|
-
set_data(value)
|
383
|
-
end
|
384
|
-
|
385
|
-
def check_active
|
386
|
-
assigned?
|
387
|
-
end
|
388
|
-
|
389
|
-
end
|
390
|
-
|
391
|
-
|
392
|
-
def manage
|
393
|
-
ObjectManagerBuilder.new(self)
|
394
|
-
end
|
395
|
-
|
396
|
-
end
|
397
|
-
|
398
|
-
end
|
data/lib/hakuban/thread.rb
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
require 'hakuban'
|
2
|
-
require 'hakuban/manager'
|
3
|
-
|
4
|
-
module Hakuban
|
5
|
-
|
6
|
-
class ThreadObjectManager < ObjectManager
|
7
|
-
|
8
|
-
def async_run
|
9
|
-
Thread.new { yield }
|
10
|
-
end
|
11
|
-
|
12
|
-
def async_join(thread)
|
13
|
-
thread.value
|
14
|
-
end
|
15
|
-
|
16
|
-
def async_stop(thread)
|
17
|
-
thread.kill
|
18
|
-
end
|
19
|
-
|
20
|
-
def async_filter_out_stop_exception
|
21
|
-
yield
|
22
|
-
end
|
23
|
-
|
24
|
-
end
|
25
|
-
|
26
|
-
|
27
|
-
class ObjectManagerBuilder
|
28
|
-
|
29
|
-
def with_thread(&block)
|
30
|
-
build(ThreadObjectManager, block)
|
31
|
-
end
|
32
|
-
|
33
|
-
end
|
34
|
-
|
35
|
-
end
|