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