diff --git a/Gemfile b/Gemfile index fc76bca..0d92e26 100644 --- a/Gemfile +++ b/Gemfile @@ -5,6 +5,7 @@ gem 'puma', '>= 6.5.0' # Базы данных gem 'pg' +gem 'redis-actionpack' # hiredes # Многопоточное выполнение gem 'parallel' diff --git a/Gemfile.lock b/Gemfile.lock index d66b198..8a7c1da 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -316,6 +316,19 @@ GEM rdoc (6.7.0) psych (>= 4.0.0) redcarpet (3.6.0) + redis (5.4.0) + redis-client (>= 0.22.0) + redis-actionpack (5.5.0) + actionpack (>= 5) + redis-rack (>= 2.1.0, < 4) + redis-store (>= 1.1.0, < 2) + redis-client (0.23.2) + connection_pool + redis-rack (3.0.0) + rack-session (>= 0.2.0) + redis-store (>= 1.2, < 2) + redis-store (1.11.0) + redis (>= 4, < 6) regexp_parser (2.9.2) reline (0.5.9) io-console (~> 0.5) @@ -466,6 +479,7 @@ DEPENDENCIES pry-rails puma (>= 6.5.0) rails (>= 8.0.1) + redis-actionpack rspec-rails (>= 7.1) rubocop-performance rubocop-rails diff --git a/app/admin/book.rb b/app/admin/book.rb index a780c59..541e369 100644 --- a/app/admin/book.rb +++ b/app/admin/book.rb @@ -19,19 +19,23 @@ as: :select, label: I18n.t('active_admin.filters.genre'), collection: proc { + #ActiveRecord::Base.connected_to(role: :reading, prevent_writes: true) do # TODO query + декоратор Genre.order(:name) .pluck(:name, :id) .map { |g| [ truncate(g.first, length: 25), g.last ] } + #end } filter :keywords_id_eq, as: :select, label: I18n.t('active_admin.filters.keyword'), collection: proc { + #ActiveRecord::Base.connected_to(role: :reading, prevent_writes: true) do # TODO query + декоратор Keyword.order(:name) .pluck(:name, :id) .map { |k| [ truncate(k.first, length: 25), k.last ] } + #end } controller do diff --git a/app/models/application_record.rb b/app/models/application_record.rb index 08029c7..3f94811 100644 --- a/app/models/application_record.rb +++ b/app/models/application_record.rb @@ -1,4 +1,5 @@ class ApplicationRecord < ActiveRecord::Base primary_abstract_class self.implicit_order_column = 'created_at' + # connects_to database: { writing: :primary, reading: :primary_replica } end diff --git a/config/application.rb b/config/application.rb index 483852d..1ee58af 100644 --- a/config/application.rb +++ b/config/application.rb @@ -32,6 +32,12 @@ class Application < Rails::Application # Common ones are `templates`, `generators`, or `middleware`, for example. config.autoload_lib(ignore: %w[assets tasks]) # config.eager_load_paths << Rails.root.join('extras') + config.cache_store = :redis_cache_store, { url: ENV['REDIS_CACHE_URL'] } + config.session_store :redis_store, + servers: [ENV['REDIS_SESSION_URL']], + expire_after: 90.minutes, + key: '_library_session', + threadsafe: false config.generators do |g| g.org :active_record diff --git a/config/database.yml b/config/database.yml index 48f30ee..a054252 100644 --- a/config/database.yml +++ b/config/database.yml @@ -17,9 +17,18 @@ development: test: <<: *default + <<: *postgre database: library_test production: - <<: *default - <<: *postgre - database: library_production + primary: + <<: *default + <<: *postgre + host: <%= ENV.fetch('POSTGRES_HOST_MASTER', 'localhost') %> + database: library_production + primary_replica: + <<: *default + <<: *postgre + host: <%= ENV.fetch('POSTGRES_HOST_SLAVE', 'localhost') %> + database: library_production + replica: true diff --git a/config/environments/development.rb b/config/environments/development.rb index 7d707e7..645699d 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -26,7 +26,7 @@ end # Change to :null_store to avoid any caching. - config.cache_store = :memory_store + # config.cache_store = :memory_store # Print deprecation notices to the Rails logger. config.active_support.deprecation = :log diff --git a/config/environments/production.rb b/config/environments/production.rb index c5ea502..acdf848 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -35,7 +35,8 @@ config.logger = ActiveSupport::TaggedLogging.logger(STDOUT) # Change to "debug" to log everything (including potentially personally-identifiable information!) - config.log_level = ENV.fetch('RAILS_LOG_LEVEL') { 'info' } + config.log_level = ENV.fetch('RAILS_LOG_LEVEL') { 'debug' } + config.active_record.verbose_query_logs = true # Prevent health checks from clogging up the logs. config.silence_healthcheck_path = '/up' diff --git a/db/master/init.sql b/db/master/init.sql new file mode 100644 index 0000000..9098cdd --- /dev/null +++ b/db/master/init.sql @@ -0,0 +1 @@ +CREATE USER repluser REPLICATION PASSWORD 'repluser'; diff --git a/db/master/pg_hba.conf b/db/master/pg_hba.conf new file mode 100644 index 0000000..e8b923d --- /dev/null +++ b/db/master/pg_hba.conf @@ -0,0 +1,15 @@ +# TYPE DATABASE USER ADDRESS METHOD + +# "local" is for Unix domain socket connections only +local all all trust +host all all 127.0.0.1/32 trust +host all all ::1/128 trust +# Allow replication connections from localhost, by a user with the +# replication privilege. +local replication all trust +host replication all 127.0.0.1/32 trust +host replication all ::1/128 trust + +host replication all 172.18.0.0/16 md5 + +host all all all scram-sha-256 diff --git a/db/slave/.pgpass b/db/slave/.pgpass new file mode 100644 index 0000000..f6d89c1 --- /dev/null +++ b/db/slave/.pgpass @@ -0,0 +1,2 @@ +*:*:*:postgres:postgres +*:*:*:repluser:repluser diff --git a/db/slave/init-slave.sh b/db/slave/init-slave.sh new file mode 100755 index 0000000..db24747 --- /dev/null +++ b/db/slave/init-slave.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -e + +rm -rf /var/lib/postgresql/data/* +pg_basebackup --host=master --username=repluser --pgdata=/var/lib/postgresql/data --wal-method=stream --write-recovery-conf diff --git a/docker-compose.yml b/docker-compose.yml index 9c195af..657a9fa 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,11 +1,39 @@ services: - db: + # db: + # image: postgres:17 + # environment: + # POSTGRES_USER: 'postgres' + # POSTGRES_PASSWORD: 'postgres' + # volumes: + # - db:/var/lib/postgresql/data + postgresql_master: image: postgres:17 - environment: - POSTGRES_USER: 'postgres' - POSTGRES_PASSWORD: 'postgres' + container_name: master + restart: always + networks: + - db-repl volumes: - - db:/var/lib/postgresql/data + - ./db/data/master:/var/lib/postgresql/data + - ./db/master/pg_hba.conf:/var/lib/postgresql/pg_hba.conf + - ./db/master/init.sql:/docker-entrypoint-initdb.d/init.sql + env_file: + - ./.env.postgresql + + postgresql_slave: + image: postgres:17 + container_name: slave + restart: always + networks: + - db-repl + volumes: + - ./db/slave/.pgpass:/var/lib/postgresql/.pgpass + - ./db/slave/.pgpass:/root/.pgpass + - ./db/data/slave/:/var/lib/postgresql/data + - ./db/master/pg_hba.conf:/var/lib/postgresql/pg_hba.conf + - ./db/slave/init-slave.sh:/var/lib/postgresql/init-slave.sh + env_file: + - ./.env.postgresql + web: tty: true stdin_open: true @@ -22,16 +50,28 @@ services: - .:/rails - "$DOCKER_COMPOSE_ARCHIVE_FOLDER:/rails/db/data" networks: - - default + - db-repl ports: - "8080:3000" depends_on: - - db + - postgresql_master environment: RAILS_LOG_TO_STDOUT: 'yes' PAGER: 'more' - POSTGRES_HOST: 'db' + POSTGRES_HOST_MASTER: 'postgresql_master' + POSTGRES_HOST_SLAVE: 'postgresql_slave' POSTGRES_USER: 'postgres' POSTGRES_PASSWORD: 'postgres' -volumes: - db: + +networks: + db-repl: + driver: bridge + name: db-repl + ipam: + driver: default + config: + - subnet: 172.18.0.0/16 + gateway: 172.18.0.1 + +# volumes: +# db: