rpush 1.0.0-java → 2.0.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.
Files changed (201) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -0
  3. data/README.md +37 -22
  4. data/bin/rpush +13 -4
  5. data/lib/generators/rpush_generator.rb +2 -0
  6. data/lib/generators/templates/add_adm.rb +5 -5
  7. data/lib/generators/templates/add_alert_is_json_to_rapns_notifications.rb +1 -1
  8. data/lib/generators/templates/add_app_to_rapns.rb +2 -2
  9. data/lib/generators/templates/add_fail_after_to_rpush_notifications.rb +1 -1
  10. data/lib/generators/templates/add_gcm.rb +32 -32
  11. data/lib/generators/templates/add_rpush.rb +67 -67
  12. data/lib/generators/templates/add_wpns.rb +2 -2
  13. data/lib/generators/templates/create_rapns_apps.rb +5 -5
  14. data/lib/generators/templates/create_rapns_feedback.rb +2 -2
  15. data/lib/generators/templates/create_rapns_notifications.rb +15 -15
  16. data/lib/generators/templates/rpush.rb +28 -7
  17. data/lib/generators/templates/rpush_2_0_0_updates.rb +42 -0
  18. data/lib/rpush/client/active_model/adm/app.rb +23 -0
  19. data/lib/rpush/client/active_model/adm/data_validator.rb +14 -0
  20. data/lib/rpush/client/active_model/adm/notification.rb +28 -0
  21. data/lib/rpush/client/active_model/apns/app.rb +37 -0
  22. data/lib/rpush/client/active_model/apns/binary_notification_validator.rb +16 -0
  23. data/lib/rpush/client/active_model/apns/device_token_format_validator.rb +14 -0
  24. data/lib/rpush/client/active_model/apns/notification.rb +90 -0
  25. data/lib/rpush/client/active_model/gcm/app.rb +19 -0
  26. data/lib/rpush/client/active_model/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +14 -0
  27. data/lib/rpush/client/active_model/gcm/notification.rb +31 -0
  28. data/lib/rpush/client/active_model/notification.rb +26 -0
  29. data/lib/rpush/client/active_model/payload_data_size_validator.rb +13 -0
  30. data/lib/rpush/client/active_model/registration_ids_count_validator.rb +13 -0
  31. data/lib/rpush/client/active_model/wpns/app.rb +13 -0
  32. data/lib/rpush/client/active_model/wpns/notification.rb +17 -0
  33. data/lib/rpush/client/active_model.rb +21 -0
  34. data/lib/rpush/client/active_record/adm/app.rb +11 -0
  35. data/lib/rpush/client/active_record/adm/notification.rb +11 -0
  36. data/lib/rpush/client/active_record/apns/app.rb +11 -0
  37. data/lib/rpush/client/active_record/apns/feedback.rb +22 -0
  38. data/lib/rpush/client/active_record/apns/notification.rb +46 -0
  39. data/lib/rpush/client/active_record/app.rb +17 -0
  40. data/lib/rpush/client/active_record/gcm/app.rb +11 -0
  41. data/lib/rpush/client/active_record/gcm/notification.rb +11 -0
  42. data/lib/rpush/client/active_record/notification.rb +38 -0
  43. data/lib/rpush/client/active_record/wpns/app.rb +11 -0
  44. data/lib/rpush/client/active_record/wpns/notification.rb +11 -0
  45. data/lib/rpush/client/active_record.rb +19 -0
  46. data/lib/rpush/client/redis/adm/app.rb +14 -0
  47. data/lib/rpush/client/redis/adm/notification.rb +11 -0
  48. data/lib/rpush/client/redis/apns/app.rb +11 -0
  49. data/lib/rpush/client/redis/apns/feedback.rb +20 -0
  50. data/lib/rpush/client/redis/apns/notification.rb +11 -0
  51. data/lib/rpush/client/redis/app.rb +24 -0
  52. data/lib/rpush/client/redis/gcm/app.rb +11 -0
  53. data/lib/rpush/client/redis/gcm/notification.rb +11 -0
  54. data/lib/rpush/client/redis/notification.rb +68 -0
  55. data/lib/rpush/client/redis/wpns/app.rb +11 -0
  56. data/lib/rpush/client/redis/wpns/notification.rb +11 -0
  57. data/lib/rpush/client/redis.rb +35 -0
  58. data/lib/rpush/configuration.rb +27 -6
  59. data/lib/rpush/daemon/adm/delivery.rb +56 -55
  60. data/lib/rpush/daemon/apns/delivery.rb +20 -44
  61. data/lib/rpush/daemon/apns/feedback_receiver.rb +11 -8
  62. data/lib/rpush/daemon/apns.rb +6 -5
  63. data/lib/rpush/daemon/app_runner.rb +103 -99
  64. data/lib/rpush/daemon/batch.rb +54 -40
  65. data/lib/rpush/daemon/delivery.rb +13 -3
  66. data/lib/rpush/daemon/delivery_error.rb +10 -2
  67. data/lib/rpush/daemon/dispatcher/apns_tcp.rb +114 -0
  68. data/lib/rpush/daemon/dispatcher/http.rb +3 -3
  69. data/lib/rpush/daemon/dispatcher/tcp.rb +3 -3
  70. data/lib/rpush/daemon/dispatcher_loop.rb +37 -23
  71. data/lib/rpush/daemon/errors.rb +18 -0
  72. data/lib/rpush/daemon/feeder.rb +28 -39
  73. data/lib/rpush/daemon/gcm/delivery.rb +19 -20
  74. data/lib/rpush/daemon/interruptible_sleep.rb +26 -45
  75. data/lib/rpush/daemon/loggable.rb +2 -4
  76. data/lib/rpush/daemon/proc_title.rb +16 -0
  77. data/lib/rpush/daemon/queue_payload.rb +12 -0
  78. data/lib/rpush/daemon/reflectable.rb +3 -5
  79. data/lib/rpush/daemon/retry_header_parser.rb +6 -6
  80. data/lib/rpush/daemon/retryable_error.rb +2 -0
  81. data/lib/rpush/daemon/ring_buffer.rb +16 -0
  82. data/lib/rpush/daemon/service_config_methods.rb +23 -7
  83. data/lib/rpush/daemon/signal_handler.rb +56 -0
  84. data/lib/rpush/daemon/store/active_record/reconnectable.rb +21 -17
  85. data/lib/rpush/daemon/store/active_record.rb +71 -38
  86. data/lib/rpush/daemon/store/interface.rb +19 -0
  87. data/lib/rpush/daemon/store/redis.rb +149 -0
  88. data/lib/rpush/daemon/string_helpers.rb +15 -0
  89. data/lib/rpush/daemon/synchronizer.rb +60 -0
  90. data/lib/rpush/daemon/tcp_connection.rb +6 -11
  91. data/lib/rpush/daemon/wpns/delivery.rb +21 -30
  92. data/lib/rpush/daemon.rb +40 -60
  93. data/lib/rpush/deprecatable.rb +4 -3
  94. data/lib/rpush/deprecation.rb +7 -10
  95. data/lib/rpush/embed.rb +8 -3
  96. data/lib/rpush/logger.rb +11 -15
  97. data/lib/rpush/push.rb +1 -2
  98. data/lib/rpush/reflection.rb +8 -12
  99. data/lib/rpush/version.rb +1 -1
  100. data/lib/rpush.rb +5 -29
  101. data/lib/tasks/quality.rake +35 -0
  102. data/lib/tasks/test.rake +1 -5
  103. data/spec/.rubocop.yml +4 -0
  104. data/spec/functional/adm_spec.rb +3 -6
  105. data/spec/functional/apns_spec.rb +117 -24
  106. data/spec/functional/embed_spec.rb +20 -20
  107. data/spec/functional/gcm_spec.rb +4 -7
  108. data/spec/functional/new_app_spec.rb +59 -0
  109. data/spec/functional/retry_spec.rb +46 -0
  110. data/spec/functional/synchronization_spec.rb +68 -0
  111. data/spec/functional/wpns_spec.rb +3 -6
  112. data/spec/functional_spec_helper.rb +26 -0
  113. data/spec/integration/rpush_spec.rb +13 -0
  114. data/spec/integration/support/gcm_success_response.json +1 -0
  115. data/spec/spec_helper.rb +60 -0
  116. data/spec/support/active_record_setup.rb +48 -0
  117. data/{config → spec/support/config}/database.yml +0 -0
  118. data/spec/support/install.sh +43 -7
  119. data/spec/support/simplecov_helper.rb +9 -5
  120. data/spec/support/simplecov_quality_formatter.rb +10 -6
  121. data/spec/unit/apns_feedback_spec.rb +3 -3
  122. data/spec/unit/{adm → client/active_record/adm}/app_spec.rb +3 -3
  123. data/spec/unit/{adm → client/active_record/adm}/notification_spec.rb +5 -7
  124. data/spec/unit/client/active_record/apns/app_spec.rb +29 -0
  125. data/spec/unit/client/active_record/apns/feedback_spec.rb +9 -0
  126. data/spec/unit/client/active_record/apns/notification_spec.rb +231 -0
  127. data/spec/unit/client/active_record/app_spec.rb +30 -0
  128. data/spec/unit/client/active_record/gcm/app_spec.rb +4 -0
  129. data/spec/unit/{gcm → client/active_record/gcm}/notification_spec.rb +5 -7
  130. data/spec/unit/client/active_record/notification_spec.rb +21 -0
  131. data/spec/unit/client/active_record/wpns/app_spec.rb +4 -0
  132. data/spec/unit/client/active_record/wpns/notification_spec.rb +21 -0
  133. data/spec/unit/configuration_spec.rb +12 -5
  134. data/spec/unit/daemon/adm/delivery_spec.rb +66 -55
  135. data/spec/unit/daemon/apns/certificate_expired_error_spec.rb +3 -3
  136. data/spec/unit/daemon/apns/delivery_spec.rb +90 -83
  137. data/spec/unit/daemon/apns/feedback_receiver_spec.rb +22 -17
  138. data/spec/unit/daemon/app_runner_spec.rb +78 -186
  139. data/spec/unit/daemon/batch_spec.rb +52 -115
  140. data/spec/unit/daemon/delivery_spec.rb +15 -1
  141. data/spec/unit/daemon/dispatcher/http_spec.rb +3 -2
  142. data/spec/unit/daemon/dispatcher/tcp_spec.rb +10 -9
  143. data/spec/unit/daemon/dispatcher_loop_spec.rb +6 -24
  144. data/spec/unit/daemon/feeder_spec.rb +38 -39
  145. data/spec/unit/daemon/gcm/delivery_spec.rb +122 -101
  146. data/spec/unit/daemon/reflectable_spec.rb +2 -2
  147. data/spec/unit/daemon/retryable_error_spec.rb +1 -1
  148. data/spec/unit/daemon/service_config_methods_spec.rb +6 -3
  149. data/spec/unit/daemon/signal_handler_spec.rb +95 -0
  150. data/spec/unit/daemon/store/active_record/reconnectable_spec.rb +48 -27
  151. data/spec/unit/daemon/store/active_record_spec.rb +38 -47
  152. data/spec/unit/daemon/tcp_connection_spec.rb +22 -34
  153. data/spec/unit/daemon/wpns/delivery_spec.rb +58 -50
  154. data/spec/unit/daemon_spec.rb +48 -82
  155. data/spec/unit/embed_spec.rb +6 -4
  156. data/spec/unit/logger_spec.rb +35 -43
  157. data/spec/unit/notification_shared.rb +9 -79
  158. data/spec/unit/push_spec.rb +6 -10
  159. data/spec/unit/reflection_spec.rb +25 -25
  160. data/spec/unit/rpush_spec.rb +1 -2
  161. data/spec/unit_spec_helper.rb +33 -88
  162. metadata +126 -76
  163. data/lib/rpush/TODO +0 -3
  164. data/lib/rpush/adm/app.rb +0 -15
  165. data/lib/rpush/adm/data_validator.rb +0 -11
  166. data/lib/rpush/adm/notification.rb +0 -29
  167. data/lib/rpush/apns/app.rb +0 -29
  168. data/lib/rpush/apns/binary_notification_validator.rb +0 -12
  169. data/lib/rpush/apns/device_token_format_validator.rb +0 -12
  170. data/lib/rpush/apns/feedback.rb +0 -16
  171. data/lib/rpush/apns/notification.rb +0 -84
  172. data/lib/rpush/app.rb +0 -18
  173. data/lib/rpush/daemon/apns/certificate_expired_error.rb +0 -20
  174. data/lib/rpush/daemon/apns/disconnection_error.rb +0 -20
  175. data/lib/rpush/daemon/dispatcher_loop_collection.rb +0 -33
  176. data/lib/rpush/daemon/too_many_requests_error.rb +0 -20
  177. data/lib/rpush/gcm/app.rb +0 -11
  178. data/lib/rpush/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +0 -11
  179. data/lib/rpush/gcm/notification.rb +0 -30
  180. data/lib/rpush/notification.rb +0 -69
  181. data/lib/rpush/notifier.rb +0 -52
  182. data/lib/rpush/payload_data_size_validator.rb +0 -10
  183. data/lib/rpush/railtie.rb +0 -11
  184. data/lib/rpush/registration_ids_count_validator.rb +0 -10
  185. data/lib/rpush/wpns/app.rb +0 -9
  186. data/lib/rpush/wpns/notification.rb +0 -26
  187. data/lib/tasks/cane.rake +0 -18
  188. data/lib/tasks/rpush.rake +0 -16
  189. data/spec/unit/apns/app_spec.rb +0 -29
  190. data/spec/unit/apns/feedback_spec.rb +0 -9
  191. data/spec/unit/apns/notification_spec.rb +0 -208
  192. data/spec/unit/app_spec.rb +0 -30
  193. data/spec/unit/daemon/apns/disconnection_error_spec.rb +0 -18
  194. data/spec/unit/daemon/dispatcher_loop_collection_spec.rb +0 -37
  195. data/spec/unit/daemon/interruptible_sleep_spec.rb +0 -68
  196. data/spec/unit/daemon/too_many_requests_error_spec.rb +0 -14
  197. data/spec/unit/gcm/app_spec.rb +0 -4
  198. data/spec/unit/notification_spec.rb +0 -15
  199. data/spec/unit/notifier_spec.rb +0 -49
  200. data/spec/unit/wpns/app_spec.rb +0 -4
  201. data/spec/unit/wpns/notification_spec.rb +0 -30
@@ -1,60 +1,41 @@
1
+ require 'monitor'
2
+
1
3
  module Rpush
2
4
  module Daemon
3
5
  class InterruptibleSleep
4
-
5
- def initialize
6
- @sleep_reader, @wake_writer = IO.pipe
7
- @udp_wakeup = nil
6
+ def initialize(duration)
7
+ @duration = duration
8
+ @obj = Object.new
9
+ @obj.extend(MonitorMixin)
10
+ @condition = @obj.new_cond
11
+ @stop = false
8
12
  end
9
13
 
10
- # enable wake on receiving udp packets at the given address and port
11
- # this returns the host,port used by bind in case an ephemeral port
12
- # was indicated by specifying 0 as the port number.
13
- # @return [String,Integer] host,port of bound UDP socket.
14
- def enable_wake_on_udp(host, port)
15
- @udp_wakeup = UDPSocket.new
16
- @udp_wakeup.bind(host, port)
17
- @udp_wakeup.addr.values_at(3,1)
14
+ def sleep
15
+ return if @stop
16
+ @obj.synchronize { @condition.wait(100_000) }
18
17
  end
19
18
 
20
- # wait for the given timeout in seconds, or data was written to the pipe
21
- # or the udp wakeup port if enabled.
22
- # @return [boolean] true if the sleep was interrupted, or false
23
- def sleep(timeout)
24
- read_ports = [@sleep_reader]
25
- read_ports << @udp_wakeup if @udp_wakeup
26
- rs, = IO.select(read_ports, nil, nil, timeout) rescue nil
27
-
28
- # consume all data on the readable io's so that our next call will wait for more data
29
- perform_io(rs, @sleep_reader, :read_nonblock)
30
- perform_io(rs, @udp_wakeup, :recv_nonblock)
31
-
32
- !rs.nil? && rs.any?
33
- end
19
+ def start
20
+ @stop = false
34
21
 
35
- # writing to the pipe will wake the sleeping thread
36
- def interrupt_sleep
37
- @wake_writer.write('.')
22
+ @thread = Thread.new do
23
+ loop do
24
+ break if @stop
25
+ Kernel.sleep(@duration)
26
+ wakeup
27
+ end
28
+ end
38
29
  end
39
30
 
40
- def close
41
- @sleep_reader.close rescue nil
42
- @wake_writer.close rescue nil
43
- @udp_wakeup.close if @udp_wakeup rescue nil
31
+ def stop
32
+ @stop = true
33
+ wakeup
34
+ @thread.kill if @thread
44
35
  end
45
36
 
46
- private
47
-
48
- def perform_io(selected, io, meth)
49
- if selected && selected.include?(io)
50
- while true
51
- begin
52
- io.__send__(meth, 1)
53
- rescue Errno::EAGAIN, IO::WaitReadable
54
- break
55
- end
56
- end
57
- end
37
+ def wakeup
38
+ @obj.synchronize { @condition.signal }
58
39
  end
59
40
  end
60
41
  end
@@ -20,10 +20,8 @@ module Rpush
20
20
  private
21
21
 
22
22
  def app_prefix(msg)
23
- if app = instance_variable_get('@app')
24
- msg = "[#{app.name}] #{msg}"
25
- end
26
-
23
+ app = instance_variable_get('@app')
24
+ msg = "[#{app.name}] #{msg}" if app
27
25
  msg
28
26
  end
29
27
  end
@@ -0,0 +1,16 @@
1
+ module Rpush
2
+ module Daemon
3
+ class ProcTitle
4
+ def self.update
5
+ $0 = proc_title
6
+ end
7
+
8
+ def self.proc_title
9
+ total_dispatchers = AppRunner.total_dispatchers
10
+ dispatchers_str = total_dispatchers == 1 ? 'dispatcher' : 'dispatchers'
11
+ total_queued = AppRunner.total_queued
12
+ format("rpush | %s | %d queued | %d %s", Rpush.config.environment, total_queued, total_dispatchers, dispatchers_str)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,12 @@
1
+ module Rpush
2
+ module Daemon
3
+ class QueuePayload
4
+ attr_reader :batch, :notification
5
+
6
+ def initialize(batch, notification = nil)
7
+ @batch = batch
8
+ @notification = notification
9
+ end
10
+ end
11
+ end
12
+ end
@@ -2,11 +2,9 @@ module Rpush
2
2
  module Daemon
3
3
  module Reflectable
4
4
  def reflect(name, *args)
5
- begin
6
- Rpush.reflections.__dispatch(name, *args)
7
- rescue StandardError => e
8
- Rpush.logger.error(e)
9
- end
5
+ Rpush.reflections.__dispatch(name, *args)
6
+ rescue StandardError => e
7
+ Rpush.logger.error(e)
10
8
  end
11
9
  end
12
10
  end
@@ -10,12 +10,12 @@ module Rpush
10
10
  end
11
11
 
12
12
  def parse
13
- if @header
14
- if @header.to_s =~ /^[0-9]+$/
15
- Time.now + @header.to_i
16
- else
17
- Time.httpdate(@header)
18
- end
13
+ return unless @header
14
+
15
+ if @header.to_s =~ /^[0-9]+$/
16
+ Time.now + @header.to_i
17
+ else
18
+ Time.httpdate(@header)
19
19
  end
20
20
  end
21
21
  end
@@ -17,4 +17,6 @@ module Rpush
17
17
  "Retryable error for #{@notification_id}, received error #{@code} (#{@description}) - retry after #{@response.header['retry-after']}"
18
18
  end
19
19
  end
20
+
21
+ class RateLimitError < RetryableError; end
20
22
  end
@@ -0,0 +1,16 @@
1
+ module Rpush
2
+ module Daemon
3
+ class RingBuffer < Array
4
+ def initialize(max_size)
5
+ @max_size = max_size
6
+ end
7
+
8
+ def <<(obj)
9
+ shift if size >= @max_size
10
+ super
11
+ end
12
+
13
+ alias_method :push, :<<
14
+ end
15
+ end
16
+ end
@@ -2,17 +2,27 @@ module Rpush
2
2
  module Daemon
3
3
  module ServiceConfigMethods
4
4
  DISPATCHERS = {
5
- :http => Rpush::Daemon::Dispatcher::Http,
6
- :tcp => Rpush::Daemon::Dispatcher::Tcp
5
+ http: Rpush::Daemon::Dispatcher::Http,
6
+ tcp: Rpush::Daemon::Dispatcher::Tcp,
7
+ apns_tcp: Rpush::Daemon::Dispatcher::ApnsTcp
7
8
  }
8
9
 
10
+ def batch_deliveries(value = nil)
11
+ return batch_deliveries? if value.nil?
12
+ @batch_deliveries = value
13
+ end
14
+
15
+ def batch_deliveries?
16
+ @batch_deliveries == true
17
+ end
18
+
9
19
  def dispatcher(name = nil, options = {})
10
20
  @dispatcher_name = name
11
21
  @dispatcher_options = options
12
22
  end
13
23
 
14
24
  def dispatcher_class
15
- DISPATCHERS[@dispatcher_name] || (raise NotImplementedError)
25
+ DISPATCHERS[@dispatcher_name] || (fail NotImplementedError)
16
26
  end
17
27
 
18
28
  def delivery_class
@@ -23,10 +33,16 @@ module Rpush
23
33
  dispatcher_class.new(app, delivery_class, @dispatcher_options)
24
34
  end
25
35
 
26
- def loops(*loops)
27
- @loops ||= []
28
- @loops = loops if loops.any?
29
- @loops
36
+ def loops(classes, options = {})
37
+ classes = Array[*classes]
38
+ @loops = classes.map { |cls| [cls, options] }
39
+ end
40
+
41
+ def loop_instances(app)
42
+ (@loops || []).map do |cls, options|
43
+ next unless options.key?(:if) ? options[:if].call : true
44
+ cls.new(app)
45
+ end.compact
30
46
  end
31
47
  end
32
48
  end
@@ -0,0 +1,56 @@
1
+ module Rpush
2
+ module Daemon
3
+ class SignalHandler
4
+ class << self
5
+ attr_reader :thread
6
+ end
7
+
8
+ def self.start
9
+ return unless trap_signals?
10
+
11
+ read_io, @write_io = IO.pipe
12
+ start_handler(read_io)
13
+ %w(INT TERM HUP USR2).each do |signal|
14
+ Signal.trap(signal) { @write_io.puts(signal) }
15
+ end
16
+ end
17
+
18
+ def self.stop
19
+ @write_io.puts('break') if @write_io
20
+ @thread.join if @thread
21
+ end
22
+
23
+ def self.start_handler(read_io)
24
+ @thread = Thread.new do
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)
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ def self.trap_signals?
52
+ !Rpush.config.embedded
53
+ end
54
+ end
55
+ end
56
+ end
@@ -1,11 +1,15 @@
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)
1
+ class PGError < StandardError; end unless defined?(PGError)
2
+ module PG
3
+ class Error < StandardError; end unless defined?(::PG::Error)
4
+ end
5
+ class Mysql; class Error < StandardError; end; end unless defined?(Mysql)
6
+ module Mysql2; class Error < StandardError; end; end unless defined?(Mysql2)
7
+ module ActiveRecord
8
+ class JDBCError < StandardError; end unless defined?(::ActiveRecord::JDBCError)
9
+ end
6
10
 
7
11
  # :nocov:
8
- if !defined?(::SQLite3::Exception)
12
+ unless defined?(::SQLite3::Exception)
9
13
  module SQLite3
10
14
  class Exception < StandardError; end
11
15
  end
@@ -16,19 +20,19 @@ module Rpush
16
20
  module Store
17
21
  class ActiveRecord
18
22
  module Reconnectable
19
- ADAPTER_ERRORS = [::ActiveRecord::StatementInvalid, PGError, Mysql::Error,
20
- Mysql2::Error, ::ActiveRecord::JDBCError, SQLite3::Exception]
23
+ ADAPTER_ERRORS = [::ActiveRecord::StatementInvalid, PGError, PG::Error,
24
+ Mysql::Error, Mysql2::Error, ::ActiveRecord::JDBCError,
25
+ SQLite3::Exception, ::ActiveRecord::ConnectionTimeoutError]
21
26
 
22
27
  def with_database_reconnect_and_retry
23
- begin
24
- ::ActiveRecord::Base.connection_pool.with_connection do
25
- yield
26
- end
27
- rescue *ADAPTER_ERRORS => e
28
- Rpush.logger.error(e)
29
- database_connection_lost
30
- retry
28
+ ::ActiveRecord::Base.connection_pool.with_connection do
29
+ yield
31
30
  end
31
+ rescue *ADAPTER_ERRORS => e
32
+ Rpush.logger.error(e)
33
+ sleep_to_avoid_thrashing
34
+ database_connection_lost
35
+ retry
32
36
  end
33
37
 
34
38
  def database_connection_lost
@@ -55,7 +59,7 @@ module Rpush
55
59
 
56
60
  def check_database_is_connected
57
61
  # Simply asking the adapter for the connection state is not sufficient.
58
- Rpush::Notification.count
62
+ Rpush::Client::ActiveRecord::Notification.count
59
63
  end
60
64
 
61
65
  def sleep_to_avoid_thrashing
@@ -8,49 +8,66 @@ module Rpush
8
8
  class ActiveRecord
9
9
  include Reconnectable
10
10
 
11
- DEFAULT_MARK_OPTIONS = {:persist => true}
11
+ DEFAULT_MARK_OPTIONS = { persist: true }
12
12
 
13
- def deliverable_notifications(apps)
13
+ def app(id)
14
+ Rpush::Client::ActiveRecord::App.find(id)
15
+ end
16
+
17
+ def all_apps
18
+ Rpush::Client::ActiveRecord::App.all
19
+ end
20
+
21
+ def deliverable_notifications(limit)
14
22
  with_database_reconnect_and_retry do
15
- batch_size = Rpush.config.batch_size
16
- relation = Rpush::Notification.ready_for_delivery.for_apps(apps)
17
- relation = relation.limit(batch_size) unless Rpush.config.push
18
- relation.to_a
23
+ Rpush::Client::ActiveRecord::Notification.transaction do
24
+ relation = ready_for_delivery
25
+ relation = relation.limit(limit)
26
+ notifications = relation.lock(true).to_a
27
+ mark_processing(notifications)
28
+ notifications
29
+ end
19
30
  end
20
31
  end
21
32
 
22
33
  def mark_retryable(notification, deliver_after, opts = {})
23
34
  opts = DEFAULT_MARK_OPTIONS.dup.merge(opts)
35
+ notification.processing = false
24
36
  notification.retries += 1
25
37
  notification.deliver_after = deliver_after
26
38
 
27
- if opts[:persist]
28
- with_database_reconnect_and_retry do
29
- notification.save!(:validate => false)
30
- end
39
+ return unless opts[:persist]
40
+
41
+ with_database_reconnect_and_retry do
42
+ notification.save!(validate: false)
31
43
  end
32
44
  end
33
45
 
34
46
  def mark_batch_retryable(notifications, deliver_after)
35
47
  ids = []
36
48
  notifications.each do |n|
37
- mark_retryable(n, deliver_after, :persist => false)
49
+ mark_retryable(n, deliver_after, persist: false)
38
50
  ids << n.id
39
51
  end
52
+ mark_ids_retryable(ids, deliver_after)
53
+ end
54
+
55
+ def mark_ids_retryable(ids, deliver_after)
40
56
  with_database_reconnect_and_retry do
41
- Rpush::Notification.where(:id => ids).update_all(['retries = retries + 1, deliver_after = ?', deliver_after])
57
+ Rpush::Client::ActiveRecord::Notification.where(id: ids).update_all(['processing = ?, delivered = ?, delivered_at = ?, failed = ?, failed_at = ?, retries = retries + 1, deliver_after = ?', false, false, nil, false, nil, deliver_after])
42
58
  end
43
59
  end
44
60
 
45
61
  def mark_delivered(notification, time, opts = {})
46
62
  opts = DEFAULT_MARK_OPTIONS.dup.merge(opts)
63
+ notification.processing = false
47
64
  notification.delivered = true
48
65
  notification.delivered_at = time
49
66
 
50
- if opts[:persist]
51
- with_database_reconnect_and_retry do
52
- notification.save!(:validate => false)
53
- end
67
+ return unless opts[:persist]
68
+
69
+ with_database_reconnect_and_retry do
70
+ notification.save!(validate: false)
54
71
  end
55
72
  end
56
73
 
@@ -58,16 +75,17 @@ module Rpush
58
75
  now = Time.now
59
76
  ids = []
60
77
  notifications.each do |n|
61
- mark_delivered(n, now, :persist => false)
78
+ mark_delivered(n, now, persist: false)
62
79
  ids << n.id
63
80
  end
64
81
  with_database_reconnect_and_retry do
65
- Rpush::Notification.where(:id => ids).update_all(['delivered = ?, delivered_at = ?', true, now])
82
+ Rpush::Client::ActiveRecord::Notification.where(id: ids).update_all(['processing = ?, delivered = ?, delivered_at = ?', false, true, now])
66
83
  end
67
84
  end
68
85
 
69
86
  def mark_failed(notification, code, description, time, opts = {})
70
87
  opts = DEFAULT_MARK_OPTIONS.dup.merge(opts)
88
+ notification.processing = false
71
89
  notification.delivered = false
72
90
  notification.delivered_at = nil
73
91
  notification.failed = true
@@ -75,10 +93,10 @@ module Rpush
75
93
  notification.error_code = code
76
94
  notification.error_description = description
77
95
 
78
- if opts[:persist]
79
- with_database_reconnect_and_retry do
80
- notification.save!(:validate => false)
81
- end
96
+ return unless opts[:persist]
97
+
98
+ with_database_reconnect_and_retry do
99
+ notification.save!(validate: false)
82
100
  end
83
101
  end
84
102
 
@@ -86,28 +104,32 @@ module Rpush
86
104
  now = Time.now
87
105
  ids = []
88
106
  notifications.each do |n|
89
- mark_failed(n, code, description, now, :persist => false)
107
+ mark_failed(n, code, description, now, persist: false)
90
108
  ids << n.id
91
109
  end
110
+ mark_ids_failed(ids, code, description, now)
111
+ end
112
+
113
+ def mark_ids_failed(ids, code, description, time)
92
114
  with_database_reconnect_and_retry do
93
- Rpush::Notification.where(:id => ids).update_all(['delivered = ?, delivered_at = NULL, failed = ?, failed_at = ?, error_code = ?, error_description = ?', false, true, now, code, description])
115
+ Rpush::Client::ActiveRecord::Notification.where(id: ids).update_all(['processing = ?, delivered = ?, delivered_at = NULL, failed = ?, failed_at = ?, error_code = ?, error_description = ?', false, false, true, time, code, description])
94
116
  end
95
117
  end
96
118
 
97
119
  def create_apns_feedback(failed_at, device_token, app)
98
120
  with_database_reconnect_and_retry do
99
- Rpush::Apns::Feedback.create!(:failed_at => failed_at,
100
- :device_token => device_token, :app => app)
121
+ Rpush::Client::ActiveRecord::Apns::Feedback.create!(failed_at: failed_at,
122
+ device_token: device_token, app_id: app.id)
101
123
  end
102
124
  end
103
125
 
104
126
  def create_gcm_notification(attrs, data, registration_ids, deliver_after, app)
105
- notification = Rpush::Gcm::Notification.new
127
+ notification = Rpush::Client::ActiveRecord::Gcm::Notification.new
106
128
  create_gcm_like_notification(notification, attrs, data, registration_ids, deliver_after, app)
107
129
  end
108
130
 
109
131
  def create_adm_notification(attrs, data, registration_ids, deliver_after, app)
110
- notification = Rpush::Adm::Notification.new
132
+ notification = Rpush::Client::ActiveRecord::Adm::Notification.new
111
133
  create_gcm_like_notification(notification, attrs, data, registration_ids, deliver_after, app)
112
134
  end
113
135
 
@@ -123,21 +145,15 @@ module Rpush
123
145
  end
124
146
  end
125
147
 
126
- def after_daemonize
127
- reconnect_database
128
- end
129
-
130
148
  def release_connection
131
- begin
132
- ::ActiveRecord::Base.connection_pool.release_connection
133
- rescue StandardError => e
134
- Rpush.logger.error(e)
135
- end
149
+ ::ActiveRecord::Base.connection.close
150
+ rescue StandardError => e
151
+ Rpush.logger.error(e)
136
152
  end
137
153
 
138
154
  private
139
155
 
140
- def create_gcm_like_notification(notification, attrs, data, registration_ids, deliver_after, app)
156
+ def create_gcm_like_notification(notification, attrs, data, registration_ids, deliver_after, app) # rubocop:disable ParameterLists
141
157
  with_database_reconnect_and_retry do
142
158
  notification.assign_attributes(attrs)
143
159
  notification.data = data
@@ -148,7 +164,24 @@ module Rpush
148
164
  notification
149
165
  end
150
166
  end
167
+
168
+ def ready_for_delivery
169
+ Rpush::Client::ActiveRecord::Notification.where('processing = ? AND delivered = ? AND failed = ? AND (deliver_after IS NULL OR deliver_after < ?)', false, false, false, Time.now)
170
+ end
171
+
172
+ def mark_processing(notifications)
173
+ return if notifications.empty?
174
+
175
+ ids = []
176
+ notifications.each do |n|
177
+ n.processing = true
178
+ ids << n.id
179
+ end
180
+ Rpush::Client::ActiveRecord::Notification.where(id: ids).update_all(['processing = ?', true])
181
+ end
151
182
  end
152
183
  end
153
184
  end
154
185
  end
186
+
187
+ Rpush::Daemon::Store::Interface.check(Rpush::Daemon::Store::ActiveRecord)
@@ -0,0 +1,19 @@
1
+ module Rpush
2
+ module Daemon
3
+ module Store
4
+ class Interface
5
+ PUBLIC_METHODS = [:deliverable_notifications, :mark_retryable,
6
+ :mark_batch_retryable, :mark_delivered, :mark_batch_delivered,
7
+ :mark_failed, :mark_batch_failed, :create_apns_feedback,
8
+ :create_gcm_notification, :create_adm_notification, :update_app,
9
+ :update_notification, :release_connection,
10
+ :all_apps, :app, :mark_ids_failed, :mark_ids_retryable]
11
+
12
+ def self.check(klass)
13
+ missing = PUBLIC_METHODS - klass.instance_methods.map(&:to_sym)
14
+ fail "#{klass} must implement #{missing.join(', ')}." if missing.any?
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end