lru_redux 0.8.4 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 [![Gem Version](https://badge.fury.io/rb/lru_redux.svg)](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
|