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
data/lib/iopromise/dalli.rb
DELETED
@@ -1,146 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'dalli'
|
4
|
-
require_relative 'promise'
|
5
|
-
require_relative 'patch_dalli'
|
6
|
-
|
7
|
-
module IOPromise
|
8
|
-
module Dalli
|
9
|
-
class Client
|
10
|
-
# General note:
|
11
|
-
# There is no need for explicit get_multi or batching, as requests
|
12
|
-
# are sent as soon as the IOPromise is created, multiple can be
|
13
|
-
# awaiting response at any time, and responses are automatically demuxed.
|
14
|
-
def initialize(servers = nil, options = {})
|
15
|
-
@cache_nils = !!options[:cache_nils]
|
16
|
-
options[:iopromise_async] = true
|
17
|
-
@options = options
|
18
|
-
@client = ::Dalli::Client.new(servers, options)
|
19
|
-
end
|
20
|
-
|
21
|
-
# Returns a promise that resolves to a IOPromise::Dalli::Response with the
|
22
|
-
# value for the given key, or +nil+ if the key is not found.
|
23
|
-
def get(key, options = nil)
|
24
|
-
execute_as_promise(:get, key, options)
|
25
|
-
end
|
26
|
-
|
27
|
-
# Convenience function that attempts to fetch the given key, or set
|
28
|
-
# the key with a dynamically generated value if it does not exist.
|
29
|
-
# Either way, the returned promise will resolve to the cached or computed
|
30
|
-
# value.
|
31
|
-
#
|
32
|
-
# If the value does not exist then the provided block is run to generate
|
33
|
-
# the value (which can also be a promise), after which the value is set
|
34
|
-
# if it still doesn't exist.
|
35
|
-
def fetch(key, ttl = nil, options = nil, &block)
|
36
|
-
# match the Dalli behaviour exactly
|
37
|
-
options = options.nil? ? ::Dalli::Client::CACHE_NILS : options.merge(::Dalli::Client::CACHE_NILS) if @cache_nils
|
38
|
-
get(key, options).then do |response|
|
39
|
-
not_found = @options[:cache_nils] ?
|
40
|
-
!response.exist? :
|
41
|
-
response.value.nil?
|
42
|
-
if not_found && !block.nil?
|
43
|
-
Promise.resolve(block.call).then do |new_val|
|
44
|
-
# delay the final resolution here until after the add succeeds,
|
45
|
-
# to guarantee errors are caught. we could potentially allow
|
46
|
-
# the add to resolve once it's sent (without confirmation), but
|
47
|
-
# we do need to wait on the add promise to ensure it's sent.
|
48
|
-
add(key, new_val, ttl, options).then { new_val }
|
49
|
-
end
|
50
|
-
else
|
51
|
-
Promise.resolve(response.value)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
# Unconditionally sets the +key+ to the +value+ specified.
|
57
|
-
# Returns a promise that resolves to a IOPromise::Dalli::Response.
|
58
|
-
def set(key, value, ttl = nil, options = nil)
|
59
|
-
execute_as_promise(:set, key, value, ttl_or_default(ttl), 0, options)
|
60
|
-
end
|
61
|
-
|
62
|
-
# Conditionally sets the +key+ to the +value+ specified.
|
63
|
-
# Returns a promise that resolves to a IOPromise::Dalli::Response.
|
64
|
-
def add(key, value, ttl = nil, options = nil)
|
65
|
-
execute_as_promise(:add, key, value, ttl_or_default(ttl), options)
|
66
|
-
end
|
67
|
-
|
68
|
-
# Conditionally sets the +key+ to the +value+ specified only
|
69
|
-
# if the key already exists.
|
70
|
-
# Returns a promise that resolves to a IOPromise::Dalli::Response.
|
71
|
-
def replace(key, value, ttl = nil, options = nil)
|
72
|
-
execute_as_promise(:replace, key, value, ttl_or_default(ttl), 0, options)
|
73
|
-
end
|
74
|
-
|
75
|
-
# Deletes the specified key, resolving the promise when complete.
|
76
|
-
def delete(key)
|
77
|
-
execute_as_promise(:delete, key, 0)
|
78
|
-
end
|
79
|
-
|
80
|
-
# Appends a value to the specified key, resolving the promise when complete.
|
81
|
-
# Appending only works for values stored with :raw => true.
|
82
|
-
def append(key, value)
|
83
|
-
Promise.resolve(value).then do |resolved_value|
|
84
|
-
execute_as_promise(:append, key, resolved_value.to_s)
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
# Prepend a value to the specified key, resolving the promise when complete.
|
89
|
-
# Prepending only works for values stored with :raw => true.
|
90
|
-
def prepend(key, value)
|
91
|
-
Promise.resolve(value).then do |resolved_value|
|
92
|
-
execute_as_promise(:prepend, key, resolved_value.to_s)
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
##
|
97
|
-
# Incr adds the given amount to the counter on the memcached server.
|
98
|
-
# Amt must be a positive integer value.
|
99
|
-
#
|
100
|
-
# If default is nil, the counter must already exist or the operation
|
101
|
-
# will fail and will return nil. Otherwise this method will return
|
102
|
-
# the new value for the counter.
|
103
|
-
#
|
104
|
-
# Note that the ttl will only apply if the counter does not already
|
105
|
-
# exist. To increase an existing counter and update its TTL, use
|
106
|
-
# #cas.
|
107
|
-
def incr(key, amt = 1, ttl = nil, default = nil)
|
108
|
-
raise ArgumentError, "Positive values only: #{amt}" if amt < 0
|
109
|
-
execute_as_promise(:incr, key, amt.to_i, ttl_or_default(ttl), default)
|
110
|
-
end
|
111
|
-
|
112
|
-
##
|
113
|
-
# Decr subtracts the given amount from the counter on the memcached server.
|
114
|
-
# Amt must be a positive integer value.
|
115
|
-
#
|
116
|
-
# memcached counters are unsigned and cannot hold negative values. Calling
|
117
|
-
# decr on a counter which is 0 will just return 0.
|
118
|
-
#
|
119
|
-
# If default is nil, the counter must already exist or the operation
|
120
|
-
# will fail and will return nil. Otherwise this method will return
|
121
|
-
# the new value for the counter.
|
122
|
-
#
|
123
|
-
# Note that the ttl will only apply if the counter does not already
|
124
|
-
# exist. To decrease an existing counter and update its TTL, use
|
125
|
-
# #cas.
|
126
|
-
def decr(key, amt = 1, ttl = nil, default = nil)
|
127
|
-
raise ArgumentError, "Positive values only: #{amt}" if amt < 0
|
128
|
-
execute_as_promise(:decr, key, amt.to_i, ttl_or_default(ttl), default)
|
129
|
-
end
|
130
|
-
|
131
|
-
# TODO: touch, gat, CAS operations
|
132
|
-
|
133
|
-
private
|
134
|
-
|
135
|
-
def execute_as_promise(*args)
|
136
|
-
@client.perform_async(*args)
|
137
|
-
end
|
138
|
-
|
139
|
-
def ttl_or_default(ttl)
|
140
|
-
(ttl || @options[:expires_in]).to_i
|
141
|
-
rescue NoMethodError
|
142
|
-
raise ArgumentError, "Cannot convert ttl (#{ttl}) to an integer"
|
143
|
-
end
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|
@@ -1,13 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module IOPromise
|
4
|
-
module Dalli
|
5
|
-
class DalliExecutorPool < IOPromise::ExecutorPool::Base
|
6
|
-
def execute_continue(ready_readers, ready_writers, ready_exceptions)
|
7
|
-
dalli_server = @connection_pool
|
8
|
-
|
9
|
-
dalli_server.execute_continue(ready_readers, ready_writers, ready_exceptions)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
@@ -1,337 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'dalli'
|
4
|
-
require_relative 'response'
|
5
|
-
|
6
|
-
module IOPromise
|
7
|
-
module Dalli
|
8
|
-
module AsyncClient
|
9
|
-
def initialize(servers = nil, options = {})
|
10
|
-
@async = options[:iopromise_async] == true
|
11
|
-
|
12
|
-
super
|
13
|
-
end
|
14
|
-
|
15
|
-
def perform_async(*args)
|
16
|
-
if @async
|
17
|
-
perform(*args)
|
18
|
-
else
|
19
|
-
raise ArgumentError, "Cannot perform_async when async is not enabled."
|
20
|
-
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
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
module AsyncServer
|
29
|
-
def initialize(attribs, options = {})
|
30
|
-
@async = options.delete(:iopromise_async) == true
|
31
|
-
|
32
|
-
if @async
|
33
|
-
async_reset
|
34
|
-
|
35
|
-
@next_opaque_id = 0
|
36
|
-
@pending_ops = {}
|
37
|
-
end
|
38
|
-
|
39
|
-
super
|
40
|
-
end
|
41
|
-
|
42
|
-
def async?
|
43
|
-
@async
|
44
|
-
end
|
45
|
-
|
46
|
-
def close
|
47
|
-
if async?
|
48
|
-
async_reset
|
49
|
-
end
|
50
|
-
|
51
|
-
super
|
52
|
-
end
|
53
|
-
|
54
|
-
def async_reset
|
55
|
-
@write_buffer = +""
|
56
|
-
@write_offset = 0
|
57
|
-
|
58
|
-
@read_buffer = +""
|
59
|
-
@read_offset = 0
|
60
|
-
end
|
61
|
-
|
62
|
-
# called by ExecutorPool to continue processing for this server
|
63
|
-
def execute_continue(ready_readers, ready_writers, ready_exceptions)
|
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
|
67
|
-
end
|
68
|
-
|
69
|
-
readers_empty = ready_readers.nil? || ready_readers.empty?
|
70
|
-
exceptions_empty = ready_exceptions.nil? || ready_exceptions.empty?
|
71
|
-
|
72
|
-
if !readers_empty || !exceptions_empty
|
73
|
-
sock_read_nonblock
|
74
|
-
end
|
75
|
-
|
76
|
-
readers = []
|
77
|
-
writers = []
|
78
|
-
exceptions = [@sock]
|
79
|
-
timeout = nil
|
80
|
-
|
81
|
-
to_timeout = @pending_ops.select { |key, op| op.timeout? }
|
82
|
-
to_timeout.each do |key, op|
|
83
|
-
@pending_ops.delete(key)
|
84
|
-
op.reject(Timeout::Error.new)
|
85
|
-
op.execute_pool.complete(op)
|
86
|
-
end
|
87
|
-
|
88
|
-
unless @pending_ops.empty?
|
89
|
-
# wait for writability if we have pending data to write
|
90
|
-
writers << @sock if @write_buffer.bytesize > @write_offset
|
91
|
-
# and always call back when there is data available to read
|
92
|
-
readers << @sock
|
93
|
-
|
94
|
-
# let all pending operations know that they are seeing the
|
95
|
-
# select loop. this starts the timer for the operation, because
|
96
|
-
# it guarantees we're now working on it.
|
97
|
-
# this is more accurate than starting the timer when we buffer
|
98
|
-
# the write.
|
99
|
-
@pending_ops.each do |_, op|
|
100
|
-
op.in_select_loop
|
101
|
-
end
|
102
|
-
|
103
|
-
# mark the amount of time left of the closest to timeout.
|
104
|
-
timeout = @pending_ops.map { |_, op| op.timeout_remaining }.min
|
105
|
-
end
|
106
|
-
|
107
|
-
[readers, writers, exceptions, timeout]
|
108
|
-
end
|
109
|
-
|
110
|
-
private
|
111
|
-
|
112
|
-
REQUEST = ::Dalli::Server::REQUEST
|
113
|
-
OPCODES = ::Dalli::Server::OPCODES
|
114
|
-
FORMAT = ::Dalli::Server::FORMAT
|
115
|
-
|
116
|
-
|
117
|
-
def promised_request(key, &block)
|
118
|
-
promise, opaque = new_pending(key)
|
119
|
-
buffered_write(block.call(opaque))
|
120
|
-
promise
|
121
|
-
end
|
122
|
-
|
123
|
-
def get(key, options = nil)
|
124
|
-
return super unless async?
|
125
|
-
|
126
|
-
promised_request(key) do |opaque|
|
127
|
-
[REQUEST, OPCODES[:get], key.bytesize, 0, 0, 0, key.bytesize, opaque, 0, key].pack(FORMAT[:get])
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
def generic_write_op(op, key, value, ttl, cas, options)
|
132
|
-
Promise.resolve(value).then do |value|
|
133
|
-
(value, flags) = serialize(key, value, options)
|
134
|
-
ttl = sanitize_ttl(ttl)
|
135
|
-
|
136
|
-
guard_max_value(key, value)
|
137
|
-
|
138
|
-
promised_request(key) do |opaque|
|
139
|
-
[REQUEST, OPCODES[op], key.bytesize, 8, 0, 0, value.bytesize + key.bytesize + 8, opaque, cas, flags, ttl, key, value].pack(FORMAT[op])
|
140
|
-
end
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
def set(key, value, ttl, cas, options)
|
145
|
-
return super unless async?
|
146
|
-
|
147
|
-
generic_write_op(:set, key, value, ttl, cas, options)
|
148
|
-
end
|
149
|
-
|
150
|
-
def add(key, value, ttl, options)
|
151
|
-
return super unless async?
|
152
|
-
|
153
|
-
generic_write_op(:add, key, value, ttl, 0, options)
|
154
|
-
end
|
155
|
-
|
156
|
-
def replace(key, value, ttl, cas, options)
|
157
|
-
return super unless async?
|
158
|
-
|
159
|
-
generic_write_op(:replace, key, value, ttl, cas, options)
|
160
|
-
end
|
161
|
-
|
162
|
-
def delete(key, cas)
|
163
|
-
return super unless async?
|
164
|
-
|
165
|
-
promised_request(key) do |opaque|
|
166
|
-
[REQUEST, OPCODES[:delete], key.bytesize, 0, 0, 0, key.bytesize, opaque, cas, key].pack(FORMAT[:delete])
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
def append_prepend_op(op, key, value)
|
171
|
-
promised_request(key) do |opaque|
|
172
|
-
[REQUEST, OPCODES[op], key.bytesize, 0, 0, 0, value.bytesize + key.bytesize, opaque, 0, key, value].pack(FORMAT[op])
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
def append(key, value)
|
177
|
-
return super unless async?
|
178
|
-
|
179
|
-
append_prepend_op(:append, key, value)
|
180
|
-
end
|
181
|
-
|
182
|
-
def prepend(key, value)
|
183
|
-
return super unless async?
|
184
|
-
|
185
|
-
append_prepend_op(:prepend, key, value)
|
186
|
-
end
|
187
|
-
|
188
|
-
def flush
|
189
|
-
return super unless async?
|
190
|
-
|
191
|
-
promised_request(nil) do |opaque|
|
192
|
-
[REQUEST, OPCODES[:flush], 0, 4, 0, 0, 4, opaque, 0, 0].pack(FORMAT[:flush])
|
193
|
-
end
|
194
|
-
end
|
195
|
-
|
196
|
-
def decr_incr(opcode, key, count, ttl, default)
|
197
|
-
expiry = default ? sanitize_ttl(ttl) : 0xFFFFFFFF
|
198
|
-
default ||= 0
|
199
|
-
(h, l) = split(count)
|
200
|
-
(dh, dl) = split(default)
|
201
|
-
promised_request(key) do |opaque|
|
202
|
-
req = [REQUEST, OPCODES[opcode], key.bytesize, 20, 0, 0, key.bytesize + 20, opaque, 0, h, l, dh, dl, expiry, key].pack(FORMAT[opcode])
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
def decr(key, count, ttl, default)
|
207
|
-
return super unless async?
|
208
|
-
|
209
|
-
decr_incr :decr, key, count, ttl, default
|
210
|
-
end
|
211
|
-
|
212
|
-
def incr(key, count, ttl, default)
|
213
|
-
return super unless async?
|
214
|
-
|
215
|
-
decr_incr :incr, key, count, ttl, default
|
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]
|
224
|
-
end
|
225
|
-
|
226
|
-
def buffered_write(data)
|
227
|
-
@write_buffer << data
|
228
|
-
sock_write_nonblock
|
229
|
-
end
|
230
|
-
|
231
|
-
def sock_write_nonblock
|
232
|
-
begin
|
233
|
-
bytes_written = @sock.write_nonblock(@write_buffer.byteslice(@write_offset..-1))
|
234
|
-
rescue IO::WaitWritable, Errno::EINTR
|
235
|
-
return # no room to write immediately
|
236
|
-
end
|
237
|
-
|
238
|
-
@write_offset += bytes_written
|
239
|
-
if @write_offset == @write_buffer.length
|
240
|
-
@write_buffer = +""
|
241
|
-
@write_offset = 0
|
242
|
-
end
|
243
|
-
rescue SystemCallError, Timeout::Error => e
|
244
|
-
failure!(e)
|
245
|
-
end
|
246
|
-
|
247
|
-
FULL_HEADER = 'CCnCCnNNQ'
|
248
|
-
|
249
|
-
def sock_read_nonblock
|
250
|
-
@read_buffer << @sock.read_available
|
251
|
-
|
252
|
-
buf = @read_buffer
|
253
|
-
pos = @read_offset
|
254
|
-
|
255
|
-
while buf.bytesize - pos >= 24
|
256
|
-
header = buf.slice(pos, 24)
|
257
|
-
(magic, opcode, key_length, extra_length, data_type, status, body_length, opaque, cas) = header.unpack(FULL_HEADER)
|
258
|
-
|
259
|
-
if buf.bytesize - pos >= 24 + body_length
|
260
|
-
flags = 0
|
261
|
-
if extra_length >= 4
|
262
|
-
flags = buf.slice(pos + 24, 4).unpack1("N")
|
263
|
-
end
|
264
|
-
|
265
|
-
key = buf.slice(pos + 24 + extra_length, key_length)
|
266
|
-
value = buf.slice(pos + 24 + extra_length + key_length, body_length - key_length - extra_length)
|
267
|
-
|
268
|
-
pos = pos + 24 + body_length
|
269
|
-
|
270
|
-
promise = @pending_ops.delete(opaque)
|
271
|
-
next if promise.nil?
|
272
|
-
|
273
|
-
result = Promise.resolve(true).then do # auto capture exceptions below
|
274
|
-
raise Dalli::DalliError, "Response error #{status}: #{Dalli::RESPONSE_CODES[status]}" unless [0,1,2,5].include?(status)
|
275
|
-
|
276
|
-
exists = (status != 1) # Key not found
|
277
|
-
final_value = nil
|
278
|
-
if opcode == OPCODES[:incr] || opcode == OPCODES[:decr]
|
279
|
-
final_value = value.unpack1("Q>")
|
280
|
-
elsif exists
|
281
|
-
final_value = deserialize(value, flags)
|
282
|
-
end
|
283
|
-
|
284
|
-
::IOPromise::Dalli::Response.new(
|
285
|
-
key: promise.key,
|
286
|
-
value: final_value,
|
287
|
-
exists: exists,
|
288
|
-
stored: !(status == 2 || status == 5), # Key exists or Item not stored
|
289
|
-
cas: cas,
|
290
|
-
)
|
291
|
-
end
|
292
|
-
|
293
|
-
promise.fulfill(result)
|
294
|
-
promise.execute_pool.complete(promise)
|
295
|
-
else
|
296
|
-
# not enough data yet, wait for more
|
297
|
-
break
|
298
|
-
end
|
299
|
-
end
|
300
|
-
|
301
|
-
@read_offset = pos
|
302
|
-
|
303
|
-
if @read_offset == @read_buffer.length
|
304
|
-
@read_buffer = +""
|
305
|
-
@read_offset = 0
|
306
|
-
end
|
307
|
-
|
308
|
-
rescue SystemCallError, Timeout::Error, EOFError => e
|
309
|
-
failure!(e)
|
310
|
-
end
|
311
|
-
|
312
|
-
def failure!(ex)
|
313
|
-
if async?
|
314
|
-
# all pending operations need to be rejected when a failure occurs
|
315
|
-
@pending_ops.each do |op|
|
316
|
-
op.reject(ex)
|
317
|
-
op.execute_pool.complete(op)
|
318
|
-
end
|
319
|
-
@pending_ops = {}
|
320
|
-
end
|
321
|
-
|
322
|
-
super
|
323
|
-
end
|
324
|
-
|
325
|
-
# FIXME: this is from the master version, rather than using the yield block.
|
326
|
-
def guard_max_value(key, value)
|
327
|
-
return if value.bytesize <= @options[:value_max_bytes]
|
328
|
-
|
329
|
-
message = "Value for #{key} over max size: #{@options[:value_max_bytes]} <= #{value.bytesize}"
|
330
|
-
raise Dalli::ValueOverMaxSize, message
|
331
|
-
end
|
332
|
-
end
|
333
|
-
end
|
334
|
-
end
|
335
|
-
|
336
|
-
::Dalli::Server.prepend(IOPromise::Dalli::AsyncServer)
|
337
|
-
::Dalli::Client.prepend(IOPromise::Dalli::AsyncClient)
|