fly-ruby 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +7 -1
- data/README.md +12 -10
- data/Rakefile +10 -1
- data/lib/fly-ruby/configuration.rb +30 -3
- data/lib/fly-ruby/railtie.rb +21 -1
- data/lib/fly-ruby/regional_database.rb +10 -20
- data/lib/fly-ruby/version.rb +2 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 59f0a5a6d3bbfc27e9440a2a96f230b662d42628c0d520df2cfb8269ada04a33
|
4
|
+
data.tar.gz: b21db455e7927c6c10219db01d2ba38fe24c258b3bfe454fd5f25212b95e78c5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0a1a404ac38a7ee8a257bcd97f9e32c25d990b7ba61e9fb4226fe49862934aa7bfbf00e947b39981903a42e54ce232dad508faaadac1b9724ccb7ce01236bdca
|
7
|
+
data.tar.gz: 7cb20aec2cc044cca21d3010cf80da5f3ce902b269b72ffa6739bb61f4abb5fac32e95d57faef5d18c98fc27603704420ae1deb9250cdbf52ba8d5d937c75851
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,23 +1,25 @@
|
|
1
1
|
[![Test](https://github.com/superfly/fly-ruby/actions/workflows/test.yml/badge.svg)](https://github.com/superfly/fly-ruby/actions/workflows/test.yml)
|
2
2
|
|
3
|
-
|
3
|
+
This gem contains helper code and Rack middleware for deploying Ruby web apps on [Fly.io](https://fly.io). Supported features:
|
4
4
|
|
5
|
-
|
5
|
+
* Speed up apps by using region-local Postgresql replicas for database reads
|
6
6
|
|
7
|
-
##
|
7
|
+
## Speed up apps using region-local database replicas
|
8
8
|
|
9
|
-
|
9
|
+
Fly's cross-region private networking makes it easy to run database replicas [alongside your app instances in multiple regions](https://fly.io/docs/getting-started/multi-region-databases/). These replicas can be used for faster reads, leading to faster application performance.
|
10
10
|
|
11
|
-
|
11
|
+
Writes, however, will be slow if performed across regions. Fly allows web apps to specify that a request be *replayed*, at the routing layer, in another region.
|
12
12
|
|
13
|
-
This
|
14
|
-
with any Rack-compatible Ruby framework.
|
13
|
+
This gem includes Rack middleware to automatically route such requests to the primary region. It's designed should work with any Rack-compatible Ruby framework.
|
15
14
|
|
16
15
|
Currently, it does this by:
|
17
16
|
|
18
17
|
* modifying the `DATABASE_URL` to point apps to their local regional replica
|
19
18
|
* replaying non-idempotent (post/put/patch/delete) requests in the primary region
|
20
|
-
* catching Postgresql exceptions caused by writes to a read-only replica, and
|
19
|
+
* catching Postgresql exceptions caused by writes to a read-only replica, and asking for
|
20
|
+
these requests to be replayed in the primary region
|
21
|
+
* replaying all requests within a time threshold after a write, to avoid users seeing
|
22
|
+
their own stale data due to replication lag
|
21
23
|
|
22
24
|
## Requirements
|
23
25
|
|
@@ -35,7 +37,7 @@ Add to your Gemfile and `bundle install`:
|
|
35
37
|
|
36
38
|
`gem "fly-ruby"`
|
37
39
|
|
38
|
-
If you're on Rails, the middleware will insert itself automatically
|
40
|
+
If you're on Rails, the middleware will insert itself automatically, and attempt to reconnect the database.
|
39
41
|
|
40
42
|
## Configuration
|
41
43
|
|
@@ -52,7 +54,7 @@ See [the source code](https://github.com/soupedup/fly-rails/blob/main/lib/fly-ra
|
|
52
54
|
|
53
55
|
This middleware send all requests to the primary if you do something like update a user's database session on every GET request.
|
54
56
|
|
55
|
-
If your replica becomes writeable for some reason, your
|
57
|
+
If your replica becomes writeable for some reason, your cluster may get out of sync.
|
56
58
|
|
57
59
|
## TODO
|
58
60
|
|
data/Rakefile
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
2
|
require "rake/testtask"
|
3
|
+
require_relative "lib/fly-ruby/version"
|
3
4
|
|
4
5
|
Rake::TestTask.new do |t|
|
5
6
|
t.libs << "test"
|
@@ -10,7 +11,15 @@ end
|
|
10
11
|
desc "Run tests"
|
11
12
|
task default: :test
|
12
13
|
|
13
|
-
|
14
14
|
task :top do
|
15
15
|
puts Rake.application.top_level_tasks
|
16
16
|
end
|
17
|
+
|
18
|
+
task :publish do
|
19
|
+
version = Fly::VERSION
|
20
|
+
puts "Publishing fly-ruby #{version}..."
|
21
|
+
sh "git tag -f v#{version}"
|
22
|
+
sh "gem build"
|
23
|
+
sh "gem push fly-ruby-#{version}.gem"
|
24
|
+
sh "git push --tags"
|
25
|
+
end
|
@@ -24,6 +24,8 @@ module Fly
|
|
24
24
|
# primary region after a successful write replay
|
25
25
|
attr_accessor :replay_threshold_in_seconds
|
26
26
|
|
27
|
+
attr_accessor :database_url
|
28
|
+
|
27
29
|
def initialize
|
28
30
|
self.primary_region = ENV["PRIMARY_REGION"]
|
29
31
|
self.current_region = ENV["FLY_REGION"]
|
@@ -33,14 +35,39 @@ module Fly
|
|
33
35
|
self.database_port_env_var = "DATABASE_PORT"
|
34
36
|
self.replay_threshold_cookie = "fly-replay-threshold"
|
35
37
|
self.replay_threshold_in_seconds = 5
|
38
|
+
self.database_url = ENV[database_url_env_var]
|
39
|
+
end
|
40
|
+
|
41
|
+
def regional_database_uri
|
42
|
+
@uri ||= URI.parse(database_url)
|
43
|
+
@uri
|
36
44
|
end
|
37
45
|
|
38
|
-
|
39
|
-
|
46
|
+
# Rails-compatible database configuration
|
47
|
+
def regional_database_config
|
48
|
+
{
|
49
|
+
"host" => "#{current_region}.#{regional_database_uri.hostname}",
|
50
|
+
"port" => 5433,
|
51
|
+
"adapter" => "postgresql"
|
52
|
+
}
|
40
53
|
end
|
41
54
|
|
42
55
|
def eligible_for_activation?
|
43
|
-
database_url && primary_region && current_region
|
56
|
+
database_url && primary_region && current_region && web?
|
57
|
+
end
|
58
|
+
|
59
|
+
# Is the current process a Rails console?
|
60
|
+
def console?
|
61
|
+
defined?(::Rails::Console) && $stdout.isatty && $stdin.isatty
|
62
|
+
end
|
63
|
+
|
64
|
+
# Is the current process a rake task?
|
65
|
+
def rake_task?
|
66
|
+
defined?(::Rake) && !Rake.application.top_level_tasks.empty?
|
67
|
+
end
|
68
|
+
|
69
|
+
def web?
|
70
|
+
!console? && !rake_task?
|
44
71
|
end
|
45
72
|
end
|
46
73
|
end
|
data/lib/fly-ruby/railtie.rb
CHANGED
@@ -1,8 +1,28 @@
|
|
1
|
+
require_relative '../fly-ruby'
|
2
|
+
|
1
3
|
class Fly::Railtie < Rails::Railtie
|
2
4
|
initializer("fly.regional_database") do |app|
|
3
5
|
if Fly.configuration.eligible_for_activation?
|
6
|
+
# Run the middleware high in the stack, but after static file delivery
|
4
7
|
app.config.middleware.insert_after ActionDispatch::Executor, Fly::RegionalDatabase
|
5
|
-
|
8
|
+
|
9
|
+
ActiveSupport::Reloader.to_prepare do
|
10
|
+
# If we already have a database connection when this initializer runs,
|
11
|
+
# we should reconnect to the region-local database. This may need some additional
|
12
|
+
# hooks for forking servers to work correctly.
|
13
|
+
if defined?(ActiveRecord) && ActiveRecord::Base.connected?
|
14
|
+
config = ActiveRecord::Base.connection_db_config.configuration_hash
|
15
|
+
ActiveRecord::Base.establish_connection(config.merge(Fly.configuration.regional_database_config))
|
16
|
+
end
|
17
|
+
|
18
|
+
# Set useful headers for debugging
|
19
|
+
::ApplicationController.send(:after_action) do
|
20
|
+
response.headers['Fly-Region'] = ENV['FLY_REGION']
|
21
|
+
response.headers['Fly-Database-Host'] = Fly.configuration.regional_database_config["host"]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
elsif Fly.configuration.web?
|
6
26
|
puts "Warning: DATABASE_URL, PRIMARY_REGION and FLY_REGION must be set to activate the fly-ruby middleware. Middleware not loaded."
|
7
27
|
end
|
8
28
|
end
|
@@ -8,33 +8,23 @@ module Fly
|
|
8
8
|
class RegionalDatabase
|
9
9
|
def initialize(app)
|
10
10
|
@app = app
|
11
|
-
prefer_regional_database! unless in_primary_region?
|
12
11
|
end
|
13
12
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end
|
13
|
+
# Overwrite the database connection string environment variable
|
14
|
+
# to prefer connections to the regional replica.
|
15
|
+
#
|
16
|
+
# For Rails apps, this process will be repeated at middleware insertion time,
|
17
|
+
# to support situations where the database is already accessed by other
|
18
|
+
# initialization code. See Fly::Railtie.
|
21
19
|
|
22
|
-
# Overwrite the primary database URL with that of the regional replica
|
23
20
|
def prefer_regional_database!
|
24
|
-
|
25
|
-
return if console? || rake_task?
|
26
|
-
|
27
|
-
uri = URI.parse(Fly.configuration.database_url)
|
28
|
-
hostname = "#{Fly.configuration.current_region}.#{uri.hostname}"
|
29
|
-
port = 5433
|
21
|
+
return if Fly.configuration.web?
|
30
22
|
|
31
|
-
uri
|
32
|
-
uri.port = port
|
33
|
-
uri.to_s
|
23
|
+
uri = Fly.configuration.database_uri
|
34
24
|
|
35
25
|
ENV[Fly.configuration.database_url_env_var] = uri.to_s
|
36
|
-
ENV[Fly.configuration.database_host_env_var] = hostname
|
37
|
-
ENV[Fly.configuration.database_port_env_var] = port.to_s
|
26
|
+
ENV[Fly.configuration.database_host_env_var] = uri.hostname
|
27
|
+
ENV[Fly.configuration.database_port_env_var] = uri.port.to_s
|
38
28
|
end
|
39
29
|
|
40
30
|
def in_primary_region?
|
data/lib/fly-ruby/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
|
2
|
-
VERSION = "0.
|
1
|
+
module Fly
|
2
|
+
VERSION = "0.2.0"
|
3
3
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fly-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joshua Sierles
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-07-
|
11
|
+
date: 2021-07-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|