readthis 1.0.0.pre.beta → 1.0.0.pre.rc.1

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: b0b93b4402db2eaaff687eb60a40296bb618dd4e
4
- data.tar.gz: 81f108e00b2b7dfe0ce92d9209c1160e3cc59a6f
3
+ metadata.gz: c641f0b14297f5f65cc9e2a36dc525c92e2f4a48
4
+ data.tar.gz: cf5625a9a3b874f5f715979c615ee0db5c27daad
5
5
  SHA512:
6
- metadata.gz: d8e81b60017904895db2c2d2666f9f9d2fdad7fa2178c6bbca617abc0d49e5d8934c3128e0f8dba1014c5b30f02d2101fcf3102d2ecee441d5f43f9be9a35282
7
- data.tar.gz: 1b8c28d2a8fdcff75d38bc565dceaf14fb442a1d14ca2c547c53e315d5d6eae08f35f96dbf3f97e268f71774ab27eb9afa5faf4266a1f3becf8af7303c92eee9
6
+ metadata.gz: 3774c714c24caa36cdb07b8ce5b39f1694ed2e9b9b116b2b3c463fc516e2b2bfebf68e1481c5181502fddd76e3690ae3af7bfcf7ef3210c5ae8ca634ad3f16c9
7
+ data.tar.gz: 274020a823960b9f4b7efdf101d02b3cc6a08074c508a6b32d4c990de0ccacf47571d98a1e74796f2d12d417971c297dffdc81abc1e24b3a770a74c64874810a
data/README.md CHANGED
@@ -96,28 +96,28 @@ config.cache_store = :readthis_store, {
96
96
  }
97
97
  ```
98
98
 
99
- ### Marshalling
99
+ ### Serializing
100
100
 
101
- Readthis uses Ruby's `Marshal` module for dumping and loading all values by
102
- default. This isn't always the fastest option, and depending on your use case it
103
- may be desirable to use a faster but less flexible marshaller.
101
+ Readthis uses Ruby's `Marshal` module for serializing all values by default.
102
+ This isn't always the fastest option, and depending on your use case it may be
103
+ desirable to use a faster but less flexible serializer.
104
104
 
105
- By default Readthis knows about 3 different serializers for marshalling:
105
+ By default Readthis knows about 3 different serializers:
106
106
 
107
107
  * Marshal
108
108
  * JSON
109
109
  * Passthrough
110
110
 
111
111
  If all cached data can safely be represented as a string then use the
112
- pass-through marshaller:
112
+ pass-through serializer:
113
113
 
114
114
  ```ruby
115
115
  Readthis::Cache.new(marshal: Readthis::Passthrough)
116
116
  ```
117
117
 
118
- You can introduce up to four additional marshals by configuring `serializers` on
119
- the Readthis module. For example, if you wanted to use Oj for JSON marshalling,
120
- it is extremely fast, but supports limited types:
118
+ You can introduce up to four additional serializers by configuring `serializers`
119
+ on the Readthis module. For example, if you wanted to use the extremely fast Oj
120
+ library for JSON serialization:
121
121
 
122
122
  ```ruby
123
123
  Readthis.serializers << Oj
@@ -2,6 +2,6 @@ require 'readthis'
2
2
 
3
3
  module ActiveSupport
4
4
  module Cache
5
- ReadthisStore ||= Readthis::Cache
5
+ ReadthisStore ||= Readthis::Cache # rubocop:disable Style/ConstantName
6
6
  end
7
7
  end
@@ -7,7 +7,7 @@ require 'connection_pool'
7
7
 
8
8
  module Readthis
9
9
  class Cache
10
- attr_reader :entity, :expires_in, :namespace, :options, :pool
10
+ attr_reader :entity, :options, :pool
11
11
 
12
12
  # Provide a class level lookup of the proper notifications module.
13
13
  # Instrumention is expected to occur within applications that have
@@ -39,9 +39,7 @@ module Readthis
39
39
  # Readthis::Cache.new(compress: true, compression_threshold: 2048)
40
40
  #
41
41
  def initialize(options = {})
42
- @options = options
43
- @expires_in = options.fetch(:expires_in, nil)
44
- @namespace = options.fetch(:namespace, nil)
42
+ @options = options
45
43
 
46
44
  @entity = Readthis::Entity.new(
47
45
  marshal: options.fetch(:marshal, Marshal),
@@ -171,7 +169,7 @@ module Readthis
171
169
  # cache.increment('counter', 2) # => 3
172
170
  #
173
171
  def increment(key, amount = 1, options = {})
174
- invoke(:increment, key) do |store|
172
+ invoke(:increment, key) do |_store|
175
173
  alter(key, amount, options)
176
174
  end
177
175
  end
@@ -192,7 +190,7 @@ module Readthis
192
190
  # cache.decrement('counter', 2) # => 17
193
191
  #
194
192
  def decrement(key, amount = 1, options = {})
195
- invoke(:decrement, key) do |store|
193
+ invoke(:decrement, key) do |_store|
196
194
  alter(key, amount * -1, options)
197
195
  end
198
196
  end
@@ -271,13 +269,13 @@ module Readthis
271
269
  extracted = extract_options!(keys)
272
270
  missing = {}
273
271
 
274
- invoke(:fetch_multi, keys) do |store|
272
+ invoke(:fetch_multi, keys) do |_store|
275
273
  results.each do |key, value|
276
- if value.nil?
277
- value = yield(key)
278
- missing[key] = value
279
- results[key] = value
280
- end
274
+ next unless value.nil?
275
+
276
+ value = yield(key)
277
+ missing[key] = value
278
+ results[key] = value
281
279
  end
282
280
  end
283
281
 
@@ -354,10 +352,7 @@ module Readthis
354
352
  end
355
353
 
356
354
  def merged_options(options)
357
- options = options || {}
358
- options[:namespace] ||= namespace
359
- options[:expires_in] ||= expires_in
360
- options
355
+ (options || {}).merge!(@options)
361
356
  end
362
357
 
363
358
  def pool_options(options)
@@ -9,7 +9,6 @@ module Readthis
9
9
  }.freeze
10
10
 
11
11
  COMPRESSED_FLAG = 0x8
12
- MARSHAL_FLAG = 0x3
13
12
 
14
13
  # Creates a Readthis::Entity with default options. Each option can be
15
14
  # overridden later when entities are being dumped.
@@ -105,7 +104,7 @@ module Readthis
105
104
  flags = string[0].unpack('C').first
106
105
 
107
106
  if flags < 16
108
- marshal = serializers.rassoc(flags & MARSHAL_FLAG)
107
+ marshal = serializers.rassoc(flags)
109
108
  compress = (flags & COMPRESSED_FLAG) != 0
110
109
 
111
110
  [marshal, compress, string[1..-1]]
@@ -7,7 +7,7 @@ module Readthis
7
7
  when key.is_a?(Array)
8
8
  key.flat_map { |elem| expand_key(elem) }.join('/')
9
9
  when key.is_a?(Hash)
10
- key.sort_by { |key, _| key.to_s }.map { |key, val| "#{key}=#{val}" }.join('/')
10
+ key.sort_by { |hkey, _| hkey.to_s }.map { |hkey, val| "#{hkey}=#{val}" }.join('/')
11
11
  when key.respond_to?(:to_param)
12
12
  key.to_param
13
13
  else
@@ -2,8 +2,9 @@ require 'json'
2
2
  require 'readthis/passthrough'
3
3
 
4
4
  module Readthis
5
- SerializersFrozenError = Class.new(Exception)
6
- SerializersLimitError = Class.new(Exception)
5
+ SerializersFrozenError = Class.new(StandardError)
6
+ SerializersLimitError = Class.new(StandardError)
7
+ UnknownSerializerError = Class.new(StandardError)
7
8
 
8
9
  class Serializers
9
10
  BASE_SERIALIZERS = {
@@ -38,9 +39,9 @@ module Readthis
38
39
  def <<(serializer)
39
40
  case
40
41
  when serializers.frozen?
41
- raise SerializersFrozenError
42
+ fail SerializersFrozenError
42
43
  when serializers.length > SERIALIZER_LIMIT
43
- raise SerializersLimitError
44
+ fail SerializersLimitError
44
45
  else
45
46
  @serializers[serializer] = flags.max.succ
46
47
  @inverted = @serializers.invert
@@ -63,36 +64,46 @@ module Readthis
63
64
  @inverted = @serializers.invert
64
65
  end
65
66
 
66
- # Find a flag by the marshal object.
67
+ # Find a flag for a serializer object.
67
68
  #
68
69
  # @param [Object] Look up a flag by object
69
- # @return [Number] Corresponding flag for the marshal object
70
+ # @return [Number] Corresponding flag for the serializer object
71
+ # @raise [UnknownSerializerError] Indicates that a serializer was
72
+ # specified, but hasn't been configured for usage.
70
73
  #
71
74
  # @example
72
75
  #
73
- # serializers.assoc(Marshal) #=> 1
76
+ # serializers.assoc(JSON) #=> 1
74
77
  #
75
- def assoc(marshal)
76
- serializers[marshal]
78
+ def assoc(serializer)
79
+ flag = serializers[serializer]
80
+
81
+ unless flag
82
+ fail UnknownSerializerError, "'#{serializer}' hasn't been configured"
83
+ end
84
+
85
+ flag
77
86
  end
78
87
 
79
- # Find a marshal object by flag value.
88
+ # Find a serializer object by flag value.
89
+ #
90
+ # @param [Number] Flag to look up the serializer object by
91
+ # @return [Module] The serializer object
80
92
  #
81
- # @param [Number] Flag to look up the marshal object by
82
- # @return [Module] The marshal object
93
+ # @example
94
+ #
95
+ # serializers.rassoc(1) #=> Marshal
83
96
  #
84
97
  def rassoc(flag)
85
- inverted[flag]
98
+ inverted[flag & inverted.length]
86
99
  end
87
100
 
88
- # The current list of marshal objects.
89
- #
101
+ # @private
90
102
  def marshals
91
103
  serializers.keys
92
104
  end
93
105
 
94
- # The current list of flags.
95
- #
106
+ # @private
96
107
  def flags
97
108
  serializers.values
98
109
  end
@@ -1,3 +1,3 @@
1
1
  module Readthis
2
- VERSION = '1.0.0-beta'
2
+ VERSION = '1.0.0-rc.1'
3
3
  end
@@ -8,18 +8,6 @@ RSpec.describe Readthis::Cache do
8
8
  end
9
9
 
10
10
  describe '#initialize' do
11
- it 'accepts and persists a namespace' do
12
- cache = Readthis::Cache.new(namespace: 'kash')
13
-
14
- expect(cache.namespace).to eq('kash')
15
- end
16
-
17
- it 'accepts and persists an expiration' do
18
- cache = Readthis::Cache.new(expires_in: 10)
19
-
20
- expect(cache.expires_in).to eq(10)
21
- end
22
-
23
11
  it 'makes options available' do
24
12
  cache = Readthis::Cache.new(namespace: 'cache', expires_in: 1)
25
13
 
@@ -89,20 +77,20 @@ RSpec.describe Readthis::Cache do
89
77
 
90
78
  it 'uses globally configured serializers' do
91
79
  custom = Class.new do
92
- def self.dump(_)
93
- 'dumped'
80
+ def self.dump(value)
81
+ value
94
82
  end
95
83
 
96
- def self.load(_)
97
- 'dumped'
84
+ def self.load(value)
85
+ value
98
86
  end
99
87
  end
100
88
 
101
89
  Readthis.serializers << custom
102
90
 
103
- cache.write('customized', 'overwrite me', marshal: custom)
91
+ cache.write('customized', 'some value', marshal: custom)
104
92
 
105
- expect(cache.read('customized')).to include('dumped')
93
+ expect(cache.read('customized')).to eq('some value')
106
94
  end
107
95
  end
108
96
 
@@ -179,7 +167,7 @@ RSpec.describe Readthis::Cache do
179
167
  expect(cache.read_multi('a', 'b', 'c')).to eq(
180
168
  'a' => 1,
181
169
  'b' => 2,
182
- 'c' => '3',
170
+ 'c' => '3'
183
171
  )
184
172
  end
185
173
 
@@ -189,7 +177,7 @@ RSpec.describe Readthis::Cache do
189
177
 
190
178
  expect(cache.read_multi('d', 'e', namespace: 'cache')).to eq(
191
179
  'd' => 1,
192
- 'e' => 2,
180
+ 'e' => 2
193
181
  )
194
182
  end
195
183
 
@@ -208,7 +196,11 @@ RSpec.describe Readthis::Cache do
208
196
  end
209
197
 
210
198
  it 'respects passed options' do
211
- cache.write_multi({ 'a' => 1, 'b' => 2 }, namespace: 'multi', expires_in: 1)
199
+ cache.write_multi(
200
+ { 'a' => 1, 'b' => 2 },
201
+ namespace: 'multi',
202
+ expires_in: 1
203
+ )
212
204
 
213
205
  expect(cache.read('a')).to be_nil
214
206
  expect(cache.read('a', namespace: 'multi')).to eq(1)
@@ -227,7 +219,7 @@ RSpec.describe Readthis::Cache do
227
219
  expect(results).to eq(
228
220
  'a' => 1,
229
221
  'b' => 'bb',
230
- 'c' => 3,
222
+ 'c' => 3
231
223
  )
232
224
 
233
225
  expect(cache.read('b')).to eq('bb')
@@ -131,16 +131,6 @@ RSpec.describe Readthis::Entity do
131
131
  expect(value).to eq(string)
132
132
  end
133
133
 
134
- it 'can reconstruct longer qualified module names' do
135
- string = 'a' * 30
136
- entity = Readthis::Entity.new
137
- marked = entity.compose(string, Readthis::Passthrough, false)
138
-
139
- marshal, _, _ = entity.decompose(marked)
140
-
141
- expect(marshal).to eq(Readthis::Passthrough)
142
- end
143
-
144
134
  it 'returns the original string without a marker' do
145
135
  string = 'the quick brown fox'
146
136
  entity = Readthis::Entity.new
@@ -19,7 +19,7 @@ RSpec.describe Readthis::Expanders do
19
19
  it 'expands an array of objects' do
20
20
  object = double(cache_key: 'gamma')
21
21
 
22
- expect(expand(['alpha', 'beta'])).to eq('alpha/beta')
22
+ expect(expand(%w[alpha beta])).to eq('alpha/beta')
23
23
  expect(expand([object, object])).to eq('gamma/gamma')
24
24
  end
25
25
 
@@ -11,7 +11,7 @@ RSpec.describe Readthis::Serializers do
11
11
  serializers << CustomSerializer
12
12
 
13
13
  expect(serializers.marshals).to include(CustomSerializer)
14
- expect(serializers.flags).to eq((1..4).to_a)
14
+ expect(serializers.flags).to eq([1, 2, 3, 4])
15
15
  end
16
16
 
17
17
  it 'increments flags' do
@@ -25,9 +25,9 @@ RSpec.describe Readthis::Serializers do
25
25
  it 'prevents more than seven serializers' do
26
26
  serializers = Readthis::Serializers.new
27
27
 
28
- expect {
28
+ expect do
29
29
  10.times { serializers << Class.new }
30
- }.to raise_error(Readthis::SerializersLimitError)
30
+ end.to raise_error(Readthis::SerializersLimitError)
31
31
  end
32
32
  end
33
33
 
@@ -37,6 +37,14 @@ RSpec.describe Readthis::Serializers do
37
37
 
38
38
  expect(serializers.assoc(Marshal)).to eq(0x1)
39
39
  end
40
+
41
+ it 'raises a helpful error when the serializer is unknown' do
42
+ serializers = Readthis::Serializers.new
43
+
44
+ expect do
45
+ serializers.assoc(CustomSerializer)
46
+ end.to raise_error(Readthis::UnknownSerializerError)
47
+ end
40
48
  end
41
49
 
42
50
  describe '#rassoc' do
@@ -45,6 +53,13 @@ RSpec.describe Readthis::Serializers do
45
53
 
46
54
  expect(serializers.rassoc(1)).to eq(Marshal)
47
55
  end
56
+
57
+ it 'returns custom serializers' do
58
+ serializers = Readthis::Serializers.new
59
+ serializers << CustomSerializer
60
+
61
+ expect(serializers.rassoc(4)).to eq(CustomSerializer)
62
+ end
48
63
  end
49
64
 
50
65
  describe '#freeze!' do
@@ -53,9 +68,9 @@ RSpec.describe Readthis::Serializers do
53
68
 
54
69
  serializers.freeze!
55
70
 
56
- expect {
71
+ expect do
57
72
  serializers << CustomSerializer
58
- }.to raise_error(Readthis::SerializersFrozenError)
73
+ end.to raise_error(Readthis::SerializersFrozenError)
59
74
  end
60
75
  end
61
76
 
@@ -66,7 +81,7 @@ RSpec.describe Readthis::Serializers do
66
81
  serializers << Class.new
67
82
  serializers.reset!
68
83
 
69
- expect(serializers.marshals.length).to eq(3)
84
+ expect(serializers.serializers.length).to eq(3)
70
85
  end
71
86
  end
72
87
  end
data/spec/spec_helper.rb CHANGED
@@ -16,9 +16,7 @@ RSpec.configure do |config|
16
16
 
17
17
  config.disable_monkey_patching!
18
18
 
19
- if config.files_to_run.one?
20
- config.default_formatter = 'doc'
21
- end
19
+ config.default_formatter = 'doc' if config.files_to_run.one?
22
20
 
23
21
  config.order = :random
24
22
  Kernel.srand config.seed
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: readthis
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.pre.beta
4
+ version: 1.0.0.pre.rc.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Parker Selbert
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-09-18 00:00:00.000000000 Z
11
+ date: 2015-09-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -94,33 +94,14 @@ dependencies:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
96
  version: '3.1'
97
- description: Pooled active support compliant caching with redis
97
+ description:
98
98
  email:
99
99
  - parker@sorentwo.com
100
100
  executables: []
101
101
  extensions: []
102
102
  extra_rdoc_files: []
103
103
  files:
104
- - ".gitignore"
105
- - ".rspec"
106
- - ".travis.yml"
107
- - CHANGELOG.md
108
- - CONTRIBUTING.md
109
- - Gemfile
110
- - LICENSE.txt
111
- - PERFORMANCE.md
112
104
  - README.md
113
- - Rakefile
114
- - benchmarks/composing.rb
115
- - benchmarks/compressed.rb
116
- - benchmarks/driver.rb
117
- - benchmarks/generic.rb
118
- - benchmarks/marshalling.rb
119
- - benchmarks/memory.rb
120
- - benchmarks/multi.rb
121
- - benchmarks/parsing.rb
122
- - benchmarks/profile.rb
123
- - bin/rspec
124
105
  - lib/active_support/cache/readthis_store.rb
125
106
  - lib/readthis.rb
126
107
  - lib/readthis/cache.rb
@@ -130,7 +111,6 @@ files:
130
111
  - lib/readthis/passthrough.rb
131
112
  - lib/readthis/serializers.rb
132
113
  - lib/readthis/version.rb
133
- - readthis.gemspec
134
114
  - spec/readthis/cache_spec.rb
135
115
  - spec/readthis/entity_spec.rb
136
116
  - spec/readthis/expanders_spec.rb
data/.gitignore DELETED
@@ -1,15 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /Gemfile.lock
4
- /_yardoc/
5
- /coverage/
6
- /doc/
7
- /pkg/
8
- /spec/reports/
9
- /tmp/
10
- *.bundle
11
- *.so
12
- *.o
13
- *.a
14
- mkmf.log
15
- TODO
data/.rspec DELETED
@@ -1,2 +0,0 @@
1
- --color
2
- --require spec_helper
data/.travis.yml DELETED
@@ -1,15 +0,0 @@
1
- sudo: false
2
- language: ruby
3
- rvm:
4
- - 2.1.0
5
- - 2.2.2
6
- - ruby-head
7
- - rbx-2
8
- matrix:
9
- allow_failures:
10
- - rvm: ruby-head
11
- - rvm: rbx-2
12
- services:
13
- - redis-server
14
- script: bin/rspec
15
- bundler_args: --without benchmarking
data/CHANGELOG.md DELETED
@@ -1,135 +0,0 @@
1
- ## v1.0.0-beta
2
-
3
- - Breaking: This change is necessary for the consistency and portability of
4
- values going forward. All entities are now written with a set of option flags
5
- as the initial byte. This flag is later used to determine whether the entity
6
- was compressed and what was used to marshal it. There are a number of
7
- advantages to this approach, consistency and reliability being the most
8
- important. See [readthis#17][pull-17] for additional background.
9
- - Added: Per-entity options can be passed through to any cache method that
10
- writes a value (`write`, `fetch`, etc). For example, this allows certain
11
- entities to be cached as JSON while all other entities are cached using
12
- Marshal. Thanks to @fabn.
13
- - Fixed: A hash containing the cache key is passed as the payload for
14
- `ActiveSupport::Notifications` instrumentation, rather than the key directly.
15
- This moves the implementation in-line with the tests for the code, and
16
- prevents errors from being masked when an error occurs inside an instrumented
17
- block. [readthis#20][pull-20]. Discovered by @banister and fixed by @workmad3.
18
-
19
- [pull-17]: https://github.com/sorentwo/readthis/pull/17
20
- [pull-20]: https://github.com/sorentwo/readthis/pull/20
21
-
22
- ## v0.8.1 2015-09-04
23
-
24
- - Changed: `Readthis::Cache` now has an accessor for the options that were
25
- passed during initialization. This is primarily to support the session store
26
- middleware provided by `ActionDispatch`. See [readthis#16][issue-16].
27
- - Fixed: Caching `nil` values is now possible. Previously the value would be
28
- converted into a blank string, causing a Marshal error when loading the data.
29
- There is still some non-standard handling of `nil` within `fetch` or
30
- `fetch_multi`, where a cached `nil` value will always result in a cache miss.
31
- See [readthis#15][issue-15].
32
- - Fixed: Entity compression was broken, it wouldn't unload data when the
33
- compressed size was below the compression limit. Data is now decompressed
34
- when it can the value looks to be compressed, falling back to the initial
35
- value when decompression fails. See [readthis#13][issue-13] for details.
36
-
37
- [issue-13]: https://github.com/sorentwo/readthis/issues/13
38
- [issue-15]: https://github.com/sorentwo/readthis/issues/15
39
- [issue-16]: https://github.com/sorentwo/readthis/issues/16
40
-
41
- ## v0.8.0 2015-08-26
42
-
43
- - Breaking: The initializer now takes a single options argument instead of a
44
- `url` and `options` separately. This allows the underlying redis client to
45
- accept any options, rather than just the driver. For example, it's now
46
- possible to use Readthis with sentinel directly through the configuration.
47
- - Changed: The `hiredis` driver is *no longer the default*. In order to use the
48
- vastly faster `hiredis` driver you need to pass it in during construction.
49
- See [readthis#9][issue-9] for more discussion.
50
-
51
- [issue-9]: https://github.com/sorentwo/readthis/issues/9
52
-
53
- ## v0.7.0 2015-08-11
54
-
55
- - Changed: Entity initialization uses an options hash rather than keyword
56
- arguments. This allows flexibility with older Ruby versions (1.9) that aren't
57
- officially supported.
58
- - Changed: There is no longer a hard dependency on `hiredis`, though it is the
59
- default. The redis driver can be configured by passing a `driver: :ruby`
60
- option through to the constructor.
61
-
62
- ## v0.6.2 2015-04-28
63
-
64
- - Fixed: Set expiration during `write_multi`, primarily effecting `fetch_multi`.
65
- This fixes the real issue underlying the change in `v0.6.1`.
66
-
67
- ## v0.6.1 2015-04-28
68
-
69
- - Changed: Expiration values are always cast to an integer before use in write
70
- operations. This prevents subtle ActiveSupport bugs where the value would be
71
- ignored by `setex`.
72
-
73
- ## v0.6.0 2015-03-09
74
-
75
- - Fixed: Safely handle calling `read_multi` without any keys. [Michael Rykov]
76
- - Fixed: Pointed `redis-activesupport` at master. Only effected development and
77
- testing.
78
- - Added: A `write_multi` method is no available to bulk set keys and values. It
79
- is used by `fetch_multi` internally to ensure that there are at most two Redis
80
- calls.
81
-
82
- ## v0.5.2 2015-01-09
83
-
84
- - Fixed: Remove the `pipeline` around `fetch_multi` writing. This will slow down
85
- `fetch_multi` in cache miss situations for now. It prevents a difficult to
86
- track down exception in multi-threaded situations.
87
-
88
- ## v0.5.1 2014-12-30
89
-
90
- - Fixed: The `clear` method now accepts an argument for compatibility with other
91
- caches. The argument is not actually used for anything.
92
- - Changed: The `delete` method will always return a boolean value rather than an
93
- integer.
94
- - Changed: Avoid multiple instrumentation calls and pool checkouts within
95
- `fetch_multi` calls.
96
-
97
- ## v0.5.0 2014-12-12
98
-
99
- - Added: All read and write operations are marshalled to and from storage. This
100
- allows hashes, arrays, etc. to be restored instead of always returning a
101
- string. Unlike `ActiveSupport::Store::Entity`, no new objects are allocated
102
- for each entity, reducing GC and improving performance.
103
- - Fixed: Increment/Decrement interface was only accepting two params instead of
104
- three. Now accepts `amount` as the second parameter.
105
- - Changed: Increment/Decrement no longer use `incby` and `decby`, as they don't
106
- work with marshalled values. This means they are not entirely atomic, so race
107
- conditions are possible.
108
-
109
- ## v0.4.0 2014-12-11
110
-
111
- - Added: Force the use of `hiredis` as the adapter. It is dramatically faster,
112
- but prevents the project from being used in `jruby`. If we get interest from
113
- some `jruby` projects we can soften the requirement.
114
- - Added: Compression! Adheres to the `ActiveSupport::Store` documentation.
115
- - Fixed: Gracefully handle `nil` passed as `options` to any cache method.
116
-
117
- ## v0.3.0 2014-12-01
118
-
119
- - Added: Use `to_param` for key expansion, only when available. Makes it
120
- possible to extract a key from any object when ActiveSupport is loaded.
121
- - Added: Expand hashes as cache keys.
122
- - Changed: Use `mget` for `read_multi`, faster and more synchronous than relying on
123
- `pipelined`.
124
- - Changed: Delimit compound objects with a slash rather than a colon.
125
-
126
- ## v0.2.0 2014-11-24
127
-
128
- - Added: Instrument all caching methods. Will use `ActiveSupport::Notifications`
129
- if available, otherwise falls back to a polyfill.
130
- - Added: Expand objects with a `cache_key` method and arrays of strings or objects into
131
- consistent naespaced keys.
132
-
133
- ## v0.1.0 2014-11-22
134
-
135
- - Initial release! Working as a drop in replacement for `redis_store`.
data/CONTRIBUTING.md DELETED
@@ -1,14 +0,0 @@
1
- # Contributing
2
-
3
- ## Open an Issue
4
-
5
- Let us know about bugs. Include your version of Readthis, Ruby, and the
6
- environment you are using.
7
-
8
- ## Submit a Pull Request
9
-
10
- 1. Fork it ( https://github.com/sorentwo/readthis/fork )
11
- 2. Create your feature branch (`git checkout -b my-new-feature`)
12
- 3. Commit your changes (`git commit -am 'Add some feature'`)
13
- 4. Push to the branch (`git push origin my-new-feature`)
14
- 5. Create a new Pull Request
data/Gemfile DELETED
@@ -1,14 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec
4
-
5
- gem 'coveralls', require: false
6
-
7
- group :benchmarking do
8
- gem 'benchmark-ips'
9
- gem 'dalli'
10
- gem 'oj'
11
- gem 'msgpack'
12
- gem 'redis-activesupport'
13
- gem 'stackprof'
14
- end
data/LICENSE.txt DELETED
@@ -1,22 +0,0 @@
1
- Copyright (c) 2014 Parker Selbert
2
-
3
- MIT License
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining
6
- a copy of this software and associated documentation files (the
7
- "Software"), to deal in the Software without restriction, including
8
- without limitation the rights to use, copy, modify, merge, publish,
9
- distribute, sublicense, and/or sell copies of the Software, and to
10
- permit persons to whom the Software is furnished to do so, subject to
11
- the following conditions:
12
-
13
- The above copyright notice and this permission notice shall be
14
- included in all copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/PERFORMANCE.md DELETED
@@ -1,73 +0,0 @@
1
- Results from the various benchmarks in `./bencharks`. Hardware doesn't matter
2
- much, as we're simply looking for a comparison against other libraries and prior
3
- verions.
4
-
5
- ## Footprint
6
-
7
- Footprint compared to `redis-activesupport`:
8
-
9
- ```
10
- # Total allocated objects after require
11
- readthis: 20602
12
- redis-activesupport: 78630
13
- ```
14
-
15
- ## Performance
16
-
17
- Performance compared to `dalli` and `redis-activesupport`:
18
-
19
- ```
20
- Raw Read Multi:
21
- Calculating -------------------------------------
22
- readthis:read-multi 358.000 i/100ms
23
- redisas:read-multi 94.000 i/100ms
24
- dalli:read-multi 99.000 i/100ms
25
- -------------------------------------------------
26
- readthis:read-multi 3.800k (± 2.3%) i/s - 19.332k
27
- redisas:read-multi 962.199 (± 3.6%) i/s - 4.888k
28
- dalli:read-multi 995.353 (± 1.1%) i/s - 5.049k
29
-
30
- Comparison:
31
- readthis:read-multi: 3799.8 i/s
32
- dalli:read-multi: 995.4 i/s - 3.82x slower
33
- redisas:read-multi: 962.2 i/s - 3.95x slower
34
-
35
- Raw Fetch Multi:
36
- Calculating -------------------------------------
37
- readthis:fetch-multi 336.000 i/100ms
38
- redisas:fetch-multi 86.000 i/100ms
39
- dalli:fetch-multi 102.000 i/100ms
40
- -------------------------------------------------
41
- readthis:fetch-multi 3.424k (± 2.6%) i/s - 17.136k
42
- redisas:fetch-multi 874.803 (± 2.7%) i/s - 4.386k
43
- dalli:fetch-multi 1.028k (± 1.2%) i/s - 5.202k
44
-
45
- Comparison:
46
- readthis:fetch-multi: 3424.2 i/s
47
- dalli:fetch-multi: 1027.7 i/s - 3.33x slower
48
- redisas:fetch-multi: 874.8 i/s - 3.91x slower
49
-
50
- Compressed Writes:
51
- Calculating -------------------------------------
52
- readthis:write 924.000 i/100ms
53
- dalli:write 903.000 i/100ms
54
- -------------------------------------------------
55
- readthis:write 10.105k (± 4.9%) i/s - 50.820k
56
- dalli:write 9.662k (± 1.6%) i/s - 48.762k
57
-
58
- Comparison:
59
- readthis:write: 10105.3 i/s
60
- dalli:write: 9662.4 i/s - 1.05x slower
61
-
62
- Compressed Read Multi:
63
- Calculating -------------------------------------
64
- readthis:read_multi 325.000 i/100ms
65
- dalli:read_multi 100.000 i/100ms
66
- -------------------------------------------------
67
- readthis:read_multi 3.357k (± 2.3%) i/s - 16.900k
68
- dalli:read_multi 1.014k (± 3.1%) i/s - 5.100k
69
-
70
- Comparison:
71
- readthis:read_multi: 3356.5 i/s
72
- dalli:read_multi: 1014.1 i/s - 3.31x slower
73
- ```
data/Rakefile DELETED
@@ -1,2 +0,0 @@
1
- require "bundler/gem_tasks"
2
-
@@ -1,43 +0,0 @@
1
- require 'benchmark/ips'
2
-
3
- def compose_a(marshal, compress)
4
- prefix = ''
5
- prefix << 'R|'.freeze
6
- prefix << marshal.name.ljust(24)
7
- prefix << (compress ? '1'.freeze : '0'.freeze)
8
- prefix << 1
9
- prefix << '|R'.freeze
10
- end
11
-
12
- def compose_b(marshal, compress)
13
- "R|#{marshal.name.ljust(24)}#{compress ? '1'.freeze : '0'.freeze}1|R"
14
- end
15
-
16
- def compose_c(marshal, compress)
17
- name = marshal.name.ljust(24)
18
- comp = compress ? '1'.freeze : '0'.freeze
19
-
20
- "R|#{name}#{comp}1|R"
21
- end
22
-
23
- SERIALIZER_FLAG = { Marshal => 0x1 }.freeze
24
- COMPRESSED_FLAG = 0x8
25
-
26
- # | 0000 | 0 | 000 |
27
- # four unused bits, # 1 compression bit, 3 bits for serializer, allow up to 8
28
- # different marshalers
29
- def compose_d(marshal, compress)
30
- flags = SERIALIZER_FLAG[marshal]
31
- flags |= COMPRESSED_FLAG if compress
32
-
33
- [flags].pack('C')
34
- end
35
-
36
- Benchmark.ips do |x|
37
- x.report('a') { compose_a(Marshal, true) }
38
- x.report('b') { compose_b(Marshal, true) }
39
- x.report('c') { compose_c(Marshal, true) }
40
- x.report('d') { compose_d(Marshal, true) }
41
-
42
- x.compare!
43
- end
@@ -1,74 +0,0 @@
1
- require 'bundler'
2
-
3
- Bundler.setup
4
-
5
- require 'benchmark/ips'
6
- require 'dalli'
7
- require 'active_support'
8
- require 'active_support/cache/dalli_store'
9
- require 'readthis'
10
-
11
- dalli = ActiveSupport::Cache::DalliStore.new(
12
- 'localhost',
13
- pool_size: 5,
14
- compressed: true,
15
- compression_threshold: 8
16
- )
17
-
18
- readthis = Readthis::Cache.new(
19
- pool_size: 5,
20
- compressed: true,
21
- compression_threshold: 128
22
- )
23
-
24
- KEY = 'key'
25
- TEXT = <<-TEXT
26
- An abstract cache store class. There are multiple cache store implementations, each having its own additional features. See the classes under the ActiveSupport::Cache module, e.g. ActiveSupport::Cache::MemCacheStore. MemCacheStore is currently the most popular cache store for large production websites.
27
- Some implementations may not support all methods beyond the basic cache methods of fetch, write, read, exist?, and delete.
28
- ActiveSupport::Cache::Store can store any serializable Ruby object.
29
- cache = ActiveSupport::Cache::MemoryStore.new
30
- cache.read('city') # => nil
31
- cache.write('city', "Duckburgh")
32
- cache.read('city') # => "Duckburgh"
33
- Keys are always translated into Strings and are case sensitive. When an object is specified as a key and has a cache_key method defined, this method will be called to define the key. Otherwise, the to_param method will be called. Hashes and Arrays can also be used as keys. The elements will be delimited by slashes, and the elements within a Hash will be sorted by key so they are consistent.
34
- cache.read('city') == cache.read(:city) # => true
35
- Nil values can be cached.
36
- If your cache is on a shared infrastructure, you can define a namespace for your cache entries. If a namespace is defined, it will be prefixed on to every key. The namespace can be either a static value or a Proc. If it is a Proc, it will be invoked when each key is evaluated so that you can use application logic to invalidate keys.
37
- cache.namespace = -> { @last_mod_time } # Set the namespace to a variable
38
- @last_mod_time = Time.now # Invalidate the entire cache by changing namespace
39
- Caches can also store values in a compressed format to save space and reduce time spent sending data. Since there is overhead, values must be large enough to warrant compression. To turn on compression either pass compress: true in the initializer or as an option to fetch or write. To specify the threshold at which to compress values, set the :compress_threshold option. The default threshold is 16K.
40
- TEXT
41
-
42
- puts 'Compressed Write/Read:'
43
- Benchmark.ips do |x|
44
- x.report 'readthis:write/read' do
45
- readthis.write(KEY, TEXT)
46
- readthis.read(KEY)
47
- end
48
-
49
- x.report 'dalli:write/read' do
50
- dalli.write(KEY, TEXT)
51
- dalli.read(KEY)
52
- end
53
-
54
- x.compare!
55
- end
56
-
57
- puts 'Compressed Read Multi:'
58
- MULTI_KEY = (1..30).to_a
59
- MULTI_KEY.each do |key|
60
- readthis.write(key, TEXT)
61
- dalli.write(key, TEXT)
62
- end
63
-
64
- Benchmark.ips do |x|
65
- x.report 'readthis:read_multi' do
66
- readthis.read_multi(*MULTI_KEY)
67
- end
68
-
69
- x.report 'dalli:read_multi' do
70
- dalli.read_multi(*MULTI_KEY)
71
- end
72
-
73
- x.compare!
74
- end
data/benchmarks/driver.rb DELETED
@@ -1,18 +0,0 @@
1
- require 'bundler'
2
-
3
- Bundler.setup
4
-
5
- require 'benchmark/ips'
6
- require 'readthis'
7
-
8
- native = Readthis::Cache.new(redis: { driver: :ruby }, expires_in: 60)
9
- hiredis = Readthis::Cache.new(redis: { driver: :hiredis }, expires_in: 60)
10
-
11
- ('a'..'z').each { |key| native.write(key, key * 1024) }
12
-
13
- Benchmark.ips do |x|
14
- x.report('native:read-multi') { native.read_multi(*('a'..'z')) }
15
- x.report('hiredis:read-multi') { hiredis.read_multi(*('a'..'z')) }
16
-
17
- x.compare!
18
- end
@@ -1,30 +0,0 @@
1
- require 'benchmark/ips'
2
- require 'json'
3
- require 'readthis'
4
-
5
- READTHIS = Readthis::Cache.new(
6
- expires_in: 120,
7
- marshal: JSON,
8
- compress: true
9
- )
10
-
11
- def write_key(key)
12
- READTHIS.write(key, key.to_s * 2048)
13
- end
14
-
15
- KEYS = (1..1_000).to_a
16
- KEYS.each { |key| write_key(key) }
17
-
18
- Benchmark.ips do |x|
19
- x.report 'readthis:write' do
20
- write_key(KEYS.sample)
21
- end
22
-
23
- x.report 'readthis:read' do
24
- READTHIS.read(KEYS.sample)
25
- end
26
-
27
- x.report 'readthis:read_multi' do
28
- READTHIS.read(KEYS.sample(30))
29
- end
30
- end
@@ -1,38 +0,0 @@
1
- require 'bundler'
2
-
3
- Bundler.setup
4
-
5
- require 'benchmark/ips'
6
- require 'json'
7
- require 'oj'
8
- require 'msgpack'
9
- require 'readthis'
10
- require 'readthis/passthrough'
11
-
12
- Readthis.serializers << Oj
13
- Readthis.serializers << MessagePack
14
-
15
- readthis_oj = Readthis::Cache.new(marshal: Oj)
16
- readthis_msgpack = Readthis::Cache.new(marshal: MessagePack)
17
- readthis_json = Readthis::Cache.new(marshal: JSON)
18
- readthis_ruby = Readthis::Cache.new(marshal: Marshal)
19
-
20
- HASH = ('a'..'z').each_with_object({}) { |key, memo| memo[key] = key }
21
-
22
- Benchmark.ips do |x|
23
- x.report('oj:hash:dump') { readthis_oj.write('oj', HASH) }
24
- x.report('json:hash:dump') { readthis_json.write('json', HASH) }
25
- x.report('msgpack:hash:dump') { readthis_msgpack.write('msgpack', HASH) }
26
- x.report('ruby:hash:dump') { readthis_ruby.write('ruby', HASH) }
27
-
28
- x.compare!
29
- end
30
-
31
- Benchmark.ips do |x|
32
- x.report('oj:hash:load') { readthis_oj.read('oj') }
33
- x.report('json:hash:load') { readthis_json.read('json') }
34
- x.report('msgpack:hash:load') { readthis_msgpack.read('msgpack') }
35
- x.report('ruby:hash:load') { readthis_ruby.read('ruby') }
36
-
37
- x.compare!
38
- end
data/benchmarks/memory.rb DELETED
@@ -1,11 +0,0 @@
1
- require 'bundler'; Bundler.setup
2
- a = GC.stat(:total_allocated_objects)
3
-
4
- require 'readthis'
5
- b = GC.stat(:total_allocated_objects)
6
-
7
- require 'redis-activesupport'
8
- c = GC.stat(:total_allocated_objects)
9
-
10
- puts "readthis: #{b - a}"
11
- puts "redis-activesupport: #{c - b}"
data/benchmarks/multi.rb DELETED
@@ -1,60 +0,0 @@
1
- require 'benchmark/ips'
2
- require 'dalli'
3
- require 'redis-activesupport'
4
- require 'active_support/cache/memory_store'
5
- require 'active_support/cache/dalli_store'
6
- require 'readthis'
7
-
8
- memory = ActiveSupport::Cache::MemoryStore.new(expires_in: 60, namespace: 'mm')
9
- dalli = ActiveSupport::Cache::DalliStore.new('localhost', namespace: 'da', pool_size: 5, expires_in: 60)
10
- redisas = ActiveSupport::Cache::RedisStore.new('redis://localhost:6379/11/ra', expires_in: 60)
11
- readthis = Readthis::Cache.new(namespace: 'rd', expires_in: 60)
12
-
13
- ('a'..'z').each do |key|
14
- value = key * 1024
15
-
16
- memory.write(key, value)
17
- dalli.write(key, value)
18
- readthis.write(key, value)
19
- redisas.write(key, value)
20
- end
21
-
22
- Benchmark.ips do |x|
23
- x.report 'memory:read-multi' do
24
- memory.read_multi(*('a'..'z'))
25
- end
26
-
27
- x.report 'readthis:read-multi' do
28
- readthis.read_multi(*('a'..'z'))
29
- end
30
-
31
- x.report 'redisas:read-multi' do
32
- redisas.read_multi(*('a'..'z'))
33
- end
34
-
35
- x.report 'dalli:read-multi' do
36
- dalli.read_multi(*('a'..'z'))
37
- end
38
-
39
- x.compare!
40
- end
41
-
42
- Benchmark.ips do |x|
43
- x.report 'memory:fetch-multi' do
44
- memory.fetch_multi(*('a'..'z')) { |_| 'missing' }
45
- end
46
-
47
- x.report 'readthis:fetch-multi' do
48
- readthis.fetch_multi(*('a'..'z')) { |_| 'missing' }
49
- end
50
-
51
- x.report 'redisas:fetch-multi' do
52
- redisas.fetch_multi(*('a'..'z')) { |_| 'missing' }
53
- end
54
-
55
- x.report 'dalli:fetch-multi' do
56
- dalli.fetch_multi(*('a'..'z')) { |_| 'missing' }
57
- end
58
-
59
- x.compare!
60
- end
@@ -1,42 +0,0 @@
1
- require 'benchmark/ips'
2
-
3
- def parse_a(string)
4
- marshal = string[2, 12].strip
5
- compress = string[15] == '1'.freeze
6
-
7
- [Kernel.const_get(marshal), compress, string[18..-1]]
8
- end
9
-
10
- def parse_b(marked)
11
- prefix = marked[0, 32].scrub('*'.freeze)[/R\|(.*)\|R/, 1]
12
- offset = prefix.size + 4
13
-
14
- marshal, c_name, _ = prefix.split(' '.freeze)
15
-
16
- compress = c_name == 'true'.freeze
17
-
18
- [Kernel.const_get(marshal), compress, marked[offset..-1]]
19
- end
20
-
21
- SERIALIZERS = { Marshal => 0x1 }.freeze
22
- DESERIALIZERS = SERIALIZERS.invert.freeze
23
- COMPRESSED_FLAG = 0x8
24
- MARSHAL_FLAG = 0x3
25
- BINARY_FLAG = [SERIALIZERS[Marshal] | COMPRESSED_FLAG].pack('C')
26
-
27
- def parse_c(binary_string)
28
- flags = binary_string[0].unpack('C').first
29
-
30
- [DESERIALIZERS[flags & MARSHAL_FLAG], flags & COMPRESSED_FLAG, binary_string[1..-1]]
31
- end
32
-
33
- STR = 'R|Marshal 0|Rafdlkadfjadfj asdlkfjasdlfkj asdlfkjdasflkjadsflkjadslkjfadslkjfasdlkjfadlskjf laksdjflkajsdflkjadsflkadjsfladskjf laksjflakdjfalsdkjfadlskjf laksdjflkajdsflk j'
34
- STR2 = BINARY_FLAG << 'Rafdlkadfjadfj asdlkfjasdlfkj asdlfkjdasflkjadsflkjadslkjfadslkjfasdlkjfadlskjf laksdjflkajsdflkjadsflkadjsfladskjf laksjflakdjfalsdkjfadlskjf laksdjflkajdsflk j'
35
-
36
- Benchmark.ips do |x|
37
- x.report('a') { parse_a(STR) }
38
- x.report('b') { parse_b(STR) }
39
- x.report('c') { parse_c(STR2) }
40
-
41
- x.compare!
42
- end
@@ -1,20 +0,0 @@
1
- require 'bundler'
2
-
3
- Bundler.setup
4
-
5
- require 'fileutils'
6
- require 'stackprof'
7
- require 'readthis'
8
-
9
- readthis = Readthis::Cache.new
10
-
11
- FileUtils.mkdir_p('tmp')
12
- readthis.clear
13
-
14
- ('a'..'z').each { |key| readthis.write(key, key * 1024) }
15
-
16
- StackProf.run(mode: :object, interval: 500, out: "tmp/stackprof-object.dump") do
17
- 1000.times do
18
- readthis.read_multi(*('a'..'z'))
19
- end
20
- end
data/bin/rspec DELETED
@@ -1,16 +0,0 @@
1
- #!/usr/bin/env ruby
2
- #
3
- # This file was generated by Bundler.
4
- #
5
- # The application 'rspec' is installed as part of a gem, and
6
- # this file is here to facilitate running it.
7
- #
8
-
9
- require 'pathname'
10
- ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
- Pathname.new(__FILE__).realpath)
12
-
13
- require 'rubygems'
14
- require 'bundler/setup'
15
-
16
- load Gem.bin_path('rspec-core', 'rspec')
data/readthis.gemspec DELETED
@@ -1,27 +0,0 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'readthis/version'
5
-
6
- Gem::Specification.new do |spec|
7
- spec.name = 'readthis'
8
- spec.version = Readthis::VERSION
9
- spec.authors = ['Parker Selbert']
10
- spec.email = ['parker@sorentwo.com']
11
- spec.summary = 'Pooled active support compliant caching with redis'
12
- spec.description = 'Pooled active support compliant caching with redis'
13
- spec.homepage = 'https://github.com/sorentwo/readthis'
14
- spec.license = 'MIT'
15
-
16
- spec.files = `git ls-files -z`.split("\x0")
17
- spec.test_files = spec.files.grep(%r{^(spec)/})
18
- spec.require_paths = ['lib']
19
-
20
- spec.add_dependency 'redis', '~> 3.0'
21
- spec.add_dependency 'connection_pool', '~> 2.1'
22
-
23
- spec.add_development_dependency 'bundler'
24
- spec.add_development_dependency 'hiredis', '~> 0.5'
25
- spec.add_development_dependency 'rake'
26
- spec.add_development_dependency 'rspec', '~> 3.1'
27
- end