rpush 1.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 +7 -0
- data/CHANGELOG.md +99 -0
- data/LICENSE +7 -0
- data/README.md +189 -0
- data/bin/rpush +36 -0
- data/config/database.yml +44 -0
- data/lib/generators/rpush_generator.rb +44 -0
- data/lib/generators/templates/add_adm.rb +23 -0
- data/lib/generators/templates/add_alert_is_json_to_rapns_notifications.rb +9 -0
- data/lib/generators/templates/add_app_to_rapns.rb +11 -0
- data/lib/generators/templates/add_fail_after_to_rpush_notifications.rb +9 -0
- data/lib/generators/templates/add_gcm.rb +102 -0
- data/lib/generators/templates/add_rpush.rb +349 -0
- data/lib/generators/templates/add_wpns.rb +16 -0
- data/lib/generators/templates/create_rapns_apps.rb +16 -0
- data/lib/generators/templates/create_rapns_feedback.rb +18 -0
- data/lib/generators/templates/create_rapns_notifications.rb +29 -0
- data/lib/generators/templates/rename_rapns_to_rpush.rb +63 -0
- data/lib/generators/templates/rpush.rb +104 -0
- data/lib/rpush.rb +62 -0
- data/lib/rpush/TODO +3 -0
- data/lib/rpush/adm/app.rb +15 -0
- data/lib/rpush/adm/data_validator.rb +11 -0
- data/lib/rpush/adm/notification.rb +29 -0
- data/lib/rpush/apns/app.rb +29 -0
- data/lib/rpush/apns/binary_notification_validator.rb +12 -0
- data/lib/rpush/apns/device_token_format_validator.rb +12 -0
- data/lib/rpush/apns/feedback.rb +16 -0
- data/lib/rpush/apns/notification.rb +84 -0
- data/lib/rpush/apns_feedback.rb +13 -0
- data/lib/rpush/app.rb +18 -0
- data/lib/rpush/configuration.rb +75 -0
- data/lib/rpush/daemon.rb +140 -0
- data/lib/rpush/daemon/adm.rb +9 -0
- data/lib/rpush/daemon/adm/delivery.rb +222 -0
- data/lib/rpush/daemon/apns.rb +16 -0
- data/lib/rpush/daemon/apns/certificate_expired_error.rb +20 -0
- data/lib/rpush/daemon/apns/delivery.rb +64 -0
- data/lib/rpush/daemon/apns/disconnection_error.rb +20 -0
- data/lib/rpush/daemon/apns/feedback_receiver.rb +79 -0
- data/lib/rpush/daemon/app_runner.rb +187 -0
- data/lib/rpush/daemon/batch.rb +115 -0
- data/lib/rpush/daemon/constants.rb +59 -0
- data/lib/rpush/daemon/delivery.rb +28 -0
- data/lib/rpush/daemon/delivery_error.rb +19 -0
- data/lib/rpush/daemon/dispatcher/http.rb +21 -0
- data/lib/rpush/daemon/dispatcher/tcp.rb +30 -0
- data/lib/rpush/daemon/dispatcher_loop.rb +54 -0
- data/lib/rpush/daemon/dispatcher_loop_collection.rb +33 -0
- data/lib/rpush/daemon/feeder.rb +68 -0
- data/lib/rpush/daemon/gcm.rb +9 -0
- data/lib/rpush/daemon/gcm/delivery.rb +222 -0
- data/lib/rpush/daemon/interruptible_sleep.rb +61 -0
- data/lib/rpush/daemon/loggable.rb +31 -0
- data/lib/rpush/daemon/reflectable.rb +13 -0
- data/lib/rpush/daemon/retry_header_parser.rb +23 -0
- data/lib/rpush/daemon/retryable_error.rb +20 -0
- data/lib/rpush/daemon/service_config_methods.rb +33 -0
- data/lib/rpush/daemon/store/active_record.rb +154 -0
- data/lib/rpush/daemon/store/active_record/reconnectable.rb +68 -0
- data/lib/rpush/daemon/tcp_connection.rb +143 -0
- data/lib/rpush/daemon/too_many_requests_error.rb +20 -0
- data/lib/rpush/daemon/wpns.rb +9 -0
- data/lib/rpush/daemon/wpns/delivery.rb +132 -0
- data/lib/rpush/deprecatable.rb +23 -0
- data/lib/rpush/deprecation.rb +23 -0
- data/lib/rpush/embed.rb +28 -0
- data/lib/rpush/gcm/app.rb +11 -0
- data/lib/rpush/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +11 -0
- data/lib/rpush/gcm/notification.rb +30 -0
- data/lib/rpush/logger.rb +63 -0
- data/lib/rpush/multi_json_helper.rb +16 -0
- data/lib/rpush/notification.rb +69 -0
- data/lib/rpush/notifier.rb +52 -0
- data/lib/rpush/payload_data_size_validator.rb +10 -0
- data/lib/rpush/push.rb +16 -0
- data/lib/rpush/railtie.rb +11 -0
- data/lib/rpush/reflection.rb +58 -0
- data/lib/rpush/registration_ids_count_validator.rb +10 -0
- data/lib/rpush/version.rb +3 -0
- data/lib/rpush/wpns/app.rb +9 -0
- data/lib/rpush/wpns/notification.rb +26 -0
- data/lib/tasks/cane.rake +18 -0
- data/lib/tasks/rpush.rake +16 -0
- data/lib/tasks/test.rake +38 -0
- data/spec/functional/adm_spec.rb +43 -0
- data/spec/functional/apns_spec.rb +58 -0
- data/spec/functional/embed_spec.rb +49 -0
- data/spec/functional/gcm_spec.rb +42 -0
- data/spec/functional/wpns_spec.rb +41 -0
- data/spec/support/cert_with_password.pem +90 -0
- data/spec/support/cert_without_password.pem +59 -0
- data/spec/support/install.sh +32 -0
- data/spec/support/simplecov_helper.rb +20 -0
- data/spec/support/simplecov_quality_formatter.rb +8 -0
- data/spec/tmp/.gitkeep +0 -0
- data/spec/unit/adm/app_spec.rb +58 -0
- data/spec/unit/adm/notification_spec.rb +45 -0
- data/spec/unit/apns/app_spec.rb +29 -0
- data/spec/unit/apns/feedback_spec.rb +9 -0
- data/spec/unit/apns/notification_spec.rb +208 -0
- data/spec/unit/apns_feedback_spec.rb +21 -0
- data/spec/unit/app_spec.rb +30 -0
- data/spec/unit/configuration_spec.rb +45 -0
- data/spec/unit/daemon/adm/delivery_spec.rb +243 -0
- data/spec/unit/daemon/apns/certificate_expired_error_spec.rb +11 -0
- data/spec/unit/daemon/apns/delivery_spec.rb +101 -0
- data/spec/unit/daemon/apns/disconnection_error_spec.rb +18 -0
- data/spec/unit/daemon/apns/feedback_receiver_spec.rb +117 -0
- data/spec/unit/daemon/app_runner_spec.rb +292 -0
- data/spec/unit/daemon/batch_spec.rb +232 -0
- data/spec/unit/daemon/delivery_error_spec.rb +13 -0
- data/spec/unit/daemon/delivery_spec.rb +38 -0
- data/spec/unit/daemon/dispatcher/http_spec.rb +33 -0
- data/spec/unit/daemon/dispatcher/tcp_spec.rb +38 -0
- data/spec/unit/daemon/dispatcher_loop_collection_spec.rb +37 -0
- data/spec/unit/daemon/dispatcher_loop_spec.rb +71 -0
- data/spec/unit/daemon/feeder_spec.rb +98 -0
- data/spec/unit/daemon/gcm/delivery_spec.rb +310 -0
- data/spec/unit/daemon/interruptible_sleep_spec.rb +68 -0
- data/spec/unit/daemon/reflectable_spec.rb +27 -0
- data/spec/unit/daemon/retryable_error_spec.rb +14 -0
- data/spec/unit/daemon/service_config_methods_spec.rb +33 -0
- data/spec/unit/daemon/store/active_record/reconnectable_spec.rb +114 -0
- data/spec/unit/daemon/store/active_record_spec.rb +357 -0
- data/spec/unit/daemon/tcp_connection_spec.rb +287 -0
- data/spec/unit/daemon/too_many_requests_error_spec.rb +14 -0
- data/spec/unit/daemon/wpns/delivery_spec.rb +159 -0
- data/spec/unit/daemon_spec.rb +159 -0
- data/spec/unit/deprecatable_spec.rb +32 -0
- data/spec/unit/deprecation_spec.rb +15 -0
- data/spec/unit/embed_spec.rb +50 -0
- data/spec/unit/gcm/app_spec.rb +4 -0
- data/spec/unit/gcm/notification_spec.rb +36 -0
- data/spec/unit/logger_spec.rb +127 -0
- data/spec/unit/notification_shared.rb +105 -0
- data/spec/unit/notification_spec.rb +15 -0
- data/spec/unit/notifier_spec.rb +49 -0
- data/spec/unit/push_spec.rb +43 -0
- data/spec/unit/reflection_spec.rb +30 -0
- data/spec/unit/rpush_spec.rb +9 -0
- data/spec/unit/wpns/app_spec.rb +4 -0
- data/spec/unit/wpns/notification_spec.rb +30 -0
- data/spec/unit_spec_helper.rb +101 -0
- metadata +276 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Rpush
|
|
2
|
+
module Daemon
|
|
3
|
+
module Apns
|
|
4
|
+
extend ServiceConfigMethods
|
|
5
|
+
|
|
6
|
+
HOSTS = {
|
|
7
|
+
:production => ['gateway.push.apple.com', 2195],
|
|
8
|
+
:development => ['gateway.sandbox.push.apple.com', 2195], # deprecated
|
|
9
|
+
:sandbox => ['gateway.sandbox.push.apple.com', 2195]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
dispatcher :tcp, :host => Proc.new { |app| HOSTS[app.environment.to_sym] }
|
|
13
|
+
loops Rpush::Daemon::Apns::FeedbackReceiver
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Rpush
|
|
2
|
+
module Apns
|
|
3
|
+
class CertificateExpiredError < StandardError
|
|
4
|
+
attr_reader :app, :time
|
|
5
|
+
|
|
6
|
+
def initialize(app, time)
|
|
7
|
+
@app = app
|
|
8
|
+
@time = time
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def to_s
|
|
12
|
+
message
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def message
|
|
16
|
+
"#{app.name} certificate expired at #{time}."
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
module Rpush
|
|
2
|
+
module Daemon
|
|
3
|
+
module Apns
|
|
4
|
+
class Delivery < Rpush::Daemon::Delivery
|
|
5
|
+
SELECT_TIMEOUT = 0.2
|
|
6
|
+
ERROR_TUPLE_BYTES = 6
|
|
7
|
+
APN_ERRORS = {
|
|
8
|
+
1 => "Processing error",
|
|
9
|
+
2 => "Missing device token",
|
|
10
|
+
3 => "Missing topic",
|
|
11
|
+
4 => "Missing payload",
|
|
12
|
+
5 => "Missing token size",
|
|
13
|
+
6 => "Missing topic size",
|
|
14
|
+
7 => "Missing payload size",
|
|
15
|
+
8 => "Invalid token",
|
|
16
|
+
255 => "None (unknown error)"
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
def initialize(app, conneciton, notification, batch)
|
|
20
|
+
@app = app
|
|
21
|
+
@connection = conneciton
|
|
22
|
+
@notification = notification
|
|
23
|
+
@batch = batch
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def perform
|
|
27
|
+
begin
|
|
28
|
+
@connection.write(@notification.to_binary)
|
|
29
|
+
check_for_error if Rpush.config.check_for_errors
|
|
30
|
+
mark_delivered
|
|
31
|
+
log_info("#{@notification.id} sent to #{@notification.device_token}")
|
|
32
|
+
rescue Rpush::DeliveryError, Rpush::Apns::DisconnectionError => error
|
|
33
|
+
mark_failed(error.code, error.description)
|
|
34
|
+
raise
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
protected
|
|
39
|
+
|
|
40
|
+
def check_for_error
|
|
41
|
+
if @connection.select(SELECT_TIMEOUT)
|
|
42
|
+
error = nil
|
|
43
|
+
|
|
44
|
+
if tuple = @connection.read(ERROR_TUPLE_BYTES)
|
|
45
|
+
_, code, notification_id = tuple.unpack("ccN")
|
|
46
|
+
|
|
47
|
+
description = APN_ERRORS[code.to_i] || "Unknown error. Possible Rpush bug?"
|
|
48
|
+
error = Rpush::DeliveryError.new(code, notification_id, description)
|
|
49
|
+
else
|
|
50
|
+
error = Rpush::Apns::DisconnectionError.new
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
begin
|
|
54
|
+
log_error("Error received, reconnecting...")
|
|
55
|
+
@connection.reconnect
|
|
56
|
+
ensure
|
|
57
|
+
raise error if error
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Rpush
|
|
2
|
+
module Apns
|
|
3
|
+
class DisconnectionError < StandardError
|
|
4
|
+
attr_reader :code, :description
|
|
5
|
+
|
|
6
|
+
def initialize
|
|
7
|
+
@code = nil
|
|
8
|
+
@description = "APNs disconnected without returning an error."
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def to_s
|
|
12
|
+
message
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def message
|
|
16
|
+
"The APNs disconnected without returning an error. This may indicate you are using an invalid certificate for the host."
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
module Rpush
|
|
2
|
+
module Daemon
|
|
3
|
+
module Apns
|
|
4
|
+
class FeedbackReceiver
|
|
5
|
+
include Reflectable
|
|
6
|
+
include Loggable
|
|
7
|
+
|
|
8
|
+
TUPLE_BYTES = 38
|
|
9
|
+
HOSTS = {
|
|
10
|
+
:production => ['feedback.push.apple.com', 2196],
|
|
11
|
+
:development => ['feedback.sandbox.push.apple.com', 2196], # deprecated
|
|
12
|
+
:sandbox => ['feedback.sandbox.push.apple.com', 2196]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
def initialize(app)
|
|
16
|
+
@app = app
|
|
17
|
+
@host, @port = HOSTS[@app.environment.to_sym]
|
|
18
|
+
@poll = Rpush.config.feedback_poll
|
|
19
|
+
@certificate = app.certificate
|
|
20
|
+
@password = app.password
|
|
21
|
+
@interruptible_sleep = InterruptibleSleep.new
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def start
|
|
25
|
+
return if Rpush.config.push
|
|
26
|
+
|
|
27
|
+
@thread = Thread.new do
|
|
28
|
+
loop do
|
|
29
|
+
break if @stop
|
|
30
|
+
check_for_feedback
|
|
31
|
+
@interruptible_sleep.sleep @poll
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
Rpush::Daemon.store.release_connection
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def stop
|
|
39
|
+
@stop = true
|
|
40
|
+
@interruptible_sleep.interrupt_sleep
|
|
41
|
+
@thread.join if @thread
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def check_for_feedback
|
|
45
|
+
connection = nil
|
|
46
|
+
begin
|
|
47
|
+
connection = Rpush::Daemon::TcpConnection.new(@app, @host, @port)
|
|
48
|
+
connection.connect
|
|
49
|
+
|
|
50
|
+
while tuple = connection.read(TUPLE_BYTES)
|
|
51
|
+
timestamp, device_token = parse_tuple(tuple)
|
|
52
|
+
create_feedback(timestamp, device_token)
|
|
53
|
+
end
|
|
54
|
+
rescue StandardError => e
|
|
55
|
+
log_error(e)
|
|
56
|
+
reflect(:error, e)
|
|
57
|
+
ensure
|
|
58
|
+
connection.close if connection
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
protected
|
|
63
|
+
|
|
64
|
+
def parse_tuple(tuple)
|
|
65
|
+
failed_at, _, device_token = tuple.unpack("N1n1H*")
|
|
66
|
+
[Time.at(failed_at).utc, device_token]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def create_feedback(failed_at, device_token)
|
|
70
|
+
formatted_failed_at = failed_at.strftime("%Y-%m-%d %H:%M:%S UTC")
|
|
71
|
+
log_info("[FeedbackReceiver] Delivery failed at #{formatted_failed_at} for #{device_token}.")
|
|
72
|
+
|
|
73
|
+
feedback = Rpush::Daemon.store.create_apns_feedback(failed_at, device_token, @app)
|
|
74
|
+
reflect(:apns_feedback, feedback)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
module Rpush
|
|
2
|
+
module Daemon
|
|
3
|
+
class AppRunner
|
|
4
|
+
extend Reflectable
|
|
5
|
+
include Reflectable
|
|
6
|
+
include Loggable
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
attr_reader :runners
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
@runners = {}
|
|
13
|
+
|
|
14
|
+
def self.enqueue(notifications)
|
|
15
|
+
notifications.group_by(&:app_id).each do |app_id, group|
|
|
16
|
+
batch = Batch.new(group)
|
|
17
|
+
if app = runners[app_id]
|
|
18
|
+
app.enqueue(batch)
|
|
19
|
+
else
|
|
20
|
+
Rpush.logger.error("No such app '#{app_id}' for notifications #{batch.describe}.")
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.sync
|
|
26
|
+
apps = Rpush::App.all
|
|
27
|
+
apps.each { |app| sync_app(app) }
|
|
28
|
+
removed = runners.keys - apps.map(&:id)
|
|
29
|
+
removed.each { |app_id| runners.delete(app_id).stop }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.sync_app(app)
|
|
33
|
+
if runners[app.id]
|
|
34
|
+
runners[app.id].sync(app)
|
|
35
|
+
else
|
|
36
|
+
runner = new(app)
|
|
37
|
+
begin
|
|
38
|
+
runner.start
|
|
39
|
+
runners[app.id] = runner
|
|
40
|
+
rescue StandardError => e
|
|
41
|
+
Rpush.logger.error("[#{app.name}] Exception raised during startup. Notifications will not be delivered for this app.")
|
|
42
|
+
Rpush.logger.error(e)
|
|
43
|
+
reflect(:error, e)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def self.stop
|
|
49
|
+
runners.values.map(&:stop)
|
|
50
|
+
runners.clear
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.debug
|
|
54
|
+
runners.values.map(&:debug)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def self.idle
|
|
58
|
+
runners.values.select(&:idle?)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def self.wait
|
|
62
|
+
sleep 0.1 while !runners.values.all?(&:idle?)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
attr_reader :app
|
|
66
|
+
attr_accessor :batch
|
|
67
|
+
|
|
68
|
+
def initialize(app)
|
|
69
|
+
@app = app
|
|
70
|
+
@loops = []
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def start
|
|
74
|
+
app.connections.times { dispatchers.push(new_dispatcher_loop) }
|
|
75
|
+
start_loops
|
|
76
|
+
log_info("Started, #{dispatchers_str}.")
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def stop
|
|
80
|
+
dispatchers.stop
|
|
81
|
+
stop_loops
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def enqueue(batch)
|
|
85
|
+
self.batch = batch
|
|
86
|
+
batch.notifications.each do |notification|
|
|
87
|
+
queue.push([notification, batch])
|
|
88
|
+
reflect(:notification_enqueued, notification)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def sync(app)
|
|
93
|
+
@app = app
|
|
94
|
+
diff = dispatchers.size - app.connections
|
|
95
|
+
return if diff == 0
|
|
96
|
+
if diff > 0
|
|
97
|
+
decrement_dispatchers(diff)
|
|
98
|
+
log_info("Stopped #{dispatchers_str(diff)}. #{dispatchers_str} running.")
|
|
99
|
+
else
|
|
100
|
+
increment_dispatchers(diff.abs)
|
|
101
|
+
log_info("Started #{dispatchers_str(diff)}. #{dispatchers_str} running.")
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def decrement_dispatchers(num)
|
|
106
|
+
num.times { dispatchers.pop }
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def increment_dispatchers(num)
|
|
110
|
+
num.times { dispatchers.push(new_dispatcher_loop) }
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def debug
|
|
114
|
+
Rpush.logger.info <<-EOS
|
|
115
|
+
|
|
116
|
+
#{@app.name}:
|
|
117
|
+
dispatchers: #{num_dispatchers}
|
|
118
|
+
queued: #{queue_size}
|
|
119
|
+
batch size: #{batch_size}
|
|
120
|
+
batch processed: #{batch_processed}
|
|
121
|
+
idle: #{idle?}
|
|
122
|
+
EOS
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def idle?
|
|
126
|
+
batch ? batch.complete? : true
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def queue_size
|
|
130
|
+
queue.size
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def batch_size
|
|
134
|
+
batch ? batch.num_notifications : 0
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def batch_processed
|
|
138
|
+
batch ? batch.num_processed : 0
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def num_dispatchers
|
|
142
|
+
dispatchers.size
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
protected
|
|
146
|
+
|
|
147
|
+
def start_loops
|
|
148
|
+
service_module.loops.each do |loop_class|
|
|
149
|
+
instance = loop_class.new(@app)
|
|
150
|
+
instance.start
|
|
151
|
+
@loops << instance
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def stop_loops
|
|
156
|
+
@loops.map(&:stop)
|
|
157
|
+
@loops = []
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def new_dispatcher_loop
|
|
161
|
+
dispatcher = service_module.new_dispatcher(@app)
|
|
162
|
+
dispatcher_loop = Rpush::Daemon::DispatcherLoop.new(queue, dispatcher)
|
|
163
|
+
dispatcher_loop.start
|
|
164
|
+
dispatcher_loop
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def service_module
|
|
168
|
+
return @service_module if defined? @service_module
|
|
169
|
+
@service_module = "Rpush::Daemon::#{@app.service_name.camelize}".constantize
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def queue
|
|
173
|
+
@queue ||= Queue.new
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def dispatchers
|
|
177
|
+
@dispatchers ||= Rpush::Daemon::DispatcherLoopCollection.new
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def dispatchers_str(count = app.connections)
|
|
181
|
+
count = count.abs
|
|
182
|
+
str = count == 1 ? 'dispatcher' : 'dispatchers'
|
|
183
|
+
"#{count} #{str}"
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
module Rpush
|
|
2
|
+
module Daemon
|
|
3
|
+
class Batch
|
|
4
|
+
include Reflectable
|
|
5
|
+
|
|
6
|
+
attr_reader :num_processed, :notifications,
|
|
7
|
+
:delivered, :failed, :retryable
|
|
8
|
+
|
|
9
|
+
def initialize(notifications)
|
|
10
|
+
@notifications = notifications
|
|
11
|
+
@num_processed = 0
|
|
12
|
+
@delivered = []
|
|
13
|
+
@failed = {}
|
|
14
|
+
@retryable = {}
|
|
15
|
+
@mutex = Mutex.new
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def num_notifications
|
|
19
|
+
@notifications.size
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def mark_retryable(notification, deliver_after)
|
|
23
|
+
if Rpush.config.batch_storage_updates
|
|
24
|
+
retryable[deliver_after] ||= []
|
|
25
|
+
retryable[deliver_after] << notification
|
|
26
|
+
Rpush::Daemon.store.mark_retryable(notification, deliver_after, :persist => false)
|
|
27
|
+
else
|
|
28
|
+
Rpush::Daemon.store.mark_retryable(notification, deliver_after)
|
|
29
|
+
reflect(:notification_will_retry, notification)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def mark_delivered(notification)
|
|
34
|
+
if Rpush.config.batch_storage_updates
|
|
35
|
+
delivered << notification
|
|
36
|
+
Rpush::Daemon.store.mark_delivered(notification, Time.now, :persist => false)
|
|
37
|
+
else
|
|
38
|
+
Rpush::Daemon.store.mark_delivered(notification, Time.now)
|
|
39
|
+
reflect(:notification_delivered, notification)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def mark_failed(notification, code, description)
|
|
44
|
+
if Rpush.config.batch_storage_updates
|
|
45
|
+
key = [code, description]
|
|
46
|
+
failed[key] ||= []
|
|
47
|
+
failed[key] << notification
|
|
48
|
+
Rpush::Daemon.store.mark_failed(notification, code, description, Time.now, :persist => false)
|
|
49
|
+
else
|
|
50
|
+
Rpush::Daemon.store.mark_failed(notification, code, description, Time.now)
|
|
51
|
+
reflect(:notification_failed, notification)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def notification_dispatched
|
|
56
|
+
@mutex.synchronize do
|
|
57
|
+
@num_processed += 1
|
|
58
|
+
complete if @num_processed >= @notifications.size
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def complete?
|
|
63
|
+
@complete == true
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def describe
|
|
67
|
+
notifications.map(&:id).join(', ')
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def complete
|
|
73
|
+
[:complete_delivered, :complete_failed, :complete_retried].each do |method|
|
|
74
|
+
begin
|
|
75
|
+
send(method)
|
|
76
|
+
rescue StandardError => e
|
|
77
|
+
Rpush.logger.error(e)
|
|
78
|
+
reflect(:error, e)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
notifications.clear
|
|
83
|
+
@complete = true
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def complete_delivered
|
|
87
|
+
Rpush::Daemon.store.mark_batch_delivered(delivered)
|
|
88
|
+
delivered.each do |notification|
|
|
89
|
+
reflect(:notification_delivered, notification)
|
|
90
|
+
end
|
|
91
|
+
delivered.clear
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def complete_failed
|
|
95
|
+
failed.each do |(code, description), notifications|
|
|
96
|
+
Rpush::Daemon.store.mark_batch_failed(notifications, code, description)
|
|
97
|
+
notifications.each do |notification|
|
|
98
|
+
reflect(:notification_failed, notification)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
failed.clear
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def complete_retried
|
|
105
|
+
retryable.each do |deliver_after, notifications|
|
|
106
|
+
Rpush::Daemon.store.mark_batch_retryable(notifications, deliver_after)
|
|
107
|
+
notifications.each do |notification|
|
|
108
|
+
reflect(:notification_will_retry, notification)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
retryable.clear
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|