subserver 0.1.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +2 -0
- data/CONTRIBUTING.md +54 -0
- data/LICENSE +21 -0
- data/README.md +5 -0
- data/bin/subserver +16 -0
- data/lib/subserver.rb +185 -0
- data/lib/subserver/cli.rb +353 -0
- data/lib/subserver/exception_handler.rb +29 -0
- data/lib/subserver/health.rb +33 -0
- data/lib/subserver/launcher.rb +71 -0
- data/lib/subserver/listener.rb +157 -0
- data/lib/subserver/logging.rb +122 -0
- data/lib/subserver/manager.rb +153 -0
- data/lib/subserver/message_logger.rb +24 -0
- data/lib/subserver/middleware/active_record.rb +21 -0
- data/lib/subserver/middleware/chain.rb +128 -0
- data/lib/subserver/pubsub.rb +20 -0
- data/lib/subserver/rails.rb +57 -0
- data/lib/subserver/subscriber.rb +25 -0
- data/lib/subserver/util.rb +62 -0
- data/lib/subserver/version.rb +4 -0
- metadata +86 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
module Subserver
|
2
|
+
class MessageLogger
|
3
|
+
|
4
|
+
def call(message)
|
5
|
+
start = Time.now
|
6
|
+
logger.info("start")
|
7
|
+
yield
|
8
|
+
logger.info("done: #{elapsed(start)} sec")
|
9
|
+
rescue Exception
|
10
|
+
logger.info("fail: #{elapsed(start)} sec")
|
11
|
+
raise
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def elapsed(start)
|
17
|
+
(Time.now - start).round(3)
|
18
|
+
end
|
19
|
+
|
20
|
+
def logger
|
21
|
+
Subserver.logger
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Subserver
|
3
|
+
module Middleware
|
4
|
+
class ActiveRecord
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
# With Rails 5+ we must use the Reloader **always**.
|
8
|
+
# The reloader handles code loading and db connection management.
|
9
|
+
if defined?(::Rails) && ::Rails::VERSION::MAJOR >= 5
|
10
|
+
raise ArgumentError, "Rails 5 no longer needs or uses the ActiveRecord middleware."
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(*args)
|
15
|
+
yield
|
16
|
+
ensure
|
17
|
+
::ActiveRecord::Base.clear_active_connections!
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Subserver
|
3
|
+
# Middleware is code configured to run before/after
|
4
|
+
# a message is processed. It is patterned after Rack
|
5
|
+
# middleware.
|
6
|
+
#
|
7
|
+
# To modify middleware for the server, just call
|
8
|
+
# with another block:
|
9
|
+
#
|
10
|
+
# Subserver.configure do |config|
|
11
|
+
# config.middleware do |chain|
|
12
|
+
# chain.add MyServerHook
|
13
|
+
# chain.remove ActiveRecord
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# To insert immediately preceding another entry:
|
18
|
+
#
|
19
|
+
# Subserver.configure do |config|
|
20
|
+
# config.middleware do |chain|
|
21
|
+
# chain.insert_before ActiveRecord, MyServerHook
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# To insert immediately after another entry:
|
26
|
+
#
|
27
|
+
# Subserver.configure do |config|
|
28
|
+
# config.middleware do |chain|
|
29
|
+
# chain.insert_after ActiveRecord, MyServerHook
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# This is an example of a minimal server middleware:
|
34
|
+
#
|
35
|
+
# class MyServerHook
|
36
|
+
# def call(subscriber_class, message)
|
37
|
+
# puts "Before work"
|
38
|
+
# yield
|
39
|
+
# puts "After work"
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
#
|
44
|
+
module Middleware
|
45
|
+
class Chain
|
46
|
+
include Enumerable
|
47
|
+
attr_reader :entries
|
48
|
+
|
49
|
+
def initialize_copy(copy)
|
50
|
+
copy.instance_variable_set(:@entries, entries.dup)
|
51
|
+
end
|
52
|
+
|
53
|
+
def each(&block)
|
54
|
+
entries.each(&block)
|
55
|
+
end
|
56
|
+
|
57
|
+
def initialize
|
58
|
+
@entries = []
|
59
|
+
yield self if block_given?
|
60
|
+
end
|
61
|
+
|
62
|
+
def remove(klass)
|
63
|
+
entries.delete_if { |entry| entry.klass == klass }
|
64
|
+
end
|
65
|
+
|
66
|
+
def add(klass, *args)
|
67
|
+
remove(klass) if exists?(klass)
|
68
|
+
entries << Entry.new(klass, *args)
|
69
|
+
end
|
70
|
+
|
71
|
+
def prepend(klass, *args)
|
72
|
+
remove(klass) if exists?(klass)
|
73
|
+
entries.insert(0, Entry.new(klass, *args))
|
74
|
+
end
|
75
|
+
|
76
|
+
def insert_before(oldklass, newklass, *args)
|
77
|
+
i = entries.index { |entry| entry.klass == newklass }
|
78
|
+
new_entry = i.nil? ? Entry.new(newklass, *args) : entries.delete_at(i)
|
79
|
+
i = entries.index { |entry| entry.klass == oldklass } || 0
|
80
|
+
entries.insert(i, new_entry)
|
81
|
+
end
|
82
|
+
|
83
|
+
def insert_after(oldklass, newklass, *args)
|
84
|
+
i = entries.index { |entry| entry.klass == newklass }
|
85
|
+
new_entry = i.nil? ? Entry.new(newklass, *args) : entries.delete_at(i)
|
86
|
+
i = entries.index { |entry| entry.klass == oldklass } || entries.count - 1
|
87
|
+
entries.insert(i+1, new_entry)
|
88
|
+
end
|
89
|
+
|
90
|
+
def exists?(klass)
|
91
|
+
any? { |entry| entry.klass == klass }
|
92
|
+
end
|
93
|
+
|
94
|
+
def retrieve
|
95
|
+
map(&:make_new)
|
96
|
+
end
|
97
|
+
|
98
|
+
def clear
|
99
|
+
entries.clear
|
100
|
+
end
|
101
|
+
|
102
|
+
def invoke(*args)
|
103
|
+
chain = retrieve.dup
|
104
|
+
traverse_chain = lambda do
|
105
|
+
if chain.empty?
|
106
|
+
yield
|
107
|
+
else
|
108
|
+
chain.shift.call(*args, &traverse_chain)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
traverse_chain.call
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
class Entry
|
116
|
+
attr_reader :klass
|
117
|
+
|
118
|
+
def initialize(klass, *args)
|
119
|
+
@klass = klass
|
120
|
+
@args = args
|
121
|
+
end
|
122
|
+
|
123
|
+
def make_new
|
124
|
+
@klass.new(*@args)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "google/cloud/pubsub"
|
2
|
+
|
3
|
+
module Subserver
|
4
|
+
module Pubsub
|
5
|
+
def self.client
|
6
|
+
defined?(@client) ? @client : initialize_client
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.initialize_client
|
10
|
+
@client = Google::Cloud::Pubsub.new(
|
11
|
+
project_id: options[:project_id],
|
12
|
+
credentials: File.expand_path(options[:credentials])
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.options
|
17
|
+
Subserver.options
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Subserver
|
2
|
+
class Rails < ::Rails::Engine
|
3
|
+
# We need to setup this up before any application configuration which might
|
4
|
+
# change Subserver middleware.
|
5
|
+
#
|
6
|
+
# This hook happens after `Rails::Application` is inherited within
|
7
|
+
# config/application.rb and before config is touched, usually within the
|
8
|
+
# class block. Definitely before config/environments/*.rb and
|
9
|
+
# config/initializers/*.rb.
|
10
|
+
config.before_configuration do
|
11
|
+
if ::Rails::VERSION::MAJOR < 5 && defined?(::ActiveRecord)
|
12
|
+
Subserver.middleware do |chain|
|
13
|
+
require 'subserver/middleware/active_record'
|
14
|
+
chain.add Subserver::Middleware::ActiveRecord
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
config.after_initialize do
|
20
|
+
# This hook happens after all initializers are run, just before returning
|
21
|
+
# from config/environment.rb back to subserver/cli.rb.
|
22
|
+
# We have to add the reloader after initialize to see if cache_classes has
|
23
|
+
# been turned on.
|
24
|
+
#
|
25
|
+
Subserver.configure do |_|
|
26
|
+
|
27
|
+
Subserver.options[:subscriber_dir] = ::Rails.root.join('app', 'subscribers')
|
28
|
+
|
29
|
+
if ::Rails::VERSION::MAJOR >= 5
|
30
|
+
Subserver.options[:reloader] = Subserver::Rails::Reloader.new
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class Reloader
|
36
|
+
def initialize(app = ::Rails.application)
|
37
|
+
@app = app
|
38
|
+
end
|
39
|
+
|
40
|
+
def call
|
41
|
+
@app.reloader.wrap do
|
42
|
+
yield
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def inspect
|
47
|
+
"#<Subserver::Rails::Reloader @app=#{@app.class.name}>"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end if defined?(::Rails)
|
51
|
+
end
|
52
|
+
|
53
|
+
if defined?(::Rails) && ::Rails::VERSION::MAJOR < 4
|
54
|
+
$stderr.puts("**************************************************")
|
55
|
+
$stderr.puts("WARNING: Subserver is not supported on Rails < 4. Please Update to a newer version of Rails.")
|
56
|
+
$stderr.puts("**************************************************")
|
57
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Subserver
|
2
|
+
module Subscriber
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
def logger
|
9
|
+
Subserver.logger
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
@subserver_options
|
14
|
+
|
15
|
+
def subserver_options(opts={})
|
16
|
+
(@subserver_options ||= Subserver.default_subscriber_options).merge!(opts)
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_subserver_options
|
20
|
+
@subserver_options
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'socket'
|
3
|
+
require 'securerandom'
|
4
|
+
require 'subserver/exception_handler'
|
5
|
+
|
6
|
+
module Subserver
|
7
|
+
##
|
8
|
+
# This module is part of Subserver core and not intended for extensions.
|
9
|
+
#
|
10
|
+
module Util
|
11
|
+
include ExceptionHandler
|
12
|
+
|
13
|
+
EXPIRY = 60 * 60 * 24
|
14
|
+
|
15
|
+
def watchdog(last_words)
|
16
|
+
yield
|
17
|
+
rescue Exception => ex
|
18
|
+
handle_exception(ex, { context: last_words })
|
19
|
+
raise ex
|
20
|
+
end
|
21
|
+
|
22
|
+
def safe_thread(name, &block)
|
23
|
+
Thread.new do
|
24
|
+
Thread.current['subserver_label'] = name
|
25
|
+
watchdog(name, &block)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def logger
|
30
|
+
Subserver.logger
|
31
|
+
end
|
32
|
+
|
33
|
+
def hostname
|
34
|
+
ENV['DYNO'] || Socket.gethostname
|
35
|
+
end
|
36
|
+
|
37
|
+
def process_nonce
|
38
|
+
@@process_nonce ||= SecureRandom.hex(6)
|
39
|
+
end
|
40
|
+
|
41
|
+
def identity
|
42
|
+
@@identity ||= "#{hostname}:#{$$}:#{process_nonce}"
|
43
|
+
end
|
44
|
+
|
45
|
+
def fire_event(event, options={})
|
46
|
+
reverse = options[:reverse]
|
47
|
+
reraise = options[:reraise]
|
48
|
+
|
49
|
+
arr = Subserver.options[:lifecycle_events][event]
|
50
|
+
arr.reverse! if reverse
|
51
|
+
arr.each do |block|
|
52
|
+
begin
|
53
|
+
block.call
|
54
|
+
rescue => ex
|
55
|
+
handle_exception(ex, { context: "Exception during Subserver lifecycle event.", event: event })
|
56
|
+
raise ex if reraise
|
57
|
+
end
|
58
|
+
end
|
59
|
+
arr.clear
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: subserver
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Scott Hill
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-08-05 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: google-cloud-pubsub
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.31'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.31.0
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0.31'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 0.31.0
|
33
|
+
description: Subserver is a background server process for processing messages from
|
34
|
+
Google Pub/Sub.
|
35
|
+
email:
|
36
|
+
executables:
|
37
|
+
- subserver
|
38
|
+
extensions: []
|
39
|
+
extra_rdoc_files: []
|
40
|
+
files:
|
41
|
+
- CHANGELOG.md
|
42
|
+
- CONTRIBUTING.md
|
43
|
+
- LICENSE
|
44
|
+
- README.md
|
45
|
+
- bin/subserver
|
46
|
+
- lib/subserver.rb
|
47
|
+
- lib/subserver/cli.rb
|
48
|
+
- lib/subserver/exception_handler.rb
|
49
|
+
- lib/subserver/health.rb
|
50
|
+
- lib/subserver/launcher.rb
|
51
|
+
- lib/subserver/listener.rb
|
52
|
+
- lib/subserver/logging.rb
|
53
|
+
- lib/subserver/manager.rb
|
54
|
+
- lib/subserver/message_logger.rb
|
55
|
+
- lib/subserver/middleware/active_record.rb
|
56
|
+
- lib/subserver/middleware/chain.rb
|
57
|
+
- lib/subserver/pubsub.rb
|
58
|
+
- lib/subserver/rails.rb
|
59
|
+
- lib/subserver/subscriber.rb
|
60
|
+
- lib/subserver/util.rb
|
61
|
+
- lib/subserver/version.rb
|
62
|
+
homepage: https://github.com/lifechurch/subserver/
|
63
|
+
licenses:
|
64
|
+
- MIT
|
65
|
+
metadata: {}
|
66
|
+
post_install_message:
|
67
|
+
rdoc_options: []
|
68
|
+
require_paths:
|
69
|
+
- lib
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: 2.3.1
|
75
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0'
|
80
|
+
requirements: []
|
81
|
+
rubyforge_project:
|
82
|
+
rubygems_version: 2.5.2.3
|
83
|
+
signing_key:
|
84
|
+
specification_version: 4
|
85
|
+
summary: Simple background server process for Google Pub/Sub.
|
86
|
+
test_files: []
|