iopromise 0.1.0 → 0.1.4
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/.github/workflows/main.yml +1 -2
- data/Gemfile +4 -10
- data/Gemfile.lock +7 -30
- data/README.md +23 -3
- data/bin/setup +0 -3
- data/iopromise.gemspec +1 -0
- data/lib/iopromise.rb +47 -18
- data/lib/iopromise/cancel_context.rb +51 -0
- data/lib/iopromise/data_loader.rb +58 -0
- data/lib/iopromise/deferred.rb +2 -2
- data/lib/iopromise/deferred/executor_pool.rb +26 -10
- data/lib/iopromise/deferred/promise.rb +15 -2
- data/lib/iopromise/executor_context.rb +47 -59
- data/lib/iopromise/executor_pool/base.rb +26 -7
- data/lib/iopromise/executor_pool/batch.rb +5 -3
- data/lib/iopromise/executor_pool/sequential.rb +6 -14
- data/lib/iopromise/rack/context_middleware.rb +5 -6
- data/lib/iopromise/version.rb +1 -1
- data/lib/iopromise/view_component/data_loader.rb +3 -44
- metadata +18 -18
- data/lib/iopromise/dalli.rb +0 -13
- data/lib/iopromise/dalli/client.rb +0 -146
- data/lib/iopromise/dalli/executor_pool.rb +0 -13
- data/lib/iopromise/dalli/patch_dalli.rb +0 -337
- data/lib/iopromise/dalli/promise.rb +0 -52
- data/lib/iopromise/dalli/response.rb +0 -25
- 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/memcached.rb +0 -13
- data/lib/iopromise/memcached/client.rb +0 -22
- data/lib/iopromise/memcached/executor_pool.rb +0 -61
- data/lib/iopromise/memcached/promise.rb +0 -32
@@ -1,52 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'executor_pool'
|
4
|
-
|
5
|
-
module IOPromise
|
6
|
-
module Dalli
|
7
|
-
class DalliPromise < ::IOPromise::Base
|
8
|
-
attr_reader :key
|
9
|
-
|
10
|
-
def initialize(server = nil, key = nil)
|
11
|
-
super()
|
12
|
-
|
13
|
-
@server = server
|
14
|
-
@key = key
|
15
|
-
@start_time = nil
|
16
|
-
|
17
|
-
::IOPromise::ExecutorContext.current.register(self) unless @server.nil? || @key.nil?
|
18
|
-
end
|
19
|
-
|
20
|
-
def wait
|
21
|
-
if @server.nil? || @key.nil?
|
22
|
-
super
|
23
|
-
else
|
24
|
-
::IOPromise::ExecutorContext.current.wait_for_all_data(end_when_complete: self)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def execute_pool
|
29
|
-
DalliExecutorPool.for(@server)
|
30
|
-
end
|
31
|
-
|
32
|
-
def in_select_loop
|
33
|
-
if @start_time.nil?
|
34
|
-
@start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def timeout_remaining
|
39
|
-
now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
40
|
-
elapsed = now - @start_time
|
41
|
-
remaining = @server.options[:socket_timeout] - elapsed
|
42
|
-
return 0 if remaining < 0
|
43
|
-
remaining
|
44
|
-
end
|
45
|
-
|
46
|
-
def timeout?
|
47
|
-
return false if @start_time.nil?
|
48
|
-
timeout_remaining <= 0
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
@@ -1,25 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module IOPromise
|
4
|
-
module Dalli
|
5
|
-
class Response
|
6
|
-
attr_reader :key, :value, :cas
|
7
|
-
|
8
|
-
def initialize(key:, value:, exists: false, stored: false, cas: nil)
|
9
|
-
@key = key
|
10
|
-
@value = value
|
11
|
-
@exists = exists
|
12
|
-
@stored = stored
|
13
|
-
@cas = cas
|
14
|
-
end
|
15
|
-
|
16
|
-
def exist?
|
17
|
-
@exists
|
18
|
-
end
|
19
|
-
|
20
|
-
def stored?
|
21
|
-
@stored
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
data/lib/iopromise/faraday.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'faraday/connection'
|
4
|
-
|
5
|
-
module IOPromise
|
6
|
-
module Faraday
|
7
|
-
class << self
|
8
|
-
def new(url = nil, options = {}, &block)
|
9
|
-
options = ::Faraday.default_connection_options.merge(options)
|
10
|
-
::IOPromise::Faraday::Connection.new(url, options) do |faraday|
|
11
|
-
faraday.adapter :typhoeus
|
12
|
-
block.call unless block.nil?
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
@@ -1,25 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'faraday'
|
4
|
-
|
5
|
-
require_relative 'promise'
|
6
|
-
|
7
|
-
module IOPromise
|
8
|
-
module Faraday
|
9
|
-
class Connection < ::Faraday::Connection
|
10
|
-
def with_deferred_parallel
|
11
|
-
@parallel_manager = FaradayPromise.parallel_manager
|
12
|
-
yield
|
13
|
-
ensure
|
14
|
-
@parallel_manager = nil
|
15
|
-
end
|
16
|
-
|
17
|
-
def get_as_promise(*args, **kwargs)
|
18
|
-
@parallel_manager = FaradayPromise.parallel_manager
|
19
|
-
FaradayPromise.new(get(*args, **kwargs))
|
20
|
-
ensure
|
21
|
-
@parallel_manager = nil
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'typhoeus'
|
4
|
-
require_relative 'multi_socket_action'
|
5
|
-
|
6
|
-
module IOPromise
|
7
|
-
module Faraday
|
8
|
-
class ContinuableHydra < Typhoeus::Hydra
|
9
|
-
class << self
|
10
|
-
def for_current_thread
|
11
|
-
Thread.current[:faraday_promise_typhoeus_hydra] ||= new
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
def initialize(options = {})
|
16
|
-
super(options)
|
17
|
-
|
18
|
-
@multi = MultiSocketAction.new(options.reject{|k,_| k==:max_concurrency})
|
19
|
-
end
|
20
|
-
|
21
|
-
def execute_continue(ready_readers, ready_writers, ready_exceptions)
|
22
|
-
# fill up the curl easy handle as much as possible
|
23
|
-
dequeue_many
|
24
|
-
|
25
|
-
@multi.execute_continue(ready_readers, ready_writers, ready_exceptions)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'continuable_hydra'
|
4
|
-
|
5
|
-
module IOPromise
|
6
|
-
module Faraday
|
7
|
-
class FaradayExecutorPool < IOPromise::ExecutorPool::Base
|
8
|
-
def execute_continue(ready_readers, ready_writers, ready_exceptions)
|
9
|
-
# mark all pending promises as executing since they could be started any time now.
|
10
|
-
# ideally we would do this on dequeue.
|
11
|
-
@pending.each do |promise|
|
12
|
-
begin_executing(promise) unless promise.started_executing?
|
13
|
-
end
|
14
|
-
|
15
|
-
ContinuableHydra.for_current_thread.execute_continue(ready_readers, ready_writers, ready_exceptions)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
@@ -1,107 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'ethon'
|
4
|
-
|
5
|
-
Ethon::Curl.ffi_lib 'curl'
|
6
|
-
Ethon::Curl.attach_function :multi_socket_action, :curl_multi_socket_action, [:pointer, :int, :int, :pointer], :multi_code
|
7
|
-
|
8
|
-
module IOPromise
|
9
|
-
module Faraday
|
10
|
-
class MultiSocketAction < Ethon::Multi
|
11
|
-
CURL_POLL_NONE = 0
|
12
|
-
CURL_POLL_IN = 1
|
13
|
-
CURL_POLL_OUT = 2
|
14
|
-
CURL_POLL_INOUT = 3
|
15
|
-
CURL_POLL_REMOVE = 4
|
16
|
-
|
17
|
-
CURL_SOCKET_BAD = -1
|
18
|
-
CURL_SOCKET_TIMEOUT = CURL_SOCKET_BAD
|
19
|
-
|
20
|
-
CURLM_OK = 0
|
21
|
-
|
22
|
-
CURL_CSELECT_IN = 0x01
|
23
|
-
CURL_CSELECT_OUT = 0x02
|
24
|
-
CURL_CSELECT_ERR = 0x04
|
25
|
-
|
26
|
-
def initialize(options = {})
|
27
|
-
super(options)
|
28
|
-
|
29
|
-
@read_fds = {}
|
30
|
-
@write_fds = {}
|
31
|
-
@select_timeout = nil
|
32
|
-
|
33
|
-
self.socketfunction = @keep_socketfunction = proc do |handle, sock, what, userp, socketp|
|
34
|
-
if what == CURL_POLL_REMOVE
|
35
|
-
@read_fds.delete(sock)
|
36
|
-
@write_fds.delete(sock)
|
37
|
-
else
|
38
|
-
# reuse existing if we have it anywhere
|
39
|
-
io = @read_fds[sock] || @write_fds[sock] || IO.for_fd(sock).tap { |io| io.autoclose = false }
|
40
|
-
if what == CURL_POLL_INOUT
|
41
|
-
@read_fds[sock] = io
|
42
|
-
@write_fds[sock] = io
|
43
|
-
elsif what == CURL_POLL_IN
|
44
|
-
@read_fds[sock] = io
|
45
|
-
@write_fds.delete(sock)
|
46
|
-
elsif what == CURL_POLL_OUT
|
47
|
-
@read_fds.delete(sock)
|
48
|
-
@write_fds[sock] = io
|
49
|
-
end
|
50
|
-
end
|
51
|
-
CURLM_OK
|
52
|
-
end
|
53
|
-
|
54
|
-
self.timerfunction = @keep_timerfunction = proc do |handle, timeout_ms, userp|
|
55
|
-
if timeout_ms > 0x7fffffffffffffff # FIXME: wrongly encoded
|
56
|
-
@select_timeout = nil
|
57
|
-
else
|
58
|
-
@select_timeout = timeout_ms.to_f / 1_000
|
59
|
-
end
|
60
|
-
CURLM_OK
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def perform
|
65
|
-
# stubbed out, we don't want any of the multi_perform logic
|
66
|
-
end
|
67
|
-
|
68
|
-
def run
|
69
|
-
# stubbed out, we don't want any of the multi_perform logic
|
70
|
-
end
|
71
|
-
|
72
|
-
def execute_continue(ready_readers, ready_writers, ready_exceptions)
|
73
|
-
running_handles = ::FFI::MemoryPointer.new(:int)
|
74
|
-
|
75
|
-
flags = Hash.new(0)
|
76
|
-
|
77
|
-
unless ready_readers.nil?
|
78
|
-
ready_readers.each do |s|
|
79
|
-
flags[s.fileno] |= CURL_CSELECT_IN
|
80
|
-
end
|
81
|
-
end
|
82
|
-
unless ready_writers.nil?
|
83
|
-
ready_writers.each do |s|
|
84
|
-
flags[s.fileno] |= CURL_CSELECT_OUT
|
85
|
-
end
|
86
|
-
end
|
87
|
-
unless ready_exceptions.nil?
|
88
|
-
ready_exceptions.each do |s|
|
89
|
-
flags[s.fileno] |= CURL_CSELECT_ERR
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
flags.each do |fd, bitmask|
|
94
|
-
Ethon::Curl.multi_socket_action(handle, fd, bitmask, running_handles)
|
95
|
-
end
|
96
|
-
|
97
|
-
if flags.empty?
|
98
|
-
Ethon::Curl.multi_socket_action(handle, CURL_SOCKET_TIMEOUT, 0, running_handles)
|
99
|
-
end
|
100
|
-
|
101
|
-
check
|
102
|
-
|
103
|
-
[@read_fds.values, @write_fds.values, [], @select_timeout]
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|
@@ -1,42 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'continuable_hydra'
|
4
|
-
require_relative 'executor_pool'
|
5
|
-
|
6
|
-
module IOPromise
|
7
|
-
module Faraday
|
8
|
-
class FaradayPromise < ::IOPromise::Base
|
9
|
-
def self.parallel_manager
|
10
|
-
ContinuableHydra.for_current_thread
|
11
|
-
end
|
12
|
-
|
13
|
-
def initialize(response = nil)
|
14
|
-
super()
|
15
|
-
|
16
|
-
@response = response
|
17
|
-
@started = false
|
18
|
-
|
19
|
-
unless @response.nil?
|
20
|
-
@response.on_complete do |response_env|
|
21
|
-
fulfill(@response)
|
22
|
-
execute_pool.complete(self)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
::IOPromise::ExecutorContext.current.register(self) unless @response.nil?
|
27
|
-
end
|
28
|
-
|
29
|
-
def wait
|
30
|
-
if @response.nil?
|
31
|
-
super
|
32
|
-
else
|
33
|
-
::IOPromise::ExecutorContext.current.wait_for_all_data(end_when_complete: self)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def execute_pool
|
38
|
-
FaradayExecutorPool.for(Thread.current)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
data/lib/iopromise/memcached.rb
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'memcached'
|
4
|
-
require_relative 'promise'
|
5
|
-
|
6
|
-
module IOPromise
|
7
|
-
module Memcached
|
8
|
-
class Client
|
9
|
-
def initialize(*args, **kwargs)
|
10
|
-
if args.first.is_a?(::Memcached::Client)
|
11
|
-
@client = args.first.clone
|
12
|
-
else
|
13
|
-
@client = ::Memcached::Client.new(*args, **kwargs)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def get_as_promise(key)
|
18
|
-
MemcachePromise.new(@client, key)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,61 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module IOPromise
|
4
|
-
module Memcached
|
5
|
-
class MemcacheExecutorPool < ::IOPromise::ExecutorPool::Batch
|
6
|
-
def next_batch
|
7
|
-
super
|
8
|
-
|
9
|
-
unless @current_batch.empty?
|
10
|
-
@keys_to_promises = @current_batch.group_by { |promise| promise.key }
|
11
|
-
@current_batch.each { |promise| begin_executing(promise) }
|
12
|
-
begin
|
13
|
-
memcache_client.begin_get_multi(@keys_to_promises.keys)
|
14
|
-
rescue => e
|
15
|
-
@keys_to_promises.values.flatten.each do |promise|
|
16
|
-
promise.reject(e)
|
17
|
-
complete(promise)
|
18
|
-
@current_batch.delete(promise)
|
19
|
-
end
|
20
|
-
|
21
|
-
@keys_to_promises = nil
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def execute_continue(ready_readers, ready_writers, ready_exceptions)
|
27
|
-
if @current_batch.empty?
|
28
|
-
next_batch
|
29
|
-
end
|
30
|
-
|
31
|
-
return [[], [], [], nil] if @current_batch.empty?
|
32
|
-
|
33
|
-
so_far, readers, writers = memcache_client.continue_get_multi
|
34
|
-
|
35
|
-
# when we're done (nothing to wait on), fill in any remaining keys with nil for completions to occur
|
36
|
-
if readers.empty? && writers.empty?
|
37
|
-
@keys_to_promises.each do |key, _|
|
38
|
-
so_far[key] = nil unless so_far.include? key
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
so_far.each do |key, value|
|
43
|
-
next unless @keys_to_promises[key]
|
44
|
-
@keys_to_promises[key].each do |promise|
|
45
|
-
next if promise.fulfilled?
|
46
|
-
|
47
|
-
promise.fulfill(value)
|
48
|
-
complete(promise)
|
49
|
-
@current_batch.delete(promise)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
[readers, writers, [], nil]
|
54
|
-
end
|
55
|
-
|
56
|
-
def memcache_client
|
57
|
-
@connection_pool
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
@@ -1,32 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'executor_pool'
|
4
|
-
|
5
|
-
module IOPromise
|
6
|
-
module Memcached
|
7
|
-
class MemcachePromise < ::IOPromise::Base
|
8
|
-
attr_reader :key
|
9
|
-
|
10
|
-
def initialize(client = nil, key = nil)
|
11
|
-
super()
|
12
|
-
|
13
|
-
@client = client
|
14
|
-
@key = key
|
15
|
-
|
16
|
-
::IOPromise::ExecutorContext.current.register(self) unless @client.nil? || @key.nil?
|
17
|
-
end
|
18
|
-
|
19
|
-
def wait
|
20
|
-
if @client.nil? || @key.nil?
|
21
|
-
super
|
22
|
-
else
|
23
|
-
::IOPromise::ExecutorContext.current.wait_for_all_data(end_when_complete: self)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def execute_pool
|
28
|
-
MemcacheExecutorPool.for(@client)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|