iopromise 0.1.1 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- 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 +44 -15
- data/lib/iopromise/cancel_context.rb +51 -0
- data/lib/iopromise/data_loader.rb +65 -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
|