rpush 2.3.2 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -0
  3. data/README.md +1 -1
  4. data/lib/generators/rpush_migration_generator.rb +21 -6
  5. data/lib/generators/templates/rpush.rb +5 -5
  6. data/lib/generators/templates/rpush_2_0_0_updates.rb +24 -0
  7. data/lib/rpush/client/active_model/apns/notification.rb +1 -1
  8. data/lib/rpush/client/mongoid.rb +31 -0
  9. data/lib/rpush/client/mongoid/adm/app.rb +14 -0
  10. data/lib/rpush/client/mongoid/adm/notification.rb +11 -0
  11. data/lib/rpush/client/mongoid/apns/app.rb +11 -0
  12. data/lib/rpush/client/mongoid/apns/feedback.rb +21 -0
  13. data/lib/rpush/client/mongoid/apns/notification.rb +15 -0
  14. data/lib/rpush/client/mongoid/app.rb +23 -0
  15. data/lib/rpush/client/mongoid/gcm/app.rb +11 -0
  16. data/lib/rpush/client/mongoid/gcm/notification.rb +11 -0
  17. data/lib/rpush/client/mongoid/notification.rb +43 -0
  18. data/lib/rpush/client/mongoid/wpns/app.rb +11 -0
  19. data/lib/rpush/client/mongoid/wpns/notification.rb +11 -0
  20. data/lib/rpush/client/redis.rb +2 -2
  21. data/lib/rpush/configuration.rb +48 -29
  22. data/lib/rpush/daemon/adm/delivery.rb +1 -1
  23. data/lib/rpush/daemon/apns.rb +1 -1
  24. data/lib/rpush/daemon/apns/feedback_receiver.rb +2 -3
  25. data/lib/rpush/daemon/dispatcher/apns_tcp.rb +2 -1
  26. data/lib/rpush/daemon/feeder.rb +4 -7
  27. data/lib/rpush/daemon/gcm/delivery.rb +1 -1
  28. data/lib/rpush/daemon/interruptible_sleep.rb +5 -50
  29. data/lib/rpush/daemon/proc_title.rb +2 -1
  30. data/lib/rpush/daemon/store/active_record.rb +4 -0
  31. data/lib/rpush/daemon/store/interface.rb +1 -1
  32. data/lib/rpush/daemon/store/mongoid.rb +157 -0
  33. data/lib/rpush/daemon/store/redis.rb +6 -2
  34. data/lib/rpush/deprecatable.rb +1 -2
  35. data/lib/rpush/deprecation.rb +6 -0
  36. data/lib/rpush/embed.rb +5 -0
  37. data/lib/rpush/logger.rb +5 -8
  38. data/lib/rpush/push.rb +5 -0
  39. data/lib/rpush/version.rb +1 -1
  40. data/lib/tasks/quality.rake +1 -1
  41. data/lib/tasks/test.rake +9 -4
  42. data/spec/functional/apns_spec.rb +2 -1
  43. data/spec/functional_spec_helper.rb +2 -2
  44. data/spec/spec_helper.rb +18 -7
  45. data/spec/support/config/mongoid.yml +69 -0
  46. data/spec/support/mongoid_setup.rb +10 -0
  47. data/spec/unit/client/active_record/adm/app_spec.rb +1 -1
  48. data/spec/unit/client/active_record/adm/notification_spec.rb +1 -1
  49. data/spec/unit/client/active_record/apns/app_spec.rb +1 -1
  50. data/spec/unit/client/active_record/apns/feedback_spec.rb +1 -1
  51. data/spec/unit/client/active_record/apns/notification_spec.rb +11 -11
  52. data/spec/unit/client/active_record/app_spec.rb +1 -1
  53. data/spec/unit/client/active_record/gcm/notification_spec.rb +1 -1
  54. data/spec/unit/client/active_record/notification_spec.rb +1 -1
  55. data/spec/unit/client/active_record/wpns/notification_spec.rb +1 -1
  56. data/spec/unit/configuration_spec.rb +7 -0
  57. data/spec/unit/daemon/apns/feedback_receiver_spec.rb +5 -5
  58. data/spec/unit/daemon/feeder_spec.rb +2 -2
  59. data/spec/unit/daemon/proc_title_spec.rb +11 -0
  60. data/spec/unit/daemon/store/active_record/reconnectable_spec.rb +1 -1
  61. data/spec/unit/daemon/store/active_record_spec.rb +21 -12
  62. data/spec/unit/daemon/store/mongoid_spec.rb +339 -0
  63. data/spec/unit/daemon/store/redis_spec.rb +365 -0
  64. data/spec/unit/embed_spec.rb +4 -2
  65. data/spec/unit/logger_spec.rb +14 -5
  66. data/spec/unit/notification_shared.rb +1 -1
  67. data/spec/unit/push_spec.rb +4 -2
  68. data/spec/unit_spec_helper.rb +3 -3
  69. metadata +25 -2
@@ -151,7 +151,7 @@ module Rpush
151
151
  end
152
152
 
153
153
  def create_new_notification(response, registration_ids)
154
- attrs = @notification.attributes.slice('app_id', 'collapse_key', 'delay_while_idle')
154
+ attrs = { 'app_id' => @notification.app_id, 'collapse_key' => @notification.collapse_key, 'delay_while_idle' => @notification.delay_while_idle }
155
155
  Rpush::Daemon.store.create_adm_notification(attrs, @notification.data, registration_ids, deliver_after_header(response), @notification.app)
156
156
  end
157
157
 
@@ -11,7 +11,7 @@ module Rpush
11
11
 
12
12
  batch_deliveries true
13
13
  dispatcher :apns_tcp, host: proc { |app| HOSTS[app.environment.to_sym] }
14
- loops Rpush::Daemon::Apns::FeedbackReceiver, if: -> { !Rpush.config.push }
14
+ loops Rpush::Daemon::Apns::FeedbackReceiver, if: -> { Rpush.config.apns.feedback_receiver.enabled && !Rpush.config.push }
15
15
  end
16
16
  end
17
17
  end
@@ -19,19 +19,18 @@ module Rpush
19
19
  @host, @port = HOSTS[@app.environment.to_sym]
20
20
  @certificate = app.certificate
21
21
  @password = app.password
22
- @interruptible_sleep = InterruptibleSleep.new(Rpush.config.feedback_poll)
22
+ @interruptible_sleep = InterruptibleSleep.new
23
23
  end
24
24
 
25
25
  def start
26
26
  return if Rpush.config.push
27
27
  Rpush.logger.info("[#{@app.name}] Starting feedback receiver... ", true)
28
- @interruptible_sleep.start
29
28
 
30
29
  @thread = Thread.new do
31
30
  loop do
32
31
  break if @stop
33
32
  check_for_feedback
34
- @interruptible_sleep.sleep
33
+ @interruptible_sleep.sleep(Rpush.config.apns.feedback_receiver.frequency)
35
34
  end
36
35
 
37
36
  Rpush::Daemon.store.release_connection
@@ -63,7 +63,7 @@ module Rpush
63
63
  # On Linux, select returns nil from a dropped connection.
64
64
  # On OS X, Errno::EBADF is raised following a Errno::EADDRNOTAVAIL from the write call.
65
65
  return unless @connection.select(SELECT_TIMEOUT)
66
- rescue SystemCallError
66
+ rescue SystemCallError, IOError
67
67
  # Connection closed.
68
68
  return
69
69
  end
@@ -93,6 +93,7 @@ module Rpush
93
93
  end
94
94
 
95
95
  def handle_error(code, notification_id)
96
+ notification_id = Rpush::Daemon.store.translate_integer_notification_id(notification_id)
96
97
  failed_pos = delivered_buffer.index(notification_id)
97
98
  description = APNS_ERRORS[code.to_i] || "Unknown error code #{code.inspect}. Possible Rpush bug?"
98
99
  log_error(description + " (#{code})")
@@ -21,7 +21,7 @@ module Rpush
21
21
  end
22
22
 
23
23
  def self.wakeup
24
- interruptible_sleeper.wakeup
24
+ interruptible_sleeper.stop
25
25
  end
26
26
 
27
27
  class << self
@@ -35,8 +35,8 @@ module Rpush
35
35
  def self.feed_forever
36
36
  loop do
37
37
  enqueue_notifications
38
- interruptible_sleeper.sleep
39
- break if should_stop
38
+ interruptible_sleeper.sleep(Rpush.config.push_poll)
39
+ return if should_stop
40
40
  end
41
41
  end
42
42
 
@@ -51,10 +51,7 @@ module Rpush
51
51
  end
52
52
 
53
53
  def self.interruptible_sleeper
54
- return @interruptible_sleeper if @interruptible_sleeper
55
- @interruptible_sleeper = InterruptibleSleep.new(Rpush.config.push_poll)
56
- @interruptible_sleeper.start
57
- @interruptible_sleeper
54
+ @interruptible_sleeper ||= InterruptibleSleep.new
58
55
  end
59
56
  end
60
57
  end
@@ -100,7 +100,7 @@ module Rpush
100
100
  end
101
101
 
102
102
  def create_new_notification(response, unavailable_idxs)
103
- attrs = @notification.attributes.slice('app_id', 'collapse_key', 'delay_while_idle')
103
+ attrs = { 'app_id' => @notification.app_id, 'collapse_key' => @notification.collapse_key, 'delay_while_idle' => @notification.delay_while_idle }
104
104
  registration_ids = @notification.registration_ids.values_at(*unavailable_idxs)
105
105
  Rpush::Daemon.store.create_gcm_notification(attrs, @notification.data,
106
106
  registration_ids, deliver_after_header(response), @notification.app)
@@ -3,60 +3,15 @@ require 'monitor'
3
3
  module Rpush
4
4
  module Daemon
5
5
  class InterruptibleSleep
6
- def initialize(duration)
7
- @duration = duration
8
- @stop = false
9
-
10
- @wakeup_obj = Object.new
11
- @wakeup_obj.extend(MonitorMixin)
12
- @wakeup_condition = @wakeup_obj.new_cond
13
-
14
- @sleep_obj = Object.new
15
- @sleep_obj.extend(MonitorMixin)
16
- @sleep_condition = @sleep_obj.new_cond
17
- end
18
-
19
- def sleep
20
- return if @stop
21
- goto_sleep
22
- wait_for_wakeup
23
- end
24
-
25
- def start
26
- @stop = false
27
-
28
- @thread = Thread.new do
29
- loop do
30
- wait_for_sleeper
31
- break if @stop
32
- Kernel.sleep(@duration)
33
- wakeup
34
- end
35
- end
6
+ def sleep(duration)
7
+ @thread = Thread.new { Kernel.sleep duration }
8
+ Thread.pass
9
+ @thread.join
36
10
  end
37
11
 
38
12
  def stop
39
- @stop = true
40
- wakeup
41
13
  @thread.kill if @thread
42
- end
43
-
44
- def wakeup
45
- @wakeup_obj.synchronize { @wakeup_condition.signal }
46
- end
47
-
48
- private
49
-
50
- def goto_sleep
51
- @sleep_obj.synchronize { @sleep_condition.signal }
52
- end
53
-
54
- def wait_for_wakeup
55
- @wakeup_obj.synchronize { @wakeup_condition.wait(@duration * 2) }
56
- end
57
-
58
- def wait_for_sleeper
59
- @sleep_obj.synchronize { @sleep_condition.wait }
14
+ rescue StandardError # rubocop:disable Lint/HandleExceptions
60
15
  end
61
16
  end
62
17
  end
@@ -2,7 +2,8 @@ module Rpush
2
2
  module Daemon
3
3
  class ProcTitle
4
4
  def self.update
5
- $0 = proc_title
5
+ return if Rpush.config.embedded || Rpush.config.push
6
+ Process.respond_to?(:setproctitle) ? Process.setproctitle(proc_title) : $0 = proc_title
6
7
  end
7
8
 
8
9
  def self.proc_title
@@ -169,6 +169,10 @@ module Rpush
169
169
  ready_for_delivery.count
170
170
  end
171
171
 
172
+ def translate_integer_notification_id(id)
173
+ id
174
+ end
175
+
172
176
  private
173
177
 
174
178
  def create_gcm_like_notification(notification, attrs, data, registration_ids, deliver_after, app) # rubocop:disable ParameterLists
@@ -8,7 +8,7 @@ module Rpush
8
8
  :create_gcm_notification, :create_adm_notification, :update_app,
9
9
  :update_notification, :release_connection,
10
10
  :all_apps, :app, :mark_ids_failed, :mark_ids_retryable,
11
- :reopen_log, :pending_delivery_count]
11
+ :reopen_log, :pending_delivery_count, :translate_integer_notification_id]
12
12
 
13
13
  def self.check(klass)
14
14
  missing = PUBLIC_METHODS - klass.instance_methods.map(&:to_sym)
@@ -0,0 +1,157 @@
1
+ module Rpush
2
+ module Daemon
3
+ module Store
4
+ class Mongoid
5
+ DEFAULT_MARK_OPTIONS = { persist: true }
6
+
7
+ def app(app_id)
8
+ Rpush::Client::Mongoid::App.find(app_id)
9
+ end
10
+
11
+ def all_apps
12
+ Rpush::Client::Mongoid::App.all
13
+ end
14
+
15
+ def deliverable_notifications(limit)
16
+ relation = ready_for_delivery.limit(limit)
17
+ claim_notifications(relation)
18
+ end
19
+
20
+ def mark_delivered(notification, time, opts = {})
21
+ opts = DEFAULT_MARK_OPTIONS.dup.merge(opts)
22
+ notification.delivered = true
23
+ notification.delivered_at = time
24
+ notification.save!(validate: false) if opts[:persist]
25
+ end
26
+
27
+ def mark_batch_delivered(notifications)
28
+ return if notifications.empty?
29
+
30
+ now = Time.now
31
+ ids = []
32
+ notifications.each do |n|
33
+ mark_delivered(n, now, persist: false)
34
+ ids << n.id
35
+ end
36
+ Rpush::Client::Mongoid::Notification.in(id: ids).update_all(processing: false, delivered: true, delivered_at: now)
37
+ end
38
+
39
+ def mark_failed(notification, code, description, time, opts = {})
40
+ opts = DEFAULT_MARK_OPTIONS.dup.merge(opts)
41
+ notification.delivered = false
42
+ notification.delivered_at = nil
43
+ notification.failed = true
44
+ notification.failed_at = time
45
+ notification.error_code = code
46
+ notification.error_description = description
47
+ notification.save!(validate: false) if opts[:persist]
48
+ end
49
+
50
+ def mark_batch_failed(notifications, code, description)
51
+ now = Time.now
52
+ ids = []
53
+ notifications.each do |n|
54
+ mark_failed(n, code, description, now, persist: false)
55
+ ids << n.id
56
+ end
57
+ mark_ids_failed(ids, code, description, now)
58
+ end
59
+
60
+ def mark_ids_failed(ids, code, description, time)
61
+ return if ids.empty?
62
+
63
+ Rpush::Client::Mongoid::Notification.in(id: ids).update_all(processing: false, delivered: false, delivered_at: nil, failed: true, failed_at: time, error_code: code, error_description: description)
64
+ end
65
+
66
+ def mark_retryable(notification, deliver_after, opts = {})
67
+ opts = DEFAULT_MARK_OPTIONS.dup.merge(opts)
68
+ notification.delivered = false
69
+ notification.delivered_at = nil
70
+ notification.failed = false
71
+ notification.failed_at = nil
72
+ notification.retries += 1
73
+ notification.deliver_after = deliver_after
74
+
75
+ return unless opts[:persist]
76
+
77
+ notification.save!(validate: false)
78
+ end
79
+
80
+ def mark_batch_retryable(notifications, deliver_after)
81
+ ids = []
82
+ notifications.each do |n|
83
+ mark_retryable(n, deliver_after, persist: false)
84
+ ids << n.id
85
+ end
86
+ mark_ids_retryable(ids, deliver_after)
87
+ end
88
+
89
+ def mark_ids_retryable(ids, deliver_after)
90
+ return if ids.empty?
91
+
92
+ Rpush::Client::Mongoid::Notification.in(id: ids).update_all(processing: false, delivered: false, delivered_at: nil, failed: false, failed_at: nil, deliver_after: deliver_after, '$inc' => { retries: 1 })
93
+ end
94
+
95
+ def create_apns_feedback(failed_at, device_token, app)
96
+ Rpush::Client::Mongoid::Apns::Feedback.create!(failed_at: failed_at, device_token: device_token, app: app)
97
+ end
98
+
99
+ def create_gcm_notification(attrs, data, registration_ids, deliver_after, app) # rubocop:disable ParameterLists
100
+ notification = Rpush::Client::Mongoid::Gcm::Notification.new
101
+ create_gcm_like_notification(notification, attrs, data, registration_ids, deliver_after, app)
102
+ end
103
+
104
+ def create_adm_notification(attrs, data, registration_ids, deliver_after, app) # rubocop:disable ParameterLists
105
+ notification = Rpush::Client::Mongoid::Adm::Notification.new
106
+ create_gcm_like_notification(notification, attrs, data, registration_ids, deliver_after, app)
107
+ end
108
+
109
+ def update_app(app)
110
+ app.save!
111
+ end
112
+
113
+ def update_notification(notification)
114
+ notification.save!
115
+ end
116
+
117
+ def release_connection
118
+ end
119
+
120
+ def reopen_log
121
+ end
122
+
123
+ def pending_delivery_count
124
+ ready_for_delivery.count
125
+ end
126
+
127
+ def translate_integer_notification_id(id)
128
+ Rpush::Client::Mongoid::Notification.find_by(integer_id: id).id
129
+ end
130
+
131
+ private
132
+
133
+ def ready_for_delivery
134
+ Rpush::Client::Mongoid::Notification.where(processing: false, delivered: false, failed: false).or({ deliver_after: nil }, :deliver_after.lt => Time.now)
135
+ end
136
+
137
+ def claim_notifications(relation)
138
+ ids = relation.map(:id)
139
+ relation.where('$isolated' => 1).in(id: ids).update_all(processing: true, processing_pid: Process.pid)
140
+ Rpush::Client::Mongoid::Notification.where(processing: true, processing_pid: Process.pid).in(id: ids).asc('created_at')
141
+ end
142
+
143
+ def create_gcm_like_notification(notification, attrs, data, registration_ids, deliver_after, app) # rubocop:disable ParameterLists
144
+ notification.assign_attributes(attrs)
145
+ notification.data = data
146
+ notification.registration_ids = registration_ids
147
+ notification.deliver_after = deliver_after
148
+ notification.app = app
149
+ notification.save!
150
+ notification
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end
156
+
157
+ Rpush::Daemon::Store::Interface.check(Rpush::Daemon::Store::Mongoid)
@@ -49,7 +49,7 @@ module Rpush
49
49
  end
50
50
 
51
51
  def mark_ids_failed(ids, code, description, time)
52
- ids.each { |id| mark_failed(Rpush::Client::Redis::Apns::Notification.find(id), code, description, time) }
52
+ ids.each { |id| mark_failed(Rpush::Client::Redis::Notification.find(id), code, description, time) }
53
53
  end
54
54
 
55
55
  def mark_retryable(notification, deliver_after, opts = {})
@@ -75,7 +75,7 @@ module Rpush
75
75
  end
76
76
 
77
77
  def mark_ids_retryable(ids, deliver_after)
78
- ids.each { |id| mark_retryable(Rpush::Client::Redis::Apns::Notification.find(id), deliver_after) }
78
+ ids.each { |id| mark_retryable(Rpush::Client::Redis::Notification.find(id), deliver_after) }
79
79
  end
80
80
 
81
81
  def create_apns_feedback(failed_at, device_token, app)
@@ -115,6 +115,10 @@ module Rpush
115
115
  end
116
116
  end
117
117
 
118
+ def translate_integer_notification_id(id)
119
+ id
120
+ end
121
+
118
122
  private
119
123
 
120
124
  def create_gcm_like_notification(notification, attrs, data, registration_ids, deliver_after, app) # rubocop:disable ParameterLists
@@ -14,8 +14,7 @@ module Rpush
14
14
  warning << " #{msg}" if msg
15
15
  class_eval(<<-RUBY, __FILE__, __LINE__)
16
16
  def #{method_name}(*args, &blk)
17
- trace = "\n\nCALLED FROM:\n" + caller.join("\n")
18
- Rpush::Deprecation.warn(#{warning.inspect} + trace)
17
+ Rpush::Deprecation.warn_with_backtrace(#{warning.inspect})
19
18
  #{method_name_as_var}_without_warning(*args, &blk)
20
19
  end
21
20
  RUBY
@@ -16,5 +16,11 @@ module Rpush
16
16
  return if Rpush::Deprecation.muted?
17
17
  STDERR.puts "DEPRECATION WARNING: #{msg}"
18
18
  end
19
+
20
+ def self.warn_with_backtrace(msg)
21
+ return if Rpush::Deprecation.muted?
22
+ trace = "\n\nCALLED FROM:\n" + caller.join("\n")
23
+ warn(msg + trace)
24
+ end
19
25
  end
20
26
  end
@@ -2,6 +2,11 @@ module Rpush
2
2
  def self.embed(options = {})
3
3
  require 'rpush/daemon'
4
4
 
5
+ unless options.empty?
6
+ warning = "Passing configuration options directly to Rpush.embed is deprecated and will be removed from Rpush 2.5.0. Please setup configuration using Rpush.configure { |config| ... } before calling embed."
7
+ Rpush::Deprecation.warn_with_backtrace(warning)
8
+ end
9
+
5
10
  if @embed_thread
6
11
  STDERR.puts 'Rpush.embed can only be run once inside this process.'
7
12
  end
@@ -3,12 +3,9 @@ module Rpush
3
3
  attr_reader :internal_logger
4
4
 
5
5
  def initialize
6
- if Rpush.config.logger
7
- @internal_logger = Rpush.config.logger
8
- else
9
- @internal_logger = setup_logger(open_logfile)
10
- end
11
- rescue Errno::ENOENT, Errno::EPERM => e
6
+ @internal_logger = Rpush.config.logger || setup_logger(open_logfile)
7
+ @internal_logger.level = Rpush.config.log_level
8
+ rescue SystemCallError => e
12
9
  @internal_logger = nil
13
10
  error(e)
14
11
  error('Logging disabled.')
@@ -46,11 +43,11 @@ module Rpush
46
43
 
47
44
  def setup_logger(log)
48
45
  if ActiveSupport.const_defined?('BufferedLogger')
49
- logger = ActiveSupport::BufferedLogger.new(log, Rpush.config.log_level)
46
+ logger = ActiveSupport::BufferedLogger.new(log)
50
47
  logger.auto_flushing = auto_flushing
51
48
  logger
52
49
  else
53
- ActiveSupport::Logger.new(log, Rpush.config.log_level)
50
+ ActiveSupport::Logger.new(log)
54
51
  end
55
52
  end
56
53