redcache 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/redcache/configuration.rb +2 -0
- data/lib/redcache/version.rb +1 -1
- data/lib/redcache.rb +70 -54
- data/spec/redcache_spec.rb +83 -11
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b9134505b631ba6721ae691c4a9031dd3e62a551
|
4
|
+
data.tar.gz: f4a352cc66f24de9d52b8907f62b96d4aa413e91
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 04bb02ff0a7e38f07ccd7741edd113c0eb7e7b4c3c6cd10d2405a1ea03261caad59e6d7bacfa48b7b8a2a7c5cf242ef59673faee1d004657bbf08d8dfb6ac9dc
|
7
|
+
data.tar.gz: c1ff6af3a7bc067f2844440e4a33876d2a88f9d6c472caad29a78b3dfe3c01770e272c4038f0b4e740713eee162d442fe34d72d151d7c7a1f37ea94e018e85a8
|
@@ -6,6 +6,7 @@ module Redcache
|
|
6
6
|
attr_accessor :stale_time
|
7
7
|
attr_accessor :encrypt
|
8
8
|
attr_accessor :skip_cache
|
9
|
+
attr_accessor :silent
|
9
10
|
attr_accessor :logger
|
10
11
|
attr_accessor :log_prefix
|
11
12
|
|
@@ -15,6 +16,7 @@ module Redcache
|
|
15
16
|
@encrypt = false
|
16
17
|
@secret = nil
|
17
18
|
@skip_cache = false
|
19
|
+
@silent = false
|
18
20
|
@logger = nil
|
19
21
|
@log_prefix = "redcache"
|
20
22
|
end
|
data/lib/redcache/version.rb
CHANGED
data/lib/redcache.rb
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
require "redcache/version"
|
2
|
+
require "fernet"
|
2
3
|
require "redcache/configuration"
|
3
4
|
|
5
|
+
Fernet::Configuration.run do |config|
|
6
|
+
config.enforce_ttl = false
|
7
|
+
end
|
8
|
+
|
4
9
|
module Redcache
|
5
10
|
class << self
|
6
11
|
attr_writer :configuration
|
@@ -10,15 +15,67 @@ module Redcache
|
|
10
15
|
end
|
11
16
|
|
12
17
|
def cache(redis_key, &block)
|
13
|
-
return
|
14
|
-
if redis_up?
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
18
|
+
# return immediately if we shouldn't or can't cache
|
19
|
+
return block.call if skip_cache? || !redis_up?
|
20
|
+
with_redis do
|
21
|
+
# attempt to read from cache, running and caching the block if cold
|
22
|
+
value = read_from_cache(redis_key, block)
|
23
|
+
if value.nil?
|
24
|
+
value = block.call if value.nil?
|
25
|
+
write_into_cache(redis_key, value)
|
26
|
+
end
|
27
|
+
return value
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def read_from_cache(redis_key, block)
|
32
|
+
value = get_value(redis_key)
|
33
|
+
value.nil? ? log("cache.miss", redis_key) : log("cache.hit", redis_key)
|
34
|
+
refresh_cache(redis_key, block) if key_stale?(redis_key) && !value.nil?
|
35
|
+
return value
|
36
|
+
end
|
37
|
+
|
38
|
+
def refresh_cache(redis_key, block)
|
39
|
+
log("cache.stale_refresh", redis_key)
|
40
|
+
Thread.new do
|
41
|
+
write_into_cache(redis_key, block.call)
|
19
42
|
end
|
20
43
|
end
|
21
44
|
|
45
|
+
def write_into_cache(redis_key, value)
|
46
|
+
with_redis do
|
47
|
+
log("cache.write", redis_key)
|
48
|
+
set_value(redis_key, value)
|
49
|
+
end
|
50
|
+
value
|
51
|
+
end
|
52
|
+
|
53
|
+
def get_value(key)
|
54
|
+
decrypt redis.get(key)
|
55
|
+
end
|
56
|
+
|
57
|
+
def set_value(key, value)
|
58
|
+
redis.setex key, configuration.cache_time, encrypt(value)
|
59
|
+
end
|
60
|
+
|
61
|
+
def key_stale?(redis_key)
|
62
|
+
ttl = redis.ttl(redis_key)
|
63
|
+
return ttl < (configuration.cache_time - configuration.stale_time)
|
64
|
+
end
|
65
|
+
|
66
|
+
def encrypt(value)
|
67
|
+
return prep_value(value) unless encrypt?
|
68
|
+
fernet.generate(secret, prep_value(value))
|
69
|
+
end
|
70
|
+
|
71
|
+
def decrypt(value)
|
72
|
+
return nil if value.nil?
|
73
|
+
return value unless encrypt?
|
74
|
+
verifier = fernet.verifier(secret, value)
|
75
|
+
return MultiJson.load(verifier.message) if verifier.valid?
|
76
|
+
return nil
|
77
|
+
end
|
78
|
+
|
22
79
|
def configuration
|
23
80
|
@configuration ||= Configuration.new
|
24
81
|
end
|
@@ -53,73 +110,32 @@ module Redcache
|
|
53
110
|
configuration.skip_cache
|
54
111
|
end
|
55
112
|
|
56
|
-
def read_from_cache(redis_key, block)
|
57
|
-
value = get_value(redis_key)
|
58
|
-
value.nil? ? log("cache.miss", redis_key) : log("cache.hit", redis_key)
|
59
|
-
refresh_cache(redis_key, block) if key_stale?(redis_key) && !value.nil?
|
60
|
-
return value
|
61
|
-
end
|
62
|
-
|
63
113
|
def test?
|
64
114
|
ENV["RACK_ENV"] == 'test'
|
65
115
|
end
|
66
116
|
|
67
|
-
def
|
68
|
-
|
69
|
-
Thread.new do
|
70
|
-
write_into_cache(redis_key, block)
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
def write_into_cache(redis_key, block)
|
75
|
-
json = block.call
|
76
|
-
with_redis do
|
77
|
-
log("cache.write", redis_key)
|
78
|
-
set_value(redis_key, json)
|
79
|
-
end
|
80
|
-
json
|
81
|
-
end
|
82
|
-
|
83
|
-
def key_stale?(redis_key)
|
84
|
-
ttl = redis.ttl(redis_key)
|
85
|
-
return ttl < (configuration.cache_time - configuration.stale_time)
|
86
|
-
end
|
87
|
-
|
88
|
-
def get_value(key)
|
89
|
-
decrypt redis.get(key)
|
90
|
-
end
|
91
|
-
|
92
|
-
def set_value(key, value)
|
93
|
-
redis.setex key, configuration.cache_time, encrypt(value)
|
94
|
-
end
|
95
|
-
|
96
|
-
def encrypt(value)
|
97
|
-
return value unless encrypt?
|
98
|
-
Fernet.generate(secret, MultiJson.encode(value))
|
117
|
+
def prep_value(value)
|
118
|
+
MultiJson.encode(value)
|
99
119
|
end
|
100
120
|
|
101
121
|
def encrypt?
|
102
122
|
configuration.encrypt
|
103
123
|
end
|
104
124
|
|
105
|
-
def decrypt(value)
|
106
|
-
return nil if value.nil?
|
107
|
-
return value unless encrypt?
|
108
|
-
verifier = Fernet.verifier(secret, value)
|
109
|
-
return MultiJson.load(verifier.message) if verifier.valid?
|
110
|
-
return nil
|
111
|
-
end
|
112
|
-
|
113
125
|
def secret
|
114
126
|
configuration.secret
|
115
127
|
end
|
116
128
|
|
117
129
|
def log(str, key)
|
118
|
-
configuration.logger.log(log_prefix(str) => 1, :key => key)
|
130
|
+
configuration.logger.log(log_prefix(str) => 1, :key => key) unless configuration.silent
|
119
131
|
end
|
120
132
|
|
121
133
|
def log_prefix(str)
|
122
134
|
[configuration.log_prefix, str].join(".")
|
123
135
|
end
|
136
|
+
|
137
|
+
def fernet
|
138
|
+
::Fernet
|
139
|
+
end
|
124
140
|
end
|
125
141
|
end
|
data/spec/redcache_spec.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'fernet'
|
2
3
|
|
3
4
|
Redcache.configure do |c|
|
4
5
|
c.redis = Redis.new
|
5
6
|
c.logger = Pliny
|
7
|
+
c.silent = true
|
6
8
|
end
|
7
9
|
|
8
10
|
class Dummy
|
@@ -18,46 +20,116 @@ class Dummy
|
|
18
20
|
end
|
19
21
|
|
20
22
|
describe Redcache do
|
23
|
+
let(:rc) { Redcache }
|
24
|
+
let(:config) { Redcache.configuration }
|
25
|
+
|
26
|
+
before do
|
27
|
+
allow(rc.fernet).to receive(:generate){ "abd" }
|
28
|
+
end
|
21
29
|
|
22
30
|
context 'when skipping caching' do
|
23
31
|
before do
|
24
|
-
|
32
|
+
config.skip_cache = true
|
25
33
|
end
|
34
|
+
|
26
35
|
it 'does not use the cache' do
|
27
|
-
expect(
|
36
|
+
expect(rc).to_not receive(:read_from_cache)
|
28
37
|
Dummy.run
|
29
38
|
end
|
30
39
|
end
|
31
40
|
|
32
41
|
context 'when caching' do
|
33
42
|
before do
|
34
|
-
|
43
|
+
config.skip_cache = false
|
35
44
|
end
|
36
45
|
|
37
46
|
it 'uses the cached' do
|
38
|
-
expect(
|
47
|
+
expect(rc).to receive(:read_from_cache){ {} }
|
39
48
|
Dummy.run
|
40
49
|
end
|
41
50
|
|
42
51
|
it 'triggers a cache write if the cache is cold' do
|
43
|
-
allow(
|
44
|
-
expect(
|
52
|
+
allow(rc).to receive(:read_from_cache){ nil }
|
53
|
+
expect(rc).to receive(:write_into_cache) { "" }
|
45
54
|
Dummy.run
|
46
55
|
end
|
47
56
|
|
48
57
|
it 'skips cache when redis is down' do
|
49
|
-
allow(
|
50
|
-
expect(
|
58
|
+
allow(rc).to receive(:redis_up?){ false }
|
59
|
+
expect(rc).to_not receive(:read_from_cache)
|
51
60
|
expect(Dummy).to receive(:get_value)
|
52
61
|
Dummy.run
|
53
62
|
end
|
54
63
|
|
55
64
|
it 'triggers a cache refresh with a stale warm cache' do
|
56
|
-
allow(
|
57
|
-
allow(
|
58
|
-
expect(
|
65
|
+
allow(rc).to receive(:key_stale?){ true }
|
66
|
+
allow(rc).to receive(:get_value){ "" }
|
67
|
+
expect(rc).to receive(:refresh_cache).once
|
59
68
|
Dummy.run
|
60
69
|
end
|
61
70
|
end
|
62
71
|
|
72
|
+
describe 'configuration' do
|
73
|
+
it 'respects cache_time' do
|
74
|
+
config.cache_time = 100
|
75
|
+
expect(rc.cache_time).to eq(100)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'respects stale_time' do
|
79
|
+
config.stale_time = 100
|
80
|
+
expect(rc.stale_time).to eq(100)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'respects skip_cache' do
|
84
|
+
config.skip_cache = true
|
85
|
+
expect(rc.skip_cache?).to eq(true)
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'knows the secret' do
|
89
|
+
config.secret = "bar"
|
90
|
+
expect(rc.secret).to eq("bar")
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'knows when a key is stale' do
|
95
|
+
config.cache_time = 100
|
96
|
+
config.stale_time = 10
|
97
|
+
allow(rc.redis).to receive(:ttl){ 80 }
|
98
|
+
expect(rc.key_stale?("foo")).to eq(true)
|
99
|
+
|
100
|
+
allow(rc.redis).to receive(:ttl){ 91 }
|
101
|
+
expect(rc.key_stale?("foo")).to eq(false)
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'can refresh the cache' do
|
105
|
+
p = Proc.new {}
|
106
|
+
expect(Thread).to receive(:new)
|
107
|
+
rc.refresh_cache("foo", p)
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'can write into the cache' do
|
111
|
+
p = Proc.new { "" }
|
112
|
+
expect(rc).to receive(:set_value).with("foo", "")
|
113
|
+
rc.write_into_cache("foo", p.call)
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'can get a value' do
|
117
|
+
expect(rc.redis).to receive(:get).with("foo"){ "bar" }
|
118
|
+
expect(rc).to receive(:decrypt).with("bar")
|
119
|
+
rc.get_value("foo")
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'can set a value' do
|
123
|
+
config.cache_time = 100
|
124
|
+
config.encrypt = false
|
125
|
+
expect(rc.redis).to receive(:setex).with("foo", 100, '"bar"')
|
126
|
+
rc.set_value("foo", "bar")
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'encrypts' do
|
130
|
+
config.encrypt = true
|
131
|
+
config.secret = "foo"
|
132
|
+
expect(rc.fernet).to receive(:generate).with("foo", '"bar"'){ "abc" }
|
133
|
+
rc.encrypt("bar")
|
134
|
+
end
|
63
135
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redcache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Neil Middleton
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-10-
|
11
|
+
date: 2014-10-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|