Перейти к содержанию

Практикум: партиционирование существующей таблицы в окно обслуживания

Переводим таблицу events сервиса аналитики на месячные партиции. Обновлённый пример использует окно обслуживания: запросы приложения к данным приостановлены на время перехода. Диагностические запросы специально проверяют промежуточные состояния. Непрерывная работа приложения во время переноса здесь не обещается.

Нужны Docker и uv. Выполните команды из этого каталога:

uv run --no-project --python 3.13 --with-requirements requirements.txt python migrate_lab.py
uv run --no-project --python 3.13 --with-requirements requirements.txt python batch_boundary_lab.py

migrate_lab.py создаёт 615 исторических событий за июль–сентябрь 2026 года, одно событие от роли приложения и двадцать заметок. Полные исходные строки сохраняются для сравнения после переноса. Каждая строка PASS появляется после проверки результата на PostgreSQL.

Проверка Ожидаемый результат
Партиционирование по created_at со старым PRIMARY KEY (id) SQLSTATE 0A000
Удаление первичного ключа, на который ссылается внешний SQLSTATE 2BP01
Переключение при активной читающей транзакции SQLSTATE 55P03; rollback сохраняет таблицу, внешний ключ и строки
Создание июльской партиции, пока июльские строки ещё в DEFAULT SQLSTATE 23514
Перенос одной порции из 100 строк Родитель видит 516 строк, отдельная июльская таблица хранит 100; вместе они в точности совпадают с исходными 616
Новый toolkit и продолжение переноса Все месяцы подключены, все сохранённые строки видны через родителя
Старое представление до обновления запроса После переноса из старой таблицы оно насчитывает ноль строк
Восстановление составного внешнего ключа после переноса Без времени: 23502; неверная пара: 23503
Восстановление прав, представления и владельца sequence; удаление пустой DEFAULT Исходная вставка события от роли приложения проходит, sequence выдаёт следующий id
Ключи и планы запросов Одинаковая пара запрещена; тот же id с другим временем разрешён; июльский запрос читает одну партицию
Запись вне диапазонов без DEFAULT SQLSTATE 23514

batch_boundary_lab.py проверяет границу порции: если лимит порций кончился сразу после порции, которая опустошила месяц, таблица месяца остаётся неподключённой и родитель не видит её строк. plan() показывает это как unattached_rows, а следующий вызов partition_data подключает таблицу (pg-partsmith 1.5.1 мог оставить такой месяц скрытым и при этом вернуть complete=True; в 1.7.0 это исправили). Затем тест сравнивает все 100 исходных строк через родителя.

Функции из статьи находятся в migration_flow.py, а исходная схема с представлением и ролью приложения в schema.sql. SQL вставки события не меняется, но при создании заметки теперь нужны и event_id, и event_created_at. Эти шаги запускают только после паузы приложения и завершения его активных операций; готового HTTP-режима обслуживания в практикуме нет.

Версии Python-зависимостей закреплены: pg-partsmith 1.8.0, SQLAlchemy 2.0.54, asyncpg 0.31.0, pydantic 2.13.5 и testcontainers 4.15.0. Контейнер postgres:17-alpine удаляется при выходе. Фиксированные даты и небольшой набор данных нужны для проверки корректности, не для оценки длительности блокировок в продакшене. Автоматическое удаление старых партиций во время переноса не запускается.

Читать статью.

Исходный код практикума на GitHub.