rails_failover 0.2.0 → 0.5.2

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.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsFailover
4
- VERSION = "0.2.0"
4
+ VERSION = "0.5.2"
5
5
  end
data/makefile CHANGED
@@ -1,31 +1,21 @@
1
- PORT := 6381
2
- PID_PATH := /tmp/redis.pid
3
- SOCKET_PATH := /tmp/redis.sock
4
- DBFILENAME := master.rdb
5
- replica_port := 6382
6
- REPLICA_PID_PATH := /tmp/redis_replica.pid
7
- REPLICA_SOCKET_PATH := /tmp/redis_replica.sock
8
- REPLICA_DBFILENAME := replica.rdb
1
+ include postgresql.mk
2
+ include redis.mk
9
3
 
10
- default:
11
- @make -s all
4
+ all: redis
12
5
 
13
- all: start test stop
6
+ active_record: teardown_dummy_rails_server setup_dummy_rails_server test_active_record
14
7
 
15
- test:
16
- bundle exec rspec ${RSPEC_PATH}
8
+ test_active_record:
9
+ @ACTIVE_RECORD=1 bundle exec rspec --tag type:active_record ${RSPEC_PATH}
17
10
 
18
- start: start_master start_replica
19
- stop: stop_replica stop_master
11
+ setup_dummy_rails_server:
12
+ @cd spec/support/dummy_app && bundle install --quiet --without test --without development && yarn install && RAILS_ENV=production $(BUNDLER_BIN) exec rails db:create db:migrate db:seed
20
13
 
21
- stop_master:
22
- @redis-cli -p ${PORT} shutdown
14
+ start_dummy_rails_server:
15
+ @cd spec/support/dummy_app && BUNDLE_GEMFILE=Gemfile UNICORN_WORKERS=5 SECRET_KEY_BASE=somekey bundle exec unicorn -c config/unicorn.conf.rb -D -E production
23
16
 
24
- start_master:
25
- @redis-server --daemonize yes --pidfile ${PID_PATH} --port ${PORT} --unixsocket ${SOCKET_PATH} --dbfilename ${DBFILENAME} --logfile /dev/null
17
+ stop_dummy_rails_server:
18
+ @kill -TERM $(shell cat spec/support/dummy_app/tmp/pids/unicorn.pid)
26
19
 
27
- stop_replica:
28
- @redis-cli -p ${replica_port} shutdown
29
-
30
- start_replica:
31
- @redis-server --daemonize yes --pidfile ${REPLICA_PID_PATH} --port ${replica_port} --unixsocket ${REPLICA_SOCKET_PATH} --replicaof 127.0.0.1 ${PORT} --dbfilename ${REPLICA_DBFILENAME} --logfile /dev/null
20
+ teardown_dummy_rails_server:
21
+ @cd spec/support/dummy_app && DISABLE_DATABASE_ENVIRONMENT_CHECK=1 RAILS_ENV=production $(BUNDLER_BIN) exec rails db:drop
@@ -0,0 +1,54 @@
1
+ PG_BIN_DIR := $(shell pg_config --bindir)
2
+ PWD := $(shell pwd)
3
+ PG_PRIMARY_DIR := $(PWD)/tmp/primary
4
+ PG_PRIMARY_DATA_DIR := $(PG_PRIMARY_DIR)/data
5
+ PG_PRIMARY_RUN_DIR := $(PG_PRIMARY_DIR)/run
6
+ PG_REPLICA_DIR := $(PWD)/tmp/replica
7
+ PG_REPLICA_DATA_DIR := $(PG_REPLICA_DIR)/data
8
+ PG_REPLICA_RUN_DIR := $(PG_REPLICA_DIR)/run
9
+ PG_PRIMARY_PORT := 5434
10
+ PG_REPLICA_PORT := 5435
11
+ PG_REPLICATION_USER := replication
12
+ PG_REPLICATION_PASSWORD := password
13
+ PG_REPLICATION_SLOT_NAME := replication
14
+
15
+ setup_pg: init_primary start_pg_primary setup_primary init_replica stop_pg_primary
16
+
17
+ setup_primary:
18
+ @$(PG_BIN_DIR)/psql -p $(PG_PRIMARY_PORT) -h $(PG_PRIMARY_RUN_DIR) -d postgres -c "CREATE USER $(PG_REPLICATION_USER) WITH REPLICATION ENCRYPTED PASSWORD '$(PG_REPLICATION_PASSWORD)';"
19
+ @$(PG_BIN_DIR)/psql -p $(PG_PRIMARY_PORT) -h $(PG_PRIMARY_RUN_DIR) -d postgres -c "SELECT * FROM pg_create_physical_replication_slot('$(PG_REPLICATION_SLOT_NAME)');"
20
+ @$(PG_BIN_DIR)/psql -p $(PG_PRIMARY_PORT) -h $(PG_PRIMARY_RUN_DIR) -d postgres -c "CREATE USER test;"
21
+ @$(PG_BIN_DIR)/psql -p $(PG_PRIMARY_PORT) -h $(PG_PRIMARY_RUN_DIR) -d postgres -c "CREATE DATABASE test;"
22
+
23
+ start_pg: start_pg_primary start_pg_replica
24
+
25
+ stop_pg: stop_pg_replica stop_pg_primary
26
+
27
+ init_primary:
28
+ @mkdir -p $(PG_PRIMARY_DATA_DIR)
29
+ @mkdir -p $(PG_PRIMARY_RUN_DIR)
30
+ @$(PG_BIN_DIR)/initdb -E UTF8 -D $(PG_PRIMARY_DATA_DIR)
31
+
32
+ init_replica:
33
+ @mkdir -p $(PG_REPLICA_DATA_DIR)
34
+ @mkdir -p $(PG_REPLICA_RUN_DIR)
35
+ @PGPASSWORD=$(PG_REPLICATION_PASSWORD) $(PG_BIN_DIR)/pg_basebackup -D $(PG_REPLICA_DATA_DIR) -X stream -h $(PG_PRIMARY_RUN_DIR) -p $(PG_PRIMARY_PORT) -U $(PG_REPLICATION_USER) -w -R
36
+ @chmod 0700 $(PG_REPLICA_DATA_DIR)
37
+
38
+ start_pg_primary:
39
+ @$(PG_BIN_DIR)/pg_ctl --silent --log /dev/null -w -D $(PG_PRIMARY_DATA_DIR) -o "-p $(PG_PRIMARY_PORT)" -o "-k $(PG_PRIMARY_RUN_DIR)" start
40
+
41
+ start_pg_replica:
42
+ @$(PG_BIN_DIR)/pg_ctl --silent --log /dev/null -w -D $(PG_REPLICA_DATA_DIR) -o "-p $(PG_REPLICA_PORT)" -o "-k $(PG_REPLICA_RUN_DIR)" start
43
+
44
+ restart_pg_primary:
45
+ @$(PG_BIN_DIR)/pg_ctl --silent --log /dev/null -w -D $(PG_PRIMARY_DATA_DIR) -o "-p $(PG_PRIMARY_PORT)" -o "-k $(PG_PRIMARY_RUN_DIR)" restart
46
+
47
+ stop_pg_primary:
48
+ @$(PG_BIN_DIR)/pg_ctl --silent --log /dev/null -w -D $(PG_PRIMARY_DATA_DIR) -o "-p $(PG_PRIMARY_PORT)" -o "-k $(PG_PRIMARY_RUN_DIR)" stop
49
+
50
+ stop_pg_replica:
51
+ @$(PG_BIN_DIR)/pg_ctl --silent --log /dev/null -w -D $(PG_REPLICA_DATA_DIR) -o "-p $(PG_REPLICA_PORT)" -o "-k $(PG_REPLICA_RUN_DIR)" stop
52
+
53
+ cleanup_pg:
54
+ @rm -rf $(PG_PRIMARY_DIR) $(PG_REPLICA_DIR)
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
8
8
  spec.authors = ["Alan Tan"]
9
9
  spec.email = ["tgx@discourse.org"]
10
10
 
11
- spec.summary = %q{Failover for PostgreSQL and Redis}
11
+ spec.summary = %q{Failover for ActiveRecord and Redis}
12
12
  spec.license = "MIT"
13
13
  spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
14
14
 
@@ -21,5 +21,7 @@ Gem::Specification.new do |spec|
21
21
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
22
  spec.require_paths = ["lib"]
23
23
 
24
- spec.add_dependency('redis', '~> 4')
24
+ ["activerecord", "railties"].each do |gem_name|
25
+ spec.add_dependency gem_name, "~> 6.0"
26
+ end
25
27
  end
@@ -0,0 +1,28 @@
1
+ REDIS_PORT := 6381
2
+ REDIS_PID_PATH := /tmp/redis.pid
3
+ REDIS_SOCKET_PATH := /tmp/redis.sock
4
+ REDIS_DBFILENAME := primary.rdb
5
+ REDIS_REPLICA_PORT := 6382
6
+ REDIS_REPLICA_PID_PATH := /tmp/redis_replica.pid
7
+ REDIS_REPLICA_SOCKET_PATH := /tmp/redis_replica.sock
8
+ REDIS_REPLICA_DBFILENAME := replica.rdb
9
+
10
+ redis: start_redis test_redis stop_redis
11
+
12
+ test_redis:
13
+ @REDIS=1 bundle exec rspec --tag type:redis ${RSPEC_PATH}
14
+
15
+ start_redis: start_redis_primary start_redis_replica
16
+ stop_redis: stop_redis_replica stop_redis_primary
17
+
18
+ stop_redis_primary:
19
+ @redis-cli -p ${REDIS_PORT} shutdown
20
+
21
+ start_redis_primary:
22
+ @redis-server --daemonize yes --pidfile ${REDIS_PID_PATH} --port ${REDIS_PORT} --unixsocket ${REDIS_SOCKET_PATH} --dbfilename ${REDIS_DBFILENAME} --logfile /dev/null
23
+
24
+ stop_redis_replica:
25
+ @redis-cli -p ${REDIS_REPLICA_PORT} shutdown
26
+
27
+ start_redis_replica:
28
+ @redis-server --daemonize yes --pidfile ${REDIS_REPLICA_PID_PATH} --port ${REDIS_REPLICA_PORT} --unixsocket ${REDIS_REPLICA_SOCKET_PATH} --slaveof 127.0.0.1 ${REDIS_PORT} --dbfilename ${REDIS_REPLICA_DBFILENAME} --logfile /dev/null
metadata CHANGED
@@ -1,29 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_failover
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alan Tan
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-05-21 00:00:00.000000000 Z
11
+ date: 2020-06-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: redis
14
+ name: activerecord
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '4'
19
+ version: '6.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '4'
26
+ version: '6.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: railties
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '6.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '6.0'
27
41
  description:
28
42
  email:
29
43
  - tgx@discourse.org
@@ -35,6 +49,7 @@ files:
35
49
  - ".rspec"
36
50
  - ".rubocop.yml"
37
51
  - ".travis.yml"
52
+ - CHANGELOG.md
38
53
  - CODE_OF_CONDUCT.md
39
54
  - Gemfile
40
55
  - Gemfile.lock
@@ -45,13 +60,19 @@ files:
45
60
  - bin/rspec
46
61
  - bin/setup
47
62
  - lib/rails_failover.rb
63
+ - lib/rails_failover/active_record.rb
64
+ - lib/rails_failover/active_record/handler.rb
65
+ - lib/rails_failover/active_record/middleware.rb
66
+ - lib/rails_failover/active_record/railtie.rb
48
67
  - lib/rails_failover/redis.rb
49
68
  - lib/rails_failover/redis/connector.rb
50
- - lib/rails_failover/redis/failover_handler.rb
69
+ - lib/rails_failover/redis/handler.rb
51
70
  - lib/rails_failover/version.rb
52
71
  - lib/redis/patches/client.rb
53
72
  - makefile
73
+ - postgresql.mk
54
74
  - rails_failover.gemspec
75
+ - redis.mk
55
76
  homepage:
56
77
  licenses:
57
78
  - MIT
@@ -71,8 +92,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
71
92
  - !ruby/object:Gem::Version
72
93
  version: '0'
73
94
  requirements: []
74
- rubygems_version: 3.0.3
95
+ rubygems_version: 3.1.2
75
96
  signing_key:
76
97
  specification_version: 4
77
- summary: Failover for PostgreSQL and Redis
98
+ summary: Failover for ActiveRecord and Redis
78
99
  test_files: []
@@ -1,109 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'monitor'
4
- require 'singleton'
5
-
6
- module RailsFailover
7
- class Redis
8
- class FailoverHandler
9
- include Singleton
10
- include MonitorMixin
11
-
12
- MASTER_ROLE_STATUS = "role:master"
13
- MASTER_LOADED_STATUS = "loading:0"
14
-
15
- def initialize
16
- @master = true
17
- @clients = []
18
-
19
- super() # Monitor#initialize
20
- end
21
-
22
- def verify_master(options)
23
- mon_synchronize do
24
- return if @thread&.alive?
25
-
26
- self.master = false
27
- disconnect_clients
28
- RailsFailover::Redis.master_down_callbacks.each { |callback| callback.call }
29
-
30
- @thread = Thread.new do
31
- loop do
32
- thread = Thread.new { initiate_fallback_to_master(options) }
33
- thread.join
34
- break if self.master
35
- sleep (RailsFailover::Redis.verify_master_frequency_seconds + (Time.now.to_i % RailsFailover::Redis.verify_master_frequency_seconds))
36
- ensure
37
- thread.kill
38
- end
39
- end
40
- end
41
- end
42
-
43
- def initiate_fallback_to_master(options)
44
- info = nil
45
-
46
- begin
47
- master_client = ::Redis::Client.new(options.dup)
48
- log "#{log_prefix}: Checking connection to master server..."
49
- info = master_client.call([:info])
50
- rescue => e
51
- log "#{log_prefix}: Connection to Master server failed with '#{e.message}'"
52
- ensure
53
- master_client&.disconnect
54
- end
55
-
56
- if info && info.include?(MASTER_LOADED_STATUS) && info.include?(MASTER_ROLE_STATUS)
57
- self.master = true
58
- log "#{log_prefix}: Master server is active, disconnecting clients from replica"
59
- disconnect_clients
60
- RailsFailover::Redis.master_up_callbacks.each { |callback| callback.call }
61
- end
62
- end
63
-
64
- def register_client(client)
65
- mon_synchronize do
66
- @clients << client
67
- end
68
- end
69
-
70
- def deregister_client(client)
71
- mon_synchronize do
72
- @clients.delete(client)
73
- end
74
- end
75
-
76
- def clients
77
- mon_synchronize { @clients }
78
- end
79
-
80
- def master
81
- mon_synchronize { @master }
82
- end
83
-
84
- def master=(args)
85
- mon_synchronize { @master = args }
86
- end
87
-
88
- private
89
-
90
- def disconnect_clients
91
- mon_synchronize do
92
- @clients.dup.each do |c|
93
- c.disconnect
94
- end
95
- end
96
- end
97
-
98
- def log(message)
99
- if logger = RailsFailover::Redis.logger
100
- logger.warn(message)
101
- end
102
- end
103
-
104
- def log_prefix
105
- "#{self.class}"
106
- end
107
- end
108
- end
109
- end