faye-redis 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.txt ADDED
@@ -0,0 +1,9 @@
1
+ === 0.1.1 / 2013-04-28
2
+
3
+ * Improve garbage collection to avoid leaking Redis memory
4
+
5
+
6
+ === 0.1.0 / 2012-02-26
7
+
8
+ * Initial release: Redis backend for Faye 0.8
9
+
data/README.rdoc CHANGED
@@ -1,4 +1,4 @@
1
- = Faye::Redis {<img src="https://secure.travis-ci.org/faye/faye-redis-ruby.png?branch=master" alt="Build Status" />}[http://travis-ci.org/faye/faye-redis-ruby]
1
+ = Faye::Redis {<img src="https://secure.travis-ci.org/faye/faye-redis-ruby.png" alt="Build Status" />}[http://travis-ci.org/faye/faye-redis-ruby]
2
2
 
3
3
  This plugin provides a Redis-based backend for the {Faye}[http://faye.jcoglan.com]
4
4
  messaging server. It allows a single Faye service to be distributed across many
@@ -12,7 +12,7 @@ Pass in the engine and any settings you need when setting up your Faye server.
12
12
 
13
13
  require 'faye'
14
14
  require 'faye/redis'
15
-
15
+
16
16
  bayeux = Faye::RackAdapter.new(
17
17
  :mount => '/',
18
18
  :timeout => 25,
@@ -25,6 +25,7 @@ Pass in the engine and any settings you need when setting up your Faye server.
25
25
 
26
26
  The full list of settings is as follows.
27
27
 
28
+ * <b><tt>:uri</tt></b> - redis URL (example: redis://:secretpassword@example.com:9000/4)
28
29
  * <b><tt>:host</tt></b> - hostname of your Redis instance
29
30
  * <b><tt>:port</tt></b> - port number, default is +6379+
30
31
  * <b><tt>:password</tt></b> - password, if +requirepass+ is set
data/lib/faye/redis.rb CHANGED
@@ -3,60 +3,57 @@ require 'yajl'
3
3
 
4
4
  module Faye
5
5
  class Redis
6
-
6
+
7
7
  DEFAULT_HOST = 'localhost'
8
8
  DEFAULT_PORT = 6379
9
9
  DEFAULT_DATABASE = 0
10
10
  DEFAULT_GC = 60
11
11
  LOCK_TIMEOUT = 120
12
-
12
+
13
13
  def self.create(server, options)
14
14
  new(server, options)
15
15
  end
16
-
16
+
17
17
  def initialize(server, options)
18
18
  @server = server
19
19
  @options = options
20
20
  end
21
-
21
+
22
22
  def init
23
23
  return if @redis
24
-
24
+
25
+ uri = @options[:uri] || nil
25
26
  host = @options[:host] || DEFAULT_HOST
26
27
  port = @options[:port] || DEFAULT_PORT
27
28
  db = @options[:database] || DEFAULT_DATABASE
28
- auth = @options[:password]
29
+ auth = @options[:password] || nil
29
30
  gc = @options[:gc] || DEFAULT_GC
30
31
  @ns = @options[:namespace] || ''
31
- socket = @options[:socket]
32
-
33
- if socket
34
- @redis = EventMachine::Hiredis::Client.connect(socket, nil)
35
- @subscriber = EventMachine::Hiredis::Client.connect(socket, nil)
32
+ socket = @options[:socket] || nil
33
+
34
+ if uri
35
+ @redis = EventMachine::Hiredis.connect(uri)
36
+ elsif socket
37
+ @redis = EventMachine::Hiredis::Client.new(socket, nil, auth, db).connect
36
38
  else
37
- @redis = EventMachine::Hiredis::Client.connect(host, port)
38
- @subscriber = EventMachine::Hiredis::Client.connect(host, port)
39
- end
40
- if auth
41
- @redis.auth(auth)
42
- @subscriber.auth(auth)
39
+ @redis = EventMachine::Hiredis::Client.new(host, port, auth, db).connect
43
40
  end
44
- @redis.select(db)
45
- @subscriber.select(db)
46
-
41
+ @subscriber = @redis.pubsub
42
+
47
43
  @subscriber.subscribe(@ns + '/notifications')
48
44
  @subscriber.on(:message) do |topic, message|
49
45
  empty_queue(message) if topic == @ns + '/notifications'
50
46
  end
51
-
47
+
52
48
  @gc = EventMachine.add_periodic_timer(gc, &method(:gc))
53
49
  end
54
-
50
+
55
51
  def disconnect
52
+ return unless @redis
56
53
  @subscriber.unsubscribe(@ns + '/notifications')
57
54
  EventMachine.cancel_timer(@gc)
58
55
  end
59
-
56
+
60
57
  def create_client(&callback)
61
58
  init
62
59
  client_id = @server.generate_id
@@ -68,48 +65,53 @@ module Faye
68
65
  callback.call(client_id)
69
66
  end
70
67
  end
71
-
68
+
69
+ def client_exists(client_id, &callback)
70
+ init
71
+ cutoff = get_current_time - (1000 * 1.6 * @server.timeout)
72
+
73
+ @redis.zscore(@ns + '/clients', client_id) do |score|
74
+ callback.call(score.to_i > cutoff)
75
+ end
76
+ end
77
+
72
78
  def destroy_client(client_id, &callback)
73
79
  init
74
- @redis.zrem(@ns + '/clients', client_id)
75
- @redis.del(@ns + "/clients/#{client_id}/messages")
76
-
77
- @redis.smembers(@ns + "/clients/#{client_id}/channels") do |channels|
78
- i, n = 0, channels.size
79
- next after_destroy(client_id, &callback) if i == n
80
-
81
- channels.each do |channel|
82
- unsubscribe(client_id, channel) do
83
- i += 1
84
- after_destroy(client_id, &callback) if i == n
80
+ @redis.zadd(@ns + '/clients', 0, client_id) do
81
+ @redis.smembers(@ns + "/clients/#{client_id}/channels") do |channels|
82
+ i, n = 0, channels.size
83
+ next after_subscriptions_removed(client_id, &callback) if i == n
84
+
85
+ channels.each do |channel|
86
+ unsubscribe(client_id, channel) do
87
+ i += 1
88
+ after_subscriptions_removed(client_id, &callback) if i == n
89
+ end
85
90
  end
86
91
  end
87
92
  end
88
93
  end
89
-
90
- def after_destroy(client_id, &callback)
91
- @server.debug 'Destroyed client ?', client_id
92
- @server.trigger(:disconnect, client_id)
93
- callback.call if callback
94
- end
95
-
96
- def client_exists(client_id, &callback)
97
- init
98
- @redis.zscore(@ns + '/clients', client_id) do |score|
99
- callback.call(score != nil)
94
+
95
+ def after_subscriptions_removed(client_id, &callback)
96
+ @redis.del(@ns + "/clients/#{client_id}/messages") do
97
+ @redis.zrem(@ns + '/clients', client_id) do
98
+ @server.debug 'Destroyed client ?', client_id
99
+ @server.trigger(:disconnect, client_id)
100
+ callback.call if callback
101
+ end
100
102
  end
101
103
  end
102
-
104
+
103
105
  def ping(client_id)
104
106
  init
105
107
  timeout = @server.timeout
106
108
  return unless Numeric === timeout
107
-
109
+
108
110
  time = get_current_time
109
111
  @server.debug 'Ping ?, ?', client_id, time
110
112
  @redis.zadd(@ns + '/clients', time, client_id)
111
113
  end
112
-
114
+
113
115
  def subscribe(client_id, channel, &callback)
114
116
  init
115
117
  @redis.sadd(@ns + "/clients/#{client_id}/channels", channel) do |added|
@@ -120,7 +122,7 @@ module Faye
120
122
  callback.call if callback
121
123
  end
122
124
  end
123
-
125
+
124
126
  def unsubscribe(client_id, channel, &callback)
125
127
  init
126
128
  @redis.srem(@ns + "/clients/#{client_id}/channels", channel) do |removed|
@@ -131,57 +133,64 @@ module Faye
131
133
  callback.call if callback
132
134
  end
133
135
  end
134
-
136
+
135
137
  def publish(message, channels)
136
138
  init
137
139
  @server.debug 'Publishing message ?', message
138
-
140
+
139
141
  json_message = Yajl::Encoder.encode(message)
140
142
  channels = Channel.expand(message['channel'])
141
143
  keys = channels.map { |c| @ns + "/channels#{c}" }
142
-
144
+
143
145
  @redis.sunion(*keys) do |clients|
144
146
  clients.each do |client_id|
147
+ queue = @ns + "/clients/#{client_id}/messages"
148
+
145
149
  @server.debug 'Queueing for client ?: ?', client_id, message
146
- @redis.rpush(@ns + "/clients/#{client_id}/messages", json_message)
150
+ @redis.rpush(queue, json_message)
147
151
  @redis.publish(@ns + '/notifications', client_id)
152
+
153
+ client_exists(client_id) do |exists|
154
+ @redis.del(queue) unless exists
155
+ end
148
156
  end
149
157
  end
150
-
158
+
151
159
  @server.trigger(:publish, message['clientId'], message['channel'], message['data'])
152
160
  end
153
-
161
+
154
162
  def empty_queue(client_id)
155
163
  return unless @server.has_connection?(client_id)
156
164
  init
157
-
165
+
158
166
  key = @ns + "/clients/#{client_id}/messages"
159
-
167
+
160
168
  @redis.multi
161
169
  @redis.lrange(key, 0, -1)
162
170
  @redis.del(key)
163
171
  @redis.exec.callback do |json_messages, deleted|
172
+ next unless json_messages
164
173
  messages = json_messages.map { |json| Yajl::Parser.parse(json) }
165
174
  @server.deliver(client_id, messages)
166
175
  end
167
176
  end
168
-
177
+
169
178
  private
170
-
179
+
171
180
  def get_current_time
172
181
  (Time.now.to_f * 1000).to_i
173
182
  end
174
-
183
+
175
184
  def gc
176
185
  timeout = @server.timeout
177
186
  return unless Numeric === timeout
178
-
187
+
179
188
  with_lock 'gc' do |release_lock|
180
189
  cutoff = get_current_time - 1000 * 2 * timeout
181
190
  @redis.zrangebyscore(@ns + '/clients', 0, cutoff) do |clients|
182
191
  i, n = 0, clients.size
183
192
  next release_lock.call if i == n
184
-
193
+
185
194
  clients.each do |client_id|
186
195
  destroy_client(client_id) do
187
196
  i += 1
@@ -191,32 +200,32 @@ module Faye
191
200
  end
192
201
  end
193
202
  end
194
-
203
+
195
204
  def with_lock(lock_name, &block)
196
205
  lock_key = @ns + '/locks/' + lock_name
197
206
  current_time = get_current_time
198
207
  expiry = current_time + LOCK_TIMEOUT * 1000 + 1
199
-
208
+
200
209
  release_lock = lambda do
201
210
  @redis.del(lock_key) if get_current_time < expiry
202
211
  end
203
-
212
+
204
213
  @redis.setnx(lock_key, expiry) do |set|
205
214
  next block.call(release_lock) if set == 1
206
-
215
+
207
216
  @redis.get(lock_key) do |timeout|
208
217
  next unless timeout
209
-
218
+
210
219
  lock_timeout = timeout.to_i(10)
211
220
  next if current_time < lock_timeout
212
-
221
+
213
222
  @redis.getset(lock_key, expiry) do |old_value|
214
223
  block.call(release_lock) if old_value == timeout
215
224
  end
216
225
  end
217
226
  end
218
227
  end
219
-
228
+
220
229
  end
221
230
  end
222
231
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: faye-redis
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-02-26 00:00:00.000000000 Z
12
+ date: 2013-04-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: eventmachine
16
- requirement: &10557300 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,21 +21,31 @@ dependencies:
21
21
  version: 0.12.0
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *10557300
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 0.12.0
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: em-hiredis
27
- requirement: &10556820 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
35
  - - ! '>='
31
36
  - !ruby/object:Gem::Version
32
- version: 0.0.1
37
+ version: 0.2.0
33
38
  type: :runtime
34
39
  prerelease: false
35
- version_requirements: *10556820
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 0.2.0
36
46
  - !ruby/object:Gem::Dependency
37
47
  name: yajl-ruby
38
- requirement: &10556360 !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
39
49
  none: false
40
50
  requirements:
41
51
  - - ! '>='
@@ -43,18 +53,28 @@ dependencies:
43
53
  version: 1.0.0
44
54
  type: :runtime
45
55
  prerelease: false
46
- version_requirements: *10556360
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: 1.0.0
47
62
  - !ruby/object:Gem::Dependency
48
63
  name: rspec
49
- requirement: &10555900 !ruby/object:Gem::Requirement
64
+ requirement: !ruby/object:Gem::Requirement
50
65
  none: false
51
66
  requirements:
52
- - - ~>
67
+ - - ! '>='
53
68
  - !ruby/object:Gem::Version
54
- version: 2.8.0
69
+ version: '0'
55
70
  type: :development
56
71
  prerelease: false
57
- version_requirements: *10555900
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
58
78
  description:
59
79
  email: jcoglan@gmail.com
60
80
  executables: []
@@ -63,9 +83,7 @@ extra_rdoc_files:
63
83
  - README.rdoc
64
84
  files:
65
85
  - README.rdoc
66
- - spec/redis.conf
67
- - spec/faye_redis_spec.rb
68
- - spec/spec_helper.rb
86
+ - CHANGELOG.txt
69
87
  - lib/faye/redis.rb
70
88
  homepage: http://github.com/faye/faye-redis-ruby
71
89
  licenses: []
@@ -89,8 +107,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
89
107
  version: '0'
90
108
  requirements: []
91
109
  rubyforge_project:
92
- rubygems_version: 1.8.10
110
+ rubygems_version: 1.8.23
93
111
  signing_key:
94
112
  specification_version: 3
95
113
  summary: Redis backend engine for Faye
96
114
  test_files: []
115
+ has_rdoc:
@@ -1,26 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe Faye::Redis do
4
- let(:engine_opts) do
5
- pw = ENV["TRAVIS"] ? nil : "foobared"
6
- {:type => Faye::Redis, :password => pw, :namespace => Time.now.to_i.to_s}
7
- end
8
-
9
- after do
10
- engine.disconnect
11
- redis = EM::Hiredis::Client.connect('localhost', 6379)
12
- redis.auth(engine_opts[:password])
13
- redis.flushall
14
- end
15
-
16
- it_should_behave_like "faye engine"
17
- it_should_behave_like "distributed engine"
18
-
19
- next if ENV["TRAVIS"]
20
-
21
- describe "using a Unix socket" do
22
- before { engine_opts[:socket] = "/tmp/redis.sock" }
23
- it_should_behave_like "faye engine"
24
- end
25
- end
26
-
data/spec/redis.conf DELETED
@@ -1,42 +0,0 @@
1
- daemonize no
2
- pidfile /tmp/redis.pid
3
- port 6379
4
- unixsocket /tmp/redis.sock
5
- timeout 300
6
- loglevel verbose
7
- logfile stdout
8
- databases 16
9
-
10
- save 900 1
11
- save 300 10
12
- save 60 10000
13
-
14
- rdbcompression yes
15
- dbfilename dump.rdb
16
- dir ./
17
-
18
- slave-serve-stale-data yes
19
-
20
- requirepass foobared
21
-
22
- appendonly no
23
- appendfsync everysec
24
- no-appendfsync-on-rewrite no
25
-
26
- vm-enabled no
27
- vm-swap-file /tmp/redis.swap
28
- vm-max-memory 0
29
- vm-page-size 32
30
- vm-pages 134217728
31
- vm-max-threads 4
32
-
33
- hash-max-zipmap-entries 512
34
- hash-max-zipmap-value 64
35
-
36
- list-max-ziplist-entries 512
37
- list-max-ziplist-value 64
38
-
39
- set-max-intset-entries 512
40
-
41
- activerehashing yes
42
-
data/spec/spec_helper.rb DELETED
@@ -1,3 +0,0 @@
1
- require File.expand_path('../../lib/faye/redis', __FILE__)
2
- require File.expand_path('../../vendor/faye/spec/ruby/engine_examples', __FILE__)
3
-