active_postgres 0.7.0 → 0.9.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.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +87 -5
  3. data/lib/active_postgres/components/core.rb +2 -7
  4. data/lib/active_postgres/components/extensions.rb +40 -35
  5. data/lib/active_postgres/components/monitoring.rb +91 -4
  6. data/lib/active_postgres/components/pgbackrest.rb +38 -2
  7. data/lib/active_postgres/components/pgbouncer.rb +43 -4
  8. data/lib/active_postgres/components/repmgr.rb +431 -62
  9. data/lib/active_postgres/configuration.rb +59 -2
  10. data/lib/active_postgres/connection_pooler.rb +3 -12
  11. data/lib/active_postgres/credentials.rb +3 -3
  12. data/lib/active_postgres/direct_executor.rb +91 -0
  13. data/lib/active_postgres/generators/active_postgres/install_generator.rb +1 -0
  14. data/lib/active_postgres/generators/active_postgres/templates/postgres.yml.erb +18 -1
  15. data/lib/active_postgres/health_checker.rb +24 -17
  16. data/lib/active_postgres/rollback_manager.rb +4 -8
  17. data/lib/active_postgres/secrets.rb +23 -5
  18. data/lib/active_postgres/ssh_executor.rb +36 -14
  19. data/lib/active_postgres/version.rb +1 -1
  20. data/lib/active_postgres.rb +1 -0
  21. data/lib/tasks/postgres.rake +10 -4
  22. data/lib/tasks/rotate_credentials.rake +4 -16
  23. data/templates/pg_hba.conf.erb +4 -1
  24. data/templates/pgbackrest.conf.erb +28 -0
  25. data/templates/pgbouncer-follow-primary.service.erb +8 -0
  26. data/templates/pgbouncer-follow-primary.timer.erb +11 -0
  27. data/templates/pgbouncer.ini.erb +2 -0
  28. data/templates/pgbouncer_follow_primary.sh.erb +34 -0
  29. data/templates/postgresql.conf.erb +4 -0
  30. data/templates/repmgr.conf.erb +7 -3
  31. data/templates/repmgr_dns_failover.sh.erb +49 -0
  32. metadata +7 -2
@@ -16,6 +16,21 @@ repo1-s3-endpoint=s3.<%= pgbackrest_config[:s3_region] || 'us-east-1' %>.amazona
16
16
  repo1-s3-key=<%= secrets_obj.resolve('s3_access_key') %>
17
17
  repo1-s3-key-secret=<%= secrets_obj.resolve('s3_secret_key') %>
18
18
  <% end %>
19
+ <% elsif pgbackrest_config[:repo_type] == 'gcs' %>
20
+ repo1-type=gcs
21
+ repo1-path=<%= pgbackrest_config[:repo_path] || '/backups' %>
22
+ repo1-gcs-bucket=<%= pgbackrest_config[:gcs_bucket] %>
23
+ <% if secrets_obj.resolve('gcs_key_file') %>
24
+ repo1-gcs-key=<%= secrets_obj.resolve('gcs_key_file') %>
25
+ <% end %>
26
+ <% elsif pgbackrest_config[:repo_type] == 'azure' %>
27
+ repo1-type=azure
28
+ repo1-path=<%= pgbackrest_config[:repo_path] || '/backups' %>
29
+ repo1-azure-container=<%= pgbackrest_config[:azure_container] %>
30
+ <% if secrets_obj.resolve('azure_account') %>
31
+ repo1-azure-account=<%= secrets_obj.resolve('azure_account') %>
32
+ repo1-azure-key=<%= secrets_obj.resolve('azure_key') %>
33
+ <% end %>
19
34
  <% else %>
20
35
  # Local storage
21
36
  repo1-path=<%= pgbackrest_config[:repo_path] || '/var/lib/pgbackrest' %>
@@ -35,9 +50,22 @@ repo1-cipher-type=aes-256-cbc
35
50
  repo1-cipher-pass=<%= secrets_obj.resolve('backup_encryption_key') %>
36
51
  <% end %>
37
52
 
53
+ # Logging
54
+ log-level-console=info
55
+ log-level-file=detail
56
+ log-path=/var/log/pgbackrest
57
+
58
+ # Process settings
59
+ process-max=<%= pgbackrest_config[:process_max] || 2 %>
60
+
61
+ # Start/Stop (for consistent backups)
62
+ start-fast=y
63
+ stop-auto=y
64
+
38
65
  [main]
39
66
  pg1-path=/var/lib/postgresql/<%= config.version %>/main
40
67
  pg1-port=5432
41
68
  pg1-socket-path=/var/run/postgresql
69
+ pg1-user=<%= config.postgres_user %>
42
70
 
43
71
 
@@ -0,0 +1,8 @@
1
+ [Unit]
2
+ Description=Update PgBouncer target to current PostgreSQL primary
3
+ After=network-online.target
4
+ Wants=network-online.target
5
+
6
+ [Service]
7
+ Type=oneshot
8
+ ExecStart=/usr/local/bin/pgbouncer-follow-primary
@@ -0,0 +1,11 @@
1
+ [Unit]
2
+ Description=Run pgbouncer-follow-primary periodically
3
+
4
+ [Timer]
5
+ OnBootSec=10s
6
+ OnUnitActiveSec=<%= interval %>s
7
+ AccuracySec=1s
8
+ Unit=pgbouncer-follow-primary.service
9
+
10
+ [Install]
11
+ WantedBy=timers.target
@@ -49,8 +49,10 @@ log_pooler_errors = <%= pgbouncer_config[:log_pooler_errors] || 1 %>
49
49
  client_tls_sslmode = require
50
50
  client_tls_key_file = /etc/pgbouncer/server.key
51
51
  client_tls_cert_file = /etc/pgbouncer/server.crt
52
+ <% if has_ca_cert %>
52
53
  client_tls_ca_file = /etc/pgbouncer/ca.crt
53
54
  <% end %>
55
+ <% end %>
54
56
 
55
57
  # Process management
56
58
  <% if pgbouncer_config[:pidfile] %>
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ REPMGR_CONF="<%= repmgr_conf %>"
5
+ PGBOUNCER_INI="/etc/pgbouncer/pgbouncer.ini"
6
+ PGBOUNCER_SERVICE="pgbouncer"
7
+ LOG_TAG="pgbouncer-follow-primary"
8
+
9
+ cluster_csv=$(/usr/bin/sudo -u <%= postgres_user %> env HOME=/var/lib/postgresql \
10
+ repmgr -f "$REPMGR_CONF" cluster show --csv 2>/dev/null || true)
11
+ if [[ -z "$cluster_csv" ]]; then
12
+ exit 0
13
+ fi
14
+
15
+ primary_conninfo=$(printf "%s\n" "$cluster_csv" | awk -F',' 'NR>1 && tolower($3) ~ /primary/ {print $NF; exit}')
16
+ if [[ -z "$primary_conninfo" ]]; then
17
+ exit 0
18
+ fi
19
+
20
+ primary_host=$(printf "%s" "$primary_conninfo" | sed -n 's/.*host=\\([^ ]*\\).*/\\1/p')
21
+ if [[ -z "$primary_host" ]]; then
22
+ exit 0
23
+ fi
24
+
25
+ current_host=$(awk -F'host=' '/^\\* = host=/{print $2}' "$PGBOUNCER_INI" | awk '{print $1}')
26
+ if [[ -z "$current_host" ]]; then
27
+ exit 0
28
+ fi
29
+
30
+ if [[ "$current_host" != "$primary_host" ]]; then
31
+ /usr/bin/sed -i -E "s/^(\\* = host=)[^ ]+/\\1${primary_host}/" "$PGBOUNCER_INI"
32
+ /usr/bin/systemctl reload "$PGBOUNCER_SERVICE"
33
+ /usr/bin/logger -t "$LOG_TAG" "Updated PgBouncer primary target to ${primary_host}"
34
+ fi
@@ -44,9 +44,13 @@ wal_compression = <%= pg_config[:wal_compression] %>
44
44
  <% end %>
45
45
  <% if pg_config[:archive_mode] %>
46
46
  archive_mode = <%= pg_config[:archive_mode] %>
47
+ <% elsif config.component_enabled?(:pgbackrest) %>
48
+ archive_mode = on
47
49
  <% end %>
48
50
  <% if pg_config[:archive_command] %>
49
51
  archive_command = '<%= pg_config[:archive_command] %>'
52
+ <% elsif config.component_enabled?(:pgbackrest) %>
53
+ archive_command = 'pgbackrest --stanza=main archive-push %p'
50
54
  <% end %>
51
55
  <% if pg_config[:shared_memory_type] %>
52
56
  shared_memory_type = <%= pg_config[:shared_memory_type] %>
@@ -15,14 +15,14 @@ node_name='<%= node_label %>'
15
15
  # conninfo describes how OTHER nodes connect to THIS node
16
16
  repmgr_host = config.replication_host_for(host)
17
17
  %>
18
- conninfo='host=<%= repmgr_host %> user=<%= config.repmgr_user %> dbname=<%= config.repmgr_database %> password=<%= secrets_obj.resolve('repmgr_password') %> connect_timeout=2'
18
+ conninfo='host=<%= repmgr_host %> user=<%= config.repmgr_user %> dbname=<%= config.repmgr_database %> connect_timeout=2'
19
19
  data_directory='/var/lib/postgresql/<%= config.version %>/main'
20
20
 
21
21
  <% if host == config.primary_host %>
22
- failover=automatic
22
+ failover=<%= repmgr_config[:auto_failover] == false ? 'manual' : 'automatic' %>
23
23
  priority=<%= repmgr_config[:priority] || 100 %>
24
24
  <% else %>
25
- failover=automatic
25
+ failover=<%= repmgr_config[:auto_failover] == false ? 'manual' : 'automatic' %>
26
26
  priority=<%= repmgr_config[:priority] || 100 %>
27
27
  promote_command='repmgr standby promote -f /etc/repmgr.conf'
28
28
  follow_command='repmgr standby follow -f /etc/repmgr.conf --upstream-node-id=%n'
@@ -38,3 +38,7 @@ log_file='/var/log/postgresql/repmgr.log'
38
38
  monitoring_history=yes
39
39
  monitor_interval_secs=5
40
40
 
41
+ <% if repmgr_config[:dns_failover] && repmgr_config[:dns_failover][:enabled] %>
42
+ event_notification_command='/usr/local/bin/active-postgres-dns-failover'
43
+ event_notifications='<%= repmgr_config[:dns_failover][:events] || 'repmgrd_failover_promote,standby_promote,standby_switchover,standby_follow' %>'
44
+ <% end %>
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ REPMGR_CONF="/etc/repmgr.conf"
5
+ DNS_USER="<%= dns_user %>"
6
+ DNS_SERVERS=(<%= dns_servers.map(&:to_s).join(' ') %>)
7
+ DNS_SSH_KEY="<%= dns_ssh_key_path %>"
8
+ DNSMASQ_FILE="/etc/dnsmasq.d/active_postgres.conf"
9
+ PRIMARY_RECORD="<%= primary_record %>"
10
+ REPLICA_RECORD="<%= replica_record %>"
11
+ SSH_STRICT_HOST_KEY="<%= ssh_strict_host_key %>"
12
+ SSH_KNOWN_HOSTS="/var/lib/postgresql/.ssh/known_hosts"
13
+ LOG_TAG="active_postgres_dns"
14
+
15
+ cluster_csv=$(repmgr -f "$REPMGR_CONF" cluster show --csv 2>/dev/null || true)
16
+ if [[ -z "$cluster_csv" ]]; then
17
+ exit 0
18
+ fi
19
+
20
+ primary_host=$(printf "%s\n" "$cluster_csv" | awk -F',' 'NR>1 && tolower($3) ~ /primary/ {print $NF; exit}' | sed -n 's/.*host=\\([^ ]*\\).*/\\1/p')
21
+ standby_hosts=$(printf "%s\n" "$cluster_csv" | awk -F',' 'NR>1 && tolower($3) ~ /standby/ {print $NF}' | sed -n 's/.*host=\\([^ ]*\\).*/\\1/p' | sort -u)
22
+
23
+ if [[ -z "$primary_host" ]]; then
24
+ exit 0
25
+ fi
26
+
27
+ content=$'# Managed by active_postgres\n'
28
+ printf -v content '%saddress=/%s/%s\n' "$content" "$PRIMARY_RECORD" "$primary_host"
29
+
30
+ if [[ -n "$standby_hosts" ]]; then
31
+ for host in $standby_hosts; do
32
+ printf -v content '%saddress=/%s/%s\n' "$content" "$REPLICA_RECORD" "$host"
33
+ done
34
+ fi
35
+
36
+ ssh_opts=(-i "$DNS_SSH_KEY" -o BatchMode=yes -o StrictHostKeyChecking="$SSH_STRICT_HOST_KEY" -o UserKnownHostsFile="$SSH_KNOWN_HOSTS")
37
+
38
+ for server in "${DNS_SERVERS[@]}"; do
39
+ if [[ -z "$server" ]]; then
40
+ continue
41
+ fi
42
+
43
+ if ! /usr/bin/ssh "${ssh_opts[@]}" "${DNS_USER}@${server}" \
44
+ "sudo bash -c 'cat > ${DNSMASQ_FILE} << \"EOF\"\\n${content}EOF\\n' && (sudo systemctl reload dnsmasq || sudo systemctl restart dnsmasq)"; then
45
+ /usr/bin/logger -t "$LOG_TAG" "Failed updating dnsmasq on ${server}"
46
+ fi
47
+ done
48
+
49
+ /usr/bin/logger -t "$LOG_TAG" "Updated DNS records for ${PRIMARY_RECORD} and ${REPLICA_RECORD} (primary=${primary_host})"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_postgres
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - BoringCache
@@ -169,6 +169,7 @@ files:
169
169
  - lib/active_postgres/connection_pooler.rb
170
170
  - lib/active_postgres/credentials.rb
171
171
  - lib/active_postgres/deployment_flow.rb
172
+ - lib/active_postgres/direct_executor.rb
172
173
  - lib/active_postgres/error_handler.rb
173
174
  - lib/active_postgres/failover.rb
174
175
  - lib/active_postgres/generators/active_postgres/install_generator.rb
@@ -194,9 +195,13 @@ files:
194
195
  - lib/tasks/rotate_credentials.rake
195
196
  - templates/pg_hba.conf.erb
196
197
  - templates/pgbackrest.conf.erb
198
+ - templates/pgbouncer-follow-primary.service.erb
199
+ - templates/pgbouncer-follow-primary.timer.erb
197
200
  - templates/pgbouncer.ini.erb
201
+ - templates/pgbouncer_follow_primary.sh.erb
198
202
  - templates/postgresql.conf.erb
199
203
  - templates/repmgr.conf.erb
204
+ - templates/repmgr_dns_failover.sh.erb
200
205
  homepage: https://github.com/boringcache/active_postgres
201
206
  licenses:
202
207
  - MIT
@@ -220,7 +225,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
220
225
  - !ruby/object:Gem::Version
221
226
  version: '0'
222
227
  requirements: []
223
- rubygems_version: 3.7.2
228
+ rubygems_version: 3.6.9
224
229
  specification_version: 4
225
230
  summary: PostgreSQL High Availability for Rails, made simple
226
231
  test_files: []