moneta 1.4.1 → 1.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +418 -0
  3. data/CHANGES +4 -0
  4. data/Gemfile +1 -1
  5. data/README.md +16 -13
  6. data/lib/moneta/pool.rb +26 -6
  7. data/lib/moneta/version.rb +1 -1
  8. data/spec/active_support/cache_moneta_store_spec.rb +13 -13
  9. data/spec/helper.rb +14 -1
  10. data/spec/moneta/adapters/activerecord/adapter_activerecord_existing_connection_spec.rb +3 -1
  11. data/spec/moneta/adapters/activerecord/adapter_activerecord_spec.rb +15 -7
  12. data/spec/moneta/adapters/activerecord/standard_activerecord_spec.rb +5 -2
  13. data/spec/moneta/adapters/activerecord/standard_activerecord_with_expires_spec.rb +5 -2
  14. data/spec/moneta/adapters/activesupportcache/adapter_activesupportcache_spec.rb +3 -3
  15. data/spec/moneta/adapters/activesupportcache/adapter_activesupportcache_with_default_expires_spec.rb +2 -2
  16. data/spec/moneta/adapters/datamapper/adapter_datamapper_spec.rb +25 -8
  17. data/spec/moneta/adapters/datamapper/standard_datamapper_spec.rb +2 -2
  18. data/spec/moneta/adapters/datamapper/standard_datamapper_with_expires_spec.rb +2 -2
  19. data/spec/moneta/adapters/datamapper/standard_datamapper_with_repository_spec.rb +2 -2
  20. data/spec/moneta/adapters/memcached/dalli/adapter_memcached_dalli_spec.rb +13 -3
  21. data/spec/moneta/adapters/memcached/native/adapter_memcached_native_spec.rb +13 -3
  22. data/spec/moneta/adapters/mongo/adapter_mongo_with_default_expires_spec.rb +1 -1
  23. data/spec/moneta/adapters/redis/adapter_redis_spec.rb +13 -3
  24. data/spec/moneta/adapters/redis/standard_redis_spec.rb +8 -1
  25. data/spec/moneta/adapters/sequel/adapter_sequel_spec.rb +4 -4
  26. data/spec/moneta/adapters/sequel/helper.rb +10 -5
  27. data/spec/moneta/adapters/sequel/standard_sequel_spec.rb +1 -1
  28. data/spec/moneta/adapters/sequel/standard_sequel_with_expires_spec.rb +1 -1
  29. data/spec/moneta/adapters/sqlite/adapter_sqlite_spec.rb +1 -1
  30. data/spec/moneta/adapters/sqlite/standard_sqlite_spec.rb +1 -1
  31. data/spec/moneta/adapters/sqlite/standard_sqlite_with_expires_spec.rb +1 -1
  32. data/spec/moneta/proxies/pool/pool_spec.rb +31 -3
  33. metadata +7 -15
  34. data/.travis.yml +0 -146
  35. data/spec/moneta/adapters/memcached/dalli/adapter_memcached_dalli_with_default_expires_spec.rb +0 -15
  36. data/spec/moneta/adapters/memcached/native/adapter_memcached_native_with_default_expires_spec.rb +0 -15
  37. data/spec/moneta/adapters/redis/adapter_redis_with_default_expires_spec.rb +0 -10
  38. data/spec/moneta/proxies/proxy/proxy_redis_spec.rb +0 -13
data/lib/moneta/pool.rb CHANGED
@@ -87,6 +87,7 @@ module Moneta
87
87
  @waiting_since = [] if @timeout
88
88
  @last_checkout = nil
89
89
  @stopping = false
90
+ @idle_time = nil
90
91
 
91
92
  # Launch the manager thread
92
93
  @thread = run
@@ -126,10 +127,16 @@ module Moneta
126
127
  populate_stores
127
128
 
128
129
  until @stopping && @stores.empty?
130
+ loop_start = Time.now
131
+
129
132
  # Block until a message arrives, or until we time out for some reason
130
- if request = pop
131
- handle_request(request)
132
- end
133
+ request = pop
134
+
135
+ # Record how long we were idle, for stats purposes
136
+ @idle_time = Time.now - loop_start
137
+
138
+ # If a message arrived, handle it
139
+ handle_request(request) if request
133
140
 
134
141
  # Handle any stale checkout requests
135
142
  handle_timed_out_requests
@@ -186,7 +193,7 @@ module Moneta
186
193
  # @return [Integer, nil]
187
194
  def timeout
188
195
  # Time to wait before there will be stores that should be closed
189
- ttl = if @ttl && @last_checkout && !@available.empty?
196
+ ttl = if @ttl && @last_checkout && stores_available? && stores_unneeded?
190
197
  [@ttl - (Time.now - @last_checkout), 0].max
191
198
  end
192
199
 
@@ -199,6 +206,18 @@ module Moneta
199
206
  [ttl, timeout].compact.min
200
207
  end
201
208
 
209
+ def stores_available?
210
+ !@available.empty?
211
+ end
212
+
213
+ def stores_unneeded?
214
+ @stores.length > @min
215
+ end
216
+
217
+ def stores_maxed?
218
+ @max != nil && @stores.length == @max
219
+ end
220
+
202
221
  def pop
203
222
  @mutex.synchronize do
204
223
  @resource.wait(@mutex, timeout) if @inbox.empty?
@@ -218,7 +237,7 @@ module Moneta
218
237
  reply.resolve(ShutdownError.new("Shutting down"))
219
238
  elsif !@available.empty?
220
239
  reply.resolve(@available.pop)
221
- elsif !@max || @stores.length < @max
240
+ elsif !stores_maxed?
222
241
  begin
223
242
  reply.resolve(add_store)
224
243
  rescue => e
@@ -258,7 +277,8 @@ module Moneta
258
277
  waiting: @waiting.length,
259
278
  longest_wait: @timeout && !@waiting_since.empty? ? @waiting_since.first.dup : nil,
260
279
  stopping: @stopping,
261
- last_checkout: @last_checkout && @last_checkout.dup)
280
+ last_checkout: @last_checkout && @last_checkout.dup,
281
+ idle_time: @idle_time.dup)
262
282
  end
263
283
 
264
284
  def handle_request(request)
@@ -1,5 +1,5 @@
1
1
  module Moneta
2
2
  # Moneta version number
3
3
  # @api public
4
- VERSION = '1.4.1'.freeze
4
+ VERSION = '1.4.2'.freeze
5
5
  end
@@ -74,7 +74,7 @@ describe "cache_moneta_store" do
74
74
  end
75
75
  expect(result['rabbit']).to eq rabbit
76
76
  expect(result['irish whisky']).to eq 'irish whisky was missing'
77
- expect(store.fetch 'irish whisky').to eq 'irish whisky was missing'
77
+ expect(store.fetch('irish whisky')).to eq 'irish whisky was missing'
78
78
  end
79
79
  end
80
80
 
@@ -88,9 +88,9 @@ describe "cache_moneta_store" do
88
88
 
89
89
  it 'writes multiple values with expiration time' do
90
90
  store.write_multi({
91
- 'rabbit' => white_rabbit,
92
- 'irish whisky' => 'Jameson'
93
- }, expires_in: 0.2.second)
91
+ 'rabbit' => white_rabbit,
92
+ 'irish whisky' => 'Jameson'
93
+ }, expires_in: 0.2.second)
94
94
 
95
95
  expect(store.read_multi('rabbit', 'irish whisky')).to eq \
96
96
  'rabbit' => white_rabbit,
@@ -234,23 +234,23 @@ describe "cache_moneta_store" do
234
234
  end
235
235
 
236
236
  context "with :Memory store" do
237
- let(:store){ described_class.new(store: :Memory) }
237
+ let(:store) { described_class.new(store: :Memory) }
238
238
  include_examples :moneta_store
239
239
  end
240
240
 
241
241
  context "with existing :Memory store" do
242
- let(:store){ described_class.new(store: ::Moneta.new(:Memory)) }
242
+ let(:store) { described_class.new(store: ::Moneta.new(:Memory)) }
243
243
  include_examples :moneta_store
244
244
  end
245
245
 
246
- context "with Redis store" do
247
- let(:store) {described_class.new(store: :Redis) }
246
+ context "with Redis store", adapter: :Redis do
247
+ let(:store) { described_class.new(store: :Redis) }
248
248
  include_examples :moneta_store
249
249
  end
250
250
  end
251
251
 
252
252
  describe ActiveSupport::Cache::MemoryStore do
253
- let(:store){ described_class.new }
253
+ let(:store) { described_class.new }
254
254
 
255
255
  include_examples :basic_store
256
256
  include_examples :expiry
@@ -258,8 +258,8 @@ describe "cache_moneta_store" do
258
258
  include_examples :basic_instrumentation
259
259
  end
260
260
 
261
- describe ActiveSupport::Cache::MemCacheStore do
262
- let(:store){ described_class.new('127.0.0.1:11213') }
261
+ describe ActiveSupport::Cache::MemCacheStore, memcached: true do
262
+ let(:store) { described_class.new('127.0.0.1:11213') }
263
263
 
264
264
  include_context :start_memcached, 11213
265
265
 
@@ -270,8 +270,8 @@ describe "cache_moneta_store" do
270
270
  include_examples :increment_decrement_instrumentation
271
271
  end
272
272
 
273
- describe ActiveSupport::Cache::RedisCacheStore do
274
- let(:store){ described_class.new(url: 'redis:///3') }
273
+ describe ActiveSupport::Cache::RedisCacheStore, redis: true do
274
+ let(:store) { described_class.new(url: "redis:///3") }
275
275
 
276
276
  include_examples :basic_store
277
277
  include_examples :expiry
data/spec/helper.rb CHANGED
@@ -213,11 +213,18 @@ ADAPTER_SPECS = MonetaSpecs.new(
213
213
  :create, :features, :store_large, :not_each_key],
214
214
  key: %w(string path),
215
215
  value: %w(string path binary))
216
+
217
+ NATIVE_EXPIRY_SPECS = MonetaSpecs.new(
218
+ specs: [:create, :expires, :create_expires],
219
+ key: %w(string path),
220
+ value: %w(string path binary))
221
+
216
222
  STANDARD_SPECS = MonetaSpecs.new(
217
223
  specs: [:null, :store, :returndifferent,
218
224
  :marshallable_key, :marshallable_value, :transform_value, :increment,
219
225
  :concurrent_increment, :concurrent_create, :persist, :multiprocess, :create,
220
226
  :features, :store_large, :not_each_key])
227
+
221
228
  TRANSFORMER_SPECS = MonetaSpecs.new(
222
229
  specs: [:null, :store, :returndifferent,
223
230
  :transform_value, :increment, :create, :features, :store_large,
@@ -260,18 +267,24 @@ module MonetaHelpers
260
267
 
261
268
  # Used by tests that rely on MySQL. These env vars can be used if you
262
269
  # want to run the tests but don't want to grant root access to moneta
270
+ let(:mysql_host) { ENV['MYSQL_HOST'] || 'localhost' }
271
+ let(:mysql_port) { ENV['MYSQL_TCP_PORT'] || '3306' }
263
272
  let(:mysql_username) { ENV['MONETA_MYSQL_USERNAME'] || 'root' }
264
273
  let(:mysql_password) { ENV['MONETA_MYSQL_PASSWORD'] }
265
274
  let(:mysql_database1) { ENV['MONETA_MYSQL_DATABASE1'] || 'moneta' }
266
275
  let(:mysql_database2) { ENV['MONETA_MYSQL_DATABASE2'] || 'moneta2' }
267
276
 
268
- let(:postgres_username) { ENV['MONETA_POSTGRES_USERNAME'] || 'postgres' }
277
+ let(:postgres_username) { ENV['PGUSER'] || 'postgres' }
278
+ let(:postgres_password) { ENV['PGPASSWORD'] }
269
279
  let(:postgres_database1) { ENV['MONETA_POSTGRES_DATABSASE1'] || 'moneta1' }
270
280
  let(:postgres_database2) { ENV['MONETA_POSTGRES_DATABSASE1'] || 'moneta2' }
271
281
 
272
282
  let(:couch_login) { ENV['COUCH_LOGIN'] || 'admin' }
273
283
  let(:couch_password) { ENV['COUCH_PASSWORD'] || 'password' }
274
284
 
285
+ let(:redis_host) { ENV.fetch('REDIS_HOST', 'localhost') }
286
+ let(:redis_port) { ENV.fetch('REDIS_PORT', '6379') }
287
+
275
288
  before do
276
289
  store = new_store
277
290
  store.clear
@@ -1,4 +1,4 @@
1
- describe 'adapter_activerecord_existing_connection', adapter: :ActiveRecord do
1
+ describe 'adapter_activerecord_existing_connection', adapter: :ActiveRecord, mysql: true do
2
2
  before :all do
3
3
  require 'active_record'
4
4
  end
@@ -8,6 +8,8 @@ describe 'adapter_activerecord_existing_connection', adapter: :ActiveRecord do
8
8
  ActiveRecord::Base.configurations = {
9
9
  default_env => {
10
10
  'adapter' => (defined?(JRUBY_VERSION) ? 'jdbcmysql' : 'mysql2'),
11
+ 'host' => mysql_host,
12
+ 'port' => mysql_port,
11
13
  'database' => mysql_database1,
12
14
  'username' => mysql_username,
13
15
  'password' => mysql_password
@@ -58,32 +58,39 @@ describe 'adapter_activerecord', adapter: :ActiveRecord do
58
58
  end
59
59
  end
60
60
 
61
- context "with MySQL" do
61
+ context "with MySQL", mysql: true do
62
62
  let(:connection1) do
63
63
  {
64
64
  adapter: (defined?(JRUBY_VERSION) ? 'jdbcmysql' : 'mysql2'),
65
+ host: mysql_host,
66
+ port: mysql_port,
65
67
  database: mysql_database1,
66
- username: mysql_username
68
+ username: mysql_username,
69
+ password: mysql_password
67
70
  }
68
71
  end
69
72
 
70
73
  let(:connection2) do
71
74
  {
72
75
  adapter: (defined?(JRUBY_VERSION) ? 'jdbcmysql' : 'mysql2'),
76
+ host: mysql_host,
77
+ port: mysql_port,
73
78
  database: mysql_database2,
74
- username: mysql_username
79
+ username: mysql_username,
80
+ password: mysql_password
75
81
  }
76
82
  end
77
83
 
78
84
  include_examples :adapter_activerecord, activerecord_specs
79
85
  end
80
86
 
81
- context "with PostgreSQL" do
87
+ context "with PostgreSQL", postgres: true do
82
88
  let(:connection1) do
83
89
  {
84
90
  adapter: (defined?(JRUBY_VERSION) ? 'jdbcpostgresql' : 'postgresql'),
85
91
  database: postgres_database1,
86
- username: postgres_username
92
+ username: postgres_username,
93
+ password: postgres_password
87
94
  }
88
95
  end
89
96
 
@@ -91,14 +98,15 @@ describe 'adapter_activerecord', adapter: :ActiveRecord do
91
98
  {
92
99
  adapter: (defined?(JRUBY_VERSION) ? 'jdbcpostgresql' : 'postgresql'),
93
100
  database: postgres_database2,
94
- username: postgres_username
101
+ username: postgres_username,
102
+ password: postgres_password
95
103
  }
96
104
  end
97
105
 
98
106
  include_examples :adapter_activerecord, activerecord_specs
99
107
  end
100
108
 
101
- context "with SQLite" do
109
+ context "with SQLite", sqlite: true do
102
110
  let(:connection1) do
103
111
  {
104
112
  adapter: (defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3'),
@@ -1,11 +1,14 @@
1
- describe "standard_activerecord", adapter: :ActiveRecord do
1
+ describe "standard_activerecord", adapter: :ActiveRecord, mysql: true do
2
2
  moneta_store :ActiveRecord do
3
3
  {
4
4
  table: 'standard_activerecord',
5
5
  connection: {
6
6
  adapter: (defined?(JRUBY_VERSION) ? 'jdbcmysql' : 'mysql2'),
7
+ host: mysql_host,
8
+ port: mysql_port,
7
9
  database: mysql_database1,
8
- username: mysql_username
10
+ username: mysql_username,
11
+ password: mysql_password
9
12
  }
10
13
  }
11
14
  end
@@ -1,4 +1,4 @@
1
- describe "standard_activerecord_with_expires", adapter: :ActiveRecord do
1
+ describe "standard_activerecord_with_expires", adapter: :ActiveRecord, mysql: true do
2
2
  let(:t_res) { 0.125 }
3
3
  let(:min_ttl) { t_res }
4
4
  use_timecop
@@ -8,8 +8,11 @@ describe "standard_activerecord_with_expires", adapter: :ActiveRecord do
8
8
  table: 'standard_activerecord_with_expires',
9
9
  connection: {
10
10
  adapter: (defined?(JRUBY_VERSION) ? 'jdbcmysql' : 'mysql2'),
11
+ host: mysql_host,
12
+ port: mysql_port,
11
13
  database: mysql_database1,
12
- username: mysql_username
14
+ username: mysql_username,
15
+ password: mysql_password
13
16
  },
14
17
  expires: true
15
18
  }
@@ -23,7 +23,7 @@ describe 'adapter_activesupportcache', adapter: :ActiveSupportCache do
23
23
  include_examples :adapter_activesupportcache
24
24
  end
25
25
 
26
- context 'using MemCacheStore' do
26
+ context 'using MemCacheStore', memcached: true do
27
27
  let(:t_res) { 1 }
28
28
  let(:min_ttl) { 2 }
29
29
  use_timecop
@@ -34,12 +34,12 @@ describe 'adapter_activesupportcache', adapter: :ActiveSupportCache do
34
34
  include_examples :adapter_activesupportcache
35
35
  end
36
36
 
37
- context 'using RedisCacheStore' do
37
+ context 'using RedisCacheStore', redis: true do
38
38
  let(:t_res) { 1 }
39
39
  let(:min_ttl) { t_res }
40
40
  use_timecop
41
41
 
42
- let(:backend) { ActiveSupport::Cache::RedisCacheStore.new(url: 'redis:///1') }
42
+ let(:backend) { ActiveSupport::Cache::RedisCacheStore.new(url: "redis://#{redis_host}:#{redis_port}/1") }
43
43
  include_examples :adapter_activesupportcache
44
44
  end
45
45
 
@@ -23,7 +23,7 @@ describe 'adapter_activesupportcache_with_default_expires', adapter: :ActiveSupp
23
23
  include_examples :adapter_activesupportcache_with_default_expires
24
24
  end
25
25
 
26
- context 'using MemCacheStore' do
26
+ context 'using MemCacheStore', memcached: true do
27
27
  let(:t_res) { 1 }
28
28
  let(:min_ttl) { 2 }
29
29
  use_timecop
@@ -39,7 +39,7 @@ describe 'adapter_activesupportcache_with_default_expires', adapter: :ActiveSupp
39
39
  let(:min_ttl) { t_res }
40
40
  use_timecop
41
41
 
42
- let(:backend) { ActiveSupport::Cache::RedisCacheStore.new(url: 'redis:///2') }
42
+ let(:backend) { ActiveSupport::Cache::RedisCacheStore.new(url: "redis://#{redis_host}:#{redis_port}/2") }
43
43
  include_examples :adapter_activesupportcache_with_default_expires
44
44
  end
45
45
 
@@ -1,22 +1,32 @@
1
- describe 'adapter_datamapper', broken: defined?(JRUBY_VERSION), adapter: :DataMapper do
1
+ describe 'adapter_datamapper', broken: defined?(JRUBY_VERSION), adapter: :DataMapper, mysql: true do
2
2
  before :all do
3
3
  require 'dm-core'
4
4
 
5
5
  # DataMapper needs default repository to be setup
6
6
  DataMapper.setup(:default, adapter: :in_memory)
7
7
  end
8
-
8
+
9
9
  moneta_build do
10
- Moneta::Adapters::DataMapper.new(setup: "mysql://#{mysql_username}:#{mysql_password}@localhost/#{mysql_database1}", table: "adapter_datamapper")
10
+ Moneta::Adapters::DataMapper.new(
11
+ setup: "mysql://#{mysql_username}:#{mysql_password}@#{mysql_host}:#{mysql_port}/#{mysql_database1}",
12
+ table: "adapter_datamapper"
13
+ )
11
14
  end
12
15
 
13
16
  moneta_specs ADAPTER_SPECS.without_increment.with_values(:nil).without_values(:binary)
14
-
17
+
15
18
  it 'does not cross contaminate when storing' do
16
- first = Moneta::Adapters::DataMapper.new(setup: "mysql://#{mysql_username}:#{mysql_password}@localhost/#{mysql_database1}", table: "datamapper_first")
19
+ first = Moneta::Adapters::DataMapper.new(
20
+ setup: "mysql://#{mysql_username}:#{mysql_password}@#{mysql_host}:#{mysql_port}/#{mysql_database1}",
21
+ table: "datamapper_first"
22
+ )
17
23
  first.clear
18
24
 
19
- second = Moneta::Adapters::DataMapper.new(repository: :sample, setup: "mysql://#{mysql_username}:#{mysql_password}@localhost/#{mysql_database1}", table: "datamapper_second")
25
+ second = Moneta::Adapters::DataMapper.new(
26
+ repository: :sample,
27
+ setup: "mysql://#{mysql_username}:#{mysql_password}@#{mysql_host}:#{mysql_port}/#{mysql_database1}",
28
+ table: "datamapper_second"
29
+ )
20
30
  second.clear
21
31
 
22
32
  first['key'] = 'value'
@@ -27,10 +37,17 @@ describe 'adapter_datamapper', broken: defined?(JRUBY_VERSION), adapter: :DataMa
27
37
  end
28
38
 
29
39
  it 'does not cross contaminate when deleting' do
30
- first = Moneta::Adapters::DataMapper.new(setup: "mysql://#{mysql_username}:#{mysql_password}@localhost/#{mysql_database1}", table: "datamapper_first")
40
+ first = Moneta::Adapters::DataMapper.new(
41
+ setup: "mysql://#{mysql_username}:#{mysql_password}@#{mysql_host}:#{mysql_port}/#{mysql_database1}",
42
+ table: "datamapper_first"
43
+ )
31
44
  first.clear
32
45
 
33
- second = Moneta::Adapters::DataMapper.new(repository: :sample, setup: "mysql://#{mysql_username}:#{mysql_password}@localhost/#{mysql_database1}", table: "datamapper_second")
46
+ second = Moneta::Adapters::DataMapper.new(
47
+ repository: :sample,
48
+ setup: "mysql://#{mysql_username}:#{mysql_password}@#{mysql_host}:#{mysql_port}/#{mysql_database1}",
49
+ table: "datamapper_second"
50
+ )
34
51
  second.clear
35
52
 
36
53
  first['key'] = 'value'
@@ -1,4 +1,4 @@
1
- describe "standard_datamapper", broken: defined?(JRUBY_VERSION), adapter: :DataMapper do
1
+ describe "standard_datamapper", broken: defined?(JRUBY_VERSION), adapter: :DataMapper, mysql: true do
2
2
  before :all do
3
3
  require 'dm-core'
4
4
 
@@ -8,7 +8,7 @@ describe "standard_datamapper", broken: defined?(JRUBY_VERSION), adapter: :DataM
8
8
 
9
9
  moneta_store :DataMapper do
10
10
  {
11
- setup: "mysql://#{mysql_username}:#{mysql_password}@localhost/#{mysql_database1}",
11
+ setup: "mysql://#{mysql_username}:#{mysql_password}@#{mysql_host}:#{mysql_port}/#{mysql_database1}",
12
12
  table: "simple_datamapper"
13
13
  }
14
14
  end
@@ -1,4 +1,4 @@
1
- describe "standard_datamapper_with_expires", broken: defined?(JRUBY_VERSION), adapter: :DataMapper do
1
+ describe "standard_datamapper_with_expires", broken: defined?(JRUBY_VERSION), adapter: :DataMapper, mysql: true do
2
2
  let(:t_res) { 0.125 }
3
3
  let(:min_ttl) { t_res }
4
4
  use_timecop
@@ -12,7 +12,7 @@ describe "standard_datamapper_with_expires", broken: defined?(JRUBY_VERSION), ad
12
12
 
13
13
  moneta_store :DataMapper do
14
14
  {
15
- setup: "mysql://#{mysql_username}:#{mysql_password}@localhost/#{mysql_database1}",
15
+ setup: "mysql://#{mysql_username}:#{mysql_password}@#{mysql_host}:#{mysql_port}/#{mysql_database1}",
16
16
  table: "simple_datamapper_with_expires",
17
17
  expires: true
18
18
  }