rpush 1.0.0-java → 2.0.0-java

Sign up to get free protection for your applications and to get access to all the features.
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