Системы хранения данных
ErisPulse включает три асинхронных встроенных хранилища, которые можно переключать с помощью конфигурации одним кликом, API полностью идентичен, переключение не требует изменений кода:
| Хранилище | Драйвер | Установка | Особенности |
|---|---|---|---|
| SQLite (по умолчанию) | aiosqlite | Готово к использованию | Нулевая настройка, одиночный файл, WAL-параллелизм |
| MySQL / MariaDB | aiomysql | pip install ErisPulse[mysql] |
Подходит для уже существующей инфраструктуры MySQL, общий доступ между несколькими экземплярами |
| PostgreSQL | asyncpg | pip install ErisPulse[postgres] |
Сильная поддержка транзакций, экосистема JSONB, высокая параллельность |
{!--< tips >!--}
- Асинхронные методы являются основными интерфейсами (
aget/aset/atransaction/aExecute), синхронные API являются слоем совместимости - Все данные, связанные с конфигурацией фреймворка, почтовыми ящиками сессий, контрольными точками диалогов и т.д., хранятся в одном и том же хранилище — переключение хранилища означает полную миграцию {!--< /tips >!--}
Выбор хранилища
Настройте в config/config.toml:
[ErisPulse.storage]
backend = "sqlite" # "sqlite" (по умолчанию) / "mysql" / "postgres"
use_global_db = false # Только для SQLite: использование глобальной базы данных в пакете data/config.db
Также поддерживаются перекрытия через переменные окружения (Docker / 12-factor):
ERISPULSE_STORAGE_BACKEND=postgres
ERISPULSE_STORAGE_POSTGRES_HOST=db.example.com
ERISPULSE_STORAGE_POSTGRES_PASSWORD=secret
Правила именования переменных окружения: путь конфигурации в верхнем регистре, точки заменяются на нижние подчеркивания
(ErisPulse.storage.postgres.host → ERISPULSE_STORAGE_POSTGRES_HOST).
Параметры подключения
MySQL (ErisPulse.storage.mysql)
[ErisPulse.storage.mysql]
host = "127.0.0.1"
port = 3306
user = "erispulse"
password = ""
database = "erispulse"
charset = "utf8mb4"
pool_min = 1
pool_max = 10
PostgreSQL (ErisPulse.storage.postgres)
[ErisPulse.storage.postgres]
host = "127.0.0.1"
port = 5432
user = "erispulse"
password = ""
database = "erispulse"
pool_min = 1
pool_max = 10
Note
Изменения параметров подключения требуют перезапуска фреймворка для применения (при горячей перезагрузке конфигурации будет выведено предупреждение).
Пул соединений создается по требованию для каждого цикла событий, при временных сбоях (колебания сети / окно перезапуска базы данных) автоматически происходит повторная попытка с экспоненциальной задержкой.
Поведение при сбое подключения
Работа и запуск фреймворка не зависят от доступности базы данных — сбой подключения к MySQL / PostgreSQL не приведёт к краху или невозможности запуска фреймворка:
- При мгновенном сбое создания пула автоматически происходит повторная попытка с экспоненциальной задержкой (по умолчанию 3 попытки)
- После исчерпания попыток → запись в лог WARNING (с указанием причины и времени ожидания), фреймворк запускается и продолжает работу, но операции с хранилищем временно недоступны
- В течение периода ожидания (по умолчанию 30 секунд) последующие операции с хранилищем быстро завершаются с ошибкой (без блокировки, без замедления остальных функций)
- После окончания периода ожидания автоматически происходит попытка повторного подключения — при восстановлении базы данных хранилище также восстанавливается, без необходимости перезапуска
Пример записи в лог: mysql: Создание пула соединений окончательно не удалось (повторные попытки 3), автоматическое повторное подключение через 30 секунд; в течение этого времени операции с хранилищем будут быстро завершаться с ошибкой, остальные функции фреймворка не затронуты
Асинхронные нативные API
# Операции с хранилищем пар "ключ-значение"
await sdk.storage.aset("app.name", "MyApp")
value = await sdk.storage.aget("app.name")
keys = await sdk.storage.aget_all_keys()
# Операции с таблицами
await sdk.storage.aCreateTable("users", {
"id": "INTEGER PRIMARY KEY AUTOINCREMENT",
"name": "TEXT NOT NULL",
})
rows = await sdk.storage.Table("users").Select("name").ToDict().aExecute()
# Асинхронные транзакции
async with sdk.storage.atransaction():
await sdk.storage.aset("key1", "value1")
await sdk.storage.Table("users").Insert({"name": "Alice"}).aExecute()
Синхронные API (get/set/transaction/Table(...).Execute()) по-прежнему доступны, но выполняются через фоновую мостовую очередь событий AsyncBridge — при вызове из асинхронного обработчика это временно блокирует цикл событий, при первом вызове будет выведено одно разовое предупреждение («Рекомендуется использовать асинхронные методы с префиксом a»), рекомендуется использовать методы с префиксом a в первую очередь. Два важных правила необходимо соблюдать:
- Внутри мостового потока нельзя вызывать синхронные интерфейсы: синхронные интерфейсы выполняются внутри мостового потока, в этот момент повторный вызов синхронных интерфейсов приведёт к исключению
RuntimeError(защита от рекурсии, предотвращение самозаморозки) - Вызов интерфейсов после закрытия мостового цикла событий завершится неудачей: после вызова
uninit()больше не следует вызывать интерфейсы хранения
Полный список методов см. в SQL-построителе запросов.
Различия в поведении диалектов
Все различия в диалектах скрыты внутри фреймворка, код вызывающей стороны не должен их учитывать:
| Различие | SQLite | MySQL | PostgreSQL |
|---|---|---|---|
| Заполнители | ? |
%s (автоматически переводится) |
$1..$n (автоматически переводится) |
| UPSERT для пары ключ-значение | INSERT OR REPLACE |
ON DUPLICATE KEY UPDATE |
ON CONFLICT DO UPDATE |
| Тип столбца значения для пары ключ-значение | TEXT |
LONGTEXT |
TEXT |
| Автоматически увеличивающийся первичный ключ | Нативная поддержка | AUTO_INCREMENT (автоматически переводится) |
SERIAL (автоматически переводится) |
Определение типов столбцов при создании таблиц должно использовать стиль SQLite (например, "INTEGER PRIMARY KEY AUTOINCREMENT", "TEXT NOT NULL", "DOUBLE DEFAULT 0.0"), диалект автоматически переводит их в эквивалентные записи для целевой СУБД.
Пользовательские SQL-хранилища
Для чисто SQL-хранилищ можно наследовать общий базовый класс, предоставив только управление подключениями и диалект выполнения:
from ErisPulse.Core.Bases.sql_base import SQLDialect, SQLStorageBase
class MyDialect(SQLDialect):
name = "mydb"
# Переопределение: перевод заполнителей / ссылок на идентификаторы / UPSERT / сопоставление типов ...
class MyStorage(SQLStorageBase):
dialect = MyDialect()
async def _create_loop_resource(self): ... # Пул соединений
async def _destroy_loop_resource(self, r): ...
async def _acquire_resource_conn(self, r): ...
async def _release_resource_conn(self, r, c): ...
async def _open_txn_conn(self): ...
async def _close_txn_conn(self, c): ...
async def _exec_query_on(self, kind, sql, params, conn): ... # Очередь выполнения запросов
Для не-SQL хранилищ (например, Redis) можно наследовать BaseStorage и реализовать асинхронные интерфейсы для пары ключ-значение, после чего можно использовать KVQueryBuilder для получения цепочки запросов к таблицам (фильтрация в памяти, подходит для небольших объемов данных).
Связанная документация
- SQL-построитель запросов - Синтаксис цепочки запросов и асинхронный API
- API основных модулей - Полный API модуля Storage
- API базовых классов хранилищ - Абстрактные интерфейсы SQLStorageBase / SQLDialect