moneta 1.3.0 → 1.4.0
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/.rubocop.yml +26 -8
- data/CHANGES +6 -0
- data/CONTRIBUTORS +2 -1
- data/Gemfile +7 -5
- data/README.md +2 -6
- data/feature_matrix.yaml +0 -10
- data/lib/moneta.rb +9 -9
- data/lib/moneta/adapters/mongo.rb +256 -7
- data/lib/moneta/adapters/redis.rb +5 -1
- data/lib/moneta/adapters/sequel.rb +45 -464
- data/lib/moneta/adapters/sequel/mysql.rb +66 -0
- data/lib/moneta/adapters/sequel/postgres.rb +80 -0
- data/lib/moneta/adapters/sequel/postgres_hstore.rb +240 -0
- data/lib/moneta/adapters/sequel/sqlite.rb +57 -0
- data/lib/moneta/adapters/sqlite.rb +7 -7
- data/lib/moneta/create_support.rb +21 -0
- data/lib/moneta/dbm_adapter.rb +31 -0
- data/lib/moneta/{mixins.rb → defaults.rb} +1 -302
- data/lib/moneta/each_key_support.rb +27 -0
- data/lib/moneta/expires_support.rb +60 -0
- data/lib/moneta/hash_adapter.rb +68 -0
- data/lib/moneta/increment_support.rb +16 -0
- data/lib/moneta/nil_values.rb +35 -0
- data/lib/moneta/option_support.rb +51 -0
- data/lib/moneta/transformer/helper/bson.rb +5 -15
- data/lib/moneta/version.rb +1 -1
- data/lib/rack/cache/moneta.rb +14 -15
- data/moneta.gemspec +7 -9
- data/script/benchmarks +1 -2
- data/script/contributors +11 -6
- data/spec/active_support/cache_moneta_store_spec.rb +27 -29
- data/spec/features/concurrent_increment.rb +2 -3
- data/spec/features/create_expires.rb +15 -15
- data/spec/features/default_expires.rb +11 -12
- data/spec/features/expires.rb +215 -210
- data/spec/helper.rb +16 -33
- data/spec/moneta/adapters/mongo/adapter_mongo_spec.rb +16 -1
- data/spec/moneta/adapters/mongo/adapter_mongo_with_default_expires_spec.rb +1 -1
- data/spec/moneta/adapters/mongo/standard_mongo_spec.rb +1 -1
- data/spec/moneta/adapters/sequel/adapter_sequel_spec.rb +7 -34
- data/spec/moneta/adapters/sequel/helper.rb +37 -0
- data/spec/moneta/adapters/sequel/standard_sequel_spec.rb +4 -10
- data/spec/moneta/adapters/sequel/standard_sequel_with_expires_spec.rb +7 -8
- data/spec/moneta/proxies/shared/shared_unix_spec.rb +10 -0
- data/spec/restserver.rb +15 -0
- metadata +39 -58
- data/lib/moneta/adapters/mongo/base.rb +0 -103
- data/lib/moneta/adapters/mongo/moped.rb +0 -166
- data/lib/moneta/adapters/mongo/official.rb +0 -156
- data/spec/moneta/adapters/mongo/adapter_mongo_moped_spec.rb +0 -26
- data/spec/moneta/adapters/mongo/adapter_mongo_moped_with_default_expires_spec.rb +0 -14
- data/spec/moneta/adapters/mongo/adapter_mongo_official_spec.rb +0 -27
- data/spec/moneta/adapters/mongo/adapter_mongo_official_with_default_expires_spec.rb +0 -14
- data/spec/moneta/adapters/mongo/standard_mongo_moped_spec.rb +0 -7
- data/spec/moneta/adapters/mongo/standard_mongo_official_spec.rb +0 -7
- data/spec/quality_spec.rb +0 -51
@@ -0,0 +1,66 @@
|
|
1
|
+
module Moneta
|
2
|
+
module Adapters
|
3
|
+
class Sequel
|
4
|
+
# @api private
|
5
|
+
module MySQL
|
6
|
+
def store(key, value, options = {})
|
7
|
+
@store.call(key: key, value: blob(value))
|
8
|
+
value
|
9
|
+
end
|
10
|
+
|
11
|
+
def increment(key, amount = 1, options = {})
|
12
|
+
@backend.transaction do
|
13
|
+
# this creates a row-level lock even if there is no existing row (a
|
14
|
+
# "gap lock").
|
15
|
+
if row = @load_for_update.call(key: key)
|
16
|
+
# Integer() will raise an exception if the existing value cannot be parsed
|
17
|
+
amount += Integer(row[value_column])
|
18
|
+
@increment_update.call(key: key, value: amount)
|
19
|
+
else
|
20
|
+
@create.call(key: key, value: amount)
|
21
|
+
end
|
22
|
+
amount
|
23
|
+
end
|
24
|
+
rescue ::Sequel::SerializationFailure # Thrown on deadlock
|
25
|
+
tries ||= 0
|
26
|
+
(tries += 1) <= 3 ? retry : raise
|
27
|
+
end
|
28
|
+
|
29
|
+
def merge!(pairs, options = {}, &block)
|
30
|
+
@backend.transaction do
|
31
|
+
pairs = yield_merge_pairs(pairs, &block) if block_given?
|
32
|
+
@table
|
33
|
+
.on_duplicate_key_update
|
34
|
+
.import([key_column, value_column], blob_pairs(pairs).to_a)
|
35
|
+
end
|
36
|
+
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
def each_key
|
41
|
+
return super unless block_given? && @each_key_server && @table.respond_to?(:stream)
|
42
|
+
# Order is not required when streaming
|
43
|
+
@table.server(@each_key_server).select(key_column).paged_each do |row|
|
44
|
+
yield row[key_column]
|
45
|
+
end
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
protected
|
50
|
+
|
51
|
+
def prepare_store
|
52
|
+
@store = @table
|
53
|
+
.on_duplicate_key_update
|
54
|
+
.prepare(:insert, statement_id(:store), key_column => :$key, value_column => :$value)
|
55
|
+
end
|
56
|
+
|
57
|
+
def prepare_increment
|
58
|
+
@increment_update = @table
|
59
|
+
.where(key_column => :$key)
|
60
|
+
.prepare(:update, statement_id(:increment_update), value_column => :$value)
|
61
|
+
super
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Moneta
|
2
|
+
module Adapters
|
3
|
+
# @api public
|
4
|
+
class Sequel
|
5
|
+
# @api private
|
6
|
+
module Postgres
|
7
|
+
def store(key, value, options = {})
|
8
|
+
@store.call(key: key, value: blob(value))
|
9
|
+
value
|
10
|
+
end
|
11
|
+
|
12
|
+
def increment(key, amount = 1, options = {})
|
13
|
+
result = @increment.call(key: key, value: blob(amount.to_s), amount: amount)
|
14
|
+
if row = result.first
|
15
|
+
row[value_column].to_i
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def delete(key, options = {})
|
20
|
+
result = @delete.call(key: key)
|
21
|
+
if row = result.first
|
22
|
+
row[value_column]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def merge!(pairs, options = {}, &block)
|
27
|
+
@backend.transaction do
|
28
|
+
pairs = yield_merge_pairs(pairs, &block) if block_given?
|
29
|
+
@table
|
30
|
+
.insert_conflict(target: key_column,
|
31
|
+
update: { value_column => ::Sequel[:excluded][value_column] })
|
32
|
+
.import([key_column, value_column], blob_pairs(pairs).to_a)
|
33
|
+
end
|
34
|
+
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
38
|
+
def each_key
|
39
|
+
return super unless block_given? && !@each_key_server && @table.respond_to?(:use_cursor)
|
40
|
+
# With a cursor, this will Just Work.
|
41
|
+
@table.select(key_column).paged_each do |row|
|
42
|
+
yield row[key_column]
|
43
|
+
end
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
protected
|
48
|
+
|
49
|
+
def prepare_store
|
50
|
+
@store = @table
|
51
|
+
.insert_conflict(target: key_column,
|
52
|
+
update: { value_column => ::Sequel[:excluded][value_column] })
|
53
|
+
.prepare(:insert, statement_id(:store), key_column => :$key, value_column => :$value)
|
54
|
+
end
|
55
|
+
|
56
|
+
def prepare_increment
|
57
|
+
update_expr = ::Sequel[:convert_to].function(
|
58
|
+
(::Sequel[:convert_from].function(
|
59
|
+
::Sequel[@table_name][value_column],
|
60
|
+
'UTF8'
|
61
|
+
).cast(Integer) + :$amount).cast(String),
|
62
|
+
'UTF8'
|
63
|
+
)
|
64
|
+
|
65
|
+
@increment = @table
|
66
|
+
.returning(value_column)
|
67
|
+
.insert_conflict(target: key_column, update: { value_column => update_expr })
|
68
|
+
.prepare(:insert, statement_id(:increment), key_column => :$key, value_column => :$value)
|
69
|
+
end
|
70
|
+
|
71
|
+
def prepare_delete
|
72
|
+
@delete = @table
|
73
|
+
.returning(value_column)
|
74
|
+
.where(key_column => :$key)
|
75
|
+
.prepare(:delete, statement_id(:delete))
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,240 @@
|
|
1
|
+
::Sequel.extension :pg_hstore_ops
|
2
|
+
|
3
|
+
module Moneta
|
4
|
+
module Adapters
|
5
|
+
class Sequel
|
6
|
+
# @api private
|
7
|
+
module PostgresHStore
|
8
|
+
def self.extended(mod)
|
9
|
+
mod.backend.extension :pg_hstore
|
10
|
+
mod.backend.extension :pg_array
|
11
|
+
end
|
12
|
+
|
13
|
+
def key?(key, options = {})
|
14
|
+
if @key
|
15
|
+
row = @key.call(row: @row, key: key) || false
|
16
|
+
row && row[:present]
|
17
|
+
else
|
18
|
+
@key_pl.get(key)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def store(key, value, options = {})
|
23
|
+
@backend.transaction do
|
24
|
+
create_row
|
25
|
+
@store.call(row: @row, pair: ::Sequel.hstore(key => value))
|
26
|
+
end
|
27
|
+
value
|
28
|
+
end
|
29
|
+
|
30
|
+
def load(key, options = {})
|
31
|
+
if row = @load.call(row: @row, key: key)
|
32
|
+
row[:value]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def delete(key, options = {})
|
37
|
+
@backend.transaction do
|
38
|
+
value = load(key, options)
|
39
|
+
@delete.call(row: @row, key: key)
|
40
|
+
value
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def increment(key, amount = 1, options = {})
|
45
|
+
@backend.transaction do
|
46
|
+
create_row
|
47
|
+
if row = @increment.call(row: @row, key: key, amount: amount).first
|
48
|
+
row[:value].to_i
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def create(key, value, options = {})
|
54
|
+
@backend.transaction do
|
55
|
+
create_row
|
56
|
+
1 ==
|
57
|
+
if @create
|
58
|
+
@create.call(row: @row, key: key, pair: ::Sequel.hstore(key => value))
|
59
|
+
else
|
60
|
+
@table
|
61
|
+
.where(key_column => @row)
|
62
|
+
.exclude(::Sequel[value_column].hstore.key?(key))
|
63
|
+
.update(value_column => ::Sequel[value_column].hstore.merge(key => value))
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def clear(options = {})
|
69
|
+
@clear.call(row: @row)
|
70
|
+
self
|
71
|
+
end
|
72
|
+
|
73
|
+
def values_at(*keys, **options)
|
74
|
+
if row = @values_at.call(row: @row, keys: ::Sequel.pg_array(keys))
|
75
|
+
row[:values].to_a
|
76
|
+
else
|
77
|
+
[]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def slice(*keys, **options)
|
82
|
+
if row = @slice.call(row: @row, keys: ::Sequel.pg_array(keys))
|
83
|
+
row[:pairs].to_h
|
84
|
+
else
|
85
|
+
[]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def merge!(pairs, options = {}, &block)
|
90
|
+
@backend.transaction do
|
91
|
+
create_row
|
92
|
+
pairs = yield_merge_pairs(pairs, &block) if block_given?
|
93
|
+
hash = Hash === pairs ? pairs : Hash[pairs.to_a]
|
94
|
+
@store.call(row: @row, pair: ::Sequel.hstore(hash))
|
95
|
+
end
|
96
|
+
|
97
|
+
self
|
98
|
+
end
|
99
|
+
|
100
|
+
def each_key
|
101
|
+
return enum_for(:each_key) { @size.call(row: @row)[:size] } unless block_given?
|
102
|
+
|
103
|
+
ds =
|
104
|
+
if @each_key_server
|
105
|
+
@table.server(@each_key_server)
|
106
|
+
else
|
107
|
+
@table
|
108
|
+
end
|
109
|
+
ds = ds.order(:skeys) unless @table.respond_to?(:use_cursor)
|
110
|
+
ds.where(key_column => @row)
|
111
|
+
.select(::Sequel[value_column].hstore.skeys)
|
112
|
+
.paged_each do |row|
|
113
|
+
yield row[:skeys]
|
114
|
+
end
|
115
|
+
self
|
116
|
+
end
|
117
|
+
|
118
|
+
protected
|
119
|
+
|
120
|
+
def create_row
|
121
|
+
@create_row.call(row: @row)
|
122
|
+
end
|
123
|
+
|
124
|
+
def create_table
|
125
|
+
key_column = self.key_column
|
126
|
+
value_column = self.value_column
|
127
|
+
|
128
|
+
@backend.create_table?(@table_name) do
|
129
|
+
column key_column, String, null: false, primary_key: true
|
130
|
+
column value_column, :hstore
|
131
|
+
index value_column, type: :gin
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def slice_for_update(pairs)
|
136
|
+
keys = pairs.map { |k, _| k }.to_a
|
137
|
+
if row = @slice_for_update.call(row: @row, keys: ::Sequel.pg_array(keys))
|
138
|
+
row[:pairs].to_h
|
139
|
+
else
|
140
|
+
{}
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def prepare_statements
|
145
|
+
super
|
146
|
+
prepare_create_row
|
147
|
+
prepare_clear
|
148
|
+
prepare_values_at
|
149
|
+
prepare_size
|
150
|
+
end
|
151
|
+
|
152
|
+
def prepare_create_row
|
153
|
+
@create_row = @table
|
154
|
+
.insert_ignore
|
155
|
+
.prepare(:insert, statement_id(:hstore_create_row), key_column => :$row, value_column => '')
|
156
|
+
end
|
157
|
+
|
158
|
+
def prepare_clear
|
159
|
+
@clear = @table.where(key_column => :$row).prepare(:update, statement_id(:hstore_clear), value_column => '')
|
160
|
+
end
|
161
|
+
|
162
|
+
def prepare_key
|
163
|
+
if defined?(JRUBY_VERSION)
|
164
|
+
@key_pl = ::Sequel::Dataset::PlaceholderLiteralizer.loader(@table) do |pl, ds|
|
165
|
+
ds.where(key_column => @row).select(::Sequel[value_column].hstore.key?(pl.arg))
|
166
|
+
end
|
167
|
+
else
|
168
|
+
@key = @table.where(key_column => :$row)
|
169
|
+
.select(::Sequel[value_column].hstore.key?(:$key).as(:present))
|
170
|
+
.prepare(:first, statement_id(:hstore_key))
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def prepare_store
|
175
|
+
@store = @table
|
176
|
+
.where(key_column => :$row)
|
177
|
+
.prepare(:update, statement_id(:hstore_store), value_column => ::Sequel[value_column].hstore.merge(:$pair))
|
178
|
+
end
|
179
|
+
|
180
|
+
def prepare_increment
|
181
|
+
pair = ::Sequel[:hstore]
|
182
|
+
.function(:$key, (
|
183
|
+
::Sequel[:coalesce].function(::Sequel[value_column].hstore[:$key].cast(Integer), 0) +
|
184
|
+
:$amount
|
185
|
+
).cast(String))
|
186
|
+
|
187
|
+
@increment = @table
|
188
|
+
.returning(::Sequel[value_column].hstore[:$key].as(:value))
|
189
|
+
.where(key_column => :$row)
|
190
|
+
.prepare(:update, statement_id(:hstore_increment), value_column => ::Sequel.join([value_column, pair]))
|
191
|
+
end
|
192
|
+
|
193
|
+
def prepare_load
|
194
|
+
@load = @table.where(key_column => :$row)
|
195
|
+
.select(::Sequel[value_column].hstore[:$key].as(:value))
|
196
|
+
.prepare(:first, statement_id(:hstore_load))
|
197
|
+
end
|
198
|
+
|
199
|
+
def prepare_delete
|
200
|
+
@delete = @table.where(key_column => :$row)
|
201
|
+
.prepare(:update, statement_id(:hstore_delete), value_column => ::Sequel[value_column].hstore.delete(:$key))
|
202
|
+
end
|
203
|
+
|
204
|
+
def prepare_create
|
205
|
+
# Under JRuby we can't use a prepared statement for queries involving
|
206
|
+
# the hstore `?` (key?) operator. See
|
207
|
+
# https://stackoverflow.com/questions/11940401/escaping-hstore-contains-operators-in-a-jdbc-prepared-statement
|
208
|
+
return if defined?(JRUBY_VERSION)
|
209
|
+
@create = @table
|
210
|
+
.where(key_column => :$row)
|
211
|
+
.exclude(::Sequel[value_column].hstore.key?(:$key))
|
212
|
+
.prepare(:update, statement_id(:hstore_create), value_column => ::Sequel[value_column].hstore.merge(:$pair))
|
213
|
+
end
|
214
|
+
|
215
|
+
def prepare_values_at
|
216
|
+
@values_at = @table
|
217
|
+
.where(key_column => :$row)
|
218
|
+
.select(::Sequel[value_column].hstore[::Sequel.cast(:$keys, :"text[]")].as(:values))
|
219
|
+
.prepare(:first, statement_id(:hstore_values_at))
|
220
|
+
end
|
221
|
+
|
222
|
+
def prepare_slice
|
223
|
+
slice = @table
|
224
|
+
.where(key_column => :$row)
|
225
|
+
.select(::Sequel[value_column].hstore.slice(:$keys).as(:pairs))
|
226
|
+
@slice = slice.prepare(:first, statement_id(:hstore_slice))
|
227
|
+
@slice_for_update = slice.for_update.prepare(:first, statement_id(:hstore_slice_for_update))
|
228
|
+
end
|
229
|
+
|
230
|
+
def prepare_size
|
231
|
+
@size = @backend
|
232
|
+
.from(@table.where(key_column => :$row)
|
233
|
+
.select(::Sequel[value_column].hstore.each))
|
234
|
+
.select { count.function.*.as(:size) }
|
235
|
+
.prepare(:first, statement_id(:hstore_size))
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Moneta
|
2
|
+
module Adapters
|
3
|
+
class Sequel
|
4
|
+
# @api private
|
5
|
+
module SQLite
|
6
|
+
def self.extended(mod)
|
7
|
+
version = mod.backend.get(::Sequel[:sqlite_version].function)
|
8
|
+
# See https://sqlite.org/lang_UPSERT.html
|
9
|
+
mod.instance_variable_set(:@can_upsert, ::Gem::Version.new(version) >= ::Gem::Version.new('3.24.0'))
|
10
|
+
end
|
11
|
+
|
12
|
+
def store(key, value, options = {})
|
13
|
+
@table.insert_conflict(:replace).insert(key_column => key, value_column => blob(value))
|
14
|
+
value
|
15
|
+
end
|
16
|
+
|
17
|
+
def increment(key, amount = 1, options = {})
|
18
|
+
return super unless @can_upsert
|
19
|
+
@backend.transaction do
|
20
|
+
@increment.call(key: key, value: blob(amount.to_s), amount: amount)
|
21
|
+
Integer(load(key))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def merge!(pairs, options = {}, &block)
|
26
|
+
@backend.transaction do
|
27
|
+
pairs = yield_merge_pairs(pairs, &block) if block_given?
|
28
|
+
@table.insert_conflict(:replace).import([key_column, value_column], blob_pairs(pairs).to_a)
|
29
|
+
end
|
30
|
+
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
|
36
|
+
def prepare_store
|
37
|
+
@store = @table
|
38
|
+
.insert_conflict(:replace)
|
39
|
+
.prepare(:insert, statement_id(:store), key_column => :$key, value_column => :$value)
|
40
|
+
end
|
41
|
+
|
42
|
+
def prepare_increment
|
43
|
+
return super unless @can_upsert
|
44
|
+
update_expr = (::Sequel[value_column].cast(Integer) + :$amount).cast(:blob)
|
45
|
+
@increment = @table
|
46
|
+
.insert_conflict(
|
47
|
+
target: key_column,
|
48
|
+
update: { value_column => update_expr },
|
49
|
+
update_where: ::Sequel.|({ value_column => blob("0") },
|
50
|
+
{ ::Sequel.~(::Sequel[value_column].cast(Integer)) => 0 })
|
51
|
+
)
|
52
|
+
.prepare(:insert, statement_id(:increment), key_column => :$key, value_column => :$value)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -30,14 +30,14 @@ module Moneta
|
|
30
30
|
@backend.journal_mode = journal_mode.to_s
|
31
31
|
end
|
32
32
|
@stmts =
|
33
|
-
[@exists
|
34
|
-
@select
|
33
|
+
[@exists = @backend.prepare("select exists(select 1 from #{@table} where k = ?)"),
|
34
|
+
@select = @backend.prepare("select v from #{@table} where k = ?"),
|
35
35
|
@replace = @backend.prepare("replace into #{@table} values (?, ?)"),
|
36
|
-
@delete
|
37
|
-
@clear
|
38
|
-
@create
|
39
|
-
@keys
|
40
|
-
@count
|
36
|
+
@delete = @backend.prepare("delete from #{@table} where k = ?"),
|
37
|
+
@clear = @backend.prepare("delete from #{@table}"),
|
38
|
+
@create = @backend.prepare("insert into #{@table} values (?, ?)"),
|
39
|
+
@keys = @backend.prepare("select k from #{@table}"),
|
40
|
+
@count = @backend.prepare("select count(*) from #{@table}")]
|
41
41
|
|
42
42
|
version = @backend.execute("select sqlite_version()").first.first
|
43
43
|
if @can_upsert = ::Gem::Version.new(version) >= ::Gem::Version.new('3.24.0')
|