iopromise 0.1.0 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +1 -2
  3. data/Gemfile +4 -10
  4. data/Gemfile.lock +7 -30
  5. data/README.md +23 -3
  6. data/bin/setup +0 -3
  7. data/iopromise.gemspec +1 -0
  8. data/lib/iopromise.rb +47 -18
  9. data/lib/iopromise/cancel_context.rb +51 -0
  10. data/lib/iopromise/data_loader.rb +58 -0
  11. data/lib/iopromise/deferred.rb +2 -2
  12. data/lib/iopromise/deferred/executor_pool.rb +26 -10
  13. data/lib/iopromise/deferred/promise.rb +15 -2
  14. data/lib/iopromise/executor_context.rb +47 -59
  15. data/lib/iopromise/executor_pool/base.rb +26 -7
  16. data/lib/iopromise/executor_pool/batch.rb +5 -3
  17. data/lib/iopromise/executor_pool/sequential.rb +6 -14
  18. data/lib/iopromise/rack/context_middleware.rb +5 -6
  19. data/lib/iopromise/version.rb +1 -1
  20. data/lib/iopromise/view_component/data_loader.rb +3 -44
  21. metadata +18 -18
  22. data/lib/iopromise/dalli.rb +0 -13
  23. data/lib/iopromise/dalli/client.rb +0 -146
  24. data/lib/iopromise/dalli/executor_pool.rb +0 -13
  25. data/lib/iopromise/dalli/patch_dalli.rb +0 -337
  26. data/lib/iopromise/dalli/promise.rb +0 -52
  27. data/lib/iopromise/dalli/response.rb +0 -25
  28. data/lib/iopromise/faraday.rb +0 -17
  29. data/lib/iopromise/faraday/connection.rb +0 -25
  30. data/lib/iopromise/faraday/continuable_hydra.rb +0 -29
  31. data/lib/iopromise/faraday/executor_pool.rb +0 -19
  32. data/lib/iopromise/faraday/multi_socket_action.rb +0 -107
  33. data/lib/iopromise/faraday/promise.rb +0 -42
  34. data/lib/iopromise/memcached.rb +0 -13
  35. data/lib/iopromise/memcached/client.rb +0 -22
  36. data/lib/iopromise/memcached/executor_pool.rb +0 -61
  37. 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
@@ -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
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'memcached/client'
4
-
5
- module IOPromise
6
- module Memcached
7
- class << self
8
- def new(*args, **kwargs)
9
- ::IOPromise::Memcached::Client.new(*args, **kwargs)
10
- end
11
- end
12
- end
13
- end
@@ -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