rapns 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/bin/rapns +11 -9
- data/lib/rapns/daemon/feeder.rb +27 -0
- data/lib/rapns/daemon/logger.rb +5 -5
- data/lib/rapns/daemon.rb +15 -14
- data/lib/rapns/patches/rails/3.1.0/postgresql_adapter.rb +12 -0
- data/lib/rapns/patches/rails/3.1.1/postgresql_adapter.rb +17 -0
- data/lib/rapns/patches.rb +6 -0
- data/lib/rapns/version.rb +1 -1
- data/spec/rapns/daemon/feeder_spec.rb +79 -1
- data/spec/rapns/daemon/logger_spec.rb +7 -0
- data/spec/rapns/daemon_spec.rb +1 -1
- data/spec/spec_helper.rb +25 -10
- metadata +5 -2
data/bin/rapns
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
3
|
+
require 'optparse'
|
4
|
+
require 'rapns'
|
5
|
+
require 'rapns/daemon'
|
6
6
|
|
7
7
|
foreground = false
|
8
8
|
environment = ARGV[0]
|
9
|
-
banner =
|
9
|
+
banner = 'Usage: rapns <Rails environment> [options]'
|
10
10
|
ARGV.options do |opts|
|
11
11
|
opts.banner = banner
|
12
|
-
opts.on(
|
13
|
-
opts.on(
|
14
|
-
opts.on(
|
12
|
+
opts.on('-f', '--foreground', 'Run in the foreground.') { foreground = true }
|
13
|
+
opts.on('-v', '--version', 'Print this version of rapns.') { puts "rapns #{Rapns::VERSION}"; exit }
|
14
|
+
opts.on('-h', '--help', 'You\'re looking at it.') { puts opts; exit }
|
15
15
|
opts.parse!
|
16
16
|
end
|
17
17
|
|
@@ -20,7 +20,9 @@ if environment.nil?
|
|
20
20
|
exit 1
|
21
21
|
end
|
22
22
|
|
23
|
-
ENV[
|
24
|
-
load
|
23
|
+
ENV['RAILS_ENV'] = environment
|
24
|
+
load 'config/environment.rb'
|
25
|
+
|
26
|
+
require 'rapns/patches'
|
25
27
|
|
26
28
|
Rapns::Daemon.start(environment, foreground)
|
data/lib/rapns/daemon/feeder.rb
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
class PGError < StandardError; end if !defined?(PGError)
|
2
|
+
module Mysql; class Error < StandardError; end; end if !defined?(Mysql)
|
3
|
+
module Mysql2; class Error < StandardError; end; end if !defined?(Mysql2)
|
4
|
+
|
5
|
+
ADAPTER_ERRORS = [PGError, Mysql::Error, Mysql2::Error]
|
6
|
+
|
1
7
|
module Rapns
|
2
8
|
module Daemon
|
3
9
|
class Feeder
|
@@ -15,6 +21,9 @@ module Rapns
|
|
15
21
|
end
|
16
22
|
|
17
23
|
Rapns::Daemon.delivery_queue.wait_until_empty
|
24
|
+
rescue ActiveRecord::StatementInvalid, *ADAPTER_ERRORS => e
|
25
|
+
Rapns::Daemon.logger.error(e)
|
26
|
+
reconnect
|
18
27
|
rescue StandardError => e
|
19
28
|
Rapns::Daemon.logger.error(e)
|
20
29
|
end
|
@@ -25,6 +34,24 @@ module Rapns
|
|
25
34
|
def self.stop
|
26
35
|
@stop = true
|
27
36
|
end
|
37
|
+
|
38
|
+
def self.reconnect
|
39
|
+
Rapns::Daemon.logger.warn('Lost connection to database, reconnecting...')
|
40
|
+
attempts = 0
|
41
|
+
loop do
|
42
|
+
begin
|
43
|
+
Rapns::Daemon.logger.warn("Attempt #{attempts += 1}")
|
44
|
+
ActiveRecord::Base.clear_all_connections!
|
45
|
+
ActiveRecord::Base.establish_connection
|
46
|
+
Rapns::Notification.count
|
47
|
+
break
|
48
|
+
rescue *ADAPTER_ERRORS => e
|
49
|
+
Rapns::Daemon.logger.error(e, :airbrake_notify => false)
|
50
|
+
sleep 2 # Avoid thrashing.
|
51
|
+
end
|
52
|
+
end
|
53
|
+
Rapns::Daemon.logger.warn('Database reconnected')
|
54
|
+
end
|
28
55
|
end
|
29
56
|
end
|
30
57
|
end
|
data/lib/rapns/daemon/logger.rb
CHANGED
@@ -3,7 +3,7 @@ module Rapns
|
|
3
3
|
class Logger
|
4
4
|
def initialize(options)
|
5
5
|
@options = options
|
6
|
-
log_path = File.join(Rails.root,
|
6
|
+
log_path = File.join(Rails.root, 'log', 'rapns.log')
|
7
7
|
@logger = ActiveSupport::BufferedLogger.new(log_path, Rails.logger.level)
|
8
8
|
@logger.auto_flushing = Rails.logger.auto_flushing
|
9
9
|
end
|
@@ -12,13 +12,13 @@ module Rapns
|
|
12
12
|
log(:info, msg)
|
13
13
|
end
|
14
14
|
|
15
|
-
def error(msg)
|
16
|
-
airbrake_notify(msg) if msg.is_a?(Exception)
|
17
|
-
log(:error, msg,
|
15
|
+
def error(msg, options = {})
|
16
|
+
airbrake_notify(msg) if msg.is_a?(Exception) && options[:airbrake_notify] != false
|
17
|
+
log(:error, msg, 'ERROR')
|
18
18
|
end
|
19
19
|
|
20
20
|
def warn(msg)
|
21
|
-
log(:warn, msg,
|
21
|
+
log(:warn, msg, 'WARNING')
|
22
22
|
end
|
23
23
|
|
24
24
|
private
|
data/lib/rapns/daemon.rb
CHANGED
@@ -2,17 +2,17 @@ require 'thread'
|
|
2
2
|
require 'socket'
|
3
3
|
require 'pathname'
|
4
4
|
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
15
|
-
require
|
5
|
+
require 'rapns/daemon/configuration'
|
6
|
+
require 'rapns/daemon/certificate'
|
7
|
+
require 'rapns/daemon/delivery_error'
|
8
|
+
require 'rapns/daemon/pool'
|
9
|
+
require 'rapns/daemon/connection_pool'
|
10
|
+
require 'rapns/daemon/connection'
|
11
|
+
require 'rapns/daemon/delivery_queue'
|
12
|
+
require 'rapns/daemon/delivery_handler'
|
13
|
+
require 'rapns/daemon/delivery_handler_pool'
|
14
|
+
require 'rapns/daemon/feeder'
|
15
|
+
require 'rapns/daemon/logger'
|
16
16
|
|
17
17
|
module Rapns
|
18
18
|
module Daemon
|
@@ -26,7 +26,7 @@ module Rapns
|
|
26
26
|
@foreground = foreground
|
27
27
|
setup_signal_hooks
|
28
28
|
|
29
|
-
self.configuration = Configuration.new(environment, File.join(Rails.root,
|
29
|
+
self.configuration = Configuration.new(environment, File.join(Rails.root, 'config', 'rapns', 'rapns.yml'))
|
30
30
|
configuration.load
|
31
31
|
|
32
32
|
self.logger = Logger.new(:foreground => foreground, :airbrake_notify => configuration.airbrake_notify)
|
@@ -49,6 +49,7 @@ module Rapns
|
|
49
49
|
self.connection_pool = ConnectionPool.new(configuration.connections)
|
50
50
|
connection_pool.populate
|
51
51
|
|
52
|
+
logger.info("Ready")
|
52
53
|
Feeder.start
|
53
54
|
end
|
54
55
|
|
@@ -57,7 +58,7 @@ module Rapns
|
|
57
58
|
def self.setup_signal_hooks
|
58
59
|
@shutting_down = false
|
59
60
|
|
60
|
-
[
|
61
|
+
['SIGINT', 'SIGTERM'].each do |signal|
|
61
62
|
Signal.trap(signal) do
|
62
63
|
handle_shutdown_signal
|
63
64
|
end
|
@@ -93,7 +94,7 @@ module Rapns
|
|
93
94
|
|
94
95
|
def self.write_pid_file
|
95
96
|
if !configuration.pid_file.blank?
|
96
|
-
File.open(configuration.pid_file,
|
97
|
+
File.open(configuration.pid_file, 'w') do |f|
|
97
98
|
f.puts $$
|
98
99
|
end
|
99
100
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
class PostgreSQLAdapter < AbstractAdapter
|
4
|
+
class StatementPool < ConnectionAdapters::StatementPool
|
5
|
+
def dealloc(key)
|
6
|
+
@connection.query "DEALLOCATE #{key}" if connection_active?
|
7
|
+
end
|
8
|
+
|
9
|
+
def connection_active?
|
10
|
+
@connection.status == PGconn::CONNECTION_OK
|
11
|
+
rescue PGError
|
12
|
+
false
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
if ActiveRecord::Base.configurations[Rails.env]['adapter'] == 'postgresql'
|
2
|
+
if Rails::VERSION::STRING == '3.1.0' || Rails::VERSION::STRING == '3.1.1'
|
3
|
+
STDERR.puts "[WARNING] Detected Rails #{Rails::VERSION::STRING}, patching PostgreSQLAdapter to fix reconnection bug: https://github.com/rails/rails/issues/3160"
|
4
|
+
require "rapns/patches/rails/#{Rails::VERSION::STRING}/postgresql_adapter.rb"
|
5
|
+
end
|
6
|
+
end
|
data/lib/rapns/version.rb
CHANGED
@@ -4,7 +4,7 @@ describe Rapns::Daemon::Feeder do
|
|
4
4
|
before do
|
5
5
|
Rapns::Daemon::Feeder.stub(:sleep)
|
6
6
|
@notification = Rapns::Notification.create!(:device_token => "a" * 64)
|
7
|
-
@logger = mock("Logger", :info => nil, :error => nil)
|
7
|
+
@logger = mock("Logger", :info => nil, :error => nil, :warn => nil)
|
8
8
|
Rapns::Daemon.stub(:logger).and_return(@logger)
|
9
9
|
@queue = mock(:push => nil, :wait_until_empty => nil)
|
10
10
|
Rapns::Daemon.stub(:delivery_queue).and_return(@queue)
|
@@ -63,4 +63,82 @@ describe Rapns::Daemon::Feeder do
|
|
63
63
|
Rapns::Daemon.logger.should_receive(:error).with(e)
|
64
64
|
Rapns::Daemon::Feeder.enqueue_notifications
|
65
65
|
end
|
66
|
+
|
67
|
+
context "when the database connection is lost" do
|
68
|
+
let(:error) { adapter_error.new("db down!") }
|
69
|
+
before do
|
70
|
+
ActiveRecord::Base.stub(:clear_all_connections!)
|
71
|
+
ActiveRecord::Base.stub(:establish_connection)
|
72
|
+
Rapns::Notification.stub(:ready_for_delivery).and_raise(error)
|
73
|
+
end
|
74
|
+
|
75
|
+
def adapter_error
|
76
|
+
case $adapter
|
77
|
+
when 'postgresql'
|
78
|
+
PGError
|
79
|
+
when 'mysql'
|
80
|
+
Mysql::Error
|
81
|
+
when 'mysql2'
|
82
|
+
Mysql2::Error
|
83
|
+
else
|
84
|
+
raise "Please update #{__FILE__} for adapter #{$adapter}"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should log the error raised" do
|
89
|
+
Rapns::Daemon.logger.should_receive(:error).with(error)
|
90
|
+
Rapns::Daemon::Feeder.enqueue_notifications
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should log that the database is being reconnected" do
|
94
|
+
Rapns::Daemon.logger.should_receive(:warn).with("Lost connection to database, reconnecting...")
|
95
|
+
Rapns::Daemon::Feeder.enqueue_notifications
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should log the reconnection attempt" do
|
99
|
+
Rapns::Daemon.logger.should_receive(:warn).with("Attempt 1")
|
100
|
+
Rapns::Daemon::Feeder.enqueue_notifications
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should clear all connections" do
|
104
|
+
ActiveRecord::Base.should_receive(:clear_all_connections!)
|
105
|
+
Rapns::Daemon::Feeder.enqueue_notifications
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should establish a new connection" do
|
109
|
+
ActiveRecord::Base.should_receive(:establish_connection)
|
110
|
+
Rapns::Daemon::Feeder.enqueue_notifications
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should test out the new connection by performing a count" do
|
114
|
+
Rapns::Notification.should_receive(:count)
|
115
|
+
Rapns::Daemon::Feeder.enqueue_notifications
|
116
|
+
end
|
117
|
+
|
118
|
+
context "when the reconnection attempt is not successful" do
|
119
|
+
let(:error) { adapter_error.new("shit got real") }
|
120
|
+
|
121
|
+
before do
|
122
|
+
class << Rapns::Notification
|
123
|
+
def count
|
124
|
+
@count_calls += 1
|
125
|
+
return if @count_calls == 2
|
126
|
+
raise @error
|
127
|
+
end
|
128
|
+
end
|
129
|
+
Rapns::Notification.instance_variable_set("@count_calls", 0)
|
130
|
+
Rapns::Notification.instance_variable_set("@error", error)
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should log errors raised when the reconnection is not successful without notifying airbrake" do
|
134
|
+
Rapns::Daemon.logger.should_receive(:error).with(error, :airbrake_notify => false)
|
135
|
+
Rapns::Daemon::Feeder.enqueue_notifications
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should sleep to avoid thrashing when the database is down" do
|
139
|
+
Rapns::Daemon::Feeder.should_receive(:sleep).with(2)
|
140
|
+
Rapns::Daemon::Feeder.enqueue_notifications
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
66
144
|
end
|
@@ -88,6 +88,13 @@ describe Rapns::Daemon::Logger do
|
|
88
88
|
logger.error(e)
|
89
89
|
end
|
90
90
|
|
91
|
+
it "should not notify Airbrake if explicitly disabled in the call to error" do
|
92
|
+
e = RuntimeError.new("hi mom")
|
93
|
+
logger = Rapns::Daemon::Logger.new(:foreground => false, :airbrake_notify => true)
|
94
|
+
Airbrake.should_not_receive(:notify).with(e)
|
95
|
+
logger.error(e, :airbrake_notify => false)
|
96
|
+
end
|
97
|
+
|
91
98
|
it "should not attempt to notify Airbrake of the error is not an Exception" do
|
92
99
|
logger = Rapns::Daemon::Logger.new(:foreground => false)
|
93
100
|
Airbrake.should_not_receive(:notify)
|
data/spec/rapns/daemon_spec.rb
CHANGED
@@ -27,7 +27,7 @@ describe Rapns::Daemon, "when starting" do
|
|
27
27
|
Rapns::Daemon::Feeder.stub(:wait)
|
28
28
|
Rapns::Daemon.stub(:daemonize)
|
29
29
|
Rapns::Daemon.stub(:write_pid_file)
|
30
|
-
@logger = mock("Logger")
|
30
|
+
@logger = mock("Logger", :info => nil)
|
31
31
|
Rapns::Daemon::Logger.stub(:new).and_return(@logger)
|
32
32
|
end
|
33
33
|
|
data/spec/spec_helper.rb
CHANGED
@@ -1,30 +1,45 @@
|
|
1
|
-
ENV[
|
1
|
+
ENV['RAILS_ENV'] = 'test'
|
2
2
|
|
3
3
|
require 'simplecov'
|
4
4
|
SimpleCov.start do
|
5
5
|
add_filter '/spec/'
|
6
6
|
end
|
7
7
|
|
8
|
-
require
|
8
|
+
require 'active_record'
|
9
|
+
adapters = ['mysql', 'mysql2', 'postgresql']
|
10
|
+
$adapter = ENV['ADAPTER'] || 'postgresql'
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
+
if !adapters.include?($adapter)
|
13
|
+
puts "No such adapter '#{$adapter}'. Valid adapters are #{adapters.join(', ')}."
|
14
|
+
exit 1
|
15
|
+
end
|
16
|
+
|
17
|
+
puts "Using #{$adapter} adapter."
|
18
|
+
ActiveRecord::Base.establish_connection('adapter' => $adapter, 'database' => 'rapns_test')
|
19
|
+
require 'generators/templates/create_rapns_notifications'
|
12
20
|
|
13
21
|
CreateRapnsNotifications.down rescue ActiveRecord::StatementInvalid
|
14
22
|
CreateRapnsNotifications.up
|
15
23
|
|
16
24
|
Bundler.require(:default)
|
17
25
|
|
18
|
-
require
|
26
|
+
require 'shoulda'
|
19
27
|
require 'database_cleaner'
|
28
|
+
|
20
29
|
DatabaseCleaner.strategy = :truncation
|
21
30
|
|
22
|
-
require
|
23
|
-
require
|
31
|
+
require 'rapns'
|
32
|
+
require 'rapns/daemon'
|
33
|
+
|
34
|
+
require 'perftools'
|
24
35
|
|
25
36
|
RSpec.configure do |config|
|
37
|
+
# config.before :suite do
|
38
|
+
# PerfTools::CpuProfiler.start('/tmp/rapns_profile')
|
39
|
+
# end
|
40
|
+
# config.after :suite do
|
41
|
+
# PerfTools::CpuProfiler.stop
|
42
|
+
# end
|
43
|
+
|
26
44
|
config.before(:each) { DatabaseCleaner.clean }
|
27
45
|
end
|
28
|
-
|
29
|
-
|
30
|
-
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rapns
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-10-
|
12
|
+
date: 2011-10-09 00:00:00.000000000Z
|
13
13
|
dependencies: []
|
14
14
|
description: Easy to use library for Apple's Push Notification Service with Rails
|
15
15
|
3
|
@@ -38,6 +38,9 @@ files:
|
|
38
38
|
- lib/rapns/daemon/logger.rb
|
39
39
|
- lib/rapns/daemon/pool.rb
|
40
40
|
- lib/rapns/notification.rb
|
41
|
+
- lib/rapns/patches.rb
|
42
|
+
- lib/rapns/patches/rails/3.1.0/postgresql_adapter.rb
|
43
|
+
- lib/rapns/patches/rails/3.1.1/postgresql_adapter.rb
|
41
44
|
- lib/rapns/version.rb
|
42
45
|
- README.md
|
43
46
|
- spec/rapns/daemon/certificate_spec.rb
|