Skip to content

About

Я загружаю складские события в PostgreSQL: COPY, staging, транзакции и защита от повторов. Проверяю откат партии и конкурентную загрузку. Учебный проект.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Мини-склад: транзакционные партии PostgreSQL

← Каталог моих проектов · С чего я начинаю · Контракт и решения · Что я проверил

Я разбираю путь COPY → staging → транзакция и проверяю конфликт партии вместе с откатом.

Я разработал учебный мини-склад 20.10.2026. Загружаю CSV с приходом и расходом через COPY в staging, проверяю и принимаю всю партию одной транзакцией. Повтор не удваивает склад; конфликт события и отрицательный остаток откатывают всю партию. Данные вымышленные. Коммерческий опыт не заявляется.

Начать изучение: карта кода и отладка, задания, обоснование решений, подробный конспект.

Установка

Нужны Python 3.12, uv 0.13.1 и установленный PostgreSQL 16.x с initdb/pg_ctl. На Ubuntu при нескольких установленных версиях проект автоматически выбирает /usr/lib/postgresql/16/bin; явный PG_BIN сохраняет приоритет. Локально проверен 16.15. Системную службу запускать не надо. demo и лабораторная создают свой кластер со случайным паролем, слушают только 127.0.0.1, затем останавливают и удаляют его. Системный кластер не используется. Интернет нужен для получения зависимостей, не для обработки. Постоянный сервис не устанавливается.

В корне этого отдельного проекта, Linux:

python3.12 -m venv .tools/bootstrap
.tools/bootstrap/bin/python -m pip install uv==0.13.1
.tools/bootstrap/bin/uv sync --frozen --no-dev
.venv/bin/python -m stock_ledger demo

Если uv 0.13.1 уже доступен, достаточно uv sync --frozen --no-dev. На текущем компьютере окружение и PostgreSQL уже подготовлены. Если серверные команды не находятся автоматически через PATH/pg_config, укажи PG_BIN — папку установленного PostgreSQL 16, содержащую initdb и pg_ctl. Windows PowerShell, после установки официального PostgreSQL 16:

$env:PG_BIN = 'C:\Program Files\PostgreSQL\16\bin'
py -3.12 -m venv .tools/bootstrap
.tools/bootstrap/Scripts/python.exe -m pip install uv==0.13.1
.tools/bootstrap/Scripts/uv.exe sync --frozen --no-dev
.venv/Scripts/python.exe -m stock_ledger demo

Windows-инструкции и конфигурации подготовлены, но локально Windows не проверялся. Приложение не скачивает PostgreSQL и не меняет глобальные настройки системы.

Демонстрация за 5–10 минут

demo сохранит workspace/demo/result.json, report.md и пять входных CSV. Папка должна быть новой; для повторного опыта задай --output workspace/demo-2. Не удаляй свою прежнюю работу ради повторения.

Ожидаемые результаты:

Шаг Что произошло
receipts-01 4 строки, 3 уникальных события, вставлены 3; точная копия E0001 удалена
повтор receipts-01 reused=true, количество событий и партий прежнее
dispatch-01 4 уникальных события, вставлены 3; E0002 уже был принят раньше
negative-01 BAG ушёл бы ниже нуля; откатился также хороший приход BOOK
conflict-01 E0001 с другим количеством отклонён
failure-01 Исключение после INSERT перед COMMIT; ни событие, ни партия не остались

Итог — 6 событий, 2 партии, BAG=3, BOOK=7, PEN=16. При reused=true поле inserted_events показывает исторический результат первого принятия этой партии, а не число новых вставок текущего повтора. committed_at остаётся в технической таблице и не входит в воспроизводимую сводку.

Менять вход руками

Создай копии и запусти выбранные файлы в указанном порядке:

.venv/bin/python -m stock_ledger sample --output workspace/input
.venv/bin/python scripts/workshop.py workspace/input/receipts.csv workspace/input/dispatch.csv --repeat-first --output workspace/experiment

Редактируй workspace/input/dispatch.csv и повторяй с новым --output. Лабораторная стартует с пустой базой; после каждого файла сохраняет снимок в steps.json. Отвергнутая партия даёт общий код 1, но предыдущие принятые партии и отчёт сохраняются. В конце база удаляется. Для постоянной учебной базы есть отдельные команды ниже.

Своя отдельная учебная база

Для init, load, report задай строку подключения в переменной STOCK_DSN. Пароль в аргументы команд и исходники не добавляй. Выбери предназначенную для опыта базу: init создаёт только схему stock_lab, прежние события не очищает.

.venv/bin/python -m stock_ledger init
.venv/bin/python -m stock_ledger load --batch receipts-01 --input workspace/input/receipts.csv
.venv/bin/python -m stock_ledger load --batch dispatch-01 --input workspace/input/dispatch.csv
.venv/bin/python -m stock_ledger report

load --fail-before-commit намеренно поднимает исключение после вставки для проверки ROLLBACK. Ключ партии должен начинаться со строчной буквы, далее строчные буквы, цифры, _ или -, всего до 40 символов. С тем же ключом другие входные байты запрещены. Подробности параметров: python -m stock_ledger --help и load --help.

Контракт

CSV UTF-8, LF/CRLF и BOM поддерживаются; точный порядок:

event_id,sku,delta
E0001,BOOK,10
E0004,BOOK,-3

Одна строка — складское движение. ID: E и 4 цифры. SKU: 2–16 символов, первая заглавная латинская буква, далее заглавные буквы, цифры и _. delta: целое от -99999 до 99999, кроме нуля; без ведущих нулей, пробелов, +, дробей и экспонент. Нет дат и восстановления остатков «на момент времени». Внутри партии порядок строк не влияет на итог: проверяется остаток после всей партии, не после каждого события. Предел: 1 000 000 байт и 10 000 строк данных на файл. Пустой пакет отклоняется.

Проверки

.venv/bin/python -m unittest discover -s tests -v
.venv/bin/python -m unittest discover -s tests -p integration.py -v
.venv/bin/python scripts/check_public.py
.venv/bin/python scripts/package_release.py

Первая команда проверяет контракт, CLI и публикацию без сервера. Вторая действительно поднимает свой PostgreSQL и проверяет транзакции/конкуренцию. Для CI допускается STOCK_TEST_DSN только с dbname=stock_test; этот тестовый набор очищает свои таблицы. В обычной команде приложения такого очищения нет. Фактические результаты.

Границы гарантий

Каждая загрузка — отдельная транзакция; весь список файлов не является одной транзакцией. Писатели через load_batch сериализуются табличной блокировкой. Обычные читатели работают; отчёт читает согласованный снимок REPEATABLE READ. Это простой вариант мини-склада, не оптимизация для сотен одновременных загрузок.

Нельзя заявлять глобальную защиту суммы через CHECK: CHECK ограничивает отдельную строку, а сумма проверяется приложением под блокировкой. Произвольный SQL от владельца базы может обойти правило. Роли и ограничения производственного сервера не настраиваются. Начальная схема создаётся один раз до параллельных загрузок; конкурентный init не поддержан.

При неизвестном исходе COMMIT повтори тот же batch_id с теми же байтами: либо пакет уже принят, либо будет принят впервые. Автоматического retry и распределённого exactly-once здесь нет. Исправление события требует нового event_id, а не перезаписи старого. История растёт; суммирование всего журнала со временем станет дорогим.

В Git/ZIP входят только исходники по PUBLIC_FILES.txt. Пароли временного кластера, базы, CSV, логи, результаты и окружение исключены. Сканер — дополнительная проверка, а не полный аудит безопасности. Не используй реальные данные для учебных опытов.

GitHub

Исходники, релиз v0.1.0 с ZIP и SHA256SUMS, CI Ubuntu/Windows и PostgreSQL.

About

Я загружаю складские события в PostgreSQL: COPY, staging, транзакции и защита от повторов. Проверяю откат партии и конкурентную загрузку. Учебный проект.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages