← Каталог моих проектов · С чего я начинаю · Контракт и решения · Что я проверил
Я разбираю путь 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 demoWindows-инструкции и конфигурации подготовлены, но локально Windows не проверялся. Приложение не скачивает PostgreSQL и не меняет глобальные настройки системы.
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 reportload --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, логи, результаты и окружение исключены. Сканер — дополнительная проверка, а не полный аудит безопасности. Не используй реальные данные для учебных опытов.
Исходники, релиз v0.1.0 с ZIP и SHA256SUMS, CI Ubuntu/Windows и PostgreSQL.