faye-redis 0.1.0 → 0.1.1

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/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
-