instructure-redis-store 1.0.0.1.instructure1
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.
- data/.travis.yml +7 -0
- data/CHANGELOG +311 -0
- data/Gemfile +34 -0
- data/MIT-LICENSE +20 -0
- data/README.md +239 -0
- data/Rakefile +60 -0
- data/VERSION +1 -0
- data/lib/action_controller/session/redis_session_store.rb +81 -0
- data/lib/active_support/cache/redis_store.rb +254 -0
- data/lib/cache/merb/redis_store.rb +79 -0
- data/lib/cache/sinatra/redis_store.rb +131 -0
- data/lib/i18n/backend/redis.rb +67 -0
- data/lib/rack/cache/redis_entitystore.rb +48 -0
- data/lib/rack/cache/redis_metastore.rb +40 -0
- data/lib/rack/session/merb.rb +32 -0
- data/lib/rack/session/redis.rb +88 -0
- data/lib/redis-store.rb +45 -0
- data/lib/redis/distributed_store.rb +39 -0
- data/lib/redis/factory.rb +46 -0
- data/lib/redis/store.rb +39 -0
- data/lib/redis/store/interface.rb +17 -0
- data/lib/redis/store/marshalling.rb +51 -0
- data/lib/redis/store/namespace.rb +62 -0
- data/lib/redis/store/ttl.rb +37 -0
- data/lib/redis/store/version.rb +12 -0
- data/spec/action_controller/session/redis_session_store_spec.rb +126 -0
- data/spec/active_support/cache/redis_store_spec.rb +426 -0
- data/spec/cache/merb/redis_store_spec.rb +143 -0
- data/spec/cache/sinatra/redis_store_spec.rb +192 -0
- data/spec/config/node-one.conf +417 -0
- data/spec/config/node-two.conf +417 -0
- data/spec/config/redis.conf +417 -0
- data/spec/i18n/backend/redis_spec.rb +72 -0
- data/spec/rack/cache/entitystore/pony.jpg +0 -0
- data/spec/rack/cache/entitystore/redis_spec.rb +124 -0
- data/spec/rack/cache/metastore/redis_spec.rb +259 -0
- data/spec/rack/session/redis_spec.rb +234 -0
- data/spec/redis/distributed_store_spec.rb +55 -0
- data/spec/redis/factory_spec.rb +110 -0
- data/spec/redis/store/interface_spec.rb +23 -0
- data/spec/redis/store/marshalling_spec.rb +119 -0
- data/spec/redis/store/namespace_spec.rb +76 -0
- data/spec/redis/store/version_spec.rb +7 -0
- data/spec/redis/store_spec.rb +13 -0
- data/spec/spec_helper.rb +43 -0
- data/tasks/redis.tasks.rb +235 -0
- metadata +249 -0
@@ -0,0 +1,131 @@
|
|
1
|
+
module Sinatra
|
2
|
+
module Cache
|
3
|
+
class << self
|
4
|
+
def register(app)
|
5
|
+
app.set :cache, RedisStore.new
|
6
|
+
end
|
7
|
+
alias_method :registered, :register
|
8
|
+
end
|
9
|
+
|
10
|
+
class RedisStore
|
11
|
+
# Instantiate the store.
|
12
|
+
#
|
13
|
+
# Example:
|
14
|
+
# RedisStore.new
|
15
|
+
# # => host: localhost, port: 6379, db: 0
|
16
|
+
#
|
17
|
+
# RedisStore.new "example.com"
|
18
|
+
# # => host: example.com, port: 6379, db: 0
|
19
|
+
#
|
20
|
+
# RedisStore.new "example.com:23682"
|
21
|
+
# # => host: example.com, port: 23682, db: 0
|
22
|
+
#
|
23
|
+
# RedisStore.new "example.com:23682/1"
|
24
|
+
# # => host: example.com, port: 23682, db: 1
|
25
|
+
#
|
26
|
+
# RedisStore.new "example.com:23682/1/theplaylist"
|
27
|
+
# # => host: example.com, port: 23682, db: 1, namespace: theplaylist
|
28
|
+
#
|
29
|
+
# RedisStore.new "localhost:6379/0", "localhost:6380/0"
|
30
|
+
# # => instantiate a cluster
|
31
|
+
def initialize(*addresses)
|
32
|
+
@data = Redis::Factory.create addresses
|
33
|
+
end
|
34
|
+
|
35
|
+
def write(key, value, options = nil)
|
36
|
+
if options && options[:unless_exist]
|
37
|
+
@data.setnx key, value, options
|
38
|
+
else
|
39
|
+
@data.set key, value, options
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def read(key, options = nil)
|
44
|
+
@data.get(key, options)
|
45
|
+
end
|
46
|
+
|
47
|
+
def delete(key, options = nil)
|
48
|
+
@data.del key
|
49
|
+
end
|
50
|
+
|
51
|
+
def exist?(key, options = nil)
|
52
|
+
@data.exists key
|
53
|
+
end
|
54
|
+
|
55
|
+
# Increment a key in the store.
|
56
|
+
#
|
57
|
+
# If the key doesn't exist it will be initialized on 0.
|
58
|
+
# If the key exist but it isn't a Fixnum it will be initialized on 0.
|
59
|
+
#
|
60
|
+
# Example:
|
61
|
+
# We have two objects in cache:
|
62
|
+
# counter # => 23
|
63
|
+
# rabbit # => #<Rabbit:0x5eee6c>
|
64
|
+
#
|
65
|
+
# cache.increment "counter"
|
66
|
+
# cache.read "counter", :raw => true # => "24"
|
67
|
+
#
|
68
|
+
# cache.increment "counter", 6
|
69
|
+
# cache.read "counter", :raw => true # => "30"
|
70
|
+
#
|
71
|
+
# cache.increment "a counter"
|
72
|
+
# cache.read "a counter", :raw => true # => "1"
|
73
|
+
#
|
74
|
+
# cache.increment "rabbit"
|
75
|
+
# cache.read "rabbit", :raw => true # => "1"
|
76
|
+
def increment(key, amount = 1)
|
77
|
+
@data.incrby key, amount
|
78
|
+
end
|
79
|
+
|
80
|
+
# Decrement a key in the store
|
81
|
+
#
|
82
|
+
# If the key doesn't exist it will be initialized on 0.
|
83
|
+
# If the key exist but it isn't a Fixnum it will be initialized on 0.
|
84
|
+
#
|
85
|
+
# Example:
|
86
|
+
# We have two objects in cache:
|
87
|
+
# counter # => 23
|
88
|
+
# rabbit # => #<Rabbit:0x5eee6c>
|
89
|
+
#
|
90
|
+
# cache.decrement "counter"
|
91
|
+
# cache.read "counter", :raw => true # => "22"
|
92
|
+
#
|
93
|
+
# cache.decrement "counter", 2
|
94
|
+
# cache.read "counter", :raw => true # => "20"
|
95
|
+
#
|
96
|
+
# cache.decrement "a counter"
|
97
|
+
# cache.read "a counter", :raw => true # => "-1"
|
98
|
+
#
|
99
|
+
# cache.decrement "rabbit"
|
100
|
+
# cache.read "rabbit", :raw => true # => "-1"
|
101
|
+
def decrement(key, amount = 1)
|
102
|
+
@data.decrby key, amount
|
103
|
+
end
|
104
|
+
|
105
|
+
# Delete objects for matched keys.
|
106
|
+
#
|
107
|
+
# Example:
|
108
|
+
# cache.del_matched "rab*"
|
109
|
+
def delete_matched(matcher, options = nil)
|
110
|
+
@data.keys(matcher).each { |key| @data.del key }
|
111
|
+
end
|
112
|
+
|
113
|
+
def fetch(key, options = {})
|
114
|
+
(!options[:force] && data = read(key, options)) || block_given? && begin
|
115
|
+
data = yield
|
116
|
+
write(key, data, options)
|
117
|
+
end
|
118
|
+
data || nil
|
119
|
+
end
|
120
|
+
|
121
|
+
# Clear all the data from the store.
|
122
|
+
def clear
|
123
|
+
@data.flushdb
|
124
|
+
end
|
125
|
+
|
126
|
+
def stats
|
127
|
+
@data.info
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module I18n
|
2
|
+
module Backend
|
3
|
+
class Redis
|
4
|
+
include Base, Flatten
|
5
|
+
attr_accessor :store
|
6
|
+
|
7
|
+
# Instantiate the store.
|
8
|
+
#
|
9
|
+
# Example:
|
10
|
+
# RedisStore.new
|
11
|
+
# # => host: localhost, port: 6379, db: 0
|
12
|
+
#
|
13
|
+
# RedisStore.new "example.com"
|
14
|
+
# # => host: example.com, port: 6379, db: 0
|
15
|
+
#
|
16
|
+
# RedisStore.new "example.com:23682"
|
17
|
+
# # => host: example.com, port: 23682, db: 0
|
18
|
+
#
|
19
|
+
# RedisStore.new "example.com:23682/1"
|
20
|
+
# # => host: example.com, port: 23682, db: 1
|
21
|
+
#
|
22
|
+
# RedisStore.new "example.com:23682/1/theplaylist"
|
23
|
+
# # => host: example.com, port: 23682, db: 1, namespace: theplaylist
|
24
|
+
#
|
25
|
+
# RedisStore.new "localhost:6379/0", "localhost:6380/0"
|
26
|
+
# # => instantiate a cluster
|
27
|
+
def initialize(*addresses)
|
28
|
+
@store = ::Redis::Factory.create(addresses)
|
29
|
+
end
|
30
|
+
|
31
|
+
def translate(locale, key, options = {})
|
32
|
+
options[:resolve] ||= false
|
33
|
+
super locale, key, options
|
34
|
+
end
|
35
|
+
|
36
|
+
def store_translations(locale, data, options = {})
|
37
|
+
escape = options.fetch(:escape, true)
|
38
|
+
flatten_translations(locale, data, escape, false).each do |key, value|
|
39
|
+
case value
|
40
|
+
when Proc
|
41
|
+
raise "Key-value stores cannot handle procs"
|
42
|
+
else
|
43
|
+
@store.set "#{locale}.#{key}", value
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def available_locales
|
49
|
+
locales = @store.keys.map { |k| k =~ /\./; $` }
|
50
|
+
locales.uniq!
|
51
|
+
locales.compact!
|
52
|
+
locales.map! { |k| k.to_sym }
|
53
|
+
locales
|
54
|
+
end
|
55
|
+
|
56
|
+
protected
|
57
|
+
def lookup(locale, key, scope = [], options = {})
|
58
|
+
key = normalize_flat_keys(locale, key, scope, options[:separator])
|
59
|
+
@store.get "#{locale}.#{key}"
|
60
|
+
end
|
61
|
+
|
62
|
+
def resolve_link(locale, key)
|
63
|
+
key
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Rack
|
2
|
+
module Cache
|
3
|
+
class EntityStore
|
4
|
+
class RedisBase < EntityStore
|
5
|
+
# The underlying ::Redis instance used to communicate with the Redis daemon.
|
6
|
+
attr_reader :cache
|
7
|
+
|
8
|
+
extend Rack::Utils
|
9
|
+
|
10
|
+
def open(key)
|
11
|
+
data = read(key)
|
12
|
+
data && [data]
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.resolve(uri)
|
16
|
+
new ::Redis::Factory.convert_to_redis_client_options(uri.to_s)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Redis < RedisBase
|
21
|
+
def initialize(server, options = {})
|
22
|
+
@cache = ::Redis.new server
|
23
|
+
end
|
24
|
+
|
25
|
+
def exist?(key)
|
26
|
+
cache.exists key
|
27
|
+
end
|
28
|
+
|
29
|
+
def read(key)
|
30
|
+
cache.get key
|
31
|
+
end
|
32
|
+
|
33
|
+
def write(body)
|
34
|
+
buf = StringIO.new
|
35
|
+
key, size = slurp(body){|part| buf.write(part) }
|
36
|
+
[key, size] if cache.set(key, buf.string)
|
37
|
+
end
|
38
|
+
|
39
|
+
def purge(key)
|
40
|
+
cache.del key
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
REDIS = Redis
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Rack
|
2
|
+
module Cache
|
3
|
+
class MetaStore
|
4
|
+
class RedisBase < MetaStore
|
5
|
+
extend Rack::Utils
|
6
|
+
|
7
|
+
# The Redis::Store object used to communicate with the Redis daemon.
|
8
|
+
attr_reader :cache
|
9
|
+
|
10
|
+
def self.resolve(uri)
|
11
|
+
new ::Redis::Factory.convert_to_redis_client_options(uri.to_s)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Redis < RedisBase
|
16
|
+
def initialize(server, options = {})
|
17
|
+
options[:redis_server] ||= server
|
18
|
+
@cache = ::Redis::Factory.create options
|
19
|
+
end
|
20
|
+
|
21
|
+
def read(key)
|
22
|
+
key = hexdigest(key)
|
23
|
+
cache.get(key) || []
|
24
|
+
end
|
25
|
+
|
26
|
+
def write(key, entries)
|
27
|
+
key = hexdigest(key)
|
28
|
+
cache.set(key, entries)
|
29
|
+
end
|
30
|
+
|
31
|
+
def purge(key)
|
32
|
+
cache.del(hexdigest(key))
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
REDIS = Redis
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Merb
|
2
|
+
# HACK for cyclic dependency: redis-store is required before Merb session stores
|
3
|
+
class Mash < Hash; end
|
4
|
+
class SessionContainer < Mash; class_inheritable_accessor :session_store_type end
|
5
|
+
class SessionStoreContainer < SessionContainer; end
|
6
|
+
|
7
|
+
class RedisSession < SessionStoreContainer
|
8
|
+
self.session_store_type = :redis
|
9
|
+
end
|
10
|
+
|
11
|
+
module RedisStore
|
12
|
+
def retrieve_session(session_id)
|
13
|
+
get("session:#{session_id}")
|
14
|
+
end
|
15
|
+
|
16
|
+
def store_session(session_id, data)
|
17
|
+
set("session:#{session_id}", data)
|
18
|
+
end
|
19
|
+
|
20
|
+
def delete_session(session_id)
|
21
|
+
delete("session:#{session_id}")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
module Rack
|
27
|
+
module Session
|
28
|
+
class Redis
|
29
|
+
include Merb::RedisStore
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Rack
|
2
|
+
module Session
|
3
|
+
class Redis < Abstract::ID
|
4
|
+
attr_reader :mutex, :pool
|
5
|
+
DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :redis_server => "redis://127.0.0.1:6379/0"
|
6
|
+
|
7
|
+
def initialize(app, options = {})
|
8
|
+
super
|
9
|
+
@mutex = Mutex.new
|
10
|
+
options[:redis_server] ||= @default_options[:redis_server]
|
11
|
+
@pool = ::Redis::Factory.create options
|
12
|
+
end
|
13
|
+
|
14
|
+
def generate_sid
|
15
|
+
loop do
|
16
|
+
sid = super
|
17
|
+
break sid unless @pool.get(sid)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def get_session(env, sid)
|
22
|
+
session = @pool.get(sid) if sid
|
23
|
+
@mutex.lock if env['rack.multithread']
|
24
|
+
unless sid and session
|
25
|
+
env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil?
|
26
|
+
session = {}
|
27
|
+
sid = generate_sid
|
28
|
+
ret = @pool.set sid, session
|
29
|
+
raise "Session collision on '#{sid.inspect}'" unless ret
|
30
|
+
end
|
31
|
+
session.instance_variable_set('@old', {}.merge(session))
|
32
|
+
return [sid, session]
|
33
|
+
rescue Errno::ECONNREFUSED
|
34
|
+
warn "#{self} is unable to find server."
|
35
|
+
warn $!.inspect
|
36
|
+
return [ nil, {} ]
|
37
|
+
ensure
|
38
|
+
@mutex.unlock if env['rack.multithread']
|
39
|
+
end
|
40
|
+
|
41
|
+
def set_session(env, session_id, new_session, options)
|
42
|
+
@mutex.lock if env['rack.multithread']
|
43
|
+
session = @pool.get(session_id) rescue {}
|
44
|
+
if options[:renew] or options[:drop]
|
45
|
+
@pool.del session_id
|
46
|
+
return false if options[:drop]
|
47
|
+
session_id = generate_sid
|
48
|
+
@pool.set session_id, 0
|
49
|
+
end
|
50
|
+
old_session = new_session.instance_variable_get('@old') || {}
|
51
|
+
session = merge_sessions session_id, old_session, new_session, session
|
52
|
+
@pool.set session_id, session, options
|
53
|
+
return session_id
|
54
|
+
rescue Errno::ECONNREFUSED
|
55
|
+
warn "#{self} is unable to find server."
|
56
|
+
warn $!.inspect
|
57
|
+
return false
|
58
|
+
ensure
|
59
|
+
@mutex.unlock if env['rack.multithread']
|
60
|
+
end
|
61
|
+
|
62
|
+
def destroy_session(env, session_id, options)
|
63
|
+
options = { :renew => true }.update(options) unless options[:drop]
|
64
|
+
set_session(env, session_id, 0, options)
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
def merge_sessions(sid, old, new, cur=nil)
|
69
|
+
cur ||= {}
|
70
|
+
unless Hash === old and Hash === new
|
71
|
+
warn 'Bad old or new sessions provided.'
|
72
|
+
return cur
|
73
|
+
end
|
74
|
+
|
75
|
+
delete = old.keys - new.keys
|
76
|
+
warn "//@#{sid}: dropping #{delete*','}" if $DEBUG and not delete.empty?
|
77
|
+
delete.each{|k| cur.delete k }
|
78
|
+
|
79
|
+
update = new.keys.select{|k| new[k] != old[k] }
|
80
|
+
warn "//@#{sid}: updating #{update*','}" if $DEBUG and not update.empty?
|
81
|
+
update.each{|k| cur[k] = new[k] }
|
82
|
+
|
83
|
+
cur
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
data/lib/redis-store.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require "redis"
|
2
|
+
require "redis/distributed"
|
3
|
+
require "redis/factory"
|
4
|
+
require "redis/store/interface"
|
5
|
+
require "redis/store/ttl"
|
6
|
+
require "redis/store/namespace"
|
7
|
+
require "redis/store/marshalling"
|
8
|
+
require "redis/store/version"
|
9
|
+
require "redis/store"
|
10
|
+
require "redis/distributed_store"
|
11
|
+
|
12
|
+
# Cache store
|
13
|
+
if defined?(Sinatra)
|
14
|
+
require "cache/sinatra/redis_store"
|
15
|
+
elsif defined?(Merb)
|
16
|
+
# HACK for cyclic dependency: redis-store is required before merb-cache
|
17
|
+
module Merb; module Cache; class AbstractStore; end end end
|
18
|
+
require "cache/merb/redis_store"
|
19
|
+
elsif defined?(ActiveSupport)
|
20
|
+
require "active_support/cache/redis_store"
|
21
|
+
end
|
22
|
+
|
23
|
+
# Rack::Session
|
24
|
+
if defined?(Rack::Session)
|
25
|
+
require "rack/session/abstract/id"
|
26
|
+
require "rack/session/redis"
|
27
|
+
if defined?(Merb)
|
28
|
+
require "rack/session/merb"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
if defined?(Rails)
|
33
|
+
require "action_controller/session/redis_session_store"
|
34
|
+
end
|
35
|
+
|
36
|
+
# Rack::Cache
|
37
|
+
if defined?(Rack::Cache)
|
38
|
+
require "rack/cache/key"
|
39
|
+
require "rack/cache/redis_metastore"
|
40
|
+
require "rack/cache/redis_entitystore"
|
41
|
+
end
|
42
|
+
|
43
|
+
if defined?(I18n)
|
44
|
+
require "i18n/backend/redis"
|
45
|
+
end
|