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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3fc60525e45ec8cfa8a6cd5ba34bf396b77e4d42f3fd566137f14367a2ba31ae
|
4
|
+
data.tar.gz: 1e70b2147a204a220be6b268ae62f67dc06b79065b8ec07636da414264ae626b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b92e71ab4fa32e5c45f9b4a4fa607844f2558f270f69ddb3a63b1394633a4b3e7eb101f77b06e8bc4efe45ad657e00a7d4bad10f34374356f2dc660db17af525
|
7
|
+
data.tar.gz: 4034ef0ae2010b4cea930894d1f7cc05722e6dd0e3c112fe237ce6488441d24d670c098df525046c13d26514ff39ee672c3a26c49f601ddc40b757fbbe691079
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -8,8 +8,9 @@ GIT
|
|
8
8
|
PATH
|
9
9
|
remote: .
|
10
10
|
specs:
|
11
|
-
iopromise (0.1.
|
12
|
-
|
11
|
+
iopromise (0.1.1)
|
12
|
+
nio4r
|
13
|
+
promise.rb (~> 0.7.4)
|
13
14
|
|
14
15
|
GEM
|
15
16
|
remote: https://rubygems.org/
|
@@ -73,6 +74,7 @@ GEM
|
|
73
74
|
minitest (>= 5.1)
|
74
75
|
tzinfo (~> 2.0)
|
75
76
|
zeitwerk (~> 2.3)
|
77
|
+
benchmark-ips (2.9.1)
|
76
78
|
builder (3.2.4)
|
77
79
|
concurrent-ruby (1.1.8)
|
78
80
|
crass (1.0.6)
|
@@ -161,6 +163,7 @@ GEM
|
|
161
163
|
actionpack (>= 4.0)
|
162
164
|
activesupport (>= 4.0)
|
163
165
|
sprockets (>= 3.0.0)
|
166
|
+
stackprof (0.2.17)
|
164
167
|
thor (1.1.0)
|
165
168
|
typhoeus (1.4.0)
|
166
169
|
ethon (>= 0.9.0)
|
@@ -177,6 +180,7 @@ PLATFORMS
|
|
177
180
|
x86_64-linux
|
178
181
|
|
179
182
|
DEPENDENCIES
|
183
|
+
benchmark-ips
|
180
184
|
dalli (= 2.7.11)
|
181
185
|
faraday
|
182
186
|
iopromise!
|
@@ -184,6 +188,7 @@ DEPENDENCIES
|
|
184
188
|
rails
|
185
189
|
rake (~> 13.0)
|
186
190
|
rspec (~> 3.0)
|
191
|
+
stackprof
|
187
192
|
typhoeus
|
188
193
|
view_component
|
189
194
|
|
data/iopromise.gemspec
CHANGED
data/lib/iopromise.rb
CHANGED
@@ -13,32 +13,31 @@ module IOPromise
|
|
13
13
|
class Error < StandardError; end
|
14
14
|
|
15
15
|
class Base < ::Promise
|
16
|
-
def initialize(*)
|
17
|
-
@instrument_begin = []
|
18
|
-
@instrument_end = []
|
19
|
-
@started_executing = false
|
20
|
-
|
21
|
-
super
|
22
|
-
end
|
23
|
-
|
24
16
|
def instrument(begin_cb = nil, end_cb = nil)
|
25
|
-
raise ::IOPromise::Error.new("Instrumentation called after promise already started executing") if
|
26
|
-
|
27
|
-
|
17
|
+
raise ::IOPromise::Error.new("Instrumentation called after promise already started executing") if started_executing?
|
18
|
+
unless begin_cb.nil?
|
19
|
+
@instrument_begin ||= []
|
20
|
+
@instrument_begin << begin_cb
|
21
|
+
end
|
22
|
+
unless end_cb.nil?
|
23
|
+
@instrument_end ||= []
|
24
|
+
@instrument_end << end_cb
|
25
|
+
end
|
28
26
|
end
|
29
27
|
|
30
28
|
def beginning
|
31
|
-
@instrument_begin
|
29
|
+
@instrument_begin&.each { |cb| cb.call(self) }
|
30
|
+
@instrument_begin&.clear
|
32
31
|
@started_executing = true
|
33
32
|
end
|
34
33
|
|
35
34
|
def started_executing?
|
36
|
-
|
35
|
+
!!@started_executing
|
37
36
|
end
|
38
37
|
|
39
38
|
def notify_completion(value: nil, reason: nil)
|
40
|
-
@instrument_end
|
41
|
-
@instrument_end
|
39
|
+
@instrument_end&.each { |cb| cb.call(self, value: value, reason: reason) }
|
40
|
+
@instrument_end&.clear
|
42
41
|
end
|
43
42
|
|
44
43
|
def fulfill(value)
|
@@ -21,7 +21,7 @@ module IOPromise
|
|
21
21
|
# Returns a promise that resolves to a IOPromise::Dalli::Response with the
|
22
22
|
# value for the given key, or +nil+ if the key is not found.
|
23
23
|
def get(key, options = nil)
|
24
|
-
|
24
|
+
@client.perform(:get, key, options)
|
25
25
|
end
|
26
26
|
|
27
27
|
# Convenience function that attempts to fetch the given key, or set
|
@@ -40,7 +40,7 @@ module IOPromise
|
|
40
40
|
!response.exist? :
|
41
41
|
response.value.nil?
|
42
42
|
if not_found && !block.nil?
|
43
|
-
|
43
|
+
block.call.then do |new_val|
|
44
44
|
# delay the final resolution here until after the add succeeds,
|
45
45
|
# to guarantee errors are caught. we could potentially allow
|
46
46
|
# the add to resolve once it's sent (without confirmation), but
|
@@ -48,7 +48,7 @@ module IOPromise
|
|
48
48
|
add(key, new_val, ttl, options).then { new_val }
|
49
49
|
end
|
50
50
|
else
|
51
|
-
|
51
|
+
response.value
|
52
52
|
end
|
53
53
|
end
|
54
54
|
end
|
@@ -56,40 +56,40 @@ module IOPromise
|
|
56
56
|
# Unconditionally sets the +key+ to the +value+ specified.
|
57
57
|
# Returns a promise that resolves to a IOPromise::Dalli::Response.
|
58
58
|
def set(key, value, ttl = nil, options = nil)
|
59
|
-
|
59
|
+
@client.perform(:set, key, value, ttl_or_default(ttl), 0, options)
|
60
60
|
end
|
61
61
|
|
62
62
|
# Conditionally sets the +key+ to the +value+ specified.
|
63
63
|
# Returns a promise that resolves to a IOPromise::Dalli::Response.
|
64
64
|
def add(key, value, ttl = nil, options = nil)
|
65
|
-
|
65
|
+
@client.perform(:add, key, value, ttl_or_default(ttl), options)
|
66
66
|
end
|
67
67
|
|
68
68
|
# Conditionally sets the +key+ to the +value+ specified only
|
69
69
|
# if the key already exists.
|
70
70
|
# Returns a promise that resolves to a IOPromise::Dalli::Response.
|
71
71
|
def replace(key, value, ttl = nil, options = nil)
|
72
|
-
|
72
|
+
@client.perform(:replace, key, value, ttl_or_default(ttl), 0, options)
|
73
73
|
end
|
74
74
|
|
75
75
|
# Deletes the specified key, resolving the promise when complete.
|
76
76
|
def delete(key)
|
77
|
-
|
77
|
+
@client.perform(:delete, key, 0)
|
78
78
|
end
|
79
79
|
|
80
80
|
# Appends a value to the specified key, resolving the promise when complete.
|
81
81
|
# Appending only works for values stored with :raw => true.
|
82
82
|
def append(key, value)
|
83
|
-
|
84
|
-
|
83
|
+
value.then do |resolved_value|
|
84
|
+
@client.perform(:append, key, resolved_value.to_s)
|
85
85
|
end
|
86
86
|
end
|
87
87
|
|
88
88
|
# Prepend a value to the specified key, resolving the promise when complete.
|
89
89
|
# Prepending only works for values stored with :raw => true.
|
90
90
|
def prepend(key, value)
|
91
|
-
|
92
|
-
|
91
|
+
value.then do |resolved_value|
|
92
|
+
@client.perform(:prepend, key, resolved_value.to_s)
|
93
93
|
end
|
94
94
|
end
|
95
95
|
|
@@ -106,7 +106,7 @@ module IOPromise
|
|
106
106
|
# #cas.
|
107
107
|
def incr(key, amt = 1, ttl = nil, default = nil)
|
108
108
|
raise ArgumentError, "Positive values only: #{amt}" if amt < 0
|
109
|
-
|
109
|
+
@client.perform(:incr, key, amt.to_i, ttl_or_default(ttl), default)
|
110
110
|
end
|
111
111
|
|
112
112
|
##
|
@@ -125,17 +125,13 @@ module IOPromise
|
|
125
125
|
# #cas.
|
126
126
|
def decr(key, amt = 1, ttl = nil, default = nil)
|
127
127
|
raise ArgumentError, "Positive values only: #{amt}" if amt < 0
|
128
|
-
|
128
|
+
@client.perform(:decr, key, amt.to_i, ttl_or_default(ttl), default)
|
129
129
|
end
|
130
130
|
|
131
131
|
# TODO: touch, gat, CAS operations
|
132
132
|
|
133
133
|
private
|
134
134
|
|
135
|
-
def execute_as_promise(*args)
|
136
|
-
@client.perform_async(*args)
|
137
|
-
end
|
138
|
-
|
139
135
|
def ttl_or_default(ttl)
|
140
136
|
(ttl || @options[:expires_in]).to_i
|
141
137
|
rescue NoMethodError
|
@@ -3,10 +3,43 @@
|
|
3
3
|
module IOPromise
|
4
4
|
module Dalli
|
5
5
|
class DalliExecutorPool < IOPromise::ExecutorPool::Base
|
6
|
-
def
|
7
|
-
|
6
|
+
def initialize(*)
|
7
|
+
super
|
8
8
|
|
9
|
-
|
9
|
+
@iop_monitor = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def dalli_server
|
13
|
+
@connection_pool
|
14
|
+
end
|
15
|
+
|
16
|
+
def execute_continue
|
17
|
+
dalli_server.execute_continue
|
18
|
+
end
|
19
|
+
|
20
|
+
def connected_socket(sock)
|
21
|
+
close_socket
|
22
|
+
|
23
|
+
@iop_monitor = ::IOPromise::ExecutorContext.current.register_observer_io(self, sock, :r)
|
24
|
+
end
|
25
|
+
|
26
|
+
def close_socket
|
27
|
+
unless @iop_monitor.nil?
|
28
|
+
@iop_monitor.close
|
29
|
+
@iop_monitor = nil
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def monitor_ready(monitor, readiness)
|
34
|
+
dalli_server.async_io_ready(monitor.readable?, monitor.writable?)
|
35
|
+
end
|
36
|
+
|
37
|
+
def set_interest(direction, interested)
|
38
|
+
if interested
|
39
|
+
@iop_monitor.add_interest(direction)
|
40
|
+
else
|
41
|
+
@iop_monitor.remove_interest(direction)
|
42
|
+
end
|
10
43
|
end
|
11
44
|
end
|
12
45
|
end
|
@@ -12,16 +12,16 @@ module IOPromise
|
|
12
12
|
super
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
def perform(*)
|
16
|
+
return super unless @async
|
17
|
+
|
18
|
+
begin
|
19
|
+
super
|
20
|
+
rescue => ex
|
21
|
+
# Wrap any connection errors into a promise, this is more forwards-compatible
|
22
|
+
# if we ever attempt to make connecting/server fallback nonblocking too.
|
23
|
+
Promise.new.tap { |p| p.reject(ex) }
|
20
24
|
end
|
21
|
-
rescue => ex
|
22
|
-
# Wrap any connection errors into a promise, this is more forwards-compatible
|
23
|
-
# if we ever attempt to make connecting/server fallback nonblocking too.
|
24
|
-
Promise.new.tap { |p| p.reject(ex) }
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
@@ -30,10 +30,14 @@ module IOPromise
|
|
30
30
|
@async = options.delete(:iopromise_async) == true
|
31
31
|
|
32
32
|
if @async
|
33
|
+
@write_buffer = +""
|
34
|
+
@read_buffer = +""
|
33
35
|
async_reset
|
34
36
|
|
35
37
|
@next_opaque_id = 0
|
36
38
|
@pending_ops = {}
|
39
|
+
|
40
|
+
@executor_pool = DalliExecutorPool.for(self)
|
37
41
|
end
|
38
42
|
|
39
43
|
super
|
@@ -51,60 +55,53 @@ module IOPromise
|
|
51
55
|
super
|
52
56
|
end
|
53
57
|
|
54
|
-
def
|
55
|
-
|
56
|
-
@write_offset = 0
|
57
|
-
|
58
|
-
@read_buffer = +""
|
59
|
-
@read_offset = 0
|
60
|
-
end
|
58
|
+
def connect
|
59
|
+
super
|
61
60
|
|
62
|
-
|
63
|
-
|
64
|
-
unless ready_writers.nil? || ready_writers.empty?
|
65
|
-
# we are able to write, so write as much as we can.
|
66
|
-
sock_write_nonblock
|
61
|
+
if async?
|
62
|
+
@executor_pool.connected_socket(@sock)
|
67
63
|
end
|
64
|
+
end
|
68
65
|
|
69
|
-
|
70
|
-
|
66
|
+
def async_reset
|
67
|
+
@write_buffer.clear
|
68
|
+
@write_offset = 0
|
71
69
|
|
72
|
-
|
73
|
-
|
74
|
-
end
|
70
|
+
@read_buffer.clear
|
71
|
+
@read_offset = 0
|
75
72
|
|
76
|
-
|
77
|
-
|
78
|
-
exceptions = [@sock]
|
79
|
-
timeout = nil
|
73
|
+
@executor_pool.close_socket if defined? @executor_pool
|
74
|
+
end
|
80
75
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
op.execute_pool.complete(op)
|
86
|
-
end
|
76
|
+
def async_io_ready(readable, writable)
|
77
|
+
async_sock_write_nonblock if writable
|
78
|
+
async_sock_read_nonblock if readable
|
79
|
+
end
|
87
80
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
81
|
+
# called by ExecutorPool to continue processing for this server
|
82
|
+
def execute_continue
|
83
|
+
timeout = @options[:socket_timeout]
|
84
|
+
@pending_ops.select! do |key, op|
|
85
|
+
if op.timeout?
|
86
|
+
op.reject(Timeout::Error.new)
|
87
|
+
next false # this op is done
|
88
|
+
end
|
93
89
|
|
94
90
|
# let all pending operations know that they are seeing the
|
95
91
|
# select loop. this starts the timer for the operation, because
|
96
92
|
# it guarantees we're now working on it.
|
97
93
|
# this is more accurate than starting the timer when we buffer
|
98
94
|
# the write.
|
99
|
-
|
100
|
-
op.in_select_loop
|
101
|
-
end
|
95
|
+
op.in_select_loop
|
102
96
|
|
103
|
-
|
104
|
-
timeout =
|
97
|
+
remaining = op.timeout_remaining
|
98
|
+
timeout = remaining if remaining < timeout
|
99
|
+
|
100
|
+
true # keep
|
105
101
|
end
|
106
102
|
|
107
|
-
|
103
|
+
@executor_pool.select_timeout = timeout
|
104
|
+
@executor_pool.set_interest(:r, !@pending_ops.empty?)
|
108
105
|
end
|
109
106
|
|
110
107
|
private
|
@@ -115,8 +112,14 @@ module IOPromise
|
|
115
112
|
|
116
113
|
|
117
114
|
def promised_request(key, &block)
|
118
|
-
promise
|
119
|
-
|
115
|
+
promise = ::IOPromise::Dalli::DalliPromise.new(self, key)
|
116
|
+
|
117
|
+
new_id = @next_opaque_id
|
118
|
+
@pending_ops[new_id] = promise
|
119
|
+
@next_opaque_id = (@next_opaque_id + 1) & 0xffff_ffff
|
120
|
+
|
121
|
+
async_buffered_write(block.call(new_id))
|
122
|
+
|
120
123
|
promise
|
121
124
|
end
|
122
125
|
|
@@ -128,12 +131,12 @@ module IOPromise
|
|
128
131
|
end
|
129
132
|
end
|
130
133
|
|
131
|
-
def
|
132
|
-
|
134
|
+
def async_generic_write_op(op, key, value, ttl, cas, options)
|
135
|
+
value.then do |value|
|
133
136
|
(value, flags) = serialize(key, value, options)
|
134
137
|
ttl = sanitize_ttl(ttl)
|
135
138
|
|
136
|
-
|
139
|
+
guard_max_value_with_raise(key, value)
|
137
140
|
|
138
141
|
promised_request(key) do |opaque|
|
139
142
|
[REQUEST, OPCODES[op], key.bytesize, 8, 0, 0, value.bytesize + key.bytesize + 8, opaque, cas, flags, ttl, key, value].pack(FORMAT[op])
|
@@ -143,20 +146,19 @@ module IOPromise
|
|
143
146
|
|
144
147
|
def set(key, value, ttl, cas, options)
|
145
148
|
return super unless async?
|
146
|
-
|
147
|
-
generic_write_op(:set, key, value, ttl, cas, options)
|
149
|
+
async_generic_write_op(:set, key, value, ttl, cas, options)
|
148
150
|
end
|
149
151
|
|
150
152
|
def add(key, value, ttl, options)
|
151
153
|
return super unless async?
|
152
154
|
|
153
|
-
|
155
|
+
async_generic_write_op(:add, key, value, ttl, 0, options)
|
154
156
|
end
|
155
157
|
|
156
158
|
def replace(key, value, ttl, cas, options)
|
157
159
|
return super unless async?
|
158
160
|
|
159
|
-
|
161
|
+
async_generic_write_op(:replace, key, value, ttl, cas, options)
|
160
162
|
end
|
161
163
|
|
162
164
|
def delete(key, cas)
|
@@ -167,7 +169,7 @@ module IOPromise
|
|
167
169
|
end
|
168
170
|
end
|
169
171
|
|
170
|
-
def
|
172
|
+
def async_append_prepend_op(op, key, value)
|
171
173
|
promised_request(key) do |opaque|
|
172
174
|
[REQUEST, OPCODES[op], key.bytesize, 0, 0, 0, value.bytesize + key.bytesize, opaque, 0, key, value].pack(FORMAT[op])
|
173
175
|
end
|
@@ -176,13 +178,13 @@ module IOPromise
|
|
176
178
|
def append(key, value)
|
177
179
|
return super unless async?
|
178
180
|
|
179
|
-
|
181
|
+
async_append_prepend_op(:append, key, value)
|
180
182
|
end
|
181
183
|
|
182
184
|
def prepend(key, value)
|
183
185
|
return super unless async?
|
184
186
|
|
185
|
-
|
187
|
+
async_append_prepend_op(:prepend, key, value)
|
186
188
|
end
|
187
189
|
|
188
190
|
def flush
|
@@ -193,7 +195,7 @@ module IOPromise
|
|
193
195
|
end
|
194
196
|
end
|
195
197
|
|
196
|
-
def
|
198
|
+
def async_decr_incr(opcode, key, count, ttl, default)
|
197
199
|
expiry = default ? sanitize_ttl(ttl) : 0xFFFFFFFF
|
198
200
|
default ||= 0
|
199
201
|
(h, l) = split(count)
|
@@ -206,103 +208,118 @@ module IOPromise
|
|
206
208
|
def decr(key, count, ttl, default)
|
207
209
|
return super unless async?
|
208
210
|
|
209
|
-
|
211
|
+
async_decr_incr :decr, key, count, ttl, default
|
210
212
|
end
|
211
213
|
|
212
214
|
def incr(key, count, ttl, default)
|
213
215
|
return super unless async?
|
214
216
|
|
215
|
-
|
216
|
-
end
|
217
|
-
|
218
|
-
def new_pending(key)
|
219
|
-
promise = ::IOPromise::Dalli::DalliPromise.new(self, key)
|
220
|
-
new_id = @next_opaque_id
|
221
|
-
@pending_ops[new_id] = promise
|
222
|
-
@next_opaque_id = (@next_opaque_id + 1) & 0xffff_ffff
|
223
|
-
[promise, new_id]
|
217
|
+
async_decr_incr :incr, key, count, ttl, default
|
224
218
|
end
|
225
219
|
|
226
|
-
def
|
220
|
+
def async_buffered_write(data)
|
227
221
|
@write_buffer << data
|
228
|
-
|
222
|
+
async_sock_write_nonblock
|
229
223
|
end
|
230
224
|
|
231
|
-
def
|
225
|
+
def async_sock_write_nonblock
|
226
|
+
remaining = @write_buffer.byteslice(@write_offset, @write_buffer.length)
|
232
227
|
begin
|
233
|
-
bytes_written = @sock.write_nonblock(
|
234
|
-
rescue
|
235
|
-
|
228
|
+
bytes_written = @sock.write_nonblock(remaining, exception: false)
|
229
|
+
rescue Errno::EINTR
|
230
|
+
retry
|
236
231
|
end
|
232
|
+
|
233
|
+
return if bytes_written == :wait_writable
|
237
234
|
|
238
235
|
@write_offset += bytes_written
|
239
|
-
|
240
|
-
|
236
|
+
completed = (@write_offset == @write_buffer.length)
|
237
|
+
if completed
|
238
|
+
@write_buffer.clear
|
241
239
|
@write_offset = 0
|
242
240
|
end
|
241
|
+
@executor_pool.set_interest(:w, !completed)
|
243
242
|
rescue SystemCallError, Timeout::Error => e
|
244
243
|
failure!(e)
|
245
244
|
end
|
246
245
|
|
247
246
|
FULL_HEADER = 'CCnCCnNNQ'
|
248
247
|
|
249
|
-
def
|
250
|
-
|
248
|
+
def read_available
|
249
|
+
loop do
|
250
|
+
result = @sock.read_nonblock(8196, exception: false)
|
251
|
+
if result == :wait_readable
|
252
|
+
break
|
253
|
+
elsif result == :wait_writable
|
254
|
+
break
|
255
|
+
elsif result
|
256
|
+
@read_buffer << result
|
257
|
+
else
|
258
|
+
raise Errno::ECONNRESET, "Connection reset: #{safe_options.inspect}"
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
def async_sock_read_nonblock
|
264
|
+
read_available
|
251
265
|
|
252
266
|
buf = @read_buffer
|
253
267
|
pos = @read_offset
|
254
268
|
|
255
269
|
while buf.bytesize - pos >= 24
|
256
|
-
header = buf.
|
270
|
+
header = buf.byteslice(pos, 24)
|
257
271
|
(magic, opcode, key_length, extra_length, data_type, status, body_length, opaque, cas) = header.unpack(FULL_HEADER)
|
258
272
|
|
259
273
|
if buf.bytesize - pos >= 24 + body_length
|
260
|
-
|
261
|
-
|
262
|
-
flags = buf.slice(pos + 24, 4).unpack1("N")
|
263
|
-
end
|
274
|
+
exists = (status != 1) # Key not found
|
275
|
+
this_pos = pos
|
264
276
|
|
265
|
-
key = buf.
|
266
|
-
value = buf.
|
277
|
+
# key = buf.byteslice(this_pos + 24 + extra_length, key_length)
|
278
|
+
value = buf.byteslice(this_pos + 24 + extra_length + key_length, body_length - key_length - extra_length) if exists
|
267
279
|
|
268
280
|
pos = pos + 24 + body_length
|
269
281
|
|
270
282
|
promise = @pending_ops.delete(opaque)
|
271
283
|
next if promise.nil?
|
272
284
|
|
273
|
-
|
274
|
-
raise Dalli::DalliError, "Response error #{status}: #{Dalli::RESPONSE_CODES[status]}" unless
|
275
|
-
|
276
|
-
exists = (status != 1) # Key not found
|
285
|
+
begin
|
286
|
+
raise Dalli::DalliError, "Response error #{status}: #{Dalli::RESPONSE_CODES[status]}" unless status == 0 || status == 1 || status == 2 || status == 5
|
287
|
+
|
277
288
|
final_value = nil
|
278
289
|
if opcode == OPCODES[:incr] || opcode == OPCODES[:decr]
|
279
290
|
final_value = value.unpack1("Q>")
|
280
291
|
elsif exists
|
292
|
+
flags = if extra_length >= 4
|
293
|
+
buf.byteslice(this_pos + 24, 4).unpack1("N")
|
294
|
+
else
|
295
|
+
0
|
296
|
+
end
|
281
297
|
final_value = deserialize(value, flags)
|
282
298
|
end
|
283
299
|
|
284
|
-
::IOPromise::Dalli::Response.new(
|
300
|
+
response = ::IOPromise::Dalli::Response.new(
|
285
301
|
key: promise.key,
|
286
302
|
value: final_value,
|
287
303
|
exists: exists,
|
288
304
|
stored: !(status == 2 || status == 5), # Key exists or Item not stored
|
289
305
|
cas: cas,
|
290
306
|
)
|
291
|
-
end
|
292
307
|
|
293
|
-
|
294
|
-
|
308
|
+
promise.fulfill(response)
|
309
|
+
rescue => ex
|
310
|
+
promise.reject(ex)
|
311
|
+
end
|
295
312
|
else
|
296
313
|
# not enough data yet, wait for more
|
297
314
|
break
|
298
315
|
end
|
299
316
|
end
|
300
317
|
|
301
|
-
|
302
|
-
|
303
|
-
if @read_offset == @read_buffer.length
|
304
|
-
@read_buffer = +""
|
318
|
+
if pos == @read_buffer.length
|
319
|
+
@read_buffer.clear
|
305
320
|
@read_offset = 0
|
321
|
+
else
|
322
|
+
@read_offset = pos
|
306
323
|
end
|
307
324
|
|
308
325
|
rescue SystemCallError, Timeout::Error, EOFError => e
|
@@ -314,7 +331,6 @@ module IOPromise
|
|
314
331
|
# all pending operations need to be rejected when a failure occurs
|
315
332
|
@pending_ops.each do |op|
|
316
333
|
op.reject(ex)
|
317
|
-
op.execute_pool.complete(op)
|
318
334
|
end
|
319
335
|
@pending_ops = {}
|
320
336
|
end
|
@@ -322,8 +338,8 @@ module IOPromise
|
|
322
338
|
super
|
323
339
|
end
|
324
340
|
|
325
|
-
#
|
326
|
-
def
|
341
|
+
# this is guard_max_value from the master version, rather than using the yield block.
|
342
|
+
def guard_max_value_with_raise(key, value)
|
327
343
|
return if value.bytesize <= @options[:value_max_bytes]
|
328
344
|
|
329
345
|
message = "Value for #{key} over max size: #{@options[:value_max_bytes]} <= #{value.bytesize}"
|