active_record_serverless 0.1.0

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