rapns 3.0.1 → 3.1.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.
- data/CHANGELOG.md +10 -2
- data/README.md +26 -3
- data/bin/rapns +2 -7
- data/lib/generators/templates/add_gcm.rb +3 -1
- data/lib/generators/templates/rapns.rb +42 -11
- data/lib/rapns/apns/notification.rb +8 -2
- data/lib/rapns/app.rb +3 -3
- data/lib/rapns/configuration.rb +46 -13
- data/lib/rapns/daemon/apns/connection.rb +12 -9
- data/lib/rapns/daemon/apns/delivery_handler.rb +1 -1
- data/lib/rapns/daemon/apns/feedback_receiver.rb +6 -2
- data/lib/rapns/daemon/app_runner.rb +23 -7
- data/lib/rapns/daemon/delivery.rb +5 -1
- data/lib/rapns/daemon/delivery_handler.rb +4 -0
- data/lib/rapns/daemon/feeder.rb +26 -5
- data/lib/rapns/daemon/reflectable.rb +13 -0
- data/lib/rapns/daemon.rb +33 -22
- data/lib/rapns/embed.rb +28 -0
- data/lib/rapns/gcm/notification.rb +7 -2
- data/lib/rapns/gcm/payload_data_size_validator.rb +13 -0
- data/lib/rapns/gcm/registration_ids_count_validator.rb +13 -0
- data/lib/rapns/push.rb +12 -0
- data/lib/rapns/reflection.rb +44 -0
- data/lib/rapns/version.rb +1 -1
- data/lib/rapns.rb +11 -1
- data/spec/support/cert_with_password.pem +90 -0
- data/spec/support/cert_without_password.pem +59 -0
- data/spec/unit/apns/app_spec.rb +15 -1
- data/spec/unit/apns/notification_spec.rb +16 -1
- data/spec/unit/configuration_spec.rb +10 -1
- data/spec/unit/daemon/apns/connection_spec.rb +11 -2
- data/spec/unit/daemon/apns/delivery_handler_spec.rb +1 -1
- data/spec/unit/daemon/apns/delivery_spec.rb +10 -0
- data/spec/unit/daemon/apns/feedback_receiver_spec.rb +16 -7
- data/spec/unit/daemon/delivery_handler_shared.rb +8 -0
- data/spec/unit/daemon/feeder_spec.rb +37 -6
- data/spec/unit/daemon/gcm/delivery_spec.rb +33 -1
- data/spec/unit/daemon/reflectable_spec.rb +27 -0
- data/spec/unit/daemon_spec.rb +55 -9
- data/spec/unit/embed_spec.rb +44 -0
- data/spec/unit/gcm/notification_spec.rb +9 -3
- data/spec/unit/push_spec.rb +28 -0
- data/spec/unit/reflection_spec.rb +34 -0
- data/spec/unit_spec_helper.rb +4 -62
- metadata +22 -5
- data/lib/rapns/gcm/payload_size_validator.rb +0 -13
data/CHANGELOG.md
CHANGED
@@ -1,7 +1,15 @@
|
|
1
|
-
## 3.0
|
1
|
+
## 3.1.0 (Jan 26, 2013)
|
2
|
+
* Rapns.reflect API for fine-grained introspection.
|
3
|
+
* Rapns.embed API for embedding Rapns into an existing process.
|
4
|
+
* Rapns.push API for using Rapns in scheduled jobs.
|
5
|
+
* Fix issue with integration with ActiveScaffold (#98) (@jeffarena).
|
6
|
+
* Fix content-available setter for APNs (#95) (@dup2).
|
7
|
+
* GCM validation fixes (#96) (@DianthuDia).
|
8
|
+
|
9
|
+
## 3.0.1 (Dec 16, 2012)
|
2
10
|
* Fix compatibility with Rails 3.0.x. Fixes #89.
|
3
11
|
|
4
|
-
## 3.0.0 (
|
12
|
+
## 3.0.0 (Dec 15, 2012)
|
5
13
|
* Add support for Google Cloud Messaging.
|
6
14
|
* Fix Heroku logging issue.
|
7
15
|
|
data/README.md
CHANGED
@@ -1,14 +1,18 @@
|
|
1
1
|
[](http://travis-ci.org/ileitch/rapns)
|
2
2
|
|
3
|
-
### Rapns - Professional grade APNs and GCM
|
3
|
+
### Rapns - Professional grade APNs and GCM for Ruby.
|
4
4
|
|
5
5
|
* Supports both APNs (iOS) and GCM (Google Cloud Messaging, Android).
|
6
6
|
* Seamless Rails integration.
|
7
7
|
* Scalable - choose the number of threads each app spawns.
|
8
8
|
* Designed for uptime - signal -HUP to add, update apps.
|
9
9
|
* Stable - reconnects database and network connections when lost.
|
10
|
+
* Run as a daemon or inside an existing process.
|
11
|
+
* Use in a scheduler for low-workload deployments ([Push API](rapns/wiki/Push-API)).
|
12
|
+
* Reflection API for fine-grained instrumentation ([Reflection API](rapns/wiki/Relfection-API)).
|
10
13
|
* Works with MRI, JRuby, Rubinius 1.8 and 1.9.
|
11
14
|
* [Airbrake](http://airbrakeapp.com/) integration.
|
15
|
+
* Built with a love for Open Source :)
|
12
16
|
|
13
17
|
#### 2.x users please read [upgrading from 2.x to 3.0](rapns/wiki/Upgrading-from-version-2.x-to-3.0)
|
14
18
|
|
@@ -36,11 +40,16 @@ Generate the migrations, rapns.yml and migrate:
|
|
36
40
|
3. Select both the certificate and private key.
|
37
41
|
4. Right click and select `Export 2 items...`.
|
38
42
|
5. Save the file as `cert.p12`, make sure the File Format is `Personal Information Exchange (p12)`.
|
39
|
-
6.
|
40
|
-
|
43
|
+
6. Convert the certificate to a .pem, where `<environment>` should be `development` or `production`, depending on the certificate you exported.
|
44
|
+
|
45
|
+
Without a password:
|
41
46
|
|
42
47
|
`openssl pkcs12 -nodes -clcerts -in cert.p12 -out <environment>.pem`
|
43
48
|
|
49
|
+
With a password:
|
50
|
+
|
51
|
+
`openssl pkcs12 -clcerts -in cert.p12 -out <environment>.pem`
|
52
|
+
|
44
53
|
## Create an App
|
45
54
|
|
46
55
|
#### APNs
|
@@ -86,9 +95,17 @@ n.save!
|
|
86
95
|
|
87
96
|
## Starting Rapns
|
88
97
|
|
98
|
+
As a daemon:
|
99
|
+
|
89
100
|
cd /path/to/rails/app
|
90
101
|
rapns <Rails environment> [options]
|
91
102
|
|
103
|
+
Inside an existing process:
|
104
|
+
|
105
|
+
Rapns.embed
|
106
|
+
|
107
|
+
*Please note that only ever a single instance of Rapns should be running.*
|
108
|
+
|
92
109
|
See [Configuration](rapns/wiki/Configuration) for a list of options, or run `rapns --help`.
|
93
110
|
|
94
111
|
## Updating Rapns
|
@@ -102,6 +119,9 @@ After updating you should run `rails g rapns` to check for any new migrations.
|
|
102
119
|
* [Upgrading from 2.x to 3.0](rapns/wiki/Upgrading-from-version-2.x-to-3.0)
|
103
120
|
* [Deploying to Heroku](rapns/wiki/Heroku)
|
104
121
|
* [Hot App Updates](rapns/wiki/Hot-App-Updates)
|
122
|
+
* [Reflection API](rapns/wiki/Reflection-API)
|
123
|
+
* [Push API](rapns/wiki/Push-API)
|
124
|
+
* [Embedding API](rapns/wiki/Embedding-API)
|
105
125
|
|
106
126
|
### APNs
|
107
127
|
* [Advanced APNs Features](rapns/wiki/Advanced-APNs-Features)
|
@@ -138,3 +158,6 @@ Thank you to the following wonderful people for contributing:
|
|
138
158
|
* [@adorr](https://github.com/adorr)
|
139
159
|
* [@mattconnolly](https://github.com/mattconnolly)
|
140
160
|
* [@emeitch](https://github.com/emeitch)
|
161
|
+
* [@jeffarena](https://github.com/jeffarena)
|
162
|
+
* [@DianthuDia](https://github.com/DianthuDia)
|
163
|
+
* [@dup2](https://github.com/dup2)
|
data/bin/rapns
CHANGED
@@ -11,9 +11,7 @@ if environment.nil? || environment =~ /^-/
|
|
11
11
|
exit 1
|
12
12
|
end
|
13
13
|
|
14
|
-
|
15
|
-
class CommandlineConfig < Struct.new(*Rapns::CONFIG_ATTRS); end
|
16
|
-
config = CommandlineConfig.new
|
14
|
+
config = Rapns::ConfigurationWithoutDefaults.new
|
17
15
|
|
18
16
|
ARGV.options do |opts|
|
19
17
|
opts.banner = banner
|
@@ -34,8 +32,5 @@ load 'config/environment.rb'
|
|
34
32
|
load 'config/initializers/rapns.rb' if File.exist?('config/initializers/rapns.rb')
|
35
33
|
|
36
34
|
Rapns.config.update(config)
|
37
|
-
|
38
|
-
require 'rapns/daemon'
|
39
|
-
require 'rapns/patches'
|
40
|
-
|
35
|
+
Rapns.require_for_daemon
|
41
36
|
Rapns::Daemon.start
|
@@ -34,7 +34,9 @@ class AddGcm < ActiveRecord::Migration
|
|
34
34
|
|
35
35
|
add_column :rapns_notifications, :collapse_key, :string, :null => true
|
36
36
|
add_column :rapns_notifications, :delay_while_idle, :boolean, :null => false, :default => false
|
37
|
-
|
37
|
+
|
38
|
+
reg_ids_type = ActiveRecord::Base.connection.adapter_name.include?('Mysql') ? :mediumtext : :text
|
39
|
+
add_column :rapns_notifications, :registration_ids, reg_ids_type, :null => true
|
38
40
|
add_column :rapns_notifications, :app_id, :integer, :null => true
|
39
41
|
add_column :rapns_notifications, :retries, :integer, :null => true, :default => 0
|
40
42
|
|
@@ -23,17 +23,48 @@
|
|
23
23
|
# Path to write PID file. Relative to Rails root unless absolute.
|
24
24
|
# config.pid_file = '/path/to/rapns.pid'
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
|
26
|
+
end
|
27
|
+
|
28
|
+
Rapns.reflect do |on|
|
29
|
+
|
30
|
+
# Called with a Rapns::Apns::Feedback instance when feedback is received
|
31
|
+
# from the APNs that a notification has failed to be delivered.
|
32
|
+
# Further notifications should not be sent to the device.
|
33
|
+
# on.apns_feedback do |feedback|
|
34
|
+
# end
|
35
|
+
|
36
|
+
# Called when a notification is queued internally for delivery.
|
37
|
+
# The internal queue for each app runner can be inspected:
|
29
38
|
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
# end
|
39
|
+
# Rapns::Daemon::AppRunner.runners.each do |app_id, runner|
|
40
|
+
# runner.app
|
41
|
+
# runner.queue_size
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# on.notification_enqueued do |notification|
|
37
45
|
# end
|
38
46
|
|
39
|
-
|
47
|
+
# Called when a notification is successfully delivered.
|
48
|
+
# on.notification_delivered do |notification|
|
49
|
+
# end
|
50
|
+
|
51
|
+
# Called when notification delivery failed.
|
52
|
+
# Call 'error_code' and 'error_description' on the notification for the cause.
|
53
|
+
# on.notification_failed do |notification|
|
54
|
+
# end
|
55
|
+
|
56
|
+
# Called when a notification will be retried at a later date.
|
57
|
+
# Call 'deliver_after' on the notification for the next delivery date
|
58
|
+
# and 'retries' for the number of times this notification has been retried.
|
59
|
+
# on.notification_will_retry do |notification|
|
60
|
+
# end
|
61
|
+
|
62
|
+
# Called when an APNs connection is lost and will be reconnected.
|
63
|
+
# on.apns_connection_lost do |app, error|
|
64
|
+
# end
|
65
|
+
|
66
|
+
# Called when an exception is raised.
|
67
|
+
# on.error do |error|
|
68
|
+
# end
|
69
|
+
|
70
|
+
end
|
@@ -43,13 +43,13 @@ module Rapns
|
|
43
43
|
|
44
44
|
MDM_KEY = '__rapns_mdm__'
|
45
45
|
def mdm=(magic)
|
46
|
-
self.attributes_for_device = { MDM_KEY => magic }
|
46
|
+
self.attributes_for_device = (attributes_for_device || {}).merge({ MDM_KEY => magic })
|
47
47
|
end
|
48
48
|
|
49
49
|
CONTENT_AVAILABLE_KEY = '__rapns_content_available__'
|
50
50
|
def content_available=(bool)
|
51
51
|
return unless bool
|
52
|
-
self.attributes_for_device = { CONTENT_AVAILABLE_KEY => true }
|
52
|
+
self.attributes_for_device = (attributes_for_device || {}).merge({ CONTENT_AVAILABLE_KEY => true })
|
53
53
|
end
|
54
54
|
|
55
55
|
def as_json
|
@@ -81,6 +81,12 @@ module Rapns
|
|
81
81
|
[1, id_for_pack, expiry, 0, 32, device_token, payload_size, payload].pack("cNNccH*na*")
|
82
82
|
end
|
83
83
|
|
84
|
+
def data=(attrs)
|
85
|
+
return unless attrs
|
86
|
+
raise ArgumentError, "must be a Hash" if !attrs.is_a?(Hash)
|
87
|
+
super attrs.merge(data || {})
|
88
|
+
end
|
89
|
+
|
84
90
|
end
|
85
91
|
end
|
86
92
|
end
|
data/lib/rapns/app.rb
CHANGED
@@ -4,7 +4,7 @@ module Rapns
|
|
4
4
|
|
5
5
|
attr_accessible :name, :environment, :certificate, :password, :connections, :auth_key
|
6
6
|
|
7
|
-
has_many :notifications
|
7
|
+
has_many :notifications, :class_name => 'Rapns::Notification'
|
8
8
|
|
9
9
|
validates :name, :presence => true, :uniqueness => { :scope => [:type, :environment] }
|
10
10
|
validates_numericality_of :connections, :greater_than => 0, :only_integer => true
|
@@ -16,8 +16,8 @@ module Rapns
|
|
16
16
|
def certificate_has_matching_private_key
|
17
17
|
result = false
|
18
18
|
if certificate.present?
|
19
|
-
x509 = OpenSSL::X509::Certificate.new
|
20
|
-
pkey = OpenSSL::PKey::RSA.new
|
19
|
+
x509 = OpenSSL::X509::Certificate.new(certificate) rescue nil
|
20
|
+
pkey = OpenSSL::PKey::RSA.new(certificate, password) rescue nil
|
21
21
|
result = !x509.nil? && !pkey.nil?
|
22
22
|
unless result
|
23
23
|
errors.add :certificate, 'Certificate value must contain a certificate and a private key.'
|
data/lib/rapns/configuration.rb
CHANGED
@@ -7,21 +7,21 @@ module Rapns
|
|
7
7
|
yield config if block_given?
|
8
8
|
end
|
9
9
|
|
10
|
-
CONFIG_ATTRS = [:foreground, :push_poll, :feedback_poll,
|
11
|
-
:airbrake_notify, :check_for_errors, :pid_file, :batch_size
|
10
|
+
CONFIG_ATTRS = [:foreground, :push_poll, :feedback_poll, :embedded,
|
11
|
+
:airbrake_notify, :check_for_errors, :pid_file, :batch_size,
|
12
|
+
:push]
|
13
|
+
|
14
|
+
class ConfigurationWithoutDefaults < Struct.new(*CONFIG_ATTRS)
|
15
|
+
end
|
12
16
|
|
13
17
|
class Configuration < Struct.new(*CONFIG_ATTRS)
|
18
|
+
include Deprecatable
|
19
|
+
|
14
20
|
attr_accessor :apns_feedback_callback
|
15
21
|
|
16
22
|
def initialize
|
17
23
|
super
|
18
|
-
|
19
|
-
self.foreground = false
|
20
|
-
self.push_poll = 2
|
21
|
-
self.feedback_poll = 60
|
22
|
-
self.airbrake_notify = true
|
23
|
-
self.check_for_errors = true
|
24
|
-
self.batch_size = 5000
|
24
|
+
set_defaults
|
25
25
|
end
|
26
26
|
|
27
27
|
def update(other)
|
@@ -31,10 +31,6 @@ module Rapns
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
def on_apns_feedback(&block)
|
35
|
-
self.apns_feedback_callback = block
|
36
|
-
end
|
37
|
-
|
38
34
|
def pid_file=(path)
|
39
35
|
if path && !Pathname.new(path).absolute?
|
40
36
|
super(File.join(Rails.root, path))
|
@@ -42,5 +38,42 @@ module Rapns
|
|
42
38
|
super
|
43
39
|
end
|
44
40
|
end
|
41
|
+
|
42
|
+
def foreground=(bool)
|
43
|
+
if defined? JRUBY_VERSION
|
44
|
+
# The JVM does not support fork().
|
45
|
+
super(true)
|
46
|
+
else
|
47
|
+
super
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def on_apns_feedback(&block)
|
52
|
+
self.apns_feedback_callback = block
|
53
|
+
end
|
54
|
+
deprecated(:on_apns_feedback, 3.2, "Please use the Rapns.reflect API instead.")
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def set_defaults
|
59
|
+
if defined? JRUBY_VERSION
|
60
|
+
# The JVM does not support fork().
|
61
|
+
self.foreground = true
|
62
|
+
else
|
63
|
+
self.foreground = false
|
64
|
+
end
|
65
|
+
|
66
|
+
self.push_poll = 2
|
67
|
+
self.feedback_poll = 60
|
68
|
+
self.airbrake_notify = true
|
69
|
+
self.check_for_errors = true
|
70
|
+
self.batch_size = 5000
|
71
|
+
self.pid_file = nil
|
72
|
+
self.apns_feedback_callback = nil
|
73
|
+
|
74
|
+
# Internal options.
|
75
|
+
self.embedded = false
|
76
|
+
self.push = false
|
77
|
+
end
|
45
78
|
end
|
46
79
|
end
|
@@ -4,18 +4,20 @@ module Rapns
|
|
4
4
|
class ConnectionError < StandardError; end
|
5
5
|
|
6
6
|
class Connection
|
7
|
+
include Reflectable
|
8
|
+
|
7
9
|
attr_accessor :last_write
|
8
10
|
|
9
11
|
def self.idle_period
|
10
12
|
30.minutes
|
11
13
|
end
|
12
14
|
|
13
|
-
def initialize(
|
14
|
-
@
|
15
|
+
def initialize(app, host, port)
|
16
|
+
@app = app
|
15
17
|
@host = host
|
16
18
|
@port = port
|
17
|
-
@certificate = certificate
|
18
|
-
@password = password
|
19
|
+
@certificate = app.certificate
|
20
|
+
@password = app.password
|
19
21
|
written
|
20
22
|
end
|
21
23
|
|
@@ -51,7 +53,8 @@ module Rapns
|
|
51
53
|
retry_count += 1;
|
52
54
|
|
53
55
|
if retry_count == 1
|
54
|
-
Rapns::Daemon.logger.error("[#{@name}] Lost connection to #{@host}:#{@port} (#{e.class.name}), reconnecting...")
|
56
|
+
Rapns::Daemon.logger.error("[#{@app.name}] Lost connection to #{@host}:#{@port} (#{e.class.name}), reconnecting...")
|
57
|
+
reflect(:apns_connection_lost, @app, e)
|
55
58
|
end
|
56
59
|
|
57
60
|
if retry_count <= 3
|
@@ -59,7 +62,7 @@ module Rapns
|
|
59
62
|
sleep 1
|
60
63
|
retry
|
61
64
|
else
|
62
|
-
raise ConnectionError, "#{@name} tried #{retry_count-1} times to reconnect but failed (#{e.class.name})."
|
65
|
+
raise ConnectionError, "#{@app.name} tried #{retry_count-1} times to reconnect but failed (#{e.class.name})."
|
63
66
|
end
|
64
67
|
end
|
65
68
|
end
|
@@ -72,7 +75,7 @@ module Rapns
|
|
72
75
|
protected
|
73
76
|
|
74
77
|
def reconnect_idle
|
75
|
-
Rapns::Daemon.logger.info("[#{@name}] Idle period exceeded, reconnecting...")
|
78
|
+
Rapns::Daemon.logger.info("[#{@app.name}] Idle period exceeded, reconnecting...")
|
76
79
|
reconnect
|
77
80
|
end
|
78
81
|
|
@@ -104,10 +107,10 @@ module Rapns
|
|
104
107
|
ssl_socket = OpenSSL::SSL::SSLSocket.new(tcp_socket, @ssl_context)
|
105
108
|
ssl_socket.sync = true
|
106
109
|
ssl_socket.connect
|
107
|
-
Rapns::Daemon.logger.info("[#{@name}] Connected to #{@host}:#{@port}")
|
110
|
+
Rapns::Daemon.logger.info("[#{@app.name}] Connected to #{@host}:#{@port}")
|
108
111
|
[tcp_socket, ssl_socket]
|
109
112
|
end
|
110
113
|
end
|
111
114
|
end
|
112
115
|
end
|
113
|
-
end
|
116
|
+
end
|
@@ -4,7 +4,7 @@ module Rapns
|
|
4
4
|
class DeliveryHandler < Rapns::Daemon::DeliveryHandler
|
5
5
|
def initialize(app, host, port)
|
6
6
|
@app = app
|
7
|
-
@connection = Connection.new(@app
|
7
|
+
@connection = Connection.new(@app, host, port)
|
8
8
|
@connection.connect
|
9
9
|
end
|
10
10
|
|
@@ -2,6 +2,7 @@ module Rapns
|
|
2
2
|
module Daemon
|
3
3
|
module Apns
|
4
4
|
class FeedbackReceiver
|
5
|
+
include Reflectable
|
5
6
|
include InterruptibleSleep
|
6
7
|
include DatabaseReconnectable
|
7
8
|
|
@@ -35,7 +36,7 @@ module Rapns
|
|
35
36
|
def check_for_feedback
|
36
37
|
connection = nil
|
37
38
|
begin
|
38
|
-
connection = Connection.new(
|
39
|
+
connection = Connection.new(@app, @host, @port)
|
39
40
|
connection.connect
|
40
41
|
|
41
42
|
while tuple = connection.read(FEEDBACK_TUPLE_BYTES)
|
@@ -59,8 +60,11 @@ module Rapns
|
|
59
60
|
def create_feedback(failed_at, device_token)
|
60
61
|
formatted_failed_at = failed_at.strftime("%Y-%m-%d %H:%M:%S UTC")
|
61
62
|
with_database_reconnect_and_retry do
|
62
|
-
Rapns::Daemon.logger.info("[
|
63
|
+
Rapns::Daemon.logger.info("[#{@app.name}] [FeedbackReceiver] Delivery failed at #{formatted_failed_at} for #{device_token}.")
|
63
64
|
feedback = Rapns::Apns::Feedback.create!(:failed_at => failed_at, :device_token => device_token, :app => @app)
|
65
|
+
reflect(:apns_feedback, feedback)
|
66
|
+
|
67
|
+
# Deprecated.
|
64
68
|
begin
|
65
69
|
Rapns.config.apns_feedback_callback.call(feedback) if Rapns.config.apns_feedback_callback
|
66
70
|
rescue StandardError => e
|
@@ -2,7 +2,7 @@ module Rapns
|
|
2
2
|
module Daemon
|
3
3
|
class AppRunner
|
4
4
|
class << self
|
5
|
-
attr_reader :runners
|
5
|
+
attr_reader :runners
|
6
6
|
end
|
7
7
|
|
8
8
|
@runners = {}
|
@@ -86,20 +86,28 @@ module Rapns
|
|
86
86
|
diff = handlers.size - app.connections
|
87
87
|
return if diff == 0
|
88
88
|
if diff > 0
|
89
|
-
diff.times {
|
90
|
-
Rapns::Daemon.logger.info("[#{app.name}]
|
89
|
+
diff.times { decrement_handlers }
|
90
|
+
Rapns::Daemon.logger.info("[#{app.name}] Stopped #{handlers_str(diff)}. #{handlers_str} remaining.")
|
91
91
|
else
|
92
|
-
diff.abs.times {
|
93
|
-
Rapns::Daemon.logger.info("[#{app.name}]
|
92
|
+
diff.abs.times { increment_handlers }
|
93
|
+
Rapns::Daemon.logger.info("[#{app.name}] Started #{handlers_str(diff)}. #{handlers_str} remaining.")
|
94
94
|
end
|
95
95
|
end
|
96
96
|
|
97
|
+
def decrement_handlers
|
98
|
+
handlers.pop.stop
|
99
|
+
end
|
100
|
+
|
101
|
+
def increment_handlers
|
102
|
+
handlers << start_handler
|
103
|
+
end
|
104
|
+
|
97
105
|
def debug
|
98
106
|
Rapns::Daemon.logger.info <<-EOS
|
99
107
|
|
100
108
|
#{@app.name}:
|
101
|
-
handlers: #{
|
102
|
-
queued: #{
|
109
|
+
handlers: #{num_handlers}
|
110
|
+
queued: #{queue_size}
|
103
111
|
idle: #{idle?}
|
104
112
|
EOS
|
105
113
|
end
|
@@ -108,6 +116,14 @@ module Rapns
|
|
108
116
|
queue.notifications_processed?
|
109
117
|
end
|
110
118
|
|
119
|
+
def queue_size
|
120
|
+
queue.size
|
121
|
+
end
|
122
|
+
|
123
|
+
def num_handlers
|
124
|
+
handlers.size
|
125
|
+
end
|
126
|
+
|
111
127
|
protected
|
112
128
|
|
113
129
|
def start_handler
|
@@ -2,6 +2,7 @@ module Rapns
|
|
2
2
|
module Daemon
|
3
3
|
class Delivery
|
4
4
|
include DatabaseReconnectable
|
5
|
+
include Reflectable
|
5
6
|
|
6
7
|
def self.perform(*args)
|
7
8
|
new(*args).perform
|
@@ -13,6 +14,7 @@ module Rapns
|
|
13
14
|
notification.deliver_after = deliver_after
|
14
15
|
notification.save!(:validate => false)
|
15
16
|
end
|
17
|
+
reflect(:notification_will_retry, notification)
|
16
18
|
end
|
17
19
|
|
18
20
|
def retry_exponentially(notification)
|
@@ -25,6 +27,7 @@ module Rapns
|
|
25
27
|
@notification.delivered_at = Time.now
|
26
28
|
@notification.save!(:validate => false)
|
27
29
|
end
|
30
|
+
reflect(:notification_delivered, @notification)
|
28
31
|
end
|
29
32
|
|
30
33
|
def mark_failed(code, description)
|
@@ -37,7 +40,8 @@ module Rapns
|
|
37
40
|
@notification.error_description = description
|
38
41
|
@notification.save!(:validate => false)
|
39
42
|
end
|
43
|
+
reflect(:notification_failed, @notification)
|
40
44
|
end
|
41
45
|
end
|
42
46
|
end
|
43
|
-
end
|
47
|
+
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module Rapns
|
2
2
|
module Daemon
|
3
3
|
class DeliveryHandler
|
4
|
+
include Reflectable
|
5
|
+
|
4
6
|
attr_accessor :queue
|
5
7
|
|
6
8
|
def start
|
@@ -35,8 +37,10 @@ module Rapns
|
|
35
37
|
|
36
38
|
begin
|
37
39
|
deliver(notification)
|
40
|
+
reflect(:notification_delivered, notification)
|
38
41
|
rescue StandardError => e
|
39
42
|
Rapns::Daemon.logger.error(e)
|
43
|
+
reflect(:error, e)
|
40
44
|
ensure
|
41
45
|
queue.notification_processed
|
42
46
|
end
|
data/lib/rapns/daemon/feeder.rb
CHANGED
@@ -3,12 +3,17 @@ module Rapns
|
|
3
3
|
class Feeder
|
4
4
|
extend InterruptibleSleep
|
5
5
|
extend DatabaseReconnectable
|
6
|
+
extend Reflectable
|
6
7
|
|
7
|
-
def self.start
|
8
|
-
|
8
|
+
def self.start
|
9
|
+
@stop = false
|
10
|
+
|
11
|
+
if Rapns.config.embedded
|
12
|
+
Thread.new { feed_forever }
|
13
|
+
elsif Rapns.config.push
|
9
14
|
enqueue_notifications
|
10
|
-
|
11
|
-
|
15
|
+
else
|
16
|
+
feed_forever
|
12
17
|
end
|
13
18
|
end
|
14
19
|
|
@@ -19,17 +24,33 @@ module Rapns
|
|
19
24
|
|
20
25
|
protected
|
21
26
|
|
27
|
+
def self.feed_forever
|
28
|
+
loop do
|
29
|
+
enqueue_notifications
|
30
|
+
interruptible_sleep(Rapns.config.push_poll)
|
31
|
+
break if stop?
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.stop?
|
36
|
+
@stop
|
37
|
+
end
|
38
|
+
|
22
39
|
def self.enqueue_notifications
|
23
40
|
begin
|
24
41
|
with_database_reconnect_and_retry do
|
25
42
|
batch_size = Rapns.config.batch_size
|
26
43
|
idle = Rapns::Daemon::AppRunner.idle.map(&:app)
|
27
|
-
Rapns::Notification.ready_for_delivery.for_apps(idle)
|
44
|
+
relation = Rapns::Notification.ready_for_delivery.for_apps(idle)
|
45
|
+
relation = relation.limit(batch_size) unless Rapns.config.push
|
46
|
+
relation.each do |notification|
|
28
47
|
Rapns::Daemon::AppRunner.enqueue(notification)
|
48
|
+
reflect(:notification_enqueued, notification)
|
29
49
|
end
|
30
50
|
end
|
31
51
|
rescue StandardError => e
|
32
52
|
Rapns::Daemon.logger.error(e)
|
53
|
+
reflect(:error, e)
|
33
54
|
end
|
34
55
|
end
|
35
56
|
end
|