memcached_store 0.12.7 → 0.12.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 705667e198b18bcd4d65f50390e5b739e12065b2
4
- data.tar.gz: 238f5c0a7cbcbdec4000fe7d753eb439e2868765
3
+ metadata.gz: 3e458052cfc2aac92c629b37e02fc7ae66077dcd
4
+ data.tar.gz: af048ce101065d5bc3463a9e118b6c3fb45c7763
5
5
  SHA512:
6
- metadata.gz: a50651983aa2380199e6ccb94b912e0d24f99791977cea55951fbd286adbb305f7e230308e6efcd27a7393775ede1e4dc4a78cb4cbf8eef974269e445f042d3a
7
- data.tar.gz: bf9f4f47644e8e73d5d0a4bf3b0f7679902b457ada045f0f0c5c4a8b6736a3f2958b3f60eb0f90b9b77d94fe735327ce86cf0345fec87a011035e0562d7bb34a
6
+ metadata.gz: 376570dd86abe4ca47cff7cb74aa934c7b26dac30ad24eeab5ee02b8310a24d9866d8c92ba2c85364c9c7bec426fe842e282b65d84742f2ba371db2cef478254
7
+ data.tar.gz: 8d958e238be593341eb041916ee4b47648a841ef3c87f45a3ebeeb1a2bca09808c43b1cf16aaf0ae5dba0b842ce49300ea9a3d600621e7f26d3deaeaa223b761
@@ -1,5 +1,7 @@
1
1
  language: ruby
2
2
  services: memcache
3
+ before_install:
4
+ - gem update bundler
3
5
  rvm:
4
6
  - 1.9.3
5
7
  - 2.0.0
data/Gemfile CHANGED
@@ -11,5 +11,11 @@ else
11
11
  "~> #{version}"
12
12
  end
13
13
 
14
- gem 'minitest', '~> 4.0' if version == "3.2.18"
15
14
  gem "activesupport", as_version
15
+
16
+ group :test do
17
+ gem "minitest", '~> 4.0' if version == "3.2.18"
18
+ gem "mocha"
19
+ gem "timecop"
20
+ gem "snappy"
21
+ end
@@ -20,6 +20,11 @@ module ActiveSupport
20
20
  raise UnsupportedOperation.new("decrement is not supported by: #{self.class.name}")
21
21
  end
22
22
 
23
+ # IdentityCache has its own handling for read only.
24
+ def read_only
25
+ false
26
+ end
27
+
23
28
  private
24
29
  def serialize_entry(entry, options)
25
30
  value = options[:raw] ? entry.value.to_s : Marshal.dump(entry)
@@ -30,6 +30,8 @@ module ActiveSupport
30
30
 
31
31
  ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/n
32
32
 
33
+ attr_accessor :read_only
34
+
33
35
  def initialize(*addresses)
34
36
  addresses = addresses.flatten
35
37
  options = addresses.extract_options!
@@ -46,6 +48,21 @@ module ActiveSupport
46
48
  extend Strategy::LocalCache
47
49
  end
48
50
 
51
+ def logger
52
+ return @logger if defined?(@logger)
53
+ @logger = ::Rails.logger if defined?(::Rails)
54
+ end
55
+
56
+ def write(*args)
57
+ return true if read_only
58
+ super(*args)
59
+ end
60
+
61
+ def delete(*args)
62
+ return true if read_only
63
+ super(*args)
64
+ end
65
+
49
66
  def read_multi(*names)
50
67
  options = names.extract_options!
51
68
  options = merged_options(options)
@@ -74,6 +91,7 @@ module ActiveSupport
74
91
  @data.cas(key, expiration(options), cas_raw?(options)) do |raw_value|
75
92
  entry = deserialize_entry(raw_value)
76
93
  value = yield entry.value
94
+ break true if read_only
77
95
  serialize_entry(Entry.new(value, options), options).first
78
96
  end
79
97
  end
@@ -83,6 +101,7 @@ module ActiveSupport
83
101
  end
84
102
 
85
103
  def cas_multi(*names)
104
+
86
105
  options = names.extract_options!
87
106
  options = merged_options(options)
88
107
  keys_to_names = Hash[names.map{|name| [escape_key(namespaced_key(name, options)), name]}]
@@ -95,6 +114,7 @@ module ActiveSupport
95
114
  values[keys_to_names[key]] = entry.value unless entry.expired?
96
115
  end
97
116
  values = yield values
117
+ break true if read_only
98
118
  Hash[values.map{|name, value| [escape_key(namespaced_key(name, options)), serialize_entry(Entry.new(value, options), options).first]}]
99
119
  end
100
120
  end
@@ -151,6 +171,7 @@ module ActiveSupport
151
171
  end
152
172
 
153
173
  def write_entry(key, entry, options) # :nodoc:
174
+ return true if read_only
154
175
  method = options && options[:unless_exist] ? :add : :set
155
176
  expires_in = expiration(options)
156
177
  value, raw = serialize_entry(entry, options)
@@ -161,6 +182,7 @@ module ActiveSupport
161
182
  end
162
183
 
163
184
  def delete_entry(key, options) # :nodoc:
185
+ return true if read_only
164
186
  @data.delete(escape_key(key))
165
187
  true
166
188
  rescue *NONFATAL_EXCEPTIONS => e
@@ -23,6 +23,7 @@ module MemcachedStore
23
23
  def exist_with_rescue?(*args)
24
24
  exist_without_rescue?(*args)
25
25
  rescue *NONFATAL_EXCEPTIONS
26
+ report_exception($!)
26
27
  end
27
28
  alias_method :exist_without_rescue?, :exist?
28
29
  alias_method :exist?, :exist_with_rescue?
@@ -30,6 +31,7 @@ module MemcachedStore
30
31
  def cas_with_rescue(*args)
31
32
  cas_without_rescue(*args)
32
33
  rescue *NONFATAL_EXCEPTIONS
34
+ report_exception($!)
33
35
  false
34
36
  end
35
37
  alias_method_chain :cas, :rescue
@@ -37,6 +39,7 @@ module MemcachedStore
37
39
  def get_multi_with_rescue(*args)
38
40
  get_multi_without_rescue(*args)
39
41
  rescue *NONFATAL_EXCEPTIONS
42
+ report_exception($!)
40
43
  {}
41
44
  end
42
45
  alias_method_chain :get_multi, :rescue
@@ -44,6 +47,7 @@ module MemcachedStore
44
47
  def set_with_rescue(*args)
45
48
  set_without_rescue(*args)
46
49
  rescue *NONFATAL_EXCEPTIONS
50
+ report_exception($!)
47
51
  false
48
52
  end
49
53
  alias_method_chain :set, :rescue
@@ -51,6 +55,7 @@ module MemcachedStore
51
55
  def add_with_rescue(*args)
52
56
  add_without_rescue(*args)
53
57
  rescue *NONFATAL_EXCEPTIONS
58
+ report_exception($!)
54
59
  @string_return_types? "NOT STORED\r\n" : true
55
60
  end
56
61
  alias_method_chain :add, :rescue
@@ -60,9 +65,24 @@ module MemcachedStore
60
65
  def #{meth}_with_rescue(*args)
61
66
  #{meth}_without_rescue(*args)
62
67
  rescue *NONFATAL_EXCEPTIONS
68
+ report_exception($!)
63
69
  end
64
70
  alias_method_chain :#{meth}, :rescue
65
71
  ENV
66
72
  end
73
+
74
+ def logger
75
+ return @logger if @logger
76
+ @logger = ::Rails.logger if defined?(::Rails)
77
+ end
78
+
79
+ private
80
+
81
+ def report_exception(exception)
82
+ if defined?(::Rails)
83
+ logger.error "[#{self.class}] exception=#{exception}"
84
+ end
85
+ nil # make sure return value is nil
86
+ end
67
87
  end
68
88
  end
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module MemcachedStore
3
- VERSION = "0.12.7"
3
+ VERSION = "0.12.8"
4
4
  end
@@ -19,8 +19,4 @@ Gem::Specification.new do |gem|
19
19
  gem.add_runtime_dependency "memcached", "~> 1.8.0"
20
20
 
21
21
  gem.add_development_dependency "rake"
22
- gem.add_development_dependency "minitest"
23
- gem.add_development_dependency "mocha"
24
- gem.add_development_dependency "timecop"
25
- gem.add_development_dependency "snappy"
26
22
  end
@@ -1 +1,4 @@
1
- # use default config
1
+ dependencies:
2
+ bundler:
3
+ without:
4
+ - test
@@ -5,3 +5,17 @@ require 'timecop'
5
5
  require 'active_support/test_case'
6
6
 
7
7
  require 'memcached_store'
8
+
9
+ class Rails
10
+ def self.logger
11
+ @logger ||= Logger.new("/dev/null")
12
+ end
13
+
14
+ def self.env
15
+ Struct.new("Env") do
16
+ def self.test?
17
+ true
18
+ end
19
+ end
20
+ end
21
+ end
@@ -125,6 +125,11 @@ class TestMemcachedSafety < ActiveSupport::TestCase
125
125
  @cache.prepend("a-key", "other")
126
126
  end
127
127
  end
128
+
129
+ test "logger defaults to rails logger" do
130
+ assert_equal Rails.logger, @cache.logger
131
+ end
132
+
128
133
  private
129
134
 
130
135
  def expect_nonfatal(sym)
@@ -1,9 +1,13 @@
1
1
  require 'test_helper'
2
+ require 'logger'
2
3
 
3
4
  class TestMemcachedStore < ActiveSupport::TestCase
4
5
  setup do
5
6
  @cache = ActiveSupport::Cache.lookup_store(:memcached_store, expires_in: 60, support_cas: true)
6
7
  @cache.clear
8
+
9
+ # Enable ActiveSupport notifications. Can be disabled in Rails 5.
10
+ Thread.current[:instrument_cache_store] = true
7
11
  end
8
12
 
9
13
  def test_write_not_found
@@ -153,26 +157,11 @@ class TestMemcachedStore < ActiveSupport::TestCase
153
157
  assert_equal({"fu" => "baz"}, @cache.read_multi('foo', 'fu'))
154
158
  end
155
159
 
156
- def test_read_multi
157
- @cache.write('foo', 'bar')
158
- @cache.write('fu', 'baz')
159
- @cache.write('fud', 'biz')
160
- assert_equal({"foo" => "bar", "fu" => "baz"}, @cache.read_multi('foo', 'fu'))
161
- end
162
-
163
160
  def test_read_multi_not_found
164
161
  expect_not_found
165
162
  assert_equal({}, @cache.read_multi('foe', 'fue'))
166
163
  end
167
164
 
168
- def test_read_multi_with_expires
169
- @cache.write('foo', 'bar', :expires_in => 0.001)
170
- @cache.write('fu', 'baz')
171
- @cache.write('fud', 'biz')
172
- sleep(0.002)
173
- assert_equal({"fu" => "baz"}, @cache.read_multi('foo', 'fu'))
174
- end
175
-
176
165
  def test_read_and_write_compressed_small_data
177
166
  @cache.write('foo', 'bar', :compress => true)
178
167
  assert_equal 'bar', @cache.read('foo')
@@ -388,15 +377,224 @@ class TestMemcachedStore < ActiveSupport::TestCase
388
377
  assert_equal "", client.prefix_key, "should not send the namespace to the client"
389
378
  assert_equal "foo::key", cache.send(:namespaced_key, "key", cache.options)
390
379
  end
391
-
380
+
392
381
  def test_reset
393
382
  client = @cache.instance_variable_get(:@data)
394
383
  client.expects(:reset).once
395
384
  @cache.reset
396
385
  end
397
386
 
387
+ def test_write_to_read_only_memcached_store_should_not_write
388
+ with_read_only(@cache) do
389
+ assert @cache.write("walrus", "awesome"), "Writing to a disabled memcached
390
+ store should return truthy to make clients not care"
391
+
392
+ assert_nil @cache.read("walrus"), "Key should have nil value in disabled cache"
393
+ end
394
+ end
395
+
396
+ def test_delete_with_read_only_memcached_store_should_not_delete
397
+ assert @cache.write("walrus", "big")
398
+
399
+ with_read_only(@cache) do
400
+ assert @cache.delete("walrus"), "Should return truthy when deleted to not raise in client"
401
+ end
402
+
403
+ assert_equal "big", @cache.read("walrus"), "Cache entry should not have been deleted from read only client"
404
+ end
405
+
406
+ def test_cas_with_read_only_memcached_store_should_not_s
407
+ called_block = false
408
+ @cache.write('walrus', 'slimy')
409
+
410
+ with_read_only(@cache) do
411
+ assert(@cache.cas('walrus') { |value|
412
+ assert_equal 'slimy', value
413
+ called_block = true
414
+ 'full'
415
+ })
416
+ end
417
+
418
+ assert_equal 'slimy', @cache.read('walrus')
419
+ assert called_block, "CAS with read only should have called the inner block with an assertion"
420
+ end
421
+
422
+ def test_cas_multi_with_read_only_memcached_store_should_not_s
423
+ called_block = false
424
+
425
+ @cache.write('walrus', 'cool')
426
+ @cache.write('narwhal', 'horn')
427
+
428
+ with_read_only(@cache) do
429
+ assert(@cache.cas_multi('walrus', 'narwhal') {
430
+ called_block = true
431
+ { "walrus" => "not cool", "narwhal" => "not with horns" }
432
+ })
433
+ end
434
+
435
+ assert_equal 'cool', @cache.read('walrus')
436
+ assert_equal 'horn', @cache.read('narwhal')
437
+ assert called_block, "CAS with read only should have called the inner block with an assertion"
438
+ end
439
+
440
+ def test_write_with_read_only_should_not_send_activesupport_notification
441
+ assert_notifications(/cache/, 0) do
442
+ with_read_only(@cache) do
443
+ assert @cache.write("walrus", "bestest")
444
+ end
445
+ end
446
+ end
447
+
448
+ def test_delete_with_read_only_should_not_send_activesupport_notification
449
+ assert_notifications(/cache/, 0) do
450
+ with_read_only(@cache) do
451
+ assert @cache.delete("walrus")
452
+ end
453
+ end
454
+ end
455
+
456
+ def test_fetch_with_expires_in_with_read_only_should_not_send_activesupport_notification
457
+ expires_in = 10
458
+ @cache.fetch("walrus", expires_in: expires_in) { "yo" }
459
+
460
+ Timecop.travel(Time.now + expires_in + 1) do
461
+ assert_notifications(/cache_write/, 0) do
462
+ with_read_only(@cache) do
463
+ @cache.fetch("walrus") { "no" }
464
+ end
465
+ end
466
+ end
467
+ end
468
+
469
+ def test_fetch_with_expired_entry_with_read_only_should_return_nil_and_not_delete_from_cache
470
+ expires_in = 10
471
+ @cache.fetch("walrus", expires_in: expires_in) { "yo" }
472
+
473
+ Timecop.travel(Time.now + expires_in + 1) do
474
+ with_read_only(@cache) do
475
+ value = @cache.fetch("walrus", expires_in: expires_in) { "no" }
476
+
477
+ assert_equal "no", value
478
+ refute @cache.fetch("walrus"), "Client should return nil for expired key"
479
+ assert_equal "yo", @cache.instance_variable_get(:@data).get("walrus").value
480
+ end
481
+ end
482
+ end
483
+
484
+ def test_fetch_with_expired_entry_and_race_condition_ttl_with_read_only_should_return_nil_and_not_delete_from_cache
485
+ expires_in = 10
486
+ race_condition_ttl = 10
487
+ @cache.fetch("walrus", expires_in: expires_in) { "yo" }
488
+
489
+ Timecop.travel(Time.now + expires_in + 1) do
490
+ with_read_only(@cache) do
491
+ value = @cache.fetch("walrus", expires_in: expires_in, race_condition_ttl: race_condition_ttl) { "no" }
492
+
493
+ assert_equal "no", value
494
+ assert_equal "no", @cache.fetch("walrus") { "no" }
495
+ refute @cache.fetch("walrus")
496
+
497
+ assert_equal "yo", @cache.instance_variable_get(:@data).get("walrus").value
498
+ end
499
+ end
500
+ end
501
+
502
+ def test_read_with_expired_with_read_only_entry_should_return_nil_and_not_delete_from_cache
503
+ expires_in = 10
504
+ @cache.fetch("walrus", expires_in: expires_in) { "yo" }
505
+
506
+ Timecop.travel(Time.now + expires_in + 1) do
507
+ with_read_only(@cache) do
508
+ refute @cache.read("walrus")
509
+
510
+ assert_equal "yo", @cache.instance_variable_get(:@data).get("walrus").value
511
+ end
512
+ end
513
+ end
514
+
515
+ def test_read_multi_with_expired_entry_should_return_nil_and_not_delete_from_cache
516
+ expires_in = 10
517
+ @cache.fetch("walrus", expires_in: expires_in) { "yo" }
518
+ @cache.fetch("narwhal", expires_in: expires_in) { "yiir" }
519
+
520
+ Timecop.travel(Time.now + expires_in + 1) do
521
+ with_read_only(@cache) do
522
+ assert_predicate @cache.read_multi("walrus", "narwhal"), :empty?
523
+
524
+ assert_equal "yo", @cache.instance_variable_get(:@data).get("walrus").value
525
+ assert_equal "yiir", @cache.instance_variable_get(:@data).get("narwhal").value
526
+ end
527
+ end
528
+ end
529
+
530
+ def test_fetch_with_race_condition_ttl_with_read_only_should_not_send_activesupport_notification
531
+ expires_in = 10
532
+ race_condition_ttl = 10
533
+ @cache.fetch("walrus", expires_in: expires_in) { "yo" }
534
+
535
+ Timecop.travel(Time.now + expires_in + 1) do
536
+ assert_notifications(/cache_write/, 0) do
537
+ with_read_only(@cache) do
538
+ @cache.fetch("walrus", expires_in: expires_in, race_condition_ttl: race_condition_ttl) { "no" }
539
+ end
540
+ end
541
+ end
542
+ end
543
+
544
+ def test_cas_with_read_only_should_send_activesupport_notification
545
+ @cache.write("walrus", "yes")
546
+
547
+ with_read_only(@cache) do
548
+ assert_notifications(/cache_cas/, 1) do
549
+ assert(@cache.cas("walrus") { |value| "no" })
550
+ end
551
+ end
552
+
553
+ assert_equal "yes", @cache.fetch("walrus")
554
+ end
555
+
556
+ def test_cas_multi_with_read_only_should_send_activesupport_notification
557
+ @cache.write("walrus", "yes")
558
+ @cache.write("narwhal", "yes")
559
+
560
+ with_read_only(@cache) do
561
+ assert_notifications(/cache_cas/, 1) do
562
+ assert(@cache.cas_multi("walrus", "narwhal") { |*values|
563
+ { "walrus" => "no", "narwhal" => "no" }
564
+ })
565
+ end
566
+ end
567
+
568
+ assert_equal "yes", @cache.fetch("walrus")
569
+ assert_equal "yes", @cache.fetch("narwhal")
570
+ end
571
+
572
+ def test_logger_defaults_to_rails_logger
573
+ assert_equal Rails.logger, @cache.logger
574
+ end
575
+
398
576
  private
399
577
 
578
+ def assert_notifications(pattern, num)
579
+ count = 0
580
+ subscriber = ActiveSupport::Notifications.subscribe(pattern) do |name, start, finish, id, payload|
581
+ count += 1
582
+ end
583
+
584
+ yield
585
+
586
+ assert_equal num, count, "Expected #{num} notifications for #{pattern}, but got #{count}"
587
+ ensure
588
+ ActiveSupport::Notifications.unsubscribe(subscriber)
589
+ end
590
+
591
+ def with_read_only(client)
592
+ previous, client.read_only = client.read_only, true
593
+ yield
594
+ ensure
595
+ client.read_only = previous
596
+ end
597
+
400
598
  def expect_not_found
401
599
  @cache.instance_variable_get(:@data).expects(:check_return_code).raises(Memcached::NotFound)
402
600
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: memcached_store
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.7
4
+ version: 0.12.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Camilo Lopez
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2014-06-17 00:00:00.000000000 Z
14
+ date: 2016-11-30 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: activesupport
@@ -55,62 +55,6 @@ dependencies:
55
55
  - - ">="
56
56
  - !ruby/object:Gem::Version
57
57
  version: '0'
58
- - !ruby/object:Gem::Dependency
59
- name: minitest
60
- requirement: !ruby/object:Gem::Requirement
61
- requirements:
62
- - - ">="
63
- - !ruby/object:Gem::Version
64
- version: '0'
65
- type: :development
66
- prerelease: false
67
- version_requirements: !ruby/object:Gem::Requirement
68
- requirements:
69
- - - ">="
70
- - !ruby/object:Gem::Version
71
- version: '0'
72
- - !ruby/object:Gem::Dependency
73
- name: mocha
74
- requirement: !ruby/object:Gem::Requirement
75
- requirements:
76
- - - ">="
77
- - !ruby/object:Gem::Version
78
- version: '0'
79
- type: :development
80
- prerelease: false
81
- version_requirements: !ruby/object:Gem::Requirement
82
- requirements:
83
- - - ">="
84
- - !ruby/object:Gem::Version
85
- version: '0'
86
- - !ruby/object:Gem::Dependency
87
- name: timecop
88
- requirement: !ruby/object:Gem::Requirement
89
- requirements:
90
- - - ">="
91
- - !ruby/object:Gem::Version
92
- version: '0'
93
- type: :development
94
- prerelease: false
95
- version_requirements: !ruby/object:Gem::Requirement
96
- requirements:
97
- - - ">="
98
- - !ruby/object:Gem::Version
99
- version: '0'
100
- - !ruby/object:Gem::Dependency
101
- name: snappy
102
- requirement: !ruby/object:Gem::Requirement
103
- requirements:
104
- - - ">="
105
- - !ruby/object:Gem::Version
106
- version: '0'
107
- type: :development
108
- prerelease: false
109
- version_requirements: !ruby/object:Gem::Requirement
110
- requirements:
111
- - - ">="
112
- - !ruby/object:Gem::Version
113
- version: '0'
114
58
  description: Plugin-able Memcached adapters to add features (compression, safety)
115
59
  email:
116
60
  - camilo@camilolopez.com
@@ -157,7 +101,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
157
101
  version: '0'
158
102
  requirements: []
159
103
  rubyforge_project:
160
- rubygems_version: 2.2.2
104
+ rubygems_version: 2.5.1
161
105
  signing_key:
162
106
  specification_version: 4
163
107
  summary: Plugin-able Memcached adapters to add features (compression, safety)