moneta 1.1.0 → 1.1.1
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/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
|