iopromise 0.1.1 → 0.1.2
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/Gemfile +4 -0
- data/Gemfile.lock +7 -2
- data/iopromise.gemspec +1 -0
- data/lib/iopromise.rb +14 -15
- data/lib/iopromise/dalli/client.rb +13 -17
- data/lib/iopromise/dalli/executor_pool.rb +36 -3
- data/lib/iopromise/dalli/patch_dalli.rb +118 -102
- data/lib/iopromise/dalli/promise.rb +13 -5
- data/lib/iopromise/deferred/executor_pool.rb +1 -3
- data/lib/iopromise/deferred/promise.rb +1 -1
- data/lib/iopromise/executor_context.rb +41 -58
- data/lib/iopromise/executor_pool/base.rb +23 -7
- data/lib/iopromise/executor_pool/batch.rb +5 -3
- data/lib/iopromise/executor_pool/sequential.rb +6 -14
- data/lib/iopromise/memcached/executor_pool.rb +26 -5
- data/lib/iopromise/version.rb +1 -1
- metadata +16 -9
- data/lib/iopromise/faraday.rb +0 -17
- data/lib/iopromise/faraday/connection.rb +0 -25
- data/lib/iopromise/faraday/continuable_hydra.rb +0 -29
- data/lib/iopromise/faraday/executor_pool.rb +0 -19
- data/lib/iopromise/faraday/multi_socket_action.rb +0 -107
- data/lib/iopromise/faraday/promise.rb +0 -42
- data/lib/iopromise/rack/context_middleware.rb +0 -20
@@ -9,16 +9,19 @@ module IOPromise
|
|
9
9
|
|
10
10
|
def initialize(server = nil, key = nil)
|
11
11
|
super()
|
12
|
-
|
12
|
+
|
13
|
+
# when created from a 'then' call, initialize nothing
|
14
|
+
return if server.nil? || key.nil?
|
15
|
+
|
13
16
|
@server = server
|
14
17
|
@key = key
|
15
18
|
@start_time = nil
|
16
|
-
|
17
|
-
::IOPromise::ExecutorContext.current.register(self)
|
19
|
+
|
20
|
+
::IOPromise::ExecutorContext.current.register(self)
|
18
21
|
end
|
19
22
|
|
20
23
|
def wait
|
21
|
-
|
24
|
+
unless defined?(@server)
|
22
25
|
super
|
23
26
|
else
|
24
27
|
::IOPromise::ExecutorContext.current.wait_for_all_data(end_when_complete: self)
|
@@ -26,7 +29,12 @@ module IOPromise
|
|
26
29
|
end
|
27
30
|
|
28
31
|
def execute_pool
|
29
|
-
|
32
|
+
return @pool if defined?(@pool)
|
33
|
+
if defined?(@server)
|
34
|
+
@pool = DalliExecutorPool.for(@server)
|
35
|
+
else
|
36
|
+
@pool = nil
|
37
|
+
end
|
30
38
|
end
|
31
39
|
|
32
40
|
def in_select_loop
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module IOPromise
|
4
4
|
module Deferred
|
5
5
|
class DeferredExecutorPool < ::IOPromise::ExecutorPool::Batch
|
6
|
-
def execute_continue
|
6
|
+
def execute_continue
|
7
7
|
if @current_batch.empty?
|
8
8
|
next_batch
|
9
9
|
end
|
@@ -13,7 +13,6 @@ module IOPromise
|
|
13
13
|
@current_batch.each do |promise|
|
14
14
|
begin_executing(promise)
|
15
15
|
promise.run_deferred
|
16
|
-
complete(promise)
|
17
16
|
end
|
18
17
|
|
19
18
|
@current_batch = []
|
@@ -22,7 +21,6 @@ module IOPromise
|
|
22
21
|
end
|
23
22
|
|
24
23
|
# we always fully complete each cycle
|
25
|
-
return [[], [], [], nil]
|
26
24
|
end
|
27
25
|
end
|
28
26
|
end
|
@@ -1,32 +1,30 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'set'
|
4
|
+
require 'nio'
|
4
5
|
|
5
6
|
module IOPromise
|
6
7
|
class ExecutorContext
|
7
8
|
class << self
|
8
|
-
def push
|
9
|
-
@contexts ||= []
|
10
|
-
@contexts << ExecutorContext.new
|
11
|
-
end
|
12
|
-
|
13
9
|
def current
|
14
|
-
@
|
15
|
-
end
|
16
|
-
|
17
|
-
def pop
|
18
|
-
@contexts.pop
|
10
|
+
@context ||= ExecutorContext.new
|
19
11
|
end
|
20
12
|
end
|
21
13
|
|
22
14
|
def initialize
|
23
|
-
@pools =
|
24
|
-
|
25
|
-
@pool_ready_readers = {}
|
26
|
-
@pool_ready_writers = {}
|
27
|
-
@pool_ready_exceptions = {}
|
15
|
+
@pools = {}
|
28
16
|
|
29
17
|
@pending_registrations = []
|
18
|
+
|
19
|
+
@selector = NIO::Selector.new
|
20
|
+
|
21
|
+
super
|
22
|
+
end
|
23
|
+
|
24
|
+
def register_observer_io(observer, io, interest)
|
25
|
+
monitor = @selector.register(io, interest)
|
26
|
+
monitor.value = observer
|
27
|
+
monitor
|
30
28
|
end
|
31
29
|
|
32
30
|
def register(promise)
|
@@ -37,32 +35,34 @@ module IOPromise
|
|
37
35
|
loop do
|
38
36
|
complete_pending_registrations
|
39
37
|
|
40
|
-
|
41
|
-
|
38
|
+
@pools.each do |pool, _|
|
39
|
+
pool.execute_continue
|
40
|
+
end
|
41
|
+
|
42
42
|
unless end_when_complete.nil?
|
43
43
|
return unless end_when_complete.pending?
|
44
44
|
end
|
45
|
-
|
46
|
-
break if
|
45
|
+
|
46
|
+
break if @selector.empty?
|
47
47
|
|
48
48
|
# if we have any pending promises to register, we'll not block at all so we immediately continue
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
49
|
+
unless @pending_registrations.empty?
|
50
|
+
wait_time = 0
|
51
|
+
else
|
52
|
+
wait_time = nil
|
53
|
+
@pools.each do |pool, _|
|
54
|
+
timeout = pool.select_timeout
|
55
|
+
wait_time = timeout if wait_time.nil? || (!timeout.nil? && timeout < wait_time)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
ready_count = select(wait_time)
|
60
60
|
end
|
61
|
-
|
61
|
+
|
62
62
|
unless end_when_complete.nil?
|
63
63
|
raise ::IOPromise::Error.new('Internal error: IO loop completed without fulfilling the desired promise')
|
64
64
|
else
|
65
|
-
@pools.each do |pool|
|
65
|
+
@pools.each do |pool, _|
|
66
66
|
pool.wait
|
67
67
|
end
|
68
68
|
end
|
@@ -72,7 +72,15 @@ module IOPromise
|
|
72
72
|
|
73
73
|
private
|
74
74
|
|
75
|
+
def select(wait_time)
|
76
|
+
@selector.select(wait_time) do |monitor|
|
77
|
+
observer = monitor.value
|
78
|
+
observer.monitor_ready(monitor, monitor.readiness)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
75
82
|
def complete_pending_registrations
|
83
|
+
return if @pending_registrations.empty?
|
76
84
|
pending = @pending_registrations
|
77
85
|
@pending_registrations = []
|
78
86
|
pending.each do |promise|
|
@@ -80,35 +88,10 @@ module IOPromise
|
|
80
88
|
end
|
81
89
|
end
|
82
90
|
|
83
|
-
def continue_to_read_pools
|
84
|
-
readers = {}
|
85
|
-
writers = {}
|
86
|
-
exceptions = {}
|
87
|
-
max_timeout = nil
|
88
|
-
|
89
|
-
@pools.each do |pool|
|
90
|
-
rd, wr, ex, ti = pool.execute_continue(@pool_ready_readers[pool], @pool_ready_writers[pool], @pool_ready_exceptions[pool])
|
91
|
-
rd.each do |io|
|
92
|
-
readers[io] = pool
|
93
|
-
end
|
94
|
-
wr.each do |io|
|
95
|
-
writers[io] = pool
|
96
|
-
end
|
97
|
-
ex.each do |io|
|
98
|
-
exceptions[io] = pool
|
99
|
-
end
|
100
|
-
if max_timeout.nil? || (!ti.nil? && ti < max_timeout)
|
101
|
-
max_timeout = ti
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
[readers, writers, exceptions, max_timeout]
|
106
|
-
end
|
107
|
-
|
108
91
|
def register_now(promise)
|
109
92
|
pool = promise.execute_pool
|
110
93
|
pool.register(promise)
|
111
|
-
@pools
|
94
|
+
@pools[pool] = true
|
112
95
|
end
|
113
96
|
end
|
114
97
|
end
|
@@ -1,25 +1,39 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'promise/observer'
|
4
|
+
|
3
5
|
module IOPromise
|
4
6
|
module ExecutorPool
|
5
7
|
class Base
|
8
|
+
include Promise::Observer
|
9
|
+
|
6
10
|
class << self
|
7
11
|
def for(connection_pool)
|
8
12
|
@executors ||= {}
|
9
13
|
@executors[connection_pool] ||= new(connection_pool)
|
10
14
|
end
|
11
15
|
end
|
16
|
+
|
17
|
+
attr_accessor :select_timeout
|
12
18
|
|
13
19
|
def initialize(connection_pool)
|
14
20
|
@connection_pool = connection_pool
|
15
21
|
@pending = []
|
22
|
+
|
23
|
+
@monitors = {}
|
24
|
+
|
25
|
+
@select_timeout = nil
|
16
26
|
end
|
17
27
|
|
18
28
|
def register(item)
|
19
29
|
@pending << item
|
30
|
+
item.subscribe(self, item, item)
|
20
31
|
end
|
21
32
|
|
22
|
-
def
|
33
|
+
def promise_fulfilled(_value, item)
|
34
|
+
@pending.delete(item)
|
35
|
+
end
|
36
|
+
def promise_rejected(_reason, item)
|
23
37
|
@pending.delete(item)
|
24
38
|
end
|
25
39
|
|
@@ -28,15 +42,17 @@ module IOPromise
|
|
28
42
|
end
|
29
43
|
|
30
44
|
# Continue execution of one or more pending IOPromises assigned to this pool.
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
# this function
|
45
|
+
# Implementations may choose to pre-register IO handled using:
|
46
|
+
# ExecutorContext.current.register_observer_io(...)
|
47
|
+
# Alternatively, they can be registered when this function is called.
|
48
|
+
# During this function, implementations should check for timeouts and run
|
49
|
+
# any housekeeping operations.
|
50
|
+
#
|
35
51
|
# Must be implemented by subclasses.
|
36
|
-
def execute_continue
|
52
|
+
def execute_continue
|
37
53
|
raise NotImplementedError
|
38
54
|
end
|
39
|
-
|
55
|
+
|
40
56
|
def sync
|
41
57
|
@pending.each do |promise|
|
42
58
|
promise.sync if promise.is_a?(Promise)
|
@@ -3,8 +3,8 @@
|
|
3
3
|
module IOPromise
|
4
4
|
module ExecutorPool
|
5
5
|
class Batch < Base
|
6
|
-
def initialize(
|
7
|
-
super
|
6
|
+
def initialize(*)
|
7
|
+
super
|
8
8
|
|
9
9
|
@current_batch = []
|
10
10
|
end
|
@@ -16,7 +16,9 @@ module IOPromise
|
|
16
16
|
end
|
17
17
|
|
18
18
|
# every pending operation becomes part of the current batch
|
19
|
-
|
19
|
+
# we don't include promises with a source set, because that
|
20
|
+
# indicates that they depend on another promise now.
|
21
|
+
@current_batch = @pending.select { |p| p.pending? && p.source.nil? }
|
20
22
|
end
|
21
23
|
end
|
22
24
|
end
|
@@ -3,29 +3,21 @@
|
|
3
3
|
module IOPromise
|
4
4
|
module ExecutorPool
|
5
5
|
class Sequential < Base
|
6
|
-
def execute_continue_item(item
|
7
|
-
item.execute_continue
|
6
|
+
def execute_continue_item(item)
|
7
|
+
item.execute_continue
|
8
8
|
end
|
9
9
|
|
10
|
-
def execute_continue
|
10
|
+
def execute_continue
|
11
11
|
@pending.dup.each do |active|
|
12
|
-
|
13
|
-
nil
|
14
|
-
else
|
15
|
-
execute_continue_item(active, ready_readers, ready_writers, ready_exceptions)
|
16
|
-
end
|
12
|
+
execute_continue_item(active)
|
17
13
|
|
18
|
-
unless
|
14
|
+
unless active.fulfilled?
|
19
15
|
# once we're waiting on our one next item, we're done
|
20
|
-
return
|
21
|
-
else
|
22
|
-
# we're done with this one, so remove it
|
23
|
-
complete(active)
|
16
|
+
return
|
24
17
|
end
|
25
18
|
end
|
26
19
|
|
27
20
|
# if we fall through to here, we have nothing to wait on.
|
28
|
-
[[], [], [], nil]
|
29
21
|
end
|
30
22
|
end
|
31
23
|
end
|
@@ -3,6 +3,12 @@
|
|
3
3
|
module IOPromise
|
4
4
|
module Memcached
|
5
5
|
class MemcacheExecutorPool < ::IOPromise::ExecutorPool::Batch
|
6
|
+
def initialize(*)
|
7
|
+
super
|
8
|
+
|
9
|
+
@monitors = {}
|
10
|
+
end
|
11
|
+
|
6
12
|
def next_batch
|
7
13
|
super
|
8
14
|
|
@@ -14,7 +20,6 @@ module IOPromise
|
|
14
20
|
rescue => e
|
15
21
|
@keys_to_promises.values.flatten.each do |promise|
|
16
22
|
promise.reject(e)
|
17
|
-
complete(promise)
|
18
23
|
@current_batch.delete(promise)
|
19
24
|
end
|
20
25
|
|
@@ -23,12 +28,17 @@ module IOPromise
|
|
23
28
|
end
|
24
29
|
end
|
25
30
|
|
26
|
-
def execute_continue
|
31
|
+
def execute_continue
|
27
32
|
if @current_batch.empty?
|
28
33
|
next_batch
|
29
34
|
end
|
30
35
|
|
31
|
-
|
36
|
+
if @current_batch.empty?
|
37
|
+
@monitors.each do |_, monitor|
|
38
|
+
monitor.interests = nil
|
39
|
+
end
|
40
|
+
return
|
41
|
+
end
|
32
42
|
|
33
43
|
so_far, readers, writers = memcache_client.continue_get_multi
|
34
44
|
|
@@ -45,12 +55,23 @@ module IOPromise
|
|
45
55
|
next if promise.fulfilled?
|
46
56
|
|
47
57
|
promise.fulfill(value)
|
48
|
-
complete(promise)
|
49
58
|
@current_batch.delete(promise)
|
50
59
|
end
|
51
60
|
end
|
52
61
|
|
53
|
-
|
62
|
+
@monitors.each do |_, monitor|
|
63
|
+
monitor.interests = nil
|
64
|
+
end
|
65
|
+
|
66
|
+
readers.each do |reader|
|
67
|
+
@monitors[reader] ||= ::IOPromise::ExecutorContext.current.register_observer_io(self, reader, :r)
|
68
|
+
@monitors[reader].add_interest(:r)
|
69
|
+
end
|
70
|
+
|
71
|
+
writers.each do |writer|
|
72
|
+
@monitors[writer] ||= ::IOPromise::ExecutorContext.current.register_observer_io(self, writer, :w)
|
73
|
+
@monitors[writer].add_interest(:w)
|
74
|
+
end
|
54
75
|
end
|
55
76
|
|
56
77
|
def memcache_client
|
data/lib/iopromise/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: iopromise
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Theo Julienne
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-06-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: promise.rb
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 0.7.4
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: nio4r
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
description: This gem extends promise.rb promises to support an extremely simple pattern
|
28
42
|
for "continuing" execution of the promise in an asynchronous non-blocking way.
|
29
43
|
email:
|
@@ -59,17 +73,10 @@ files:
|
|
59
73
|
- lib/iopromise/executor_pool/base.rb
|
60
74
|
- lib/iopromise/executor_pool/batch.rb
|
61
75
|
- lib/iopromise/executor_pool/sequential.rb
|
62
|
-
- lib/iopromise/faraday.rb
|
63
|
-
- lib/iopromise/faraday/connection.rb
|
64
|
-
- lib/iopromise/faraday/continuable_hydra.rb
|
65
|
-
- lib/iopromise/faraday/executor_pool.rb
|
66
|
-
- lib/iopromise/faraday/multi_socket_action.rb
|
67
|
-
- lib/iopromise/faraday/promise.rb
|
68
76
|
- lib/iopromise/memcached.rb
|
69
77
|
- lib/iopromise/memcached/client.rb
|
70
78
|
- lib/iopromise/memcached/executor_pool.rb
|
71
79
|
- lib/iopromise/memcached/promise.rb
|
72
|
-
- lib/iopromise/rack/context_middleware.rb
|
73
80
|
- lib/iopromise/version.rb
|
74
81
|
- lib/iopromise/view_component.rb
|
75
82
|
- lib/iopromise/view_component/data_loader.rb
|