Skip to content

Conversation

@Naksen
Copy link
Collaborator

@Naksen Naksen commented Jan 27, 2026

Задача 1161

Изменения:

  • В класс Settings добавлены:
    • Второй Engine под реплику.
    • Настройки для реплики.
    • Настройка для смены режимов single \ replication.
  • Добавлен EngineRegistry, который включает в себя engine мастера и реплики.
  • Добавлен класс RoutingSession, отнаследованный от Session. В классе переопределены два метода:
    • get_bind - метод, к которому обращается сессия перед выполнением запроса в базу (в момент вызовов .flush(), .execute(), .scalars и т.д.) для получения бинда - Engine или Connection.
    • flush - в метод добавлено выставление дополнительного флага _force_master для чтения после записи.
  • Для LDAP добавлена обработка исключения OperationalError, которое возникает при попытке записи в мастер, когда он не доступен. В этом случае отдаем RESPONSE_TYPE (новый ClassVar для реквестов) нашего запроса со статус кодом LDAPCodes.UNAVAILABLE.
  • Для API был добавлен препинг мастера для тех роутов, которые выполняют запись в мастер.

Новый flow:

  • Если выставлен режим single - одна база и для чтения и для записи, то ничего не меняется, RoutingSession не используется.
  • Если выставлен режим replication - то во время каждого запроса в базу мы в методе get_bind определяем в каком мы сейчас состоянии сессии:
    1. Только читаем -> отдаем движок реплики. (Например, случай Search запроса)
    2. Что-то записываем -> выставляем флаг _force_master, отдаем движок мастера.
    3. Чтение после записи -> хоть и приходит запрос на чтение, флаг _force_master уже выставлен, значит отдаем движок мастера. (Например, случай Modify запроса, мы сначала выполняем запросы поиска в реплику, после этого выполняем flush для новой директории и в дальнейшем чтение в рамках запроса идет только из мастера)

Открытые вопросы

  • Какой код бросать для веба в случае отвала мастера ? - сейчас 503 код с сообщением.
  • Что делать при финализации session в ioc ? - там происходит session.commit(). Если нам прилетит Modify запрос во время отвала мастера мы упадем в .handle методе с эксепшеном OperationalError, оно обработается выше в методе _handle_tcp \ _handle_api в base.py и LDAP нормально вернет ответ клиенту со статус кодом LDAPCodes.UNAVAILABLE, но при этом выйдет исключение в ioc.py уже после возвращения ответа клиенту. В целом не влияет на работу, но нужно как-то захендлить.
  • Что делать с обновлением logon атрибутов ? - в первой версии пока просто suppress.
  • Нужен ли препинг для LDAP аналогичный API ? - пока не делал.
  • Предложения по другим реализациям маршрутизации приветствуются.

Copilot AI review requested due to automatic review settings January 27, 2026 15:48
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds PostgreSQL master-replica read/write routing functionality to support database scalability. The implementation introduces a custom routing session that directs write operations to the master database while allowing read operations to use a replica database.

Changes:

  • New database routing infrastructure with RoutingSession class that routes database operations between master and replica
  • Configuration options for single or master-replica database modes
  • Master database availability checks added to all write API endpoints
  • Error handling for database unavailability with OperationalError suppression and user-facing error responses

Reviewed changes

Copilot reviewed 30 out of 30 changed files in this pull request and generated 18 comments.

Show a summary per file
File Description
app/database.py New file implementing RoutingSession for master-replica routing and engine configuration
app/config.py Added replica database configuration fields and removed engine property from Settings
app/ioc.py Modified session factory to support routing mode, removed engine provider
app/api/utils.py New utility function to check master database availability before write operations
app/multidirectory.py Updated to use engines dictionary instead of settings.engine
tests/conftest.py Updated test configuration to use engines dictionary
app/ldap_protocol/ldap_requests/base.py Added RESPONSE_TYPE class variable and OperationalError handling in request handlers
app/ldap_protocol/ldap_requests/*.py Added RESPONSE_TYPE class variable to request classes
app/ldap_protocol/ldap_requests/bind.py Added OperationalError suppression for user login attribute updates
app/ldap_protocol/session_storage/repository.py Added OperationalError suppression for session key creation
app/api/**/router*.py Added check_master_db dependency to write endpoints

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@Naksen Naksen force-pushed the add_postgresql_rw_routing branch from 0cbb784 to 7891162 Compare January 28, 2026 13:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants