rpush 2.0.0.rc1 → 2.0.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +2 -1
- data/README.md +27 -10
- data/bin/rpush +2 -2
- data/lib/generators/templates/rpush.rb +0 -1
- data/lib/generators/templates/rpush_2_0_0_updates.rb +1 -1
- data/lib/rpush/client/redis/app.rb +2 -0
- data/lib/rpush/daemon.rb +1 -1
- data/lib/rpush/daemon/app_runner.rb +12 -1
- data/lib/rpush/daemon/signal_handler.rb +24 -19
- data/lib/rpush/daemon/synchronizer.rb +21 -5
- data/lib/rpush/version.rb +1 -1
- data/spec/functional/apns_spec.rb +10 -11
- data/spec/functional/synchronization_spec.rb +22 -0
- data/spec/unit/daemon/app_runner_spec.rb +2 -2
- data/spec/unit/daemon/signal_handler_spec.rb +23 -0
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ddc24f899bd6b0f1eed7b50b40f16658290cec0e
|
4
|
+
data.tar.gz: bffc9e9752d7d53fa65dd6f6f1498e6a0f8d3ad4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 458e8e80010377460ad4968ac03ac448e435d221fb6385a7045a023e07114f69635d5f3f130c2c3bcf68b4ac35396d50a58de888154959275a326d5e680ab059
|
7
|
+
data.tar.gz: 85197cad6ac231852aeba0c1d405a9f2eaab8a4b60523cc7e9b223d6598436a53075dd6eadf7a7611f38e32a03173cbf04d1804445215e474f738de1af76ce61
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
## 2.0.0 (
|
1
|
+
## 2.0.0 (Sept 6, 2014)
|
2
2
|
* Use APNs enhanced binary format version 2.
|
3
3
|
* Support running multiple Rpush processes when using ActiveRecord and Redis.
|
4
4
|
* APNs error detection is now performed asynchronously, 'check_for_errors' is therefore deprecated.
|
@@ -11,6 +11,7 @@
|
|
11
11
|
* The 'batch_storage_updates' config option has been deprecated, storage backends will now always batch updates where appropriate.
|
12
12
|
* The rpush process title updates with number of queued notifications and number of dispatchers.
|
13
13
|
* Rpush::Apns::Feedback#app has been renamed to app_id and is now an Integer.
|
14
|
+
* An app is restarted when the HUP signal is received if its certificate or environment attribute changed.
|
14
15
|
|
15
16
|
## 1.0.0 (Feb 9, 2014)
|
16
17
|
* Renamed to Rpush (from Rapns). Version number reset to 1.0.0.
|
data/README.md
CHANGED
@@ -7,15 +7,23 @@
|
|
7
7
|
|
8
8
|
### Rpush. The push notification service for Ruby.
|
9
9
|
|
10
|
-
*
|
10
|
+
* Supported services:
|
11
11
|
* **Apple Push Notification Service**
|
12
12
|
* **Google Cloud Messaging**
|
13
13
|
* **Amazon Device Messaging**
|
14
|
-
* **Windows Phone Push Notification Service
|
14
|
+
* **Windows Phone Push Notification Service**
|
15
|
+
|
16
|
+
|
17
|
+
* Supported storage backends:
|
18
|
+
* ActiveRecord
|
19
|
+
* Redis
|
20
|
+
* More coming soon!
|
21
|
+
|
22
|
+
|
15
23
|
* Seamless Rails (3, 4) integration.
|
16
|
-
*
|
17
|
-
* Designed for uptime - signal
|
18
|
-
* Run as a daemon or inside an [existing
|
24
|
+
* Scales vertically (threading) and horizontally (multiple processes).
|
25
|
+
* Designed for uptime - new apps are loaded automatically, signal `HUP` to update running apps.
|
26
|
+
* Run as a daemon or inside an [existing process](https://github.com/rpush/rpush/wiki/Embedding-API).
|
19
27
|
* Use in a scheduler for low-workload deployments ([Push API](https://github.com/rpush/rpush/wiki/Push-API)).
|
20
28
|
* Hooks for fine-grained instrumentation and error handling ([Reflection API](https://github.com/rpush/rpush/wiki/Reflection-API)).
|
21
29
|
* Works with MRI, JRuby and Rubinius.
|
@@ -122,31 +130,40 @@ n.alert = "..."
|
|
122
130
|
n.save!
|
123
131
|
```
|
124
132
|
|
125
|
-
###
|
133
|
+
### Running Rpush
|
126
134
|
|
127
|
-
|
135
|
+
It is recommended to run Rpush as a separate process in most cases, though embedding and manual modes are provided for low-workload environments.
|
136
|
+
|
137
|
+
#### As a daemon (recommended):
|
128
138
|
|
129
139
|
cd /path/to/rails/app
|
130
140
|
rpush <Rails environment> [options]
|
131
141
|
|
132
|
-
|
142
|
+
#### Embedded inside an existing process
|
133
143
|
|
134
144
|
```ruby
|
145
|
+
# Call this during startup of your application, for example, by adding it to the end of config/initializers/rpush.rb
|
135
146
|
Rpush.embed
|
136
147
|
```
|
137
148
|
|
138
|
-
|
149
|
+
See [Embedding API](https://github.com/rpush/rpush/wiki/Embedding-API) for more details.
|
150
|
+
|
151
|
+
#### Manually (in a scheduler)
|
139
152
|
|
140
153
|
```ruby
|
141
154
|
Rpush.push
|
142
155
|
Rpush.apns_feedback
|
143
156
|
```
|
144
157
|
|
158
|
+
See [Push API](https://github.com/rpush/rpush/wiki/Push-API) for more details.
|
159
|
+
|
160
|
+
### Configuration
|
161
|
+
|
145
162
|
See [Configuration](https://github.com/rpush/rpush/wiki/Configuration) for a list of options, or run `rpush --help`.
|
146
163
|
|
147
164
|
### Updating Rpush
|
148
165
|
|
149
|
-
|
166
|
+
If you're using ActiveRecord, you should run `rails g rpush` after upgrading Rpush to check for any new migrations.
|
150
167
|
|
151
168
|
### Wiki
|
152
169
|
|
data/bin/rpush
CHANGED
@@ -12,10 +12,10 @@ options = ARGV.options do |opts|
|
|
12
12
|
opts.on('-f', '--foreground', 'Run in the foreground.') { config.foreground = true }
|
13
13
|
opts.on('-P N', '--db-poll N', Integer, "Frequency in seconds to check for new notifications.") { |n| config.push_poll = n }
|
14
14
|
opts.on('-F N', '--feedback-poll N', Integer, "Frequency in seconds to check for feedback.") { |n| config.feedback_poll = n }
|
15
|
-
opts.on('-e', '--no-error-checks', 'Disable APNs error checking after notification delivery.') { config.check_for_errors = false }
|
15
|
+
opts.on('-e', '--no-error-checks', 'Disable APNs error checking after notification delivery.') { config.check_for_errors = false } # deprecated
|
16
16
|
opts.on('-p PATH', '--pid-file PATH', String, 'Path to write PID file. Relative to Rails root unless absolute.') { |path| config.pid_file = path }
|
17
17
|
opts.on('-b N', '--batch-size N', Integer, 'Storage backend notification batch size.') { |n| config.batch_size = n }
|
18
|
-
opts.on('-B', '--[no-]batch-storage-updates', 'Perform storage updates in batches.') { |v| config.batch_storage_updates = v }
|
18
|
+
opts.on('-B', '--[no-]batch-storage-updates', 'Perform storage updates in batches.') { |v| config.batch_storage_updates = v } # deprecated
|
19
19
|
opts.on('-v', '--version', 'Print the version.') do
|
20
20
|
puts "rpush #{Rpush::VERSION}"
|
21
21
|
exit
|
@@ -7,7 +7,7 @@ class Rpush200Updates < ActiveRecord::Migration
|
|
7
7
|
remove_index :rpush_notifications, name: :index_rpush_notifications_multi
|
8
8
|
end
|
9
9
|
|
10
|
-
add_index :rpush_notifications, [:
|
10
|
+
add_index :rpush_notifications, [:delivered, :failed], name: 'index_rpush_notifications_multi', where: 'NOT delivered AND NOT failed'
|
11
11
|
|
12
12
|
rename_column :rpush_feedback, :app, :app_id
|
13
13
|
|
data/lib/rpush/daemon.rb
CHANGED
@@ -4,7 +4,9 @@ module Rpush
|
|
4
4
|
extend Reflectable
|
5
5
|
include Reflectable
|
6
6
|
include Loggable
|
7
|
+
extend Loggable
|
7
8
|
include StringHelpers
|
9
|
+
extend StringHelpers
|
8
10
|
|
9
11
|
@runners = {}
|
10
12
|
|
@@ -24,6 +26,7 @@ module Rpush
|
|
24
26
|
def self.start_app(app)
|
25
27
|
@runners[app.id] = new(app)
|
26
28
|
@runners[app.id].start
|
29
|
+
log_info("[#{app.name}] Started, #{pluralize(app.connections, 'dispatcher')}.")
|
27
30
|
rescue StandardError => e
|
28
31
|
@runners.delete(app.id)
|
29
32
|
Rpush.logger.error("[#{app.name}] Exception raised during startup. Notifications will not be delivered for this app.")
|
@@ -32,7 +35,15 @@ module Rpush
|
|
32
35
|
end
|
33
36
|
|
34
37
|
def self.stop_app(app_id)
|
35
|
-
@runners.delete(app_id)
|
38
|
+
runner = @runners.delete(app_id)
|
39
|
+
if runner
|
40
|
+
runner.stop
|
41
|
+
log_info("[#{runner.app.name}] Stopped.")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.app_with_id(app_id)
|
46
|
+
@runners[app_id].app
|
36
47
|
end
|
37
48
|
|
38
49
|
def self.app_running?(app)
|
@@ -7,42 +7,47 @@ module Rpush
|
|
7
7
|
|
8
8
|
def self.start
|
9
9
|
return unless trap_signals?
|
10
|
-
|
10
|
+
|
11
11
|
read_io, @write_io = IO.pipe
|
12
12
|
start_handler(read_io)
|
13
13
|
%w(INT TERM HUP USR2).each do |signal|
|
14
|
-
Signal.trap(signal) { @write_io.
|
14
|
+
Signal.trap(signal) { @write_io.puts(signal) }
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
18
|
def self.stop
|
19
|
-
@write_io.
|
19
|
+
@write_io.puts('break') if @write_io
|
20
20
|
@thread.join if @thread
|
21
21
|
end
|
22
22
|
|
23
23
|
def self.start_handler(read_io)
|
24
24
|
@thread = Thread.new do
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
25
|
+
while readable_io = IO.select([read_io]) # rubocop:disable AssignmentInCondition
|
26
|
+
signal = readable_io.first[0].gets.strip
|
27
|
+
|
28
|
+
begin
|
29
|
+
case signal
|
30
|
+
when 'HUP'
|
31
|
+
Synchronizer.sync
|
32
|
+
Feeder.wakeup
|
33
|
+
when 'USR2'
|
34
|
+
AppRunner.debug
|
35
|
+
when 'INT', 'TERM'
|
36
|
+
Thread.new { Rpush::Daemon.shutdown }
|
37
|
+
break
|
38
|
+
when 'break'
|
39
|
+
break
|
40
|
+
else
|
41
|
+
Rpush.logger.error("Unhandled signal: #{signal}")
|
42
|
+
end
|
43
|
+
rescue StandardError => e
|
44
|
+
Rpush.logger.error("Error raised when hndling signal '#{signal}'")
|
45
|
+
Rpush.logger.error(e)
|
36
46
|
end
|
37
47
|
end
|
38
48
|
end
|
39
49
|
end
|
40
50
|
|
41
|
-
def self.handle_shutdown_signal
|
42
|
-
@shutting_down = true
|
43
|
-
Rpush::Daemon.shutdown
|
44
|
-
end
|
45
|
-
|
46
51
|
def self.trap_signals?
|
47
52
|
!Rpush.config.embedded
|
48
53
|
end
|
@@ -14,13 +14,19 @@ module Rpush
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def self.sync_app(app)
|
17
|
-
|
17
|
+
if !AppRunner.app_running?(app)
|
18
18
|
AppRunner.start_app(app)
|
19
|
-
|
20
|
-
|
19
|
+
elsif certificate_changed?(app)
|
20
|
+
log_info("[#{app.name}] Certificate changed, restarting...")
|
21
|
+
AppRunner.stop_app(app.id)
|
22
|
+
AppRunner.start_app(app)
|
23
|
+
elsif environment_changed?(app)
|
24
|
+
log_info("[#{app.name}] Environment changed, restarting...")
|
25
|
+
AppRunner.stop_app(app.id)
|
26
|
+
AppRunner.start_app(app)
|
27
|
+
else
|
28
|
+
sync_dispatcher_count(app)
|
21
29
|
end
|
22
|
-
|
23
|
-
sync_dispatcher_count(app)
|
24
30
|
end
|
25
31
|
|
26
32
|
def self.sync_dispatcher_count(app)
|
@@ -39,6 +45,16 @@ module Rpush
|
|
39
45
|
num_dispatchers = AppRunner.num_dispatchers_for_app(app)
|
40
46
|
log_info("[#{app.name}] #{start_stop_str} #{pluralize(diff.abs, 'dispatcher')}. #{num_dispatchers} running.")
|
41
47
|
end
|
48
|
+
|
49
|
+
def self.certificate_changed?(app)
|
50
|
+
old_app = AppRunner.app_with_id(app.id)
|
51
|
+
app.certificate != old_app.certificate
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.environment_changed?(app)
|
55
|
+
old_app = AppRunner.app_with_id(app.id)
|
56
|
+
app.environment != old_app.environment
|
57
|
+
end
|
42
58
|
end
|
43
59
|
end
|
44
60
|
end
|
data/lib/rpush/version.rb
CHANGED
@@ -127,17 +127,16 @@ describe 'APNs' do
|
|
127
127
|
notification1.delivered.should be_true
|
128
128
|
end
|
129
129
|
|
130
|
-
it 'marks notifications following the failed one as retryable'
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
# end
|
130
|
+
it 'marks notifications following the failed one as retryable' do
|
131
|
+
Rpush.config.push_poll = 1_000_000
|
132
|
+
|
133
|
+
notifications.each { |n| wait_for_notification_to_deliver(n) }
|
134
|
+
fail_notification(notification2)
|
135
|
+
|
136
|
+
[notification3, notification4].each do |n|
|
137
|
+
wait_for_notification_to_retry(n)
|
138
|
+
end
|
139
|
+
end
|
141
140
|
|
142
141
|
describe 'without an error response' do
|
143
142
|
it 'marks all notifications as failed' do
|
@@ -16,6 +16,9 @@ describe 'Synchronization' do
|
|
16
16
|
app.name = 'test'
|
17
17
|
app.auth_key = 'abc123'
|
18
18
|
app.connections = 2
|
19
|
+
app.certificate = TEST_CERT_WITH_PASSWORD
|
20
|
+
app.password = 'fubar'
|
21
|
+
app.environment = 'sandbox'
|
19
22
|
app.save!
|
20
23
|
|
21
24
|
Rpush.embed
|
@@ -43,4 +46,23 @@ describe 'Synchronization' do
|
|
43
46
|
Rpush.sync
|
44
47
|
Rpush::Daemon::AppRunner.app_running?(app).should be_false
|
45
48
|
end
|
49
|
+
|
50
|
+
it 'restarts an app when the certificate is changed' do
|
51
|
+
app.certificate = TEST_CERT
|
52
|
+
app.password = nil
|
53
|
+
app.save!
|
54
|
+
Rpush.sync
|
55
|
+
|
56
|
+
running_app = Rpush::Daemon::AppRunner.app_with_id(app.id)
|
57
|
+
expect(running_app.certificate).to eql(TEST_CERT)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'restarts an app when the environment is changed' do
|
61
|
+
app.environment = 'production'
|
62
|
+
app.save!
|
63
|
+
Rpush.sync
|
64
|
+
|
65
|
+
running_app = Rpush::Daemon::AppRunner.app_with_id(app.id)
|
66
|
+
expect(running_app.environment).to eql('production')
|
67
|
+
end
|
46
68
|
end
|
@@ -32,7 +32,7 @@ module Rpush
|
|
32
32
|
end
|
33
33
|
|
34
34
|
describe Rpush::Daemon::AppRunner, 'enqueue' do
|
35
|
-
let(:app) { double(id: 1) }
|
35
|
+
let(:app) { double(id: 1, name: 'Test', connections: 1) }
|
36
36
|
let(:notification) { double(app_id: 1) }
|
37
37
|
let(:runner) { double(Rpush::Daemon::AppRunner, enqueue: nil, start: nil, stop: nil) }
|
38
38
|
let(:logger) { double(Rpush::Logger, error: nil, info: nil) }
|
@@ -53,7 +53,7 @@ describe Rpush::Daemon::AppRunner, 'enqueue' do
|
|
53
53
|
|
54
54
|
it 'starts the app if a runner does not exist' do
|
55
55
|
notification = double(app_id: 3)
|
56
|
-
new_app = double(Rpush::App, id: 3)
|
56
|
+
new_app = double(Rpush::App, id: 3, name: 'NewApp', connections: 1)
|
57
57
|
Rpush::Daemon.store = double(app: new_app)
|
58
58
|
Rpush::Daemon::AppRunner.enqueue([notification])
|
59
59
|
Rpush::Daemon::AppRunner.app_running?(new_app).should be_true
|
@@ -69,4 +69,27 @@ describe Rpush::Daemon::SignalHandler do
|
|
69
69
|
end
|
70
70
|
end
|
71
71
|
end
|
72
|
+
|
73
|
+
describe 'error handing' do
|
74
|
+
let(:error) { StandardError.new('test') }
|
75
|
+
|
76
|
+
before { Rpush.stub(logger: double(error: nil)) }
|
77
|
+
|
78
|
+
it 'logs errors received when handling a signal' do
|
79
|
+
Rpush::Daemon::Synchronizer.stub(:sync).and_raise(error)
|
80
|
+
expect(Rpush.logger).to receive(:error).with(error)
|
81
|
+
with_handler_start_stop do
|
82
|
+
signal_handler('HUP')
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'does not interrupt processing of further errors' do
|
87
|
+
Rpush::Daemon::Synchronizer.stub(:sync).and_raise(error)
|
88
|
+
expect(Rpush::Daemon::AppRunner).to receive(:debug)
|
89
|
+
with_handler_start_stop do
|
90
|
+
signal_handler('HUP')
|
91
|
+
signal_handler('USR2')
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
72
95
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rpush
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.0
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ian Leitch
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-09-
|
11
|
+
date: 2014-09-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: multi_json
|
@@ -249,9 +249,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
249
249
|
version: '0'
|
250
250
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
251
251
|
requirements:
|
252
|
-
- - "
|
252
|
+
- - ">="
|
253
253
|
- !ruby/object:Gem::Version
|
254
|
-
version:
|
254
|
+
version: '0'
|
255
255
|
requirements: []
|
256
256
|
rubyforge_project:
|
257
257
|
rubygems_version: 2.2.2
|