rapns 3.2.0-java → 3.3.0-java
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 +5 -0
- data/README.md +9 -18
- data/lib/generators/templates/rapns.rb +5 -0
- data/lib/rapns.rb +4 -0
- data/lib/rapns/apns_feedback.rb +1 -0
- data/lib/rapns/configuration.rb +4 -3
- data/lib/rapns/daemon.rb +20 -4
- data/lib/rapns/daemon/apns/feedback_receiver.rb +9 -11
- data/lib/rapns/daemon/delivery.rb +3 -20
- data/lib/rapns/daemon/delivery_queue.rb +2 -2
- data/lib/rapns/daemon/feeder.rb +5 -10
- data/lib/rapns/daemon/gcm/delivery.rb +19 -9
- data/lib/rapns/daemon/store/active_record.rb +74 -0
- data/lib/rapns/daemon/store/active_record/reconnectable.rb +61 -0
- data/lib/rapns/gcm/notification.rb +5 -4
- data/lib/rapns/gcm/registration_ids_count_validator.rb +1 -1
- data/lib/rapns/logger.rb +6 -2
- data/lib/rapns/push.rb +1 -0
- data/lib/rapns/reflection.rb +1 -1
- data/lib/rapns/version.rb +1 -1
- data/spec/unit/apns/notification_spec.rb +2 -0
- data/spec/unit/apns_feedback_spec.rb +5 -0
- data/spec/unit/configuration_spec.rb +1 -1
- data/spec/unit/daemon/apns/delivery_spec.rb +7 -64
- data/spec/unit/daemon/apns/feedback_receiver_spec.rb +2 -2
- data/spec/unit/daemon/feeder_spec.rb +6 -58
- data/spec/unit/daemon/gcm/delivery_spec.rb +49 -57
- data/spec/unit/daemon/{database_reconnectable_spec.rb → store/active_record/reconnectable_spec.rb} +4 -3
- data/spec/unit/daemon/store/active_record_spec.rb +181 -0
- data/spec/unit/daemon_spec.rb +27 -7
- data/spec/unit/gcm/notification_spec.rb +2 -9
- data/spec/unit/push_spec.rb +5 -0
- data/spec/unit/reflection_spec.rb +0 -4
- data/spec/unit_spec_helper.rb +4 -1
- metadata +28 -25
- data/lib/rapns/daemon/database_reconnectable.rb +0 -57
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
## 3.3.0 (April 21, 2013)
|
2
|
+
* GCM: collapse_key is no longer required to set expiry (time_to_live).
|
3
|
+
* Add reflection for GCM canonical IDs.
|
4
|
+
* Add Rapns::Daemon.store to decouple storage backend.
|
5
|
+
|
1
6
|
## 3.2.0 (Apr 1, 2013)
|
2
7
|
* Rapns.apns_feedback for one time feedback retrieval. Rapns.push no longer checks for feedback (#117, #105).
|
3
8
|
* Lazily connect to the APNs only when a notification is to be delivered (#111).
|
data/README.md
CHANGED
@@ -34,26 +34,12 @@ Generate the migrations, rapns.yml and migrate:
|
|
34
34
|
rails g rapns
|
35
35
|
rake db:migrate
|
36
36
|
|
37
|
-
## Generating Certificates (APNs only)
|
38
|
-
|
39
|
-
1. Open up Keychain Access and select the `Certificates` category in the sidebar.
|
40
|
-
2. Expand the disclosure arrow next to the iOS Push Services certificate you want to export.
|
41
|
-
3. Select both the certificate and private key.
|
42
|
-
4. Right click and select `Export 2 items...`.
|
43
|
-
5. Save the file as `cert.p12`, make sure the File Format is `Personal Information Exchange (p12)`.
|
44
|
-
6. Convert the certificate to a .pem, where `<environment>` should be `sandbox` or `production`, depending on the certificate you exported.
|
45
|
-
|
46
|
-
Without a password:
|
47
|
-
|
48
|
-
`openssl pkcs12 -nodes -clcerts -in cert.p12 -out <environment>.pem`
|
49
|
-
|
50
|
-
With a password:
|
51
|
-
|
52
|
-
`openssl pkcs12 -clcerts -in cert.p12 -out <environment>.pem`
|
53
|
-
|
54
37
|
## Create an App
|
55
38
|
|
56
39
|
#### APNs
|
40
|
+
|
41
|
+
If this is your first time using the APNs, you will need to generate SSL certificates. See [Generating Certificates](https://github.com/ileitch/rapns/wiki/Generating-Certificates) for instructions.
|
42
|
+
|
57
43
|
```ruby
|
58
44
|
app = Rapns::Apns::App.new
|
59
45
|
app.name = "ios_app"
|
@@ -107,9 +93,10 @@ Inside an existing process (see [Embedding API](https://github.com/ileitch/rapns
|
|
107
93
|
|
108
94
|
*Please note that only ever a single instance of Rapns should be running.*
|
109
95
|
|
110
|
-
In a scheduler:
|
96
|
+
In a scheduler (see [Push API](https://github.com/ileitch/rapns/wiki/Push-API)):
|
111
97
|
|
112
98
|
Rapns.push
|
99
|
+
Rapns.apns_feedback
|
113
100
|
|
114
101
|
See [Configuration](https://github.com/ileitch/rapns/wiki/Configuration) for a list of options, or run `rapns --help`.
|
115
102
|
|
@@ -130,12 +117,16 @@ After updating you should run `rails g rapns` to check for any new migrations.
|
|
130
117
|
* [Embedding API](https://github.com/ileitch/rapns/wiki/Embedding-API)
|
131
118
|
|
132
119
|
### APNs
|
120
|
+
* [Generating Certificates](https://github.com/ileitch/rapns/wiki/Generating-Certificates)
|
133
121
|
* [Advanced APNs Features](https://github.com/ileitch/rapns/wiki/Advanced-APNs-Features)
|
134
122
|
* [APNs Delivery Failure Handling](https://github.com/ileitch/rapns/wiki/APNs-Delivery-Failure-Handling)
|
135
123
|
* [Why open multiple connections to the APNs?](https://github.com/ileitch/rapns/wiki/Why-open-multiple-connections-to-the-APNs%3F)
|
136
124
|
* [Silent failures might be dropped connections](https://github.com/ileitch/rapns/wiki/Dropped-connections)
|
137
125
|
|
138
126
|
### GCM
|
127
|
+
* [Notification Options](https://github.com/ileitch/rapns/wiki//GCM-Notification-Options)
|
128
|
+
* [Canonical IDs](https://github.com/ileitch/rapns/wiki/Canonical-IDs)
|
129
|
+
* [Delivery Failures & Retries](https://github.com/ileitch/rapns/wiki/Delivery-Failures-&-Retries)
|
139
130
|
|
140
131
|
## Contributing
|
141
132
|
|
@@ -63,6 +63,11 @@ Rapns.reflect do |on|
|
|
63
63
|
# on.apns_connection_lost do |app, error|
|
64
64
|
# end
|
65
65
|
|
66
|
+
# Called when the GCM returns a canonical registration ID.
|
67
|
+
# You will need to replace old_id with canonical_id in your records.
|
68
|
+
# on.gcm_canonical_id do |old_id, canonical_id|
|
69
|
+
# end
|
70
|
+
|
66
71
|
# Called when an exception is raised.
|
67
72
|
# on.error do |error|
|
68
73
|
# end
|
data/lib/rapns.rb
CHANGED
data/lib/rapns/apns_feedback.rb
CHANGED
data/lib/rapns/configuration.rb
CHANGED
@@ -9,7 +9,7 @@ module Rapns
|
|
9
9
|
|
10
10
|
CONFIG_ATTRS = [:foreground, :push_poll, :feedback_poll, :embedded,
|
11
11
|
:airbrake_notify, :check_for_errors, :pid_file, :batch_size,
|
12
|
-
:push]
|
12
|
+
:push, :store]
|
13
13
|
|
14
14
|
class ConfigurationWithoutDefaults < Struct.new(*CONFIG_ATTRS)
|
15
15
|
end
|
@@ -40,7 +40,7 @@ module Rapns
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def foreground=(bool)
|
43
|
-
if
|
43
|
+
if Rapns.jruby?
|
44
44
|
# The JVM does not support fork().
|
45
45
|
super(true)
|
46
46
|
else
|
@@ -56,7 +56,7 @@ module Rapns
|
|
56
56
|
private
|
57
57
|
|
58
58
|
def set_defaults
|
59
|
-
if
|
59
|
+
if Rapns.jruby?
|
60
60
|
# The JVM does not support fork().
|
61
61
|
self.foreground = true
|
62
62
|
else
|
@@ -70,6 +70,7 @@ module Rapns
|
|
70
70
|
self.batch_size = 5000
|
71
71
|
self.pid_file = nil
|
72
72
|
self.apns_feedback_callback = nil
|
73
|
+
self.store = :active_record
|
73
74
|
|
74
75
|
# Internal options.
|
75
76
|
self.embedded = false
|
data/lib/rapns/daemon.rb
CHANGED
@@ -8,7 +8,6 @@ require 'net/http/persistent'
|
|
8
8
|
require 'rapns/daemon/reflectable'
|
9
9
|
require 'rapns/daemon/interruptible_sleep'
|
10
10
|
require 'rapns/daemon/delivery_error'
|
11
|
-
require 'rapns/daemon/database_reconnectable'
|
12
11
|
require 'rapns/daemon/delivery'
|
13
12
|
require 'rapns/daemon/delivery_queue'
|
14
13
|
require 'rapns/daemon/feeder'
|
@@ -28,14 +27,19 @@ require 'rapns/daemon/gcm/delivery_handler'
|
|
28
27
|
|
29
28
|
module Rapns
|
30
29
|
module Daemon
|
31
|
-
|
30
|
+
class << self
|
31
|
+
attr_accessor :store
|
32
|
+
end
|
32
33
|
|
33
34
|
def self.start
|
34
35
|
setup_signal_traps if trap_signals?
|
35
36
|
|
37
|
+
initialize_store
|
38
|
+
return unless store
|
39
|
+
|
36
40
|
if daemonize?
|
37
41
|
daemonize
|
38
|
-
|
42
|
+
store.after_daemonize
|
39
43
|
end
|
40
44
|
|
41
45
|
write_pid_file
|
@@ -51,10 +55,22 @@ module Rapns
|
|
51
55
|
delete_pid_file
|
52
56
|
end
|
53
57
|
|
58
|
+
def self.initialize_store
|
59
|
+
return if store
|
60
|
+
begin
|
61
|
+
require "rapns/daemon/store/#{Rapns.config.store}"
|
62
|
+
klass = "Rapns::Daemon::Store::#{Rapns.config.store.to_s.camelcase}".constantize
|
63
|
+
self.store = klass.new
|
64
|
+
rescue StandardError, LoadError => e
|
65
|
+
Rapns.logger.error("Failed to load '#{Rapns.config.store}' storage backend.")
|
66
|
+
Rapns.logger.error(e)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
54
70
|
protected
|
55
71
|
|
56
72
|
def self.daemonize?
|
57
|
-
!(Rapns.config.foreground || Rapns.config.embedded ||
|
73
|
+
!(Rapns.config.foreground || Rapns.config.embedded || Rapns.jruby?)
|
58
74
|
end
|
59
75
|
|
60
76
|
def self.trap_signals?
|
@@ -4,7 +4,6 @@ module Rapns
|
|
4
4
|
class FeedbackReceiver
|
5
5
|
include Reflectable
|
6
6
|
include InterruptibleSleep
|
7
|
-
include DatabaseReconnectable
|
8
7
|
|
9
8
|
FEEDBACK_TUPLE_BYTES = 38
|
10
9
|
HOSTS = {
|
@@ -63,17 +62,16 @@ module Rapns
|
|
63
62
|
|
64
63
|
def create_feedback(failed_at, device_token)
|
65
64
|
formatted_failed_at = failed_at.strftime("%Y-%m-%d %H:%M:%S UTC")
|
66
|
-
|
67
|
-
Rapns.logger.info("[#{@app.name}] [FeedbackReceiver] Delivery failed at #{formatted_failed_at} for #{device_token}.")
|
68
|
-
feedback = Rapns::Apns::Feedback.create!(:failed_at => failed_at, :device_token => device_token, :app => @app)
|
69
|
-
reflect(:apns_feedback, feedback)
|
65
|
+
Rapns.logger.info("[#{@app.name}] [FeedbackReceiver] Delivery failed at #{formatted_failed_at} for #{device_token}.")
|
70
66
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
67
|
+
feedback = Rapns::Daemon.store.create_apns_feedback(failed_at, device_token, @app)
|
68
|
+
reflect(:apns_feedback, feedback)
|
69
|
+
|
70
|
+
# Deprecated.
|
71
|
+
begin
|
72
|
+
Rapns.config.apns_feedback_callback.call(feedback) if Rapns.config.apns_feedback_callback
|
73
|
+
rescue StandardError => e
|
74
|
+
Rapns.logger.error(e)
|
77
75
|
end
|
78
76
|
end
|
79
77
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module Rapns
|
2
2
|
module Daemon
|
3
3
|
class Delivery
|
4
|
-
include DatabaseReconnectable
|
5
4
|
include Reflectable
|
6
5
|
|
7
6
|
def self.perform(*args)
|
@@ -9,11 +8,7 @@ module Rapns
|
|
9
8
|
end
|
10
9
|
|
11
10
|
def retry_after(notification, deliver_after)
|
12
|
-
|
13
|
-
notification.retries += 1
|
14
|
-
notification.deliver_after = deliver_after
|
15
|
-
notification.save!(:validate => false)
|
16
|
-
end
|
11
|
+
Rapns::Daemon.store.retry_after(notification, deliver_after)
|
17
12
|
reflect(:notification_will_retry, notification)
|
18
13
|
end
|
19
14
|
|
@@ -22,24 +17,12 @@ module Rapns
|
|
22
17
|
end
|
23
18
|
|
24
19
|
def mark_delivered
|
25
|
-
|
26
|
-
@notification.delivered = true
|
27
|
-
@notification.delivered_at = Time.now
|
28
|
-
@notification.save!(:validate => false)
|
29
|
-
end
|
20
|
+
Rapns::Daemon.store.mark_delivered(@notification)
|
30
21
|
reflect(:notification_delivered, @notification)
|
31
22
|
end
|
32
23
|
|
33
24
|
def mark_failed(code, description)
|
34
|
-
|
35
|
-
@notification.delivered = false
|
36
|
-
@notification.delivered_at = nil
|
37
|
-
@notification.failed = true
|
38
|
-
@notification.failed_at = Time.now
|
39
|
-
@notification.error_code = code
|
40
|
-
@notification.error_description = description
|
41
|
-
@notification.save!(:validate => false)
|
42
|
-
end
|
25
|
+
Rapns::Daemon.store.mark_failed(@notification, code, description)
|
43
26
|
reflect(:notification_failed, @notification)
|
44
27
|
end
|
45
28
|
end
|
data/lib/rapns/daemon/feeder.rb
CHANGED
@@ -2,7 +2,6 @@ module Rapns
|
|
2
2
|
module Daemon
|
3
3
|
class Feeder
|
4
4
|
extend InterruptibleSleep
|
5
|
-
extend DatabaseReconnectable
|
6
5
|
extend Reflectable
|
7
6
|
|
8
7
|
def self.start
|
@@ -38,15 +37,11 @@ module Rapns
|
|
38
37
|
|
39
38
|
def self.enqueue_notifications
|
40
39
|
begin
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
relation.each do |notification|
|
47
|
-
Rapns::Daemon::AppRunner.enqueue(notification)
|
48
|
-
reflect(:notification_enqueued, notification)
|
49
|
-
end
|
40
|
+
idle = Rapns::Daemon::AppRunner.idle.map(&:app)
|
41
|
+
|
42
|
+
Rapns::Daemon.store.deliverable_notifications(idle).each do |notification|
|
43
|
+
Rapns::Daemon::AppRunner.enqueue(notification)
|
44
|
+
reflect(:notification_enqueued, notification)
|
50
45
|
end
|
51
46
|
rescue StandardError => e
|
52
47
|
Rapns.logger.error(e)
|
@@ -51,6 +51,8 @@ module Rapns
|
|
51
51
|
else
|
52
52
|
handle_errors(response, body)
|
53
53
|
end
|
54
|
+
|
55
|
+
handle_canonical_ids(response, body)
|
54
56
|
end
|
55
57
|
|
56
58
|
def handle_errors(response, body)
|
@@ -69,6 +71,17 @@ module Rapns
|
|
69
71
|
end
|
70
72
|
end
|
71
73
|
|
74
|
+
def handle_canonical_ids(response, body)
|
75
|
+
if body['canonical_ids'] && body['canonical_ids'].to_i > 0
|
76
|
+
body['results'].each_with_index do |result, i|
|
77
|
+
if result['message_id'] && result['registration_id']
|
78
|
+
old_id = @notification.registration_ids[i]
|
79
|
+
reflect(:gcm_canonical_id, old_id, result['registration_id'])
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
72
85
|
def bad_request(response)
|
73
86
|
raise Rapns::DeliveryError.new(400, @notification.id, 'GCM failed to parse the JSON request. Possibly an rapns bug, please open an issue.')
|
74
87
|
end
|
@@ -94,19 +107,16 @@ module Rapns
|
|
94
107
|
|
95
108
|
def some_devices_unavailable(response, errors)
|
96
109
|
unavailable_idxs = errors.find_all { |i, error| error.in?(UNAVAILABLE_STATES) }.map(&:first)
|
97
|
-
new_notification =
|
98
|
-
with_database_reconnect_and_retry { new_notification.save! }
|
110
|
+
new_notification = create_new_notification(response, unavailable_idxs)
|
99
111
|
raise Rapns::DeliveryError.new(nil, @notification.id,
|
100
112
|
describe_errors(errors) + " #{unavailable_idxs.join(', ')} will be retried as notification #{new_notification.id}.")
|
101
113
|
end
|
102
114
|
|
103
|
-
def
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
notification.deliver_after = deliver_after_header(response)
|
109
|
-
notification
|
115
|
+
def create_new_notification(response, unavailable_idxs)
|
116
|
+
attrs = @notification.attributes.slice('app_id', 'collapse_key', 'delay_while_idle')
|
117
|
+
registration_ids = unavailable_idxs.map { |i| @notification.registration_ids[i] }
|
118
|
+
Rapns::Daemon.store.create_gcm_notification(attrs, @notification.data,
|
119
|
+
registration_ids, deliver_after_header(response), @notification.app)
|
110
120
|
end
|
111
121
|
|
112
122
|
def deliver_after_header(response)
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
require 'rapns/daemon/store/active_record/reconnectable'
|
4
|
+
|
5
|
+
module Rapns
|
6
|
+
module Daemon
|
7
|
+
module Store
|
8
|
+
class ActiveRecord
|
9
|
+
include Reconnectable
|
10
|
+
|
11
|
+
def deliverable_notifications(apps)
|
12
|
+
with_database_reconnect_and_retry do
|
13
|
+
batch_size = Rapns.config.batch_size
|
14
|
+
relation = Rapns::Notification.ready_for_delivery.for_apps(apps)
|
15
|
+
relation = relation.limit(batch_size) unless Rapns.config.push
|
16
|
+
relation.all
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def retry_after(notification, deliver_after)
|
21
|
+
with_database_reconnect_and_retry do
|
22
|
+
notification.retries += 1
|
23
|
+
notification.deliver_after = deliver_after
|
24
|
+
notification.save!(:validate => false)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def mark_delivered(notification)
|
29
|
+
with_database_reconnect_and_retry do
|
30
|
+
notification.delivered = true
|
31
|
+
notification.delivered_at = Time.now
|
32
|
+
notification.save!(:validate => false)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def mark_failed(notification, code, description)
|
37
|
+
with_database_reconnect_and_retry do
|
38
|
+
notification.delivered = false
|
39
|
+
notification.delivered_at = nil
|
40
|
+
notification.failed = true
|
41
|
+
notification.failed_at = Time.now
|
42
|
+
notification.error_code = code
|
43
|
+
notification.error_description = description
|
44
|
+
notification.save!(:validate => false)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def create_apns_feedback(failed_at, device_token, app)
|
49
|
+
with_database_reconnect_and_retry do
|
50
|
+
Rapns::Apns::Feedback.create!(:failed_at => failed_at,
|
51
|
+
:device_token => device_token, :app => app)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def create_gcm_notification(attrs, data, registration_ids, deliver_after, app)
|
56
|
+
with_database_reconnect_and_retry do
|
57
|
+
notification = Rapns::Gcm::Notification.new
|
58
|
+
notification.assign_attributes(attrs)
|
59
|
+
notification.data = data
|
60
|
+
notification.registration_ids = registration_ids
|
61
|
+
notification.deliver_after = deliver_after
|
62
|
+
notification.app = app
|
63
|
+
notification.save!
|
64
|
+
notification
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def after_daemonize
|
69
|
+
reconnect_database
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
class PGError < StandardError; end if !defined?(PGError)
|
2
|
+
class Mysql; class Error < StandardError; end; end if !defined?(Mysql)
|
3
|
+
module Mysql2; class Error < StandardError; end; end if !defined?(Mysql2)
|
4
|
+
module ActiveRecord; end
|
5
|
+
class ActiveRecord::JDBCError < StandardError; end if !defined?(::ActiveRecord::JDBCError)
|
6
|
+
|
7
|
+
module Rapns
|
8
|
+
module Daemon
|
9
|
+
module Store
|
10
|
+
class ActiveRecord
|
11
|
+
module Reconnectable
|
12
|
+
ADAPTER_ERRORS = [::ActiveRecord::StatementInvalid, PGError, Mysql::Error,
|
13
|
+
Mysql2::Error, ::ActiveRecord::JDBCError]
|
14
|
+
|
15
|
+
def with_database_reconnect_and_retry
|
16
|
+
begin
|
17
|
+
::ActiveRecord::Base.connection_pool.with_connection do
|
18
|
+
yield
|
19
|
+
end
|
20
|
+
rescue *ADAPTER_ERRORS => e
|
21
|
+
Rapns.logger.error(e)
|
22
|
+
database_connection_lost
|
23
|
+
retry
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def database_connection_lost
|
28
|
+
Rapns.logger.warn("Lost connection to database, reconnecting...")
|
29
|
+
attempts = 0
|
30
|
+
loop do
|
31
|
+
begin
|
32
|
+
Rapns.logger.warn("Attempt #{attempts += 1}")
|
33
|
+
reconnect_database
|
34
|
+
check_database_is_connected
|
35
|
+
break
|
36
|
+
rescue *ADAPTER_ERRORS => e
|
37
|
+
Rapns.logger.error(e, :airbrake_notify => false)
|
38
|
+
sleep_to_avoid_thrashing
|
39
|
+
end
|
40
|
+
end
|
41
|
+
Rapns.logger.warn("Database reconnected")
|
42
|
+
end
|
43
|
+
|
44
|
+
def reconnect_database
|
45
|
+
::ActiveRecord::Base.clear_all_connections!
|
46
|
+
::ActiveRecord::Base.establish_connection
|
47
|
+
end
|
48
|
+
|
49
|
+
def check_database_is_connected
|
50
|
+
# Simply asking the adapter for the connection state is not sufficient.
|
51
|
+
Rapns::Notification.count
|
52
|
+
end
|
53
|
+
|
54
|
+
def sleep_to_avoid_thrashing
|
55
|
+
sleep 2
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|