moneta 1.1.0 → 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES +5 -0
- data/lib/moneta.rb +2 -0
- data/lib/moneta/adapters/sequel.rb +363 -128
- data/lib/moneta/adapters/sqlite.rb +15 -1
- data/lib/moneta/version.rb +1 -1
- data/script/benchmarks +84 -12
- data/spec/moneta/adapters/sequel/adapter_sequel_spec.rb +48 -69
- data/spec/moneta/adapters/sequel/helper.rb +38 -0
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 867a31ea01c90748ed86ae701f221d4a628b7c4932c8e0aa6c8b0f13f02a516b
|
4
|
+
data.tar.gz: e3f89ab42ca23c9db8ddbcede6c6c447a8829ab78e01e1eb67edd1a6f2ac402e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 58e3592ad92768341454f0c7b637fc614f3cd3d820566840333b2c229601627ac10ae77509fec769acd9c71a08f602b2c7d059ee0e6c7e2042d814f91201acf2
|
7
|
+
data.tar.gz: 8d9eeb664e800fd1ebff92dea3701fb01aa25a453647f2ba76d54b1a3602e6897899f8cedff823046096ee675f6089355912de240ae5c8e96ed606c41fa5573f
|
data/CHANGES
CHANGED
data/lib/moneta.rb
CHANGED
@@ -110,6 +110,8 @@ module Moneta
|
|
110
110
|
when :Sequel
|
111
111
|
# Sequel accept only base64 keys
|
112
112
|
transformer[:key] << :base64
|
113
|
+
# If using HStore, binary data is not allowed
|
114
|
+
transformer[:value] << :base64 if options[:hstore]
|
113
115
|
when :ActiveRecord, :DataMapper
|
114
116
|
# DataMapper and AR accept only base64 keys and values
|
115
117
|
transformer[:key] << :base64
|
@@ -30,6 +30,10 @@ module Moneta
|
|
30
30
|
# row of the table in the value_column using the hstore format. The row to use is
|
31
31
|
# the one where the value_column is equal to the value of this option, and will be created
|
32
32
|
# if it doesn't exist.
|
33
|
+
# @option options [Symbol] :each_key_server Some adapters are unable to do
|
34
|
+
# multiple operations with a single connection. For these, it is
|
35
|
+
# possible to specify a separate connection to use for `#each_key`. Use
|
36
|
+
# in conjunction with Sequel's `:servers` option
|
33
37
|
# @option options All other options passed to `Sequel#connect`
|
34
38
|
def self.new(options = {})
|
35
39
|
extensions = options.delete(:extensions)
|
@@ -79,6 +83,7 @@ module Moneta
|
|
79
83
|
@table_name = (options.delete(:table) || :moneta).to_sym
|
80
84
|
@key_column = options.delete(:key_column) || :k
|
81
85
|
@value_column = options.delete(:value_column) || :v
|
86
|
+
@each_key_server = options.delete(:each_key_server)
|
82
87
|
|
83
88
|
create_proc = options.delete(:create_table)
|
84
89
|
if create_proc.nil?
|
@@ -88,23 +93,26 @@ module Moneta
|
|
88
93
|
end
|
89
94
|
|
90
95
|
@table = @backend[@table_name]
|
96
|
+
prepare_statements
|
91
97
|
end
|
92
98
|
|
93
99
|
# (see Proxy#key?)
|
94
100
|
def key?(key, options = {})
|
95
|
-
|
101
|
+
@key.call(key: key) != nil
|
96
102
|
end
|
97
103
|
|
98
104
|
# (see Proxy#load)
|
99
105
|
def load(key, options = {})
|
100
|
-
@
|
106
|
+
if row = @load.call(key: key)
|
107
|
+
row[value_column]
|
108
|
+
end
|
101
109
|
end
|
102
110
|
|
103
111
|
# (see Proxy#store)
|
104
112
|
def store(key, value, options = {})
|
105
113
|
blob_value = blob(value)
|
106
|
-
unless @
|
107
|
-
@
|
114
|
+
unless @store_update.call(key: key, value: blob_value) == 1
|
115
|
+
@create.call(key: key, value: blob_value)
|
108
116
|
end
|
109
117
|
value
|
110
118
|
rescue ::Sequel::DatabaseError
|
@@ -112,9 +120,9 @@ module Moneta
|
|
112
120
|
(tries += 1) < 10 ? retry : raise
|
113
121
|
end
|
114
122
|
|
115
|
-
# (see Proxy#
|
123
|
+
# (see Proxy#create)
|
116
124
|
def create(key, value, options = {})
|
117
|
-
@
|
125
|
+
@create.call(key: key, value: blob(value))
|
118
126
|
true
|
119
127
|
rescue UniqueConstraintViolation
|
120
128
|
false
|
@@ -123,13 +131,16 @@ module Moneta
|
|
123
131
|
# (see Proxy#increment)
|
124
132
|
def increment(key, amount = 1, options = {})
|
125
133
|
@backend.transaction do
|
126
|
-
if existing = @
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
134
|
+
if existing = @load_for_update.call(key: key)
|
135
|
+
existing_value = existing[value_column]
|
136
|
+
amount += Integer(existing_value)
|
137
|
+
raise IncrementError, "no update" unless @increment_update.call(
|
138
|
+
key: key,
|
139
|
+
value: existing_value,
|
140
|
+
new_value: blob(amount.to_s)
|
141
|
+
) == 1
|
131
142
|
else
|
132
|
-
@
|
143
|
+
@create.call(key: key, value: blob(amount.to_s))
|
133
144
|
end
|
134
145
|
amount
|
135
146
|
end
|
@@ -142,7 +153,7 @@ module Moneta
|
|
142
153
|
# (see Proxy#delete)
|
143
154
|
def delete(key, options = {})
|
144
155
|
value = load(key, options)
|
145
|
-
@
|
156
|
+
@delete.call(key: key)
|
146
157
|
value
|
147
158
|
end
|
148
159
|
|
@@ -160,19 +171,19 @@ module Moneta
|
|
160
171
|
|
161
172
|
# (see Proxy#slice)
|
162
173
|
def slice(*keys, **options)
|
163
|
-
@
|
174
|
+
@slice.all(keys).map! { |row| [row[key_column], row[value_column]] }
|
164
175
|
end
|
165
176
|
|
166
177
|
# (see Proxy#values_at)
|
167
178
|
def values_at(*keys, **options)
|
168
|
-
pairs = slice(*keys, **options)
|
179
|
+
pairs = Hash[slice(*keys, **options)]
|
169
180
|
keys.map { |key| pairs[key] }
|
170
181
|
end
|
171
182
|
|
172
183
|
# (see Proxy#fetch_values)
|
173
184
|
def fetch_values(*keys, **options)
|
174
185
|
return values_at(*keys, **options) unless block_given?
|
175
|
-
existing = slice(*keys, **options)
|
186
|
+
existing = Hash[slice(*keys, **options)]
|
176
187
|
keys.map do |key|
|
177
188
|
if existing.key? key
|
178
189
|
existing[key]
|
@@ -185,7 +196,7 @@ module Moneta
|
|
185
196
|
# (see Proxy#merge!)
|
186
197
|
def merge!(pairs, options = {})
|
187
198
|
@backend.transaction do
|
188
|
-
existing =
|
199
|
+
existing = Hash[slice_for_update(pairs)]
|
189
200
|
update_pairs, insert_pairs = pairs.partition { |k, _| existing.key?(k) }
|
190
201
|
@table.import([key_column, value_column], blob_pairs(insert_pairs))
|
191
202
|
|
@@ -196,7 +207,7 @@ module Moneta
|
|
196
207
|
end
|
197
208
|
|
198
209
|
update_pairs.each do |key, value|
|
199
|
-
@
|
210
|
+
@store_update.call(key: key, value: blob(value))
|
200
211
|
end
|
201
212
|
end
|
202
213
|
|
@@ -206,23 +217,22 @@ module Moneta
|
|
206
217
|
# (see Proxy#each_key)
|
207
218
|
def each_key
|
208
219
|
return enum_for(:each_key) { @table.count } unless block_given?
|
209
|
-
@
|
210
|
-
|
220
|
+
if @each_key_server
|
221
|
+
@table.server(@each_key_server).order(key_column).select(key_column).paged_each do |row|
|
222
|
+
yield row[key_column]
|
223
|
+
end
|
224
|
+
else
|
225
|
+
@table.select(key_column).order(key_column).paged_each(stream: false) do |row|
|
226
|
+
yield row[key_column]
|
227
|
+
end
|
211
228
|
end
|
212
229
|
self
|
213
230
|
end
|
214
231
|
|
215
232
|
protected
|
216
233
|
|
217
|
-
# See https://github.com/jeremyevans/sequel/issues/715
|
218
234
|
def blob(s)
|
219
|
-
|
220
|
-
nil
|
221
|
-
elsif s.empty?
|
222
|
-
''
|
223
|
-
else
|
224
|
-
::Sequel.blob(s)
|
225
|
-
end
|
235
|
+
::Sequel.blob(s) unless s == nil
|
226
236
|
end
|
227
237
|
|
228
238
|
def blob_pairs(pairs)
|
@@ -240,30 +250,89 @@ module Moneta
|
|
240
250
|
end
|
241
251
|
end
|
242
252
|
|
243
|
-
def
|
244
|
-
@
|
245
|
-
|
246
|
-
|
247
|
-
as_hash(key_column, value_column)
|
253
|
+
def slice_for_update(pairs)
|
254
|
+
@slice_for_update.all(pairs.map { |k, _| k }.to_a).map! do |row|
|
255
|
+
[row[key_column], row[value_column]]
|
256
|
+
end
|
248
257
|
end
|
249
258
|
|
250
259
|
def yield_merge_pairs(pairs)
|
251
|
-
existing =
|
260
|
+
existing = Hash[slice_for_update(pairs)]
|
252
261
|
pairs.map do |key, new_value|
|
253
262
|
new_value = yield(key, existing[key], new_value) if existing.key?(key)
|
254
263
|
[key, new_value]
|
255
264
|
end
|
256
265
|
end
|
257
266
|
|
267
|
+
def statement_id(id)
|
268
|
+
"moneta_#{@table_name}_#{id}".to_sym
|
269
|
+
end
|
270
|
+
|
271
|
+
def prepare_statements
|
272
|
+
prepare_key
|
273
|
+
prepare_load
|
274
|
+
prepare_store
|
275
|
+
prepare_create
|
276
|
+
prepare_increment
|
277
|
+
prepare_delete
|
278
|
+
prepare_slice
|
279
|
+
end
|
280
|
+
|
281
|
+
def prepare_key
|
282
|
+
@key = @table.
|
283
|
+
where(key_column => :$key).select(1).
|
284
|
+
prepare(:first, statement_id(:key))
|
285
|
+
end
|
286
|
+
|
287
|
+
def prepare_load
|
288
|
+
@load = @table.
|
289
|
+
where(key_column => :$key).select(value_column).
|
290
|
+
prepare(:first, statement_id(:load))
|
291
|
+
end
|
292
|
+
|
293
|
+
def prepare_store
|
294
|
+
@store_update = @table.
|
295
|
+
where(key_column => :$key).
|
296
|
+
prepare(:update, statement_id(:store_update), value_column => :$value)
|
297
|
+
end
|
298
|
+
|
299
|
+
def prepare_create
|
300
|
+
@create = @table.
|
301
|
+
prepare(:insert, statement_id(:create), key_column => :$key, value_column => :$value)
|
302
|
+
end
|
303
|
+
|
304
|
+
def prepare_increment
|
305
|
+
@load_for_update = @table.
|
306
|
+
where(key_column => :$key).for_update.
|
307
|
+
select(value_column).
|
308
|
+
prepare(:first, statement_id(:load_for_update))
|
309
|
+
@increment_update ||= @table.
|
310
|
+
where(key_column => :$key, value_column => :$value).
|
311
|
+
prepare(:update, statement_id(:increment_update), value_column => :$new_value)
|
312
|
+
end
|
313
|
+
|
314
|
+
def prepare_delete
|
315
|
+
@delete = @table.where(key_column => :$key).
|
316
|
+
prepare(:delete, statement_id(:delete))
|
317
|
+
end
|
318
|
+
|
319
|
+
def prepare_slice
|
320
|
+
@slice_for_update = ::Sequel::Dataset::PlaceholderLiteralizer.loader(@table) do |pl, ds|
|
321
|
+
ds.filter(key_column => pl.arg).select(key_column, value_column).for_update
|
322
|
+
end
|
323
|
+
|
324
|
+
@slice = ::Sequel::Dataset::PlaceholderLiteralizer.loader(@table) do |pl, ds|
|
325
|
+
ds.filter(key_column => pl.arg).select(key_column, value_column)
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
258
329
|
# @api private
|
259
330
|
class IncrementError < ::Sequel::DatabaseError; end
|
260
331
|
|
261
332
|
# @api private
|
262
333
|
class MySQL < Sequel
|
263
334
|
def store(key, value, options = {})
|
264
|
-
@
|
265
|
-
on_duplicate_key_update.
|
266
|
-
insert(key_column => key, value_column => blob(value))
|
335
|
+
@store.call(key: key, value: blob(value))
|
267
336
|
value
|
268
337
|
end
|
269
338
|
|
@@ -271,12 +340,12 @@ module Moneta
|
|
271
340
|
@backend.transaction do
|
272
341
|
# this creates a row-level lock even if there is no existing row (a
|
273
342
|
# "gap lock").
|
274
|
-
if
|
343
|
+
if row = @load_for_update.call(key: key)
|
275
344
|
# Integer() will raise an exception if the existing value cannot be parsed
|
276
|
-
amount += Integer(
|
277
|
-
@
|
345
|
+
amount += Integer(row[value_column])
|
346
|
+
@increment_update.call(key: key, value: amount)
|
278
347
|
else
|
279
|
-
@
|
348
|
+
@create.call(key: key, value: amount)
|
280
349
|
end
|
281
350
|
amount
|
282
351
|
end
|
@@ -295,38 +364,49 @@ module Moneta
|
|
295
364
|
|
296
365
|
self
|
297
366
|
end
|
367
|
+
|
368
|
+
def each_key
|
369
|
+
return super unless block_given? && @each_key_server && @table.respond_to?(:stream)
|
370
|
+
# Order is not required when streaming
|
371
|
+
@table.server(@each_key_server).select(key_column).paged_each do |row|
|
372
|
+
yield row[key_column]
|
373
|
+
end
|
374
|
+
self
|
375
|
+
end
|
376
|
+
|
377
|
+
protected
|
378
|
+
|
379
|
+
def prepare_store
|
380
|
+
@store = @table.
|
381
|
+
on_duplicate_key_update.
|
382
|
+
prepare(:insert, statement_id(:store), key_column => :$key, value_column => :$value)
|
383
|
+
end
|
384
|
+
|
385
|
+
def prepare_increment
|
386
|
+
@increment_update = @table.
|
387
|
+
where(key_column => :$key).
|
388
|
+
prepare(:update, statement_id(:increment_update), value_column => :$value)
|
389
|
+
super
|
390
|
+
end
|
298
391
|
end
|
299
392
|
|
300
393
|
# @api private
|
301
394
|
class Postgres < Sequel
|
302
395
|
def store(key, value, options = {})
|
303
|
-
@
|
304
|
-
insert_conflict(
|
305
|
-
target: key_column,
|
306
|
-
update: {value_column => ::Sequel[:excluded][value_column]}).
|
307
|
-
insert(key_column => key, value_column => blob(value))
|
396
|
+
@store.call(key: key, value: blob(value))
|
308
397
|
value
|
309
398
|
end
|
310
399
|
|
311
400
|
def increment(key, amount = 1, options = {})
|
312
|
-
|
313
|
-
|
314
|
-
::Sequel[@table_name][value_column],
|
315
|
-
'UTF8').cast(Integer) + amount).cast(String),
|
316
|
-
'UTF8')
|
317
|
-
|
318
|
-
if row = @table.
|
319
|
-
returning(value_column).
|
320
|
-
insert_conflict(target: key_column, update: {value_column => update_expr}).
|
321
|
-
insert(key_column => key, value_column => amount.to_s).
|
322
|
-
first
|
323
|
-
then
|
401
|
+
result = @increment.call(key: key, value: blob(amount.to_s), amount: amount)
|
402
|
+
if row = result.first
|
324
403
|
row[value_column].to_i
|
325
404
|
end
|
326
405
|
end
|
327
406
|
|
328
407
|
def delete(key, options = {})
|
329
|
-
|
408
|
+
result = @delete.call(key: key)
|
409
|
+
if row = result.first
|
330
410
|
row[value_column]
|
331
411
|
end
|
332
412
|
end
|
@@ -343,6 +423,45 @@ module Moneta
|
|
343
423
|
|
344
424
|
self
|
345
425
|
end
|
426
|
+
|
427
|
+
def each_key
|
428
|
+
return super unless block_given? && !@each_key_server && @table.respond_to?(:use_cursor)
|
429
|
+
# With a cursor, this will Just Work.
|
430
|
+
@table.select(key_column).paged_each do |row|
|
431
|
+
yield row[key_column]
|
432
|
+
end
|
433
|
+
self
|
434
|
+
end
|
435
|
+
|
436
|
+
protected
|
437
|
+
|
438
|
+
def prepare_store
|
439
|
+
@store = @table.
|
440
|
+
insert_conflict(
|
441
|
+
target: key_column,
|
442
|
+
update: {value_column => ::Sequel[:excluded][value_column]}).
|
443
|
+
prepare(:insert, statement_id(:store), key_column => :$key, value_column => :$value)
|
444
|
+
end
|
445
|
+
|
446
|
+
def prepare_increment
|
447
|
+
update_expr = ::Sequel[:convert_to].function(
|
448
|
+
(::Sequel[:convert_from].function(
|
449
|
+
::Sequel[@table_name][value_column],
|
450
|
+
'UTF8').cast(Integer) + :$amount).cast(String),
|
451
|
+
'UTF8')
|
452
|
+
|
453
|
+
@increment = @table.
|
454
|
+
returning(value_column).
|
455
|
+
insert_conflict(target: key_column, update: {value_column => update_expr}).
|
456
|
+
prepare(:insert, statement_id(:increment), key_column => :$key, value_column => :$value)
|
457
|
+
end
|
458
|
+
|
459
|
+
def prepare_delete
|
460
|
+
@delete = @table.
|
461
|
+
returning(value_column).
|
462
|
+
where(key_column => :$key).
|
463
|
+
prepare(:delete, statement_id(:delete))
|
464
|
+
end
|
346
465
|
end
|
347
466
|
|
348
467
|
# @api private
|
@@ -356,66 +475,79 @@ module Moneta
|
|
356
475
|
end
|
357
476
|
|
358
477
|
def key?(key, options = {})
|
359
|
-
|
478
|
+
if @key
|
479
|
+
row = @key.call(row: @row, key: key) || false
|
480
|
+
row && row[:present]
|
481
|
+
else
|
482
|
+
@key_pl.get(key)
|
483
|
+
end
|
360
484
|
end
|
361
485
|
|
362
486
|
def store(key, value, options = {})
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
487
|
+
@backend.transaction do
|
488
|
+
create_row
|
489
|
+
@store.call(row: @row, pair: ::Sequel.hstore(key => value))
|
490
|
+
end
|
367
491
|
value
|
368
492
|
end
|
369
493
|
|
370
494
|
def load(key, options = {})
|
371
|
-
@
|
495
|
+
if row = @load.call(row: @row, key: key)
|
496
|
+
row[:value]
|
497
|
+
end
|
372
498
|
end
|
373
499
|
|
374
500
|
def delete(key, options = {})
|
375
|
-
|
376
|
-
|
377
|
-
|
501
|
+
@backend.transaction do
|
502
|
+
value = load(key, options)
|
503
|
+
@delete.call(row: @row, key: key)
|
504
|
+
value
|
505
|
+
end
|
378
506
|
end
|
379
507
|
|
380
508
|
def increment(key, amount = 1, options = {})
|
381
|
-
|
382
|
-
|
383
|
-
key,
|
384
|
-
|
385
|
-
|
386
|
-
0) + amount).cast(String))
|
387
|
-
|
388
|
-
if row = @table.
|
389
|
-
returning(::Sequel[value_column].hstore[key].as(:value)).
|
390
|
-
where(key_column => @row).
|
391
|
-
update(value_column => ::Sequel.join([value_column, pair])).
|
392
|
-
first
|
393
|
-
then
|
394
|
-
row[:value].to_i
|
509
|
+
@backend.transaction do
|
510
|
+
create_row
|
511
|
+
if row = @increment.call(row: @row, key: key, amount: amount).first
|
512
|
+
row[:value].to_i
|
513
|
+
end
|
395
514
|
end
|
396
515
|
end
|
397
516
|
|
398
517
|
def create(key, value, options = {})
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
518
|
+
@backend.transaction do
|
519
|
+
create_row
|
520
|
+
1 ==
|
521
|
+
if @create
|
522
|
+
@create.call(row: @row, key: key, pair: ::Sequel.hstore(key => value))
|
523
|
+
else
|
524
|
+
@table.
|
525
|
+
where(key_column => @row).
|
526
|
+
exclude(::Sequel[value_column].hstore.key?(key)).
|
527
|
+
update(value_column => ::Sequel[value_column].hstore.merge(key => value))
|
528
|
+
end
|
529
|
+
end
|
404
530
|
end
|
405
531
|
|
406
532
|
def clear(options = {})
|
407
|
-
@
|
533
|
+
@clear.call(row: @row)
|
408
534
|
self
|
409
535
|
end
|
410
536
|
|
411
537
|
def values_at(*keys, **options)
|
412
|
-
@
|
413
|
-
|
414
|
-
|
538
|
+
if row = @values_at.call(row: @row, keys: ::Sequel.pg_array(keys))
|
539
|
+
row[:values].to_a
|
540
|
+
else
|
541
|
+
[]
|
542
|
+
end
|
415
543
|
end
|
416
544
|
|
417
545
|
def slice(*keys, **options)
|
418
|
-
@
|
546
|
+
if row = @slice.call(row: @row, keys: ::Sequel.pg_array(keys))
|
547
|
+
row[:pairs].to_h
|
548
|
+
else
|
549
|
+
[]
|
550
|
+
end
|
419
551
|
end
|
420
552
|
|
421
553
|
def merge!(pairs, options = {}, &block)
|
@@ -423,32 +555,25 @@ module Moneta
|
|
423
555
|
create_row
|
424
556
|
pairs = yield_merge_pairs(pairs, &block) if block_given?
|
425
557
|
hash = Hash === pairs ? pairs : Hash[pairs.to_a]
|
426
|
-
@
|
427
|
-
where(key_column => @row).
|
428
|
-
update(value_column => ::Sequel[@table_name][value_column].hstore.merge(hash))
|
558
|
+
@store.call(row: @row, pair: ::Sequel.hstore(hash))
|
429
559
|
end
|
430
560
|
|
431
561
|
self
|
432
562
|
end
|
433
563
|
|
434
564
|
def each_key
|
435
|
-
unless block_given?
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
565
|
+
return enum_for(:each_key) { @size.call(row: @row)[:size] } unless block_given?
|
566
|
+
|
567
|
+
ds =
|
568
|
+
if @each_key_server
|
569
|
+
@table.server(@each_key_server)
|
570
|
+
else
|
571
|
+
@table
|
441
572
|
end
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
select(::Sequel[@table_name][value_column].hstore.skeys).
|
447
|
-
each do |row|
|
448
|
-
if first
|
449
|
-
first = false
|
450
|
-
next
|
451
|
-
end
|
573
|
+
ds = ds.order(:skeys) unless @table.respond_to?(:use_cursor)
|
574
|
+
ds.where(key_column => @row).
|
575
|
+
select(::Sequel[value_column].hstore.skeys).
|
576
|
+
paged_each do |row|
|
452
577
|
yield row[:skeys]
|
453
578
|
end
|
454
579
|
self
|
@@ -457,9 +582,7 @@ module Moneta
|
|
457
582
|
protected
|
458
583
|
|
459
584
|
def create_row
|
460
|
-
@
|
461
|
-
insert_ignore.
|
462
|
-
insert(key_column => @row, value_column => '')
|
585
|
+
@create_row.call(row: @row)
|
463
586
|
end
|
464
587
|
|
465
588
|
def create_table
|
@@ -473,9 +596,109 @@ module Moneta
|
|
473
596
|
end
|
474
597
|
end
|
475
598
|
|
476
|
-
def
|
477
|
-
|
478
|
-
|
599
|
+
def slice_for_update(pairs)
|
600
|
+
keys = pairs.map { |k, _| k }.to_a
|
601
|
+
if row = @slice_for_update.call(row: @row, keys: ::Sequel.pg_array(keys))
|
602
|
+
row[:pairs].to_h
|
603
|
+
else
|
604
|
+
{}
|
605
|
+
end
|
606
|
+
end
|
607
|
+
|
608
|
+
def prepare_statements
|
609
|
+
super
|
610
|
+
prepare_create_row
|
611
|
+
prepare_clear
|
612
|
+
prepare_values_at
|
613
|
+
prepare_size
|
614
|
+
end
|
615
|
+
|
616
|
+
def prepare_create_row
|
617
|
+
@create_row = @table.
|
618
|
+
insert_ignore.
|
619
|
+
prepare(:insert, statement_id(:hstore_create_row), key_column => :$row, value_column => '')
|
620
|
+
end
|
621
|
+
|
622
|
+
def prepare_clear
|
623
|
+
@clear = @table.where(key_column => :$row).prepare(:update, statement_id(:hstore_clear), value_column => '')
|
624
|
+
end
|
625
|
+
|
626
|
+
def prepare_key
|
627
|
+
if defined?(JRUBY_VERSION)
|
628
|
+
@key_pl = ::Sequel::Dataset::PlaceholderLiteralizer.loader(@table) do |pl, ds|
|
629
|
+
ds.where(key_column => @row).select(::Sequel[value_column].hstore.key?(pl.arg))
|
630
|
+
end
|
631
|
+
else
|
632
|
+
@key = @table.where(key_column => :$row).
|
633
|
+
select(::Sequel[value_column].hstore.key?(:$key).as(:present)).
|
634
|
+
prepare(:first, statement_id(:hstore_key))
|
635
|
+
end
|
636
|
+
end
|
637
|
+
|
638
|
+
def prepare_store
|
639
|
+
@store = @table.
|
640
|
+
where(key_column => :$row).
|
641
|
+
prepare(:update, statement_id(:hstore_store), value_column => ::Sequel[value_column].hstore.merge(:$pair))
|
642
|
+
end
|
643
|
+
|
644
|
+
def prepare_increment
|
645
|
+
pair = ::Sequel[:hstore].function(
|
646
|
+
:$key,
|
647
|
+
(::Sequel[:coalesce].function(
|
648
|
+
::Sequel[value_column].hstore[:$key].cast(Integer),
|
649
|
+
0) + :$amount).cast(String))
|
650
|
+
|
651
|
+
@increment = @table.
|
652
|
+
returning(::Sequel[value_column].hstore[:$key].as(:value)).
|
653
|
+
where(key_column => :$row).
|
654
|
+
prepare(:update, statement_id(:hstore_increment), value_column => ::Sequel.join([value_column, pair]))
|
655
|
+
end
|
656
|
+
|
657
|
+
def prepare_load
|
658
|
+
@load = @table.where(key_column => :$row).
|
659
|
+
select(::Sequel[value_column].hstore[:$key].as(:value)).
|
660
|
+
prepare(:first, statement_id(:hstore_load))
|
661
|
+
end
|
662
|
+
|
663
|
+
def prepare_delete
|
664
|
+
@delete = @table.where(key_column => :$row).
|
665
|
+
prepare(:update, statement_id(:hstore_delete), value_column => ::Sequel[value_column].hstore.delete(:$key))
|
666
|
+
end
|
667
|
+
|
668
|
+
def prepare_create
|
669
|
+
# Under JRuby we can't use a prepared statement for queries involving
|
670
|
+
# the hstore `?` (key?) operator. See
|
671
|
+
# https://stackoverflow.com/questions/11940401/escaping-hstore-contains-operators-in-a-jdbc-prepared-statement
|
672
|
+
return if defined?(JRUBY_VERSION)
|
673
|
+
@create = @table.
|
674
|
+
where(key_column => :$row).
|
675
|
+
exclude(::Sequel[value_column].hstore.key?(:$key)).
|
676
|
+
prepare(:update, statement_id(:hstore_create), value_column => ::Sequel[value_column].hstore.merge(:$pair))
|
677
|
+
end
|
678
|
+
|
679
|
+
def prepare_values_at
|
680
|
+
@values_at = @table.
|
681
|
+
where(key_column => :$row).
|
682
|
+
select(::Sequel[value_column].hstore[::Sequel.cast(:$keys, :"text[]")].as(:values)).
|
683
|
+
prepare(:first, statement_id(:hstore_values_at))
|
684
|
+
end
|
685
|
+
|
686
|
+
def prepare_slice
|
687
|
+
slice = @table.
|
688
|
+
where(key_column => :$row).
|
689
|
+
select(::Sequel[value_column].hstore.slice(:$keys).as(:pairs))
|
690
|
+
@slice = slice.prepare(:first, statement_id(:hstore_slice))
|
691
|
+
@slice_for_update = slice.for_update.prepare(:first, statement_id(:hstore_slice_for_update))
|
692
|
+
end
|
693
|
+
|
694
|
+
def prepare_size
|
695
|
+
@size =
|
696
|
+
@backend.from(
|
697
|
+
@table.
|
698
|
+
where(key_column => :$row).
|
699
|
+
select(::Sequel[value_column].hstore.each)).
|
700
|
+
select { count.function.*.as(:size) }.
|
701
|
+
prepare(:first, statement_id(:hstore_size))
|
479
702
|
end
|
480
703
|
end
|
481
704
|
|
@@ -495,18 +718,8 @@ module Moneta
|
|
495
718
|
|
496
719
|
def increment(key, amount = 1, options = {})
|
497
720
|
return super unless @can_upsert
|
498
|
-
update_expr = (::Sequel[@table_name][value_column].cast(Integer) + amount).cast(:blob)
|
499
|
-
|
500
721
|
@backend.transaction do
|
501
|
-
@
|
502
|
-
insert_conflict(
|
503
|
-
target: key_column,
|
504
|
-
update: {value_column => update_expr},
|
505
|
-
update_where:
|
506
|
-
::Sequel.|(
|
507
|
-
{value_column => blob("0")},
|
508
|
-
::Sequel.~(::Sequel[@table_name][value_column].cast(Integer)) => 0)).
|
509
|
-
insert(key_column => key, value_column => blob(amount.to_s))
|
722
|
+
@increment.call(key: key, value: amount.to_s, amount: amount)
|
510
723
|
Integer(load(key))
|
511
724
|
end
|
512
725
|
end
|
@@ -519,6 +732,28 @@ module Moneta
|
|
519
732
|
|
520
733
|
self
|
521
734
|
end
|
735
|
+
|
736
|
+
protected
|
737
|
+
|
738
|
+
def prepare_store
|
739
|
+
@store = @table.
|
740
|
+
insert_conflict(:replace).
|
741
|
+
prepare(:insert, statement_id(:store), key_column => :$key, value_column => :$value)
|
742
|
+
end
|
743
|
+
|
744
|
+
def prepare_increment
|
745
|
+
return super unless @can_upsert
|
746
|
+
update_expr = (::Sequel[value_column].cast(Integer) + :$amount).cast(:blob)
|
747
|
+
@increment = @table.
|
748
|
+
insert_conflict(
|
749
|
+
target: key_column,
|
750
|
+
update: {value_column => update_expr},
|
751
|
+
update_where:
|
752
|
+
::Sequel.|(
|
753
|
+
{value_column => blob("0")},
|
754
|
+
::Sequel.~(::Sequel[value_column].cast(Integer)) => 0)).
|
755
|
+
prepare(:insert, statement_id(:increment), key_column => :$key, value_column => :$value)
|
756
|
+
end
|
522
757
|
end
|
523
758
|
end
|
524
759
|
end
|
@@ -38,6 +38,16 @@ module Moneta
|
|
38
38
|
@create = @backend.prepare("insert into #{@table} values (?, ?)"),
|
39
39
|
@keys = @backend.prepare("select k from #{@table}"),
|
40
40
|
@count = @backend.prepare("select count(*) from #{@table}")]
|
41
|
+
|
42
|
+
version = @backend.execute("select sqlite_version()").first.first
|
43
|
+
if @can_upsert = ::Gem::Version.new(version) >= ::Gem::Version.new('3.24.0')
|
44
|
+
@stmts << (@increment = @backend.prepare <<-SQL)
|
45
|
+
insert into #{@table} values (?, ?)
|
46
|
+
on conflict (k)
|
47
|
+
do update set v = cast(cast(v as integer) + ? as blob)
|
48
|
+
where v = '0' or v = X'30' or cast(v as integer) != 0
|
49
|
+
SQL
|
50
|
+
end
|
41
51
|
end
|
42
52
|
|
43
53
|
# (see Proxy#key?)
|
@@ -66,7 +76,11 @@ module Moneta
|
|
66
76
|
|
67
77
|
# (see Proxy#increment)
|
68
78
|
def increment(key, amount = 1, options = {})
|
69
|
-
@backend.transaction(:exclusive) { return super }
|
79
|
+
@backend.transaction(:exclusive) { return super } unless @can_upsert
|
80
|
+
@backend.transaction do
|
81
|
+
@increment.execute!(key, amount.to_s, amount)
|
82
|
+
return Integer(load(key))
|
83
|
+
end
|
70
84
|
end
|
71
85
|
|
72
86
|
# (see Proxy#clear)
|
data/lib/moneta/version.rb
CHANGED
data/script/benchmarks
CHANGED
@@ -5,6 +5,7 @@ require 'benchmark'
|
|
5
5
|
require 'moneta'
|
6
6
|
require 'fileutils'
|
7
7
|
require 'active_support'
|
8
|
+
require 'active_support/cache/moneta_store'
|
8
9
|
|
9
10
|
class String
|
10
11
|
def random(n)
|
@@ -62,6 +63,17 @@ class MonetaBenchmarks
|
|
62
63
|
}
|
63
64
|
}
|
64
65
|
},
|
66
|
+
{
|
67
|
+
name: "ActiveRecord (Sqlite)",
|
68
|
+
adapter: :ActiveRecord,
|
69
|
+
options: {
|
70
|
+
table: 'activerecord',
|
71
|
+
connection: {
|
72
|
+
adapter: (defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3'),
|
73
|
+
database: "#{DIR}/activerecord_sqlite.db"
|
74
|
+
}
|
75
|
+
}
|
76
|
+
},
|
65
77
|
{
|
66
78
|
name: "ActiveSupportCache (Memory)",
|
67
79
|
adapter: :ActiveSupportCache,
|
@@ -76,6 +88,20 @@ class MonetaBenchmarks
|
|
76
88
|
backend: ::ActiveSupport::Cache::RedisCacheStore.new
|
77
89
|
}
|
78
90
|
},
|
91
|
+
{
|
92
|
+
name: "ActiveSupportCache (Moneta Memory)",
|
93
|
+
adapter: :ActiveSupportCache,
|
94
|
+
options: {
|
95
|
+
backend: ::ActiveSupport::Cache::MonetaStore.new(store: Moneta.new(:Memory))
|
96
|
+
}
|
97
|
+
},
|
98
|
+
{
|
99
|
+
name: "ActiveSupportCache (Moneta Redis)",
|
100
|
+
adapter: :ActiveSupportCache,
|
101
|
+
options: {
|
102
|
+
backend: ::ActiveSupport::Cache::MonetaStore.new(store: Moneta.new(:Redis))
|
103
|
+
}
|
104
|
+
},
|
79
105
|
{name: "Cassandra"},
|
80
106
|
{name: "Client (Memory)", adapter: :Client},
|
81
107
|
{name: "Couch"},
|
@@ -151,6 +177,27 @@ class MonetaBenchmarks
|
|
151
177
|
}
|
152
178
|
end.merge(table: 'sequel')
|
153
179
|
},
|
180
|
+
{
|
181
|
+
name: "Sequel (HStore)",
|
182
|
+
adapter: :Sequel,
|
183
|
+
options:
|
184
|
+
if defined?(JRUBY_VERSION)
|
185
|
+
{db: "jdbc:postgresql://localhost/#{postgres_database1}?user=#{postgres_username}"}
|
186
|
+
else
|
187
|
+
{
|
188
|
+
db: "postgres://localhost/#{postgres_database1}",
|
189
|
+
user: postgres_username
|
190
|
+
}
|
191
|
+
end.merge(table: 'sequel_hstore', hstore: 'row')
|
192
|
+
},
|
193
|
+
{
|
194
|
+
name: "Sequel (Sqlite)",
|
195
|
+
adapter: :Sequel,
|
196
|
+
options: {
|
197
|
+
table: 'sequel',
|
198
|
+
db: "#{defined?(JRUBY_VERSION) && 'jdbc:'}sqlite://#{DIR}/sequel"
|
199
|
+
}
|
200
|
+
},
|
154
201
|
{
|
155
202
|
name: "Sqlite (Memory)",
|
156
203
|
adapter: :Sqlite,
|
@@ -158,12 +205,29 @@ class MonetaBenchmarks
|
|
158
205
|
file: ':memory:'
|
159
206
|
}
|
160
207
|
},
|
208
|
+
{
|
209
|
+
name: "Sqlite (File)",
|
210
|
+
adapter: :Sqlite,
|
211
|
+
options: {
|
212
|
+
file: "#{DIR}/sqlite"
|
213
|
+
}
|
214
|
+
},
|
161
215
|
{name: "TDB", options: { file: "#{DIR}/tdb" }},
|
162
216
|
{name: "TokyoCabinet", options: { file: "#{DIR}/tokyocabinet" }},
|
163
217
|
{name: "TokyoTyrant"},
|
164
218
|
].compact
|
165
219
|
|
166
220
|
CONFIGS = {
|
221
|
+
test: {
|
222
|
+
runs: 2,
|
223
|
+
keys: 10,
|
224
|
+
min_key_len: 1,
|
225
|
+
max_key_len: 32,
|
226
|
+
key_dist: :uniform,
|
227
|
+
min_val_len: 0,
|
228
|
+
max_val_len: 256,
|
229
|
+
val_dist: :uniform
|
230
|
+
},
|
167
231
|
uniform_small: {
|
168
232
|
runs: 3,
|
169
233
|
keys: 1000,
|
@@ -227,8 +291,6 @@ class MonetaBenchmarks
|
|
227
291
|
}
|
228
292
|
|
229
293
|
DICT = 'ABCDEFGHIJKLNOPQRSTUVWXYZabcdefghijklnopqrstuvwxyz123456789'.freeze
|
230
|
-
HEADER = "\n Minimum Maximum Total Mean Stddev Ops/s"
|
231
|
-
SEPARATOR = '=' * 77
|
232
294
|
|
233
295
|
module Rand
|
234
296
|
extend self
|
@@ -256,6 +318,14 @@ class MonetaBenchmarks
|
|
256
318
|
end
|
257
319
|
end
|
258
320
|
|
321
|
+
def header
|
322
|
+
(" " * @name_len) + " Minimum Maximum Total Mean Stddev Ops/s"
|
323
|
+
end
|
324
|
+
|
325
|
+
def separator
|
326
|
+
"=" * header.length
|
327
|
+
end
|
328
|
+
|
259
329
|
def parallel(&block)
|
260
330
|
if defined?(JRUBY_VERSION)
|
261
331
|
Thread.new(&block)
|
@@ -357,26 +427,26 @@ class MonetaBenchmarks
|
|
357
427
|
write_histogram("#{DIR}/key.histogram", key_lens)
|
358
428
|
write_histogram("#{DIR}/value.histogram", val_lens)
|
359
429
|
|
360
|
-
puts "\n\e[1m\e[34m#{
|
361
|
-
puts %{
|
362
|
-
puts 'Key Length
|
363
|
-
puts 'Value Length
|
430
|
+
puts "\n\e[1m\e[34m#{separator}\n\e[34mComputing keys and values...\n\e[34m#{separator}\e[0m"
|
431
|
+
puts " " * @name_len + %{ Minimum Maximum Total Mean Stddev}
|
432
|
+
puts 'Key Length'.ljust(@name_len) + ' % 8d % 8d % 8d % 8d % 8d' % [key_lens.min, key_lens.max, key_lens.sum, mean(key_lens), stddev(key_lens)]
|
433
|
+
puts 'Value Length'.ljust(@name_len) + ' % 8d % 8d % 8d % 8d % 8d' % [val_lens.min, val_lens.max, val_lens.sum, mean(val_lens), stddev(val_lens)]
|
364
434
|
end
|
365
435
|
|
366
436
|
def print_config
|
367
|
-
puts "\e[1m\e[36m#{
|
437
|
+
puts "\e[1m\e[36m#{separator}\n\e[36mConfig #{@config_name}\n\e[36m#{separator}\e[0m"
|
368
438
|
@config.each do |k,v|
|
369
439
|
puts '%-16s = %-10s' % [k,v]
|
370
440
|
end
|
371
441
|
end
|
372
442
|
|
373
443
|
def print_store_stats(name)
|
374
|
-
puts
|
444
|
+
puts "\n" + header
|
375
445
|
[:write, :read, :sum].each do |i|
|
376
446
|
ops = (1000 * @config[:runs] * @data.size) / @stats[name][i].sum
|
377
|
-
line =
|
447
|
+
line = "%-#{@name_len-1}.#{@name_len-1}s %-5s % 8d % 8d % 8d % 8d % 8d % 8d" %
|
378
448
|
[name, i, @stats[name][i].min, @stats[name][i].max, @stats[name][i].sum,
|
379
|
-
mean(@stats[name][i]),
|
449
|
+
mean(@stats[name][i]), stddev(@stats[name][i]), ops]
|
380
450
|
@summary << [-ops, line << "\n"] if i == :sum
|
381
451
|
puts line
|
382
452
|
end
|
@@ -394,7 +464,7 @@ class MonetaBenchmarks
|
|
394
464
|
name = spec[:name]
|
395
465
|
adapter = spec[:adapter] || spec[:name].to_sym
|
396
466
|
options = spec[:options] || {}
|
397
|
-
puts "\n\e[1m\e[34m#{
|
467
|
+
puts "\n\e[1m\e[34m#{separator}\n\e[34m#{name}\n\e[34m#{separator}\e[0m"
|
398
468
|
|
399
469
|
store = Moneta.new(adapter, options.dup)
|
400
470
|
|
@@ -453,7 +523,7 @@ class MonetaBenchmarks
|
|
453
523
|
end
|
454
524
|
|
455
525
|
def print_summary
|
456
|
-
puts "\n\e[1m\e[36m#{
|
526
|
+
puts "\n\e[1m\e[36m#{separator}\n\e[36mSummary #{@config_name}: #{@config[:runs]} runs, #{@data.size} keys\n\e[36m#{separator}\e[0m\n#{header}\n"
|
457
527
|
@summary.sort_by(&:first).each do |entry|
|
458
528
|
puts entry.last
|
459
529
|
end
|
@@ -478,6 +548,8 @@ class MonetaBenchmarks
|
|
478
548
|
STORES
|
479
549
|
end.select { |spec| !spec.key?(:sizes) || spec[:sizes].include?(@size) }
|
480
550
|
|
551
|
+
@name_len = (@stores.map { |spec| spec[:name] }.map(&:length) + ["Value Length".length]).max + 2
|
552
|
+
|
481
553
|
# Disable jruby stdout pollution by memcached
|
482
554
|
if defined?(JRUBY_VERSION)
|
483
555
|
require 'java'
|
@@ -1,80 +1,58 @@
|
|
1
|
+
require_relative './helper.rb'
|
1
2
|
|
2
|
-
describe '
|
3
|
-
before :all do
|
4
|
-
require 'sequel'
|
5
|
-
end
|
6
|
-
|
3
|
+
describe ':Sequel adapter', adapter: :Sequel do
|
7
4
|
specs = ADAPTER_SPECS.with_each_key.with_values(:nil)
|
8
5
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
end
|
18
|
-
))
|
19
|
-
end
|
20
|
-
|
21
|
-
moneta_specs specs
|
22
|
-
end
|
23
|
-
|
24
|
-
context "with SQLite" do
|
25
|
-
moneta_build do
|
26
|
-
Moneta::Adapters::Sequel.new(opts.merge(
|
27
|
-
db: "#{defined?(JRUBY_VERSION) && 'jdbc:'}sqlite://" + File.join(tempdir, 'adapter_sequel.db')))
|
28
|
-
end
|
29
|
-
|
30
|
-
moneta_specs specs.without_concurrent
|
31
|
-
end
|
32
|
-
|
33
|
-
context "with Postgres" do
|
34
|
-
moneta_build do
|
35
|
-
Moneta::Adapters::Sequel.new(opts.merge(
|
36
|
-
if defined?(JRUBY_VERSION)
|
37
|
-
{db: "jdbc:postgresql://localhost/#{postgres_database1}?user=#{postgres_username}"}
|
38
|
-
else
|
39
|
-
{
|
40
|
-
db: "postgres://localhost/#{postgres_database1}",
|
41
|
-
user: postgres_username
|
42
|
-
}
|
43
|
-
end
|
6
|
+
context 'with MySQL backend' do
|
7
|
+
moneta_build do
|
8
|
+
Moneta::Adapters::Sequel.new(opts.merge(
|
9
|
+
db: if defined?(JRUBY_VERSION)
|
10
|
+
"jdbc:mysql://localhost/#{mysql_database1}?user=#{mysql_username}"
|
11
|
+
else
|
12
|
+
"mysql2://#{mysql_username}:@localhost/#{mysql_database1}"
|
13
|
+
end
|
44
14
|
))
|
45
|
-
end
|
46
|
-
|
47
|
-
moneta_specs specs
|
48
15
|
end
|
49
16
|
|
50
|
-
|
51
|
-
|
52
|
-
Moneta::Adapters::Sequel.new(opts.merge(
|
53
|
-
db: "jdbc:h2:" + tempdir))
|
54
|
-
end
|
17
|
+
include_examples :adapter_sequel, specs
|
18
|
+
end
|
55
19
|
|
56
|
-
|
20
|
+
context "with SQLite backend" do
|
21
|
+
moneta_build do
|
22
|
+
Moneta::Adapters::Sequel.new(opts.merge(
|
23
|
+
db: "#{defined?(JRUBY_VERSION) && 'jdbc:'}sqlite://" + File.join(tempdir, 'adapter_sequel.db')))
|
57
24
|
end
|
25
|
+
|
26
|
+
include_examples :adapter_sequel, specs.without_concurrent
|
58
27
|
end
|
59
28
|
|
60
|
-
context
|
61
|
-
|
29
|
+
context "with Postgres backend" do
|
30
|
+
moneta_build do
|
31
|
+
Moneta::Adapters::Sequel.new(opts.merge(
|
32
|
+
if defined?(JRUBY_VERSION)
|
33
|
+
{db: "jdbc:postgresql://localhost/#{postgres_database1}?user=#{postgres_username}"}
|
34
|
+
else
|
35
|
+
{
|
36
|
+
db: "postgres://localhost/#{postgres_database1}",
|
37
|
+
user: postgres_username
|
38
|
+
}
|
39
|
+
end
|
40
|
+
))
|
41
|
+
end
|
62
42
|
|
63
|
-
include_examples :adapter_sequel
|
43
|
+
include_examples :adapter_sequel, specs
|
64
44
|
end
|
65
45
|
|
66
|
-
context
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
optimize: false
|
71
|
-
}
|
46
|
+
context "with H2 backend", unsupported: !defined?(JRUBY_VERSION) do
|
47
|
+
moneta_build do
|
48
|
+
Moneta::Adapters::Sequel.new(opts.merge(
|
49
|
+
db: "jdbc:h2:" + tempdir))
|
72
50
|
end
|
73
51
|
|
74
|
-
include_examples :adapter_sequel
|
52
|
+
include_examples :adapter_sequel, specs, optimize: false
|
75
53
|
end
|
76
54
|
|
77
|
-
context "with Postgres HStore" do
|
55
|
+
context "with Postgres HStore backend" do
|
78
56
|
moneta_build do
|
79
57
|
Moneta::Adapters::Sequel.new(
|
80
58
|
if defined?(JRUBY_VERSION)
|
@@ -86,12 +64,13 @@ describe 'adapter_sequel', adapter: :Sequel do
|
|
86
64
|
}
|
87
65
|
end.merge(
|
88
66
|
table: 'hstore_table1',
|
89
|
-
hstore: 'row'
|
67
|
+
hstore: 'row'
|
68
|
+
)
|
90
69
|
)
|
91
70
|
end
|
92
71
|
|
93
72
|
# Concurrency is too slow, and binary values cannot be stored in an hstore
|
94
|
-
|
73
|
+
include_examples :adapter_sequel, specs.without_values(:binary).without_concurrent, optimize: false
|
95
74
|
end
|
96
75
|
|
97
76
|
describe 'table creation' do
|
@@ -107,15 +86,15 @@ describe 'adapter_sequel', adapter: :Sequel do
|
|
107
86
|
|
108
87
|
before { backend.drop_table?(table_name) }
|
109
88
|
|
110
|
-
shared_examples :
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
89
|
+
shared_examples :table_creation do
|
90
|
+
shared_examples :create_table do
|
91
|
+
it "creates the table" do
|
92
|
+
store = new_store
|
93
|
+
expect(backend.table_exists?(table_name)).to be true
|
94
|
+
expect(backend[table_name].columns).to include(store.key_column, store.value_column)
|
95
|
+
end
|
115
96
|
end
|
116
|
-
end
|
117
97
|
|
118
|
-
shared_examples :table_creation do
|
119
98
|
context "with :db parameter" do
|
120
99
|
moneta_build do
|
121
100
|
Moneta::Adapters::Sequel.new(opts.merge(db: conn_str, table: table_name))
|
@@ -0,0 +1,38 @@
|
|
1
|
+
RSpec.shared_examples :adapter_sequel do |specs, optimize: true|
|
2
|
+
shared_examples :each_key_server do
|
3
|
+
context "with each_key server" do
|
4
|
+
let(:opts) do
|
5
|
+
base_opts.merge(
|
6
|
+
servers: {each_key: {}},
|
7
|
+
each_key_server: :each_key
|
8
|
+
)
|
9
|
+
end
|
10
|
+
|
11
|
+
moneta_specs specs
|
12
|
+
end
|
13
|
+
|
14
|
+
context "without each_key server" do
|
15
|
+
let(:opts) { base_opts }
|
16
|
+
moneta_specs specs
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
if optimize
|
21
|
+
context 'with backend optimizations' do
|
22
|
+
let(:base_opts) { {table: "adapter_sequel"} }
|
23
|
+
|
24
|
+
include_examples :each_key_server
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'without backend optimizations' do
|
29
|
+
let(:base_opts) do
|
30
|
+
{
|
31
|
+
table: "adapter_sequel",
|
32
|
+
optimize: false
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
include_examples :each_key_server
|
37
|
+
end
|
38
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: moneta
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Mendler
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2019-
|
14
|
+
date: 2019-04-10 00:00:00.000000000 Z
|
15
15
|
dependencies: []
|
16
16
|
description: A unified interface to key/value stores
|
17
17
|
email:
|
@@ -242,6 +242,7 @@ files:
|
|
242
242
|
- spec/moneta/adapters/sdbm/standard_sdbm_spec.rb
|
243
243
|
- spec/moneta/adapters/sdbm/standard_sdbm_with_expires_spec.rb
|
244
244
|
- spec/moneta/adapters/sequel/adapter_sequel_spec.rb
|
245
|
+
- spec/moneta/adapters/sequel/helper.rb
|
245
246
|
- spec/moneta/adapters/sequel/standard_sequel_spec.rb
|
246
247
|
- spec/moneta/adapters/sequel/standard_sequel_with_expires_spec.rb
|
247
248
|
- spec/moneta/adapters/sqlite/adapter_sqlite_spec.rb
|
@@ -348,7 +349,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
348
349
|
- !ruby/object:Gem::Version
|
349
350
|
version: '0'
|
350
351
|
requirements: []
|
351
|
-
rubygems_version: 3.0.
|
352
|
+
rubygems_version: 3.0.3
|
352
353
|
signing_key:
|
353
354
|
specification_version: 4
|
354
355
|
summary: A unified interface to key/value stores, including Redis, Memcached, TokyoCabinet,
|
@@ -481,6 +482,7 @@ test_files:
|
|
481
482
|
- spec/moneta/adapters/sdbm/standard_sdbm_spec.rb
|
482
483
|
- spec/moneta/adapters/sdbm/standard_sdbm_with_expires_spec.rb
|
483
484
|
- spec/moneta/adapters/sequel/adapter_sequel_spec.rb
|
485
|
+
- spec/moneta/adapters/sequel/helper.rb
|
484
486
|
- spec/moneta/adapters/sequel/standard_sequel_spec.rb
|
485
487
|
- spec/moneta/adapters/sequel/standard_sequel_with_expires_spec.rb
|
486
488
|
- spec/moneta/adapters/sqlite/adapter_sqlite_spec.rb
|