active_record_serverless 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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 52554bbe78950b6e39d662452b8f665a24dfd0c15edfccc8c7a2822a4a7fd0d0
4
+ data.tar.gz: 84848ac174145998e5a480a99727531d634ec10cb85fde8d5fdbba7f8707a0c7
5
+ SHA512:
6
+ metadata.gz: 12ccacf518f299a27e3035176e421e1751f300bcce1a7dbe1f0c1e1128500c19e35d31cc51c8b75d0eb816d6f8570b2e2a15484b5fb0e58aecaff793493a6ba4
7
+ data.tar.gz: e72a10dd5ff32e5b1a5b5d286c767ad8142541c08e2e343b4e0fb1f5cb2281f919911f2882e18e53a472448d9e7793a6405909c66fbc93f8661e38bb03c22f89
@@ -0,0 +1,41 @@
1
+ # ActiveRecordServerless
2
+
3
+ Handles database connection's to disconnect on a timeout. This allows [Aurora Serverless](https://aws.amazon.com/rds/aurora/serverless/) servers to spin down.
4
+
5
+ ## Installing
6
+
7
+ In a Gemfile
8
+
9
+ ```ruby
10
+ gem 'active_record_serverless'
11
+ ```
12
+
13
+ Manually
14
+
15
+ `gem install active_record_serverless`
16
+
17
+ ### Config
18
+
19
+ ```ruby
20
+ # in config/database.yml
21
+ development:
22
+ # other db params
23
+ serverless:
24
+ timeout: 60.0 # Timeout every 60 seconds.
25
+ ```
26
+
27
+ ### Forking
28
+
29
+ ```ruby
30
+ after_fork do
31
+ ActiveRecordServerless.start_timeout
32
+ end
33
+ ```
34
+
35
+ ### Non-Rails Application.
36
+
37
+ #### Setup
38
+
39
+ ```ruby
40
+ ActiveRecordServerless::Installer.install!(nil)
41
+ ```
@@ -0,0 +1,2 @@
1
+ require 'bundler/gem_tasks'
2
+ task default: :spec
@@ -0,0 +1,10 @@
1
+ require 'active_record_serverless/installer'
2
+ require 'active_record_serverless/reconnect'
3
+ require 'active_record_serverless/timeout'
4
+ require 'active_record_serverless/version'
5
+
6
+ require 'active_record_serverless/railtie' if defined?(::Rails)
7
+
8
+ module ActiveRecordServerless
9
+ class Error < StandardError; end
10
+ end
@@ -0,0 +1,17 @@
1
+ require_relative 'timeout'
2
+
3
+ module ActiveRecordServerless
4
+ class Executor
5
+ def self.setup
6
+ ActiveRecord::Base.connection.class.class_eval do
7
+ alias_method :serverless_execute, :execute
8
+
9
+ def execute(*args)
10
+ ActiveRecordServerless::Timeout.touch!
11
+
12
+ serverless_execute(*args)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,53 @@
1
+ require 'active_record'
2
+
3
+ require_relative 'timeout'
4
+ require_relative 'logger'
5
+ require_relative 'executor'
6
+
7
+ module ActiveRecordServerless
8
+ def self.start_timeout(timeout = nil)
9
+ ActiveRecordServerless::Installer.start_timeout(timeout)
10
+ end
11
+
12
+ class Installer
13
+ class << self
14
+ def config
15
+ ActiveRecord::Base.connection_config.with_indifferent_access.dig(:serverless)
16
+ end
17
+
18
+ def install!(_app)
19
+ disabled = config.dig(:disabled).nil? ? false : config.dig(:disabled)
20
+
21
+ if disabled == true
22
+ message = <<~EOF
23
+ Skipping ActiveRecordServerless Install.
24
+
25
+ Connection config explicitly marked serverless as disabled.
26
+ EOF
27
+ ActiveRecordServerless.log(message)
28
+ return
29
+ end
30
+
31
+ _install
32
+ end
33
+
34
+ def start_timeout(timeout = nil)
35
+ timeout ||= (config.dig(:timeout) || 60.0)
36
+
37
+ ActiveRecordServerless.log("Installing ActiveRecordServerless [timeout=#{timeout}]")
38
+
39
+ ActiveRecordServerless::Timeout.on_timeout(timeout) do
40
+ ActiveRecordServerless.log('ActiveRecordServerless timeout...')
41
+ ActiveRecord::Base.connection_pool.disconnect!
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def _install
48
+ ActiveRecordServerless::Executor.setup
49
+ ActiveRecordServerless::Installer.start_timeout
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,9 @@
1
+ module ActiveRecordServerless
2
+ def self.log(msg)
3
+ if defined?(::Rails)
4
+ ::Rails.logger.debug msg
5
+ else
6
+ STDERR.puts msg
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,20 @@
1
+ require 'active_record'
2
+
3
+ require_relative '../timeout'
4
+
5
+ module ActiveRecordServerless
6
+ module Middleware
7
+ class EnsureConnection
8
+ def initialize(app)
9
+ @app = app
10
+ end
11
+
12
+ def call(env)
13
+ ActiveRecordServerless::Timeout.touch! do
14
+ ActiveRecord::Base.establish_connection unless ActiveRecord::Base.connected?
15
+ end
16
+ @app.call(env)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,13 @@
1
+ require 'rails'
2
+
3
+ require_relative 'installer'
4
+ require_relative 'middleware/ensure_connection'
5
+
6
+ module ActiveRecordServerless
7
+ class Railtie < ::Rails::Railtie
8
+ initializer 'active_record_serverless.install', after: 'active_record.initialize_database' do |app|
9
+ ActiveRecordServerless::Installer.install!(app)
10
+ app.middleware.insert_before(Rack::Runtime, ActiveRecordServerless::Middleware::EnsureConnection)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ require 'active_record'
2
+ require 'active_support'
3
+
4
+ require_relative 'timeout'
5
+
6
+ module ActiveRecordServerless
7
+ def self.reconnect
8
+ ActiveRecordServerless::Timeout.touch!
9
+
10
+ return if ActiveRecord::Base.connected?
11
+
12
+ ActiveSupport::Notifications.instrument('active_record_serverless.reconnect') do
13
+ ActiveRecord::Base.establish_connection
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,69 @@
1
+ require 'active_support'
2
+
3
+ module ActiveRecordServerless
4
+ class Timeout
5
+ MUTEX = Mutex.new
6
+
7
+ class << self
8
+ def touch!
9
+ ActiveSupport::Notifications.instrument('active_record_serverless.touch') do
10
+ MUTEX.synchronize do
11
+ _last_used(Time.now)
12
+ yield if block_given?
13
+ end
14
+ end
15
+ end
16
+
17
+ def last_used
18
+ MUTEX.synchronize { _last_used }
19
+ end
20
+
21
+ def on_timeout(timeout, &block)
22
+ raise ArgumentError, 'Timeout must be greater than 1' unless timeout.to_f >= 1.0
23
+
24
+ touch!
25
+
26
+ MUTEX.synchronize do
27
+ _running_id((_running_id || 0) + 1)
28
+ end
29
+
30
+ _on_timeout(timeout.to_f, running_id, &block)
31
+ end
32
+
33
+ def running?
34
+ running_id.positive?
35
+ end
36
+
37
+ def running_id
38
+ MUTEX.synchronize { _running_id }
39
+ end
40
+
41
+ private
42
+
43
+ def _on_timeout(timeout, rid, &block)
44
+ Thread.new do
45
+ sleep(timeout)
46
+ last = last_used
47
+ delta = Time.now.to_f - last.to_f
48
+ if delta >= timeout
49
+ opts = { last_time: last, delta: delta, running_id: rid }
50
+ ActiveSupport::Notifications.instrument('active_record_serverless.timeout', opts) do
51
+ yield(last, delta)
52
+ end
53
+ end
54
+ _on_timeout(timeout, rid, &block) if rid == running_id
55
+ end
56
+ end
57
+
58
+ def _last_used(time = nil)
59
+ @_last_used = time unless time.nil?
60
+ @_last_used
61
+ end
62
+
63
+ def _running_id(running = nil)
64
+ @_running_id = running unless running.nil?
65
+ @_running_id
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,3 @@
1
+ module ActiveRecordServerless
2
+ VERSION = '0.1.0'.freeze
3
+ end
metadata ADDED
@@ -0,0 +1,178 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: active_record_serverless
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Maddie Schipper
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-03-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 4.0.0
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '7.0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 4.0.0
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '7.0'
33
+ - !ruby/object:Gem::Dependency
34
+ name: activesupport
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: 4.0.0
40
+ - - "<"
41
+ - !ruby/object:Gem::Version
42
+ version: '7.0'
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: 4.0.0
50
+ - - "<"
51
+ - !ruby/object:Gem::Version
52
+ version: '7.0'
53
+ - !ruby/object:Gem::Dependency
54
+ name: bundler
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '2.0'
60
+ type: :development
61
+ prerelease: false
62
+ version_requirements: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - "~>"
65
+ - !ruby/object:Gem::Version
66
+ version: '2.0'
67
+ - !ruby/object:Gem::Dependency
68
+ name: pry
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '0.12'
74
+ type: :development
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: '0.12'
81
+ - !ruby/object:Gem::Dependency
82
+ name: rake
83
+ requirement: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - "~>"
86
+ - !ruby/object:Gem::Version
87
+ version: '10.0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - "~>"
93
+ - !ruby/object:Gem::Version
94
+ version: '10.0'
95
+ - !ruby/object:Gem::Dependency
96
+ name: rspec-rails
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - "~>"
100
+ - !ruby/object:Gem::Version
101
+ version: '3.8'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - "~>"
107
+ - !ruby/object:Gem::Version
108
+ version: '3.8'
109
+ - !ruby/object:Gem::Dependency
110
+ name: simplecov
111
+ requirement: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - "~>"
114
+ - !ruby/object:Gem::Version
115
+ version: '0.16'
116
+ type: :development
117
+ prerelease: false
118
+ version_requirements: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - "~>"
121
+ - !ruby/object:Gem::Version
122
+ version: '0.16'
123
+ - !ruby/object:Gem::Dependency
124
+ name: sqlite3
125
+ requirement: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - "~>"
128
+ - !ruby/object:Gem::Version
129
+ version: '1.3'
130
+ type: :development
131
+ prerelease: false
132
+ version_requirements: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - "~>"
135
+ - !ruby/object:Gem::Version
136
+ version: '1.3'
137
+ description: ActiveRecord Serverless handle AWS Aurora Serverless
138
+ email:
139
+ - me@maddiesch.com
140
+ executables: []
141
+ extensions: []
142
+ extra_rdoc_files: []
143
+ files:
144
+ - README.md
145
+ - Rakefile
146
+ - lib/active_record_serverless.rb
147
+ - lib/active_record_serverless/executor.rb
148
+ - lib/active_record_serverless/installer.rb
149
+ - lib/active_record_serverless/logger.rb
150
+ - lib/active_record_serverless/middleware/ensure_connection.rb
151
+ - lib/active_record_serverless/railtie.rb
152
+ - lib/active_record_serverless/reconnect.rb
153
+ - lib/active_record_serverless/timeout.rb
154
+ - lib/active_record_serverless/version.rb
155
+ homepage: https://github.com/maddiesch/active_record_serverless
156
+ licenses:
157
+ - MIT
158
+ metadata: {}
159
+ post_install_message:
160
+ rdoc_options: []
161
+ require_paths:
162
+ - lib
163
+ required_ruby_version: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - ">="
166
+ - !ruby/object:Gem::Version
167
+ version: '0'
168
+ required_rubygems_version: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: '0'
173
+ requirements: []
174
+ rubygems_version: 3.0.2
175
+ signing_key:
176
+ specification_version: 4
177
+ summary: ActiveRecord Serverless handle AWS Aurora Serverless
178
+ test_files: []