lru_redux 0.8.4 → 1.1.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/README.md +190 -29
- data/Rakefile +1 -1
- data/bench/bench.rb +27 -29
- data/bench/bench_ttl.rb +33 -0
- data/lib/lru_redux.rb +9 -14
- data/lib/lru_redux/cache.rb +54 -109
- data/lib/lru_redux/{cache19.rb → cache_legacy.rb} +0 -53
- data/lib/lru_redux/thread_safe_cache.rb +1 -73
- data/lib/lru_redux/ttl.rb +4 -0
- data/lib/lru_redux/ttl/cache.rb +192 -0
- data/lib/lru_redux/ttl/thread_safe_cache.rb +3 -0
- data/lib/lru_redux/util.rb +4 -0
- data/lib/lru_redux/util/safe_sync.rb +103 -0
- data/lib/lru_redux/util/safe_sync_jruby.rb +17 -0
- data/lib/lru_redux/version.rb +1 -1
- data/lru_redux.gemspec +6 -3
- data/test/cache_test.rb +10 -2
- data/test/thread_safe_cache_test.rb +1 -3
- data/test/ttl/cache_test.rb +91 -0
- data/test/ttl/thread_safe_cache_test.rb +16 -0
- metadata +33 -7
- data/lib/lru_redux/thread_safe_cache_jruby.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ee0116ff3d2b67bcd4afa09cc37dff5d64d1f173
|
4
|
+
data.tar.gz: 1a8db0cbbecb94b2f112ef2482f7a76f2335dd03
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1982913819cd75633cef5158c3adc14d0680a3a5fd1c4c4bec4a5f73455817c76d4f7a65e0ffc3fd9ef2b03c73db4948c39464585439c6867e5850c5a1f080e9
|
7
|
+
data.tar.gz: 3da4da91b505b90a9ce57fc0f1abc288aee7cc1496e16e020e234dfa621c1b1cd179fed22d467addeb2fedcbc7acdbc1d797c8e072549f405a8016ec1c78cdc1
|
data/README.md
CHANGED
@@ -1,10 +1,14 @@
|
|
1
|
-
# LruRedux
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
1
|
+
# LruRedux [](http://badge.fury.io/rb/lru_redux)
|
2
|
+
An efficient, thread safe LRU cache.
|
3
|
+
|
4
|
+
- [Installation](#installation)
|
5
|
+
- [Usage](#usage)
|
6
|
+
- [TTL Cache](#ttl-cache)
|
7
|
+
- [Cache Methods](#cache-methods)
|
8
|
+
- [Benchmarks](#benchmarks)
|
9
|
+
- [Other Caches](#other-caches)
|
10
|
+
- [Contributing](#contributing)
|
11
|
+
- [Changelog](#changelog)
|
8
12
|
|
9
13
|
## Installation
|
10
14
|
|
@@ -20,6 +24,10 @@ Or install it yourself as:
|
|
20
24
|
|
21
25
|
$ gem install lru_redux
|
22
26
|
|
27
|
+
Ruby 1.8 - v0.8.4 is the last compatible release:
|
28
|
+
|
29
|
+
gem 'lru_redux', '~> 0.8.4'
|
30
|
+
|
23
31
|
## Usage
|
24
32
|
|
25
33
|
```ruby
|
@@ -61,29 +69,174 @@ cache = LruRedux::ThreadSafeCache.new(100)
|
|
61
69
|
|
62
70
|
```
|
63
71
|
|
72
|
+
#### TTL Cache
|
73
|
+
The TTL cache extends the functionality of the LRU cache with a Time To Live eviction strategy. TTL eviction occurs on every access and takes precedence over LRU eviction, meaning a 'live' value will never be evicted over an expired one.
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
# Timecop is gem that allows us to change Time.now
|
77
|
+
# and is used for demonstration purposes.
|
78
|
+
require 'lru_redux'
|
79
|
+
require 'timecop'
|
80
|
+
|
81
|
+
# Create a TTL cache with a size of 100 and TTL of 5 minutes.
|
82
|
+
# The first argument is the size and
|
83
|
+
# the second optional argument is the TTL in seconds.
|
84
|
+
cache = LruRedux::TTL::Cache.new(100, 5 * 60)
|
85
|
+
|
86
|
+
Timecop.freeze(Time.now)
|
87
|
+
|
88
|
+
cache[:a] = "1"
|
89
|
+
cache[:b] = "2"
|
90
|
+
|
91
|
+
cache.to_a
|
92
|
+
# => [[:b,"2"],[:a,"1"]]
|
93
|
+
|
94
|
+
# Now we advance time 5 min 30 sec into the future.
|
95
|
+
Timecop.freeze(Time.now + 330)
|
96
|
+
|
97
|
+
# And we see that the expired values have been evicted.
|
98
|
+
cache.to_a
|
99
|
+
# => []
|
100
|
+
|
101
|
+
# The TTL can be updated on a live cache using #ttl=.
|
102
|
+
# Currently cached items will be evicted under the new TTL.
|
103
|
+
cache[:a] = "1"
|
104
|
+
cache[:b] = "2"
|
105
|
+
|
106
|
+
Timecop.freeze(Time.now + 330)
|
107
|
+
|
108
|
+
cache.ttl = 10 * 60
|
109
|
+
|
110
|
+
# Since ttl eviction is triggered by access,
|
111
|
+
# the items are still cached when the ttl is changed and
|
112
|
+
# are now under the 10 minute TTL.
|
113
|
+
cache.to_a
|
114
|
+
# => [[:b,"2"],[:a,"1"]]
|
115
|
+
|
116
|
+
# TTL eviction can be triggered manually with the #expire method.
|
117
|
+
Timecop.freeze(Time.now + 330)
|
118
|
+
|
119
|
+
cache.expire
|
120
|
+
cache.to_a
|
121
|
+
# => []
|
122
|
+
|
123
|
+
Timecop.return
|
124
|
+
|
125
|
+
# The behavior of a TTL cache with the TTL set to `:none`
|
126
|
+
# is identical to the LRU cache.
|
127
|
+
|
128
|
+
cache = LruRedux::TTL::Cache.new(100, :none)
|
129
|
+
|
130
|
+
# The TTL argument is optional and defaults to `:none`.
|
131
|
+
cache = LruRedux::TTL::Cache.new(100)
|
132
|
+
|
133
|
+
# A thread safe version is available.
|
134
|
+
cache = LruRedux::TTL::ThreadSafeCache.new(100, 5 * 60)
|
135
|
+
```
|
136
|
+
|
137
|
+
## Cache Methods
|
138
|
+
- `#getset` Takes a key and block. Will return a value if cached, otherwise will execute the block and cache the resulting value.
|
139
|
+
- `#fetch` Takes a key and optional block. Will return a value if cached, otherwise will execute the block and return the resulting value or return nil if no block is provided.
|
140
|
+
- `#[]` Takes a key. Will return a value if cached, otherwise nil.
|
141
|
+
- `#[]=` Takes a key and value. Will cache the value under the key.
|
142
|
+
- `#delete` Takes a key. Will return the deleted value, otherwise nil.
|
143
|
+
- `#evict` Alias for `#delete`.
|
144
|
+
- `#clear` Clears the cache. Returns nil.
|
145
|
+
- `#each` Takes a block. Executes the block on each key-value pair in LRU order (most recent first).
|
146
|
+
- `#to_a` Return an array of key-value pairs (arrays) in LRU order (most recent first).
|
147
|
+
- `#key?` Takes a key. Returns true if the key is cached, otherwise false.
|
148
|
+
- `#has_key?` Alias for `#key?`.
|
149
|
+
- `#count` Return the current number of items stored in the cache.
|
150
|
+
- `#max_size` Returns the current maximum size of the cache.
|
151
|
+
- `#max_size=` Takes a positive number. Changes the current max_size and triggers a resize. Also triggers TTL eviction on the TTL cache.
|
152
|
+
|
153
|
+
#### TTL Cache Specific
|
154
|
+
- `#ttl` Returns the current TTL of the cache.
|
155
|
+
- `#ttl=` Takes `:none` or a positive number. Changes the current ttl and triggers a TTL eviction.
|
156
|
+
- `#expire` Triggers a TTL eviction.
|
157
|
+
|
64
158
|
## Benchmarks
|
65
159
|
|
66
160
|
see: benchmark directory (a million random lookup / store)
|
67
161
|
|
162
|
+
#### LRU
|
163
|
+
##### Ruby 2.2.1
|
68
164
|
```
|
69
165
|
$ ruby ./bench/bench.rb
|
70
|
-
Rehearsal ---------------------------------------------------------
|
71
|
-
thread safe lru 4.530000 0.020000 4.550000 ( 4.540861)
|
72
|
-
lru gem 2.040000 0.000000 2.040000 ( 2.046777)
|
73
|
-
lru_cache gem 1.660000 0.010000 1.670000 ( 1.670404)
|
74
|
-
lru_redux gem 1.200000 0.000000 1.200000 ( 1.197036)
|
75
|
-
lru_redux thread safe 2.520000 0.000000 2.520000 ( 2.526945)
|
76
|
-
----------------------------------------------- total: 11.980000sec
|
77
|
-
|
78
|
-
user system total real
|
79
|
-
thread safe lru 4.550000 0.030000 4.580000 ( 4.581848)
|
80
|
-
lru gem 2.060000 0.000000 2.060000 ( 2.056636)
|
81
|
-
lru_cache gem 1.660000 0.010000 1.670000 ( 1.669312)
|
82
|
-
lru_redux gem 1.180000 0.000000 1.180000 ( 1.187639)
|
83
|
-
lru_redux thread safe 2.530000 0.000000 2.530000 ( 2.532061)
|
84
166
|
|
167
|
+
Rehearsal -------------------------------------------------------------
|
168
|
+
ThreadSafeLru 4.500000 0.030000 4.530000 ( 4.524213)
|
169
|
+
LRU 2.250000 0.000000 2.250000 ( 2.249670)
|
170
|
+
LRUCache 1.720000 0.010000 1.730000 ( 1.728243)
|
171
|
+
LruRedux::Cache 0.960000 0.000000 0.960000 ( 0.961292)
|
172
|
+
LruRedux::ThreadSafeCache 2.180000 0.000000 2.180000 ( 2.187714)
|
173
|
+
--------------------------------------------------- total: 11.650000sec
|
174
|
+
|
175
|
+
user system total real
|
176
|
+
ThreadSafeLru 4.390000 0.020000 4.410000 ( 4.415703)
|
177
|
+
LRU 2.140000 0.010000 2.150000 ( 2.149626)
|
178
|
+
LRUCache 1.680000 0.010000 1.690000 ( 1.688564)
|
179
|
+
LruRedux::Cache 0.910000 0.000000 0.910000 ( 0.913108)
|
180
|
+
LruRedux::ThreadSafeCache 2.200000 0.010000 2.210000 ( 2.212108)
|
85
181
|
```
|
86
182
|
|
183
|
+
##### Ruby 2.0.0-p643
|
184
|
+
Implementation is slightly different for Ruby versions before 2.1 due to a Ruby bug. http://bugs.ruby-lang.org/issues/8312
|
185
|
+
```
|
186
|
+
$ ruby ./bench/bench.rb
|
187
|
+
Rehearsal -------------------------------------------------------------
|
188
|
+
ThreadSafeLru 4.790000 0.040000 4.830000 ( 4.828370)
|
189
|
+
LRU 2.170000 0.010000 2.180000 ( 2.180630)
|
190
|
+
LRUCache 1.810000 0.000000 1.810000 ( 1.814737)
|
191
|
+
LruRedux::Cache 1.330000 0.010000 1.340000 ( 1.325554)
|
192
|
+
LruRedux::ThreadSafeCache 2.770000 0.000000 2.770000 ( 2.777754)
|
193
|
+
--------------------------------------------------- total: 12.930000sec
|
194
|
+
|
195
|
+
user system total real
|
196
|
+
ThreadSafeLru 4.710000 0.060000 4.770000 ( 4.773233)
|
197
|
+
LRU 2.120000 0.010000 2.130000 ( 2.135111)
|
198
|
+
LRUCache 1.780000 0.000000 1.780000 ( 1.781392)
|
199
|
+
LruRedux::Cache 1.190000 0.010000 1.200000 ( 1.201908)
|
200
|
+
LruRedux::ThreadSafeCache 2.650000 0.010000 2.660000 ( 2.652580)
|
201
|
+
```
|
202
|
+
|
203
|
+
#### TTL
|
204
|
+
##### Ruby 2.2.1
|
205
|
+
```
|
206
|
+
$ ruby ./bench/bench_ttl.rb
|
207
|
+
Rehearsal -----------------------------------------------------------------------
|
208
|
+
FastCache 6.240000 0.070000 6.310000 ( 6.302569)
|
209
|
+
LruRedux::TTL::Cache 4.700000 0.010000 4.710000 ( 4.712858)
|
210
|
+
LruRedux::TTL::ThreadSafeCache 6.300000 0.010000 6.310000 ( 6.319032)
|
211
|
+
LruRedux::TTL::Cache (TTL disabled) 2.460000 0.010000 2.470000 ( 2.470629)
|
212
|
+
------------------------------------------------------------- total: 19.800000sec
|
213
|
+
|
214
|
+
user system total real
|
215
|
+
FastCache 6.470000 0.070000 6.540000 ( 6.536193)
|
216
|
+
LruRedux::TTL::Cache 4.640000 0.010000 4.650000 ( 4.661793)
|
217
|
+
LruRedux::TTL::ThreadSafeCache 6.310000 0.020000 6.330000 ( 6.328840)
|
218
|
+
LruRedux::TTL::Cache (TTL disabled) 2.440000 0.000000 2.440000 ( 2.446269)
|
219
|
+
```
|
220
|
+
|
221
|
+
## Other Caches
|
222
|
+
This is a list of the caches that are used in the benchmarks.
|
223
|
+
|
224
|
+
#### LRU
|
225
|
+
- RubyGems: https://rubygems.org/gems/lru
|
226
|
+
- Homepage: http://lru.rubyforge.org/
|
227
|
+
|
228
|
+
#### LRUCache
|
229
|
+
- RubyGems: https://rubygems.org/gems/lru_cache
|
230
|
+
- Homepage: https://github.com/brendan/lru_cache
|
231
|
+
|
232
|
+
#### ThreadSafeLru
|
233
|
+
- RubyGems: https://rubygems.org/gems/threadsafe-lru
|
234
|
+
- Homepage: https://github.com/draganm/threadsafe-lru
|
235
|
+
|
236
|
+
#### FastCache
|
237
|
+
- RubyGems: https://rubygems.org/gems/fast_cache
|
238
|
+
- Homepage: https://github.com/swoop-inc/fast_cache
|
239
|
+
|
87
240
|
|
88
241
|
## Contributing
|
89
242
|
|
@@ -94,20 +247,28 @@ lru_redux thread safe 2.530000 0.000000 2.530000 ( 2.532061)
|
|
94
247
|
5. Create new Pull Request
|
95
248
|
|
96
249
|
## Changlog
|
250
|
+
###version 1.1.0 - 30-Mar-2015
|
251
|
+
|
252
|
+
- New: TTL cache added. This cache is LRU like with the addition of time-based eviction. Check the Usage -> TTL Cache section in README.md for details.
|
253
|
+
|
254
|
+
###version 1.0.0 - 26-Mar-2015
|
255
|
+
|
256
|
+
- Ruby Support: Ruby 1.9+ is now required by LruRedux. If you need to use LruRedux in Ruby 1.8, please specify gem version 0.8.4 in your Gemfile. v0.8.4 is the last 1.8 compatible release and included a number of fixes and performance improvements for the Ruby 1.8 implementation. @Seberius
|
257
|
+
- Perf: improve performance in Ruby 2.1+ on the MRI @Seberius
|
97
258
|
|
98
|
-
###version 0.8.4 - 20-Feb-
|
259
|
+
###version 0.8.4 - 20-Feb-2015
|
99
260
|
|
100
|
-
- Fix: regression of ThreadSafeCache under JRuby 1.7 @
|
261
|
+
- Fix: regression of ThreadSafeCache under JRuby 1.7 @Seberius
|
101
262
|
|
102
|
-
###version 0.8.3 - 20-Feb-
|
263
|
+
###version 0.8.3 - 20-Feb-2015
|
103
264
|
|
104
|
-
- Perf: improve ThreadSafeCache performance @
|
265
|
+
- Perf: improve ThreadSafeCache performance @Seberius
|
105
266
|
|
106
|
-
###version 0.8.2 - 16-Feb-
|
267
|
+
###version 0.8.2 - 16-Feb-2015
|
107
268
|
|
108
|
-
- Perf: use #size instead of #count when checking length @
|
109
|
-
- Fix: Cache could grow beyond its size in Ruby 1.8 @
|
110
|
-
- Fix: #each could deadlock in Ruby 1.8 @
|
269
|
+
- Perf: use #size instead of #count when checking length @Seberius
|
270
|
+
- Fix: Cache could grow beyond its size in Ruby 1.8 @Seberius
|
271
|
+
- Fix: #each could deadlock in Ruby 1.8 @Seberius
|
111
272
|
|
112
273
|
|
113
274
|
###version 0.8.1 - 7-Sep-2013
|
data/Rakefile
CHANGED
data/bench/bench.rb
CHANGED
@@ -1,45 +1,43 @@
|
|
1
|
-
require '
|
2
|
-
require 'lru'
|
1
|
+
require 'bundler'
|
3
2
|
require 'benchmark'
|
3
|
+
require 'lru'
|
4
4
|
require 'lru_cache'
|
5
5
|
require 'threadsafe-lru'
|
6
|
-
$LOAD_PATH.unshift File.expand_path '../lib'
|
7
|
-
require File.expand_path('../../lib/lru_redux', __FILE__)
|
8
6
|
|
7
|
+
Bundler.require
|
8
|
+
|
9
|
+
# Lru
|
9
10
|
lru = Cache::LRU.new(max_elements: 1_000)
|
11
|
+
|
12
|
+
# LruCache
|
10
13
|
lru_cache = LRUCache.new(1_000)
|
11
14
|
|
12
|
-
|
13
|
-
lru_redux_thread_safe = LruRedux::ThreadSafeCache.new(1_000)
|
15
|
+
# ThreadSafeLru
|
14
16
|
thread_safe_lru = ThreadSafeLru::LruCache.new(1_000)
|
15
17
|
|
16
|
-
|
18
|
+
# LruRedux
|
19
|
+
redux = LruRedux::Cache.new(1_000)
|
20
|
+
redux_thread_safe = LruRedux::ThreadSafeCache.new(1_000)
|
21
|
+
|
22
|
+
puts "** LRU Benchmarks **"
|
23
|
+
Benchmark.bmbm do |bm|
|
24
|
+
bm.report 'ThreadSafeLru' do
|
25
|
+
1_000_000.times { thread_safe_lru.get(rand(2_000)) { :value } }
|
26
|
+
end
|
27
|
+
|
28
|
+
bm.report 'LRU' do
|
29
|
+
1_000_000.times { lru[rand(2_000)] ||= :value }
|
30
|
+
end
|
17
31
|
|
18
|
-
bm.report
|
19
|
-
1_000_000.times
|
20
|
-
thread_safe_lru.get(rand(2_000)){ :value }
|
21
|
-
end
|
32
|
+
bm.report 'LRUCache' do
|
33
|
+
1_000_000.times { lru_cache[rand(2_000)] ||= :value }
|
22
34
|
end
|
23
35
|
|
24
|
-
|
25
|
-
|
26
|
-
[lru_cache, "lru_cache gem"],
|
27
|
-
].each do |cache, name|
|
28
|
-
bm.report name do
|
29
|
-
1_000_000.times do
|
30
|
-
cache[rand(2_000)] ||= :value
|
31
|
-
end
|
32
|
-
end
|
36
|
+
bm.report 'LruRedux::Cache' do
|
37
|
+
1_000_000.times { redux.getset(rand(2_000)) { :value } }
|
33
38
|
end
|
34
39
|
|
35
|
-
|
36
|
-
|
37
|
-
[lru_redux_thread_safe, "lru_redux thread safe"]
|
38
|
-
].each do |cache, name|
|
39
|
-
bm.report name do
|
40
|
-
1_000_000.times do
|
41
|
-
cache.getset(rand(2_000)) { :value }
|
42
|
-
end
|
43
|
-
end
|
40
|
+
bm.report 'LruRedux::ThreadSafeCache' do
|
41
|
+
1_000_000.times { redux_thread_safe.getset(rand(2_000)) { :value } }
|
44
42
|
end
|
45
43
|
end
|
data/bench/bench_ttl.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
require 'benchmark'
|
3
|
+
require 'fast_cache'
|
4
|
+
|
5
|
+
Bundler.require
|
6
|
+
|
7
|
+
# FastCache
|
8
|
+
fast_cache = FastCache::Cache.new(1_000, 5 * 60)
|
9
|
+
|
10
|
+
# LruRedux
|
11
|
+
redux_ttl = LruRedux::TTL::Cache.new(1_000, 5 * 60)
|
12
|
+
redux_ttl_thread_safe = LruRedux::TTL::ThreadSafeCache.new(1_000, 5 * 60)
|
13
|
+
redux_ttl_disabled = LruRedux::TTL::Cache.new(1_000, :none)
|
14
|
+
|
15
|
+
puts
|
16
|
+
puts "** TTL Benchmarks **"
|
17
|
+
Benchmark.bmbm do |bm|
|
18
|
+
bm.report 'FastCache' do
|
19
|
+
1_000_000.times { fast_cache.fetch(rand(2_000)) { :value } }
|
20
|
+
end
|
21
|
+
|
22
|
+
bm.report 'LruRedux::TTL::Cache' do
|
23
|
+
1_000_000.times { redux_ttl.getset(rand(2_000)) { :value } }
|
24
|
+
end
|
25
|
+
|
26
|
+
bm.report 'LruRedux::TTL::ThreadSafeCache' do
|
27
|
+
1_000_000.times { redux_ttl_thread_safe.getset(rand(2_000)) { :value } }
|
28
|
+
end
|
29
|
+
|
30
|
+
bm.report 'LruRedux::TTL::Cache (TTL disabled)' do
|
31
|
+
1_000_000.times { redux_ttl_disabled.getset(rand(2_000)) { :value } }
|
32
|
+
end
|
33
|
+
end
|
data/lib/lru_redux.rb
CHANGED
@@ -1,16 +1,11 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
end
|
1
|
+
require "lru_redux/util"
|
2
|
+
|
3
|
+
require "lru_redux/cache"
|
4
|
+
require "lru_redux/cache_legacy" if
|
5
|
+
RUBY_ENGINE == "ruby" && RUBY_VERSION < "2.1.0"
|
7
6
|
|
8
|
-
require "lru_redux/version"
|
9
|
-
if LruRedux.is_19?
|
10
|
-
require "lru_redux/cache19"
|
11
|
-
else
|
12
|
-
require "lru_redux/cache"
|
13
|
-
end
|
14
7
|
require "lru_redux/thread_safe_cache"
|
15
|
-
|
16
|
-
|
8
|
+
|
9
|
+
require "lru_redux/ttl"
|
10
|
+
|
11
|
+
require "lru_redux/version"
|
data/lib/lru_redux/cache.rb
CHANGED
@@ -1,163 +1,108 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# Ruby 1.9 makes our life easier, Hash is already ordered
|
2
|
+
#
|
3
|
+
# This is an ultra efficient 1.9 freindly implementation
|
3
4
|
class LruRedux::Cache
|
5
|
+
def initialize(*args)
|
6
|
+
max_size, _ = args
|
4
7
|
|
5
|
-
|
6
|
-
# [prev,key,val,next]
|
7
|
-
#
|
8
|
-
# This makes this much more annoying to code, but gives us a 5-10% edge
|
8
|
+
raise ArgumentError.new(:max_size) if max_size < 1
|
9
9
|
|
10
|
-
def initialize(max_size)
|
11
10
|
@max_size = max_size
|
12
11
|
@data = {}
|
13
|
-
@head = nil
|
14
|
-
@tail = nil
|
15
12
|
end
|
16
13
|
|
17
|
-
def max_size=(
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
14
|
+
def max_size=(max_size)
|
15
|
+
max_size ||= @max_size
|
16
|
+
|
17
|
+
raise ArgumentError.new(:max_size) if max_size < 1
|
18
|
+
|
19
|
+
@max_size = max_size
|
20
|
+
|
21
|
+
@data.shift while @data.size > @max_size
|
22
|
+
end
|
23
|
+
|
24
|
+
def ttl=(_)
|
25
|
+
nil
|
23
26
|
end
|
24
27
|
|
25
28
|
def getset(key)
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
29
|
+
found = true
|
30
|
+
value = @data.delete(key){ found = false }
|
31
|
+
if found
|
32
|
+
@data[key] = value
|
30
33
|
else
|
31
|
-
|
34
|
+
result = @data[key] = yield
|
35
|
+
@data.shift if @data.length > @max_size
|
36
|
+
result
|
32
37
|
end
|
33
38
|
end
|
34
39
|
|
35
40
|
def fetch(key)
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
41
|
+
found = true
|
42
|
+
value = @data.delete(key){ found = false }
|
43
|
+
if found
|
44
|
+
@data[key] = value
|
40
45
|
else
|
41
46
|
yield if block_given?
|
42
47
|
end
|
43
48
|
end
|
44
49
|
|
45
50
|
def [](key)
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
51
|
+
found = true
|
52
|
+
value = @data.delete(key){ found = false }
|
53
|
+
if found
|
54
|
+
@data[key] = value
|
55
|
+
else
|
56
|
+
nil
|
50
57
|
end
|
51
58
|
end
|
52
59
|
|
53
60
|
def []=(key,val)
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
node[2] = val
|
58
|
-
else
|
59
|
-
@data[key] = add_to_head(key,val)
|
60
|
-
pop_tail
|
61
|
-
end
|
61
|
+
@data.delete(key)
|
62
|
+
@data[key] = val
|
63
|
+
@data.shift if @data.length > @max_size
|
62
64
|
val
|
63
65
|
end
|
64
66
|
|
65
67
|
def each
|
66
|
-
|
67
|
-
|
68
|
+
array = @data.to_a
|
69
|
+
array.reverse!.each do |pair|
|
68
70
|
yield pair
|
69
71
|
end
|
70
72
|
end
|
71
73
|
|
72
|
-
|
73
|
-
|
74
|
-
if n
|
75
|
-
while n
|
76
|
-
yield [n[1], n[2]]
|
77
|
-
n = n[0]
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
74
|
+
# used further up the chain, non thread safe each
|
75
|
+
alias_method :each_unsafe, :each
|
81
76
|
|
82
77
|
def to_a
|
83
|
-
|
84
|
-
|
85
|
-
a << [k,v]
|
86
|
-
end
|
87
|
-
a
|
78
|
+
array = @data.to_a
|
79
|
+
array.reverse!
|
88
80
|
end
|
89
81
|
|
90
82
|
def delete(key)
|
91
|
-
|
92
|
-
|
93
|
-
return unless node
|
94
|
-
|
95
|
-
prev = node[0]
|
96
|
-
nex = node[3]
|
83
|
+
@data.delete(key)
|
84
|
+
end
|
97
85
|
|
98
|
-
|
99
|
-
prev ? prev[3] = nex : @tail = nex
|
86
|
+
alias_method :evict, :delete
|
100
87
|
|
101
|
-
|
88
|
+
def key?(key)
|
89
|
+
@data.key?(key)
|
102
90
|
end
|
103
91
|
|
92
|
+
alias_method :has_key?, :key?
|
93
|
+
|
104
94
|
def clear
|
105
95
|
@data.clear
|
106
|
-
@head = @tail = nil
|
107
96
|
end
|
108
97
|
|
109
98
|
def count
|
110
99
|
@data.size
|
111
100
|
end
|
112
101
|
|
113
|
-
# for cache validation only, ensures all is sound
|
114
|
-
def valid?
|
115
|
-
count = 0
|
116
|
-
self.each_unsafe do |k,v|
|
117
|
-
return false if @data[k][2] != v
|
118
|
-
count += 1
|
119
|
-
end
|
120
|
-
count == @data.size
|
121
|
-
end
|
122
|
-
|
123
102
|
protected
|
124
103
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
else
|
129
|
-
node = [@head,key,val,nil]
|
130
|
-
@head = @head[3] = node
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
def move_to_head(node)
|
135
|
-
return unless @head && node[1] != @head[1]
|
136
|
-
|
137
|
-
prev = node[0]
|
138
|
-
nex = node[3]
|
139
|
-
|
140
|
-
if prev
|
141
|
-
prev[3] = nex
|
142
|
-
else
|
143
|
-
@tail = nex
|
144
|
-
end
|
145
|
-
|
146
|
-
if nex
|
147
|
-
nex[0] = prev
|
148
|
-
end
|
149
|
-
|
150
|
-
@head[3] = node
|
151
|
-
node[0] = @head
|
152
|
-
@head = node
|
153
|
-
end
|
154
|
-
|
155
|
-
def pop_tail
|
156
|
-
if @data.length > @max_size
|
157
|
-
@data.delete(@tail[1])
|
158
|
-
@tail = @tail[3]
|
159
|
-
@tail[0] = nil
|
160
|
-
true
|
161
|
-
end
|
104
|
+
# for cache validation only, ensures all is sound
|
105
|
+
def valid?
|
106
|
+
true
|
162
107
|
end
|
163
108
|
end
|