简体中文 English 繁體中文 日本語 Русский
本文为静态镜像,内容以交互版为准 在交互式文档中心打开 →

Системы хранения данных

ErisPulse включает три асинхронных встроенных хранилища, которые можно переключать с помощью конфигурации одним кликом, API полностью идентичен, переключение не требует изменений кода:

Хранилище Драйвер Установка Особенности
SQLite (по умолчанию) aiosqlite Готово к использованию Нулевая настройка, одиночный файл, WAL-параллелизм
MySQL / MariaDB aiomysql pip install ErisPulse[mysql] Подходит для уже существующей инфраструктуры MySQL, общий доступ между несколькими экземплярами
PostgreSQL asyncpg pip install ErisPulse[postgres] Сильная поддержка транзакций, экосистема JSONB, высокая параллельность

{!--< tips >!--}

  1. Асинхронные методы являются основными интерфейсами (aget/aset/atransaction/aExecute), синхронные API являются слоем совместимости
  2. Все данные, связанные с конфигурацией фреймворка, почтовыми ящиками сессий, контрольными точками диалогов и т.д., хранятся в одном и том же хранилище — переключение хранилища означает полную миграцию {!--< /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 не приведёт к краху или невозможности запуска фреймворка:

  1. При мгновенном сбое создания пула автоматически происходит повторная попытка с экспоненциальной задержкой (по умолчанию 3 попытки)
  2. После исчерпания попыток → запись в лог WARNING (с указанием причины и времени ожидания), фреймворк запускается и продолжает работу, но операции с хранилищем временно недоступны
  3. В течение периода ожидания (по умолчанию 30 секунд) последующие операции с хранилищем быстро завершаются с ошибкой (без блокировки, без замедления остальных функций)
  4. После окончания периода ожидания автоматически происходит попытка повторного подключения — при восстановлении базы данных хранилище также восстанавливается, без необходимости перезапуска

Пример записи в лог: 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 в первую очередь. Два важных правила необходимо соблюдать:

Полный список методов см. в 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 для получения цепочки запросов к таблицам (фильтрация в памяти, подходит для небольших объемов данных).

Связанная документация