jodosha-redis-store 0.3.0 → 0.3.5

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/README.textile CHANGED
@@ -1,8 +1,8 @@
1
- h1. Redis cache and session stores for Ruby web frameworks
1
+ h1. Rack::Session, Rack::Cache and cache Redis stores for Ruby web frameworks
2
2
 
3
3
  h2. Installation
4
4
 
5
- Download and install Redis from http://code.google.com/p/redis/
5
+ Download and install Redis from "http://code.google.com/p/redis/":http://code.google.com/p/redis/
6
6
 
7
7
  wget "http://redis.googlecode.com/files/redis-0.094.tar.gz":http://redis.googlecode.com/files/redis-0.094.tar.gz
8
8
  tar -zxvf redis-0.094.tar.gz
@@ -26,7 +26,7 @@ h3. How to use with Rails
26
26
 
27
27
  h3. How to use with Merb
28
28
 
29
- dependency "jodosha-redis-store", "0.3.0"
29
+ dependency "jodosha-redis-store", "0.3.5"
30
30
  dependency("merb-cache", merb_gems_version) do
31
31
  Merb::Cache.setup do
32
32
  register(:redis, Merb::Cache::RedisStore, :servers => ["127.0.0.1:6379"])
@@ -65,7 +65,7 @@ h3. How to use with Rails
65
65
 
66
66
  h3. How to use with Merb
67
67
 
68
- dependency "jodosha-redis-store", "0.3.0"
68
+ dependency "jodosha-redis-store", "0.3.5"
69
69
  Merb::Config.use do |c|
70
70
  c[:session_store] = 'redis'
71
71
  end
data/Rakefile CHANGED
@@ -5,7 +5,7 @@ require 'rake/testtask'
5
5
  require 'rake/rdoctask'
6
6
  require 'spec/rake/spectask'
7
7
 
8
- REDIS_STORE_VERSION = "0.3.0"
8
+ REDIS_STORE_VERSION = "0.3.5"
9
9
 
10
10
  task :default => :spec
11
11
 
data/lib/redis-store.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require "redis"
2
2
  require "dist_redis"
3
3
  require "redis/redis"
4
+ require "redis/server"
4
5
  require "redis/redis_factory"
5
6
  require "redis/marshaled_redis"
6
7
  require "redis/distributed_marshaled_redis"
@@ -0,0 +1,225 @@
1
+ require 'monitor'
2
+ ##
3
+ # This class represents a redis server instance.
4
+
5
+ class Server
6
+
7
+ ##
8
+ # The host the redis server is running on.
9
+
10
+ attr_reader :host
11
+
12
+ ##
13
+ # The port the redis server is listening on.
14
+
15
+ attr_reader :port
16
+
17
+ ##
18
+ #
19
+
20
+ attr_reader :replica
21
+
22
+ ##
23
+ # The time of next retry if the connection is dead.
24
+
25
+ attr_reader :retry
26
+
27
+ ##
28
+ # A text status string describing the state of the server.
29
+
30
+ attr_reader :status
31
+
32
+ ##
33
+ # Create a new Redis::Server object for the redis instance
34
+ # listening on the given host and port.
35
+
36
+ def initialize(host, port = DEFAULT_PORT, timeout = 10, size = 5)
37
+ raise ArgumentError, "No host specified" if host.nil? or host.empty?
38
+ raise ArgumentError, "No port specified" if port.nil? or port.to_i.zero?
39
+
40
+ @host = host
41
+ @port = port.to_i
42
+
43
+ @retry = nil
44
+ @status = 'NOT CONNECTED'
45
+ @timeout = timeout
46
+ @size = size
47
+
48
+ @reserved_sockets = {}
49
+
50
+ @mutex = Monitor.new
51
+ @queue = @mutex.new_cond
52
+
53
+ @sockets = []
54
+ @checked_out = []
55
+ end
56
+
57
+ ##
58
+ # Return a string representation of the server object.
59
+ def inspect
60
+ "<Redis::Server: %s:%d (%s)>" % [@host, @port, @status]
61
+ end
62
+
63
+ ##
64
+ # Try to connect to the redis server targeted by this object.
65
+ # Returns the connected socket object on success or nil on failure.
66
+
67
+ def socket
68
+ if socket = @reserved_sockets[current_connection_id]
69
+ socket
70
+ else
71
+ @reserved_sockets[current_connection_id] = checkout
72
+ end
73
+ end
74
+
75
+ def connect_to(host, port, timeout=nil)
76
+ addrs = Socket.getaddrinfo(host, nil)
77
+ addr = addrs.detect { |ad| ad[0] == 'AF_INET' }
78
+ sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
79
+ if timeout
80
+ secs = Integer(timeout)
81
+ usecs = Integer((timeout - secs) * 1_000_000)
82
+ optval = [secs, usecs].pack("l_2")
83
+ sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval
84
+ sock.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, optval
85
+ end
86
+ sock.connect(Socket.pack_sockaddr_in(port, addr[3]))
87
+ sock
88
+ end
89
+
90
+ ##
91
+ # Close the connection to the redis server targeted by this
92
+ # object. The server is not considered dead.
93
+
94
+ def close
95
+ @reserved_sockets.each do |name,sock|
96
+ checkin sock
97
+ end
98
+ @reserved_sockets = {}
99
+ @sockets.each do |sock|
100
+ sock.close
101
+ end
102
+ @sockets = []
103
+ @status = "NOT CONNECTED"
104
+ end
105
+
106
+ ##
107
+ # Mark the server as dead and close its socket.
108
+ def mark_dead(sock, error)
109
+ sock.close if sock && !sock.closed?
110
+ sock = nil
111
+
112
+ reason = "#{error.class.name}: #{error.message}"
113
+ @status = sprintf "%s:%s DEAD (%s)", @host, @port, reason
114
+ puts @status
115
+ end
116
+
117
+ protected
118
+ def new_socket
119
+ sock = nil
120
+ begin
121
+ sock = connect_to(@host, @port, @timeout)
122
+ sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
123
+ @status = 'CONNECTED'
124
+ rescue Errno::EPIPE, Errno::ECONNREFUSED => e
125
+ if sock
126
+ puts "Socket died... socket: #{sock.inspect}\n" if $debug
127
+ sock.close
128
+ end
129
+ rescue SocketError, SystemCallError, IOError => err
130
+ puts "Unable to open socket: #{err.class.name}, #{err.message}" if $debug
131
+ mark_dead sock, err
132
+ end
133
+
134
+ return sock
135
+ end
136
+
137
+ def checkout
138
+ @mutex.synchronize do
139
+ loop do
140
+ socket = if @checked_out.size < @sockets.size
141
+ checkout_existing_socket
142
+ elsif @sockets.size < @size
143
+ checkout_new_socket
144
+ end
145
+ return socket if socket
146
+ # No sockets available; wait for one
147
+ if @queue.wait(@timeout)
148
+ next
149
+ else
150
+ # try looting dead threads
151
+ clear_stale_cached_sockets!
152
+ if @size == @checked_out.size
153
+ raise RedisError, "could not obtain a socket connection#{" within #{@timeout} seconds" if @timeout}. The max pool size is currently #{@size}; consider increasing it."
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
159
+
160
+ def checkin(socket)
161
+ @mutex.synchronize do
162
+ @checked_out.delete socket
163
+ @queue.signal
164
+ end
165
+ end
166
+
167
+ def checkout_new_socket
168
+ s = new_socket
169
+ @sockets << s
170
+ checkout_and_verify(s)
171
+ end
172
+
173
+ def checkout_existing_socket
174
+ s = (@sockets - @checked_out).first
175
+ checkout_and_verify(s)
176
+ end
177
+
178
+ def clear_stale_cached_sockets!
179
+ remove_stale_cached_threads!(@reserved_sockets) do |name, socket|
180
+ checkin socket
181
+ end
182
+ end
183
+
184
+ def remove_stale_cached_threads!(cache, &block)
185
+ keys = Set.new(cache.keys)
186
+
187
+ Thread.list.each do |thread|
188
+ keys.delete(thread.object_id) if thread.alive?
189
+ end
190
+ keys.each do |key|
191
+ next unless cache.has_key?(key)
192
+ block.call(key, cache[key])
193
+ cache.delete(key)
194
+ end
195
+ end
196
+
197
+ private
198
+ def current_connection_id #:nodoc:
199
+ Thread.current.object_id
200
+ end
201
+
202
+ def checkout_and_verify(s)
203
+ s = verify!(s)
204
+ @checked_out << s
205
+ s
206
+ end
207
+
208
+ def verify!(s)
209
+ reconnect!(s) unless active?(s)
210
+ end
211
+
212
+ def reconnect!(s)
213
+ s.close
214
+ connect_to(@host, @port, @timeout)
215
+ end
216
+
217
+ def active?(s)
218
+ begin
219
+ s.write("\0")
220
+ Timeout.timeout(0.1){ s.read }
221
+ rescue Exception
222
+ false
223
+ end
224
+ end
225
+ end
data/redis-store.gemspec CHANGED
@@ -1,14 +1,14 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "redis-store"
3
- s.version = "0.3.0"
4
- s.date = "2009-05-03"
3
+ s.version = "0.3.5"
4
+ s.date = "2009-05-07"
5
5
  s.summary = "Rack::Session, Rack::Cache and cache Redis stores for Ruby web frameworks."
6
6
  s.author = "Luca Guidi"
7
7
  s.email = "guidi.luca@gmail.com"
8
8
  s.homepage = "http://lucaguidi.com"
9
9
  s.description = "Rack::Session, Rack::Cache and cache Redis stores for Ruby web frameworks."
10
10
  s.has_rdoc = true
11
- s.files = ["MIT-LICENSE", "README.textile", "Rakefile", "lib/cache/merb/redis_store.rb", "lib/cache/rails/redis_store.rb", "lib/cache/sinatra/redis_store.rb", "lib/rack/cache/redis_entitystore.rb", "lib/rack/cache/redis_metastore.rb", "lib/rack/session/merb.rb", "lib/rack/session/redis.rb", "lib/redis-store.rb", "lib/redis/distributed_marshaled_redis.rb", "lib/redis/marshaled_redis.rb", "lib/redis/redis.rb", "lib/redis/redis_factory.rb", "redis-store.gemspec", "spec/cache/merb/redis_store_spec.rb", "spec/cache/rails/redis_store_spec.rb", "spec/cache/sinatra/redis_store_spec.rb", "spec/config/master.conf", "spec/config/single.conf", "spec/config/slave.conf", "spec/rack/cache/entitystore/pony.jpg", "spec/rack/cache/entitystore/redis_spec.rb", "spec/rack/cache/metastore/redis_spec.rb", "spec/rack/session/redis_spec.rb", "spec/redis/distributed_marshaled_redis_spec.rb", "spec/redis/marshaled_redis_spec.rb", "spec/redis/redis_factory_spec.rb", "spec/spec_helper.rb"]
12
- s.test_files = ["spec/cache/merb/redis_store_spec.rb", "spec/cache/rails/redis_store_spec.rb", "spec/cache/sinatra/redis_store_spec.rb", "spec/rack/cache/entitystore/redis_spec.rb", "spec/rack/cache/metastore/redis_spec.rb", "spec/rack/session/redis_spec.rb", "spec/redis/distributed_marshaled_redis_spec.rb", "spec/redis/marshaled_redis_spec.rb", "spec/redis/redis_factory_spec.rb"]
11
+ s.files = ["MIT-LICENSE", "README.textile", "Rakefile", "lib/cache/merb/redis_store.rb", "lib/cache/rails/redis_store.rb", "lib/cache/sinatra/redis_store.rb", "lib/rack/cache/redis_entitystore.rb", "lib/rack/cache/redis_metastore.rb", "lib/rack/session/merb.rb", "lib/rack/session/redis.rb", "lib/redis-store.rb", "lib/redis/distributed_marshaled_redis.rb", "lib/redis/marshaled_redis.rb", "lib/redis/redis.rb", "lib/redis/redis_factory.rb", "lib/redis/server.rb", "redis-store.gemspec", "spec/cache/merb/redis_store_spec.rb", "spec/cache/rails/redis_store_spec.rb", "spec/cache/sinatra/redis_store_spec.rb", "spec/config/master.conf", "spec/config/single.conf", "spec/config/slave.conf", "spec/rack/cache/entitystore/pony.jpg", "spec/rack/cache/entitystore/redis_spec.rb", "spec/rack/cache/metastore/redis_spec.rb", "spec/rack/session/redis_spec.rb", "spec/redis/distributed_marshaled_redis_spec.rb", "spec/redis/marshaled_redis_spec.rb", "spec/redis/redis_factory_spec.rb", "spec/redis/server_spec.rb", "spec/spec_helper.rb"]
12
+ s.test_files = ["spec/cache/merb/redis_store_spec.rb", "spec/cache/rails/redis_store_spec.rb", "spec/cache/sinatra/redis_store_spec.rb", "spec/rack/cache/entitystore/redis_spec.rb", "spec/rack/cache/metastore/redis_spec.rb", "spec/rack/session/redis_spec.rb", "spec/redis/distributed_marshaled_redis_spec.rb", "spec/redis/marshaled_redis_spec.rb", "spec/redis/redis_factory_spec.rb", "spec/redis/server_spec.rb"]
13
13
  s.extra_rdoc_files = ["README.textile"]
14
14
  end
@@ -17,7 +17,7 @@ port 6379
17
17
  # bind 127.0.0.1
18
18
 
19
19
  # Close the connection after a client is idle for N seconds
20
- timeout 300
20
+ timeout 5
21
21
 
22
22
  # Save the DB on disk:
23
23
  #
@@ -0,0 +1,25 @@
1
+ require File.join(File.dirname(__FILE__), "/../spec_helper")
2
+
3
+ describe "Redis::Server" do
4
+ before(:each) do
5
+ @server = Server.new 'localhost', '6379'
6
+ end
7
+
8
+ it "should have a connection pool" do
9
+ @server.instance_variable_get(:@sockets).should_not be_nil
10
+ end
11
+
12
+ it "should checkout active connections" do
13
+ threads = []
14
+ 100.times do
15
+ threads << Thread.new do
16
+ lambda {
17
+ sleep 6 # redis will close idle connections in the meanwhile
18
+ socket = @server.socket
19
+ socket.write("INFO\r\n")
20
+ socket.read(1)
21
+ }.should_not raise_error(Exception)
22
+ end
23
+ end
24
+ end
25
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jodosha-redis-store
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luca Guidi
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-05-03 00:00:00 -07:00
12
+ date: 2009-05-07 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -37,6 +37,7 @@ files:
37
37
  - lib/redis/marshaled_redis.rb
38
38
  - lib/redis/redis.rb
39
39
  - lib/redis/redis_factory.rb
40
+ - lib/redis/server.rb
40
41
  - redis-store.gemspec
41
42
  - spec/cache/merb/redis_store_spec.rb
42
43
  - spec/cache/rails/redis_store_spec.rb
@@ -51,6 +52,7 @@ files:
51
52
  - spec/redis/distributed_marshaled_redis_spec.rb
52
53
  - spec/redis/marshaled_redis_spec.rb
53
54
  - spec/redis/redis_factory_spec.rb
55
+ - spec/redis/server_spec.rb
54
56
  - spec/spec_helper.rb
55
57
  has_rdoc: true
56
58
  homepage: http://lucaguidi.com
@@ -88,3 +90,4 @@ test_files:
88
90
  - spec/redis/distributed_marshaled_redis_spec.rb
89
91
  - spec/redis/marshaled_redis_spec.rb
90
92
  - spec/redis/redis_factory_spec.rb
93
+ - spec/redis/server_spec.rb