fly-ruby 0.1.1 → 0.2.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 +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
|
[](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
|