Развертывание
Поддерживаемый путь
Текущая версия проекта после squashed migrations поддерживает deployment через fresh-install.
Шаги
Проверки
Проверьте, что worker успешно применил миграции и запустился без ошибок.
Примечание
Upgrade-in-place со старых БД до squash-версии не поддерживается.
CI auto-deploy (GitHub Actions -> production server)
В репозитории используется workflow .github/workflows/deploy.yml.
Триггеры:
- push в main (кроме docs-only изменений);
- ручной запуск через workflow_dispatch.
Pipeline выполняет SSH deploy на сервер:
1. git fetch origin --prune
2. git checkout main
3. git reset --hard <github.sha>
4. docker compose up -d --build --remove-orphans
5. post-deploy проверки: docker compose ps, анализ worker логов, проверка на exited-сервисы.
Обязательная конфигурация GitHub
Secrets:
- PROD_SSH_HOST
- PROD_SSH_USER
- PROD_SSH_PRIVATE_KEY
- PROD_SSH_KNOWN_HOSTS
Variables:
- PROD_SSH_PORT (опционально, default 22)
- PROD_APP_PATH (абсолютный путь к репозиторию на сервере)
Серверный контракт
- На сервере есть clone этого репозитория и
originуказывает на GitHub. - Установлены
git,docker,docker compose(v2). - Production
.envхранится на сервере и не перезаписывается workflow.
Rollback (manual)
При падении workflow команда отката печатается в логи job. Шаблон:
cd <PROD_APP_PATH> \
&& git fetch origin --prune \
&& git checkout main \
&& git reset --hard <previous-sha> \
&& docker compose up -d --build --remove-orphans
Документация (Cloudflare -> main)
Документация публикуется Cloudflare напрямую из ветки main.
Workflow .github/workflows/docs.yml не выполняет отдельный deploy документации и не пишет в gh-pages.
Его задача только одна: проверить, что текущие файлы документации и mkdocs.yml успешно собираются через mkdocs build --strict.
Источник истины для версий docs toolchain: requirements-docs.txt.
Каноничный порядок заполнения таблиц (bootstrap + schedules)
Этот раздел является единственным источником истины по последовательности запуска data pipelines.
1. Prerequisites / readiness gates
Ожидаемое состояние:
- postgres и temporal в healthy;
- temporal-admin-tools в healthy;
- worker в started/up;
- в логах worker есть успешный старт и нет ошибки Namespace default is not found.
2. Manual bootstrap (правильная последовательность)
Шаг 1. SyncExchangesWorkflow (обязательно): актуализирует метаданные exchanges и фиксирует базовое состояние каталога бирж перед последующими sync-шагами.
docker compose exec -T temporal-admin-tools temporal workflow start \
--address temporal:7233 --namespace default --task-queue default \
--type SyncExchangesWorkflow \
--workflow-id sync-exchanges-bootstrap-$(date +%s)
Шаг 2. SyncNetworksCatalogWorkflow (обязательно): заполняет networks_catalog и связанные идентификаторы/эндпоинты/метрики.
docker compose exec -T temporal-admin-tools temporal workflow start \
--address temporal:7233 --namespace default --task-queue default \
--type SyncNetworksCatalogWorkflow \
--workflow-id sync-networks-catalog-bootstrap-$(date +%s) \
--input '{"force_full_refresh":false,"soft_delete_grace_days":30,"metrics_required":false}'
Шаг 3. SyncExchangeCoinsWorkflow (обязательно): заполняет exchange_assets*, global_assets*, asset_mapping_quarantine.
docker compose exec -T temporal-admin-tools temporal workflow start \
--address temporal:7233 --namespace default --task-queue default \
--type SyncExchangeCoinsWorkflow \
--workflow-id sync-exchange-coins-bootstrap-$(date +%s) \
--input '{"force_full_refresh":false,"soft_delete_grace_days":30}'
Шаг 4. SyncGateLaunchpoolWorkflow (обязательно для launchpool аналитики): заполняет fct_gate_launchpool_* с использованием уже созданного mapping-контура из шага 3.
docker compose exec -T temporal-admin-tools temporal workflow start \
--address temporal:7233 --namespace default --task-queue default \
--type SyncGateLaunchpoolWorkflow \
--workflow-id sync-gate-launchpool-bootstrap-$(date +%s) \
--input '{"page":1,"page_size":1000,"status":0}'
Шаг 5. EnsureGateLaunchpoolScheduleWorkflow (после успешного шага 4): включает регулярный 5-минутный ingestion.
docker compose exec -T temporal-admin-tools temporal workflow start \
--address temporal:7233 --namespace default --task-queue default \
--type EnsureGateLaunchpoolScheduleWorkflow \
--workflow-id ensure-gate-launchpool-schedule-bootstrap-$(date +%s) \
--input '{"schedule_id":"sync-gate-launchpool-5m","cron":"*/5 * * * *","timezone":"UTC","task_queue":"default","page":1,"page_size":1000,"status":0}'
Expected result: schedule sync-gate-launchpool-5m создан/обновлен idempotent-образом.
Шаг 6. SyncBybitLaunchpoolWorkflow (manual-only): снимает snapshot Bybit Launchpool в отдельные таблицы fct_bybit_launchpool_*.
docker compose exec -T temporal-admin-tools temporal workflow start \
--address temporal:7233 --namespace default --task-queue default \
--type SyncBybitLaunchpoolWorkflow \
--workflow-id sync-bybit-launchpool-bootstrap-$(date +%s) \
--input '{"current":1}'
Шаг 7. SyncBybitEasyEarnWorkflow (manual-only): снимает snapshot Bybit Easy Earn в отдельные таблицы fct_bybit_easy_earn_*.
docker compose exec -T temporal-admin-tools temporal workflow start \
--address temporal:7233 --namespace default --task-queue default \
--type SyncBybitEasyEarnWorkflow \
--workflow-id sync-bybit-easy-earn-bootstrap-$(date +%s) \
--input '{}'
Шаг 8. SyncGateSimpleEarnWorkflow (manual-only): снимает snapshot Gate Simple Earn в отдельные таблицы fct_gate_simple_earn_*.
docker compose exec -T temporal-admin-tools temporal workflow start \
--address temporal:7233 --namespace default --task-queue default \
--type SyncGateSimpleEarnWorkflow \
--workflow-id sync-gate-simple-earn-bootstrap-$(date +%s) \
--input '{}'
Шаг 9. SyncKuCoinEarnWorkflow (manual-only): снимает snapshot KuCoin Earn All Products в отдельные таблицы fct_kucoin_earn_*.
docker compose exec -T temporal-admin-tools temporal workflow start \
--address temporal:7233 --namespace default --task-queue default \
--type SyncKuCoinEarnWorkflow \
--workflow-id sync-kucoin-earn-bootstrap-$(date +%s) \
--input '{}'
Шаг 10. SyncBingXLaunchpoolWorkflow (manual-only): снимает snapshot BingX Launchpool в отдельные таблицы fct_bingx_launchpool_*.
docker compose exec -T temporal-admin-tools temporal workflow start \
--address temporal:7233 --namespace default --task-queue default \
--type SyncBingXLaunchpoolWorkflow \
--workflow-id sync-bingx-launchpool-bootstrap-$(date +%s) \
--input '{"include_details":true}'
Шаг 11. SyncBingXXPoolWorkflow (manual-only): снимает snapshot BingX XPool в отдельные таблицы fct_bingx_xpool_*.
docker compose exec -T temporal-admin-tools temporal workflow start \
--address temporal:7233 --namespace default --task-queue default \
--type SyncBingXXPoolWorkflow \
--workflow-id sync-bingx-xpool-bootstrap-$(date +%s) \
--input '{"include_details":true}'
Шаг 12. SyncBitgetLaunchpoolWorkflow (manual-only): снимает snapshot Bitget PoolX в отдельные таблицы fct_bitget_launchpool_*.
docker compose exec -T temporal-admin-tools temporal workflow start \
--address temporal:7233 --namespace default --task-queue default \
--type SyncBitgetLaunchpoolWorkflow \
--workflow-id sync-bitget-launchpool-bootstrap-$(date +%s) \
--input '{"include_details":true}'
Шаг 13. SyncKuCoinLaunchpoolWorkflow (manual-only): снимает snapshot KuCoin GemPool в отдельные таблицы fct_kucoin_launchpool_*.
docker compose exec -T temporal-admin-tools temporal workflow start \
--address temporal:7233 --namespace default --task-queue default \
--type SyncKuCoinLaunchpoolWorkflow \
--workflow-id sync-kucoin-launchpool-bootstrap-$(date +%s) \
--input '{"include_current":true,"include_history":true,"history_page_size":20,"max_history_pages":3}'
Шаг 14. SDK loans pipelines (manual-only): снимают snapshot catalog offers в отдельные таблицы fct_<exchange>_loans_*.
docker compose exec -T temporal-admin-tools temporal workflow start \
--address temporal:7233 --namespace default --task-queue default \
--type SyncGateLoansWorkflow \
--workflow-id sync-gate-loans-bootstrap-$(date +%s) \
--input '{}'
docker compose exec -T temporal-admin-tools temporal workflow start \
--address temporal:7233 --namespace default --task-queue default \
--type SyncBybitLoansWorkflow \
--workflow-id sync-bybit-loans-bootstrap-$(date +%s) \
--input '{}'
docker compose exec -T temporal-admin-tools temporal workflow start \
--address temporal:7233 --namespace default --task-queue default \
--type SyncKuCoinLoansWorkflow \
--workflow-id sync-kucoin-loans-bootstrap-$(date +%s) \
--input '{}'
docker compose exec -T temporal-admin-tools temporal workflow start \
--address temporal:7233 --namespace default --task-queue default \
--type SyncWhitebitLoansWorkflow \
--workflow-id sync-whitebit-loans-bootstrap-$(date +%s) \
--input '{}'
Шаг 15. Terry default schedule (optional): если задан TERRY_DEFAULT_DATA_SOURCE_ID, worker при старте автоматически делает paused upsert schedule для RunIngestionWorkflow. Для ручного управления можно вызвать orchestration workflow напрямую.
docker compose exec -T temporal-admin-tools temporal workflow start \
--address temporal:7233 --namespace default --task-queue default \
--type EnsureRunIngestionScheduleWorkflow \
--workflow-id ensure-run-ingestion-schedule-bootstrap-$(date +%s) \
--input '{"schedule_id":"run-ingestion","cron":"0 0 * * *","timezone":"UTC","task_queue":"default","data_source_id":"<uuid>","paused":true,"preserve_paused_state":true}'
3. Почему порядок именно такой
SyncExchangeCoinsWorkflowрезолвитnetwork_keyчерезnetworks_catalog; без шага 2 растетmissing_network_mapping.SyncGateLaunchpoolWorkflowмаппитproject_coin/subpool_coinчерезexchange_assets*+exchange_asset_mappings; без шага 3 растетfct_gate_launchpool_quarantine.SyncGateSimpleEarnWorkflowмаппитproject_coin/reward_coinчерезexchange_symbol_assets; без шага 3 растетfct_gate_simple_earn_quarantine.SyncExchangesWorkflowдолжен выполняться первым шагом как обязательный этап консистентного bootstrap каталога бирж.- Поэтому каноничный порядок bootstrap для core-контура:
SyncExchangesWorkflow -> SyncNetworksCatalogWorkflow -> SyncExchangeCoinsWorkflow -> SyncGateLaunchpoolWorkflow. - Для Bybit/BingX/Bitget/KuCoin Launchpool, Bybit Easy Earn, Gate Simple Earn, KuCoin Earn, BingX XPool и SDK loans используются отдельные manual-only pipelines (
SyncBybitLaunchpoolWorkflow,SyncBybitEasyEarnWorkflow,SyncGateSimpleEarnWorkflow,SyncKuCoinLaunchpoolWorkflow,SyncKuCoinEarnWorkflow,SyncBingXLaunchpoolWorkflow,SyncBitgetLaunchpoolWorkflow,SyncBingXXPoolWorkflow,SyncGateLoansWorkflow,SyncBybitLoansWorkflow,SyncKuCoinLoansWorkflow,SyncWhitebitLoansWorkflow) и отдельные хранилищаfct_*; schedule по умолчанию для них не включается. - Terry runtime живет отдельно от exchange-specific pipelines:
RunIngestionWorkflowвыполняет universal loader по persisteddata_source_id, аEnsureRunIngestionScheduleWorkflowуправляет paused-by-default schedulerun-ingestion.
4. Проверки после каждого шага
Проверка статуса workflow (должен быть COMPLETED):
docker compose exec -T temporal-admin-tools temporal workflow describe \
--address temporal:7233 --namespace default \
--workflow-id <workflow-id>
Минимальные SQL checks:
docker exec -i kit-postgres psql -U temporal -d kit -At -c "SELECT COUNT(*) FROM networks_catalog;"
docker exec -i kit-postgres psql -U temporal -d kit -At -c "SELECT COUNT(*) FROM exchange_assets;"
docker exec -i kit-postgres psql -U temporal -d kit -At -c "SELECT COUNT(*) FROM exchange_asset_networks;"
docker exec -i kit-postgres psql -U temporal -d kit -At -c "SELECT COUNT(*) FROM fct_gate_launchpool_snapshot;"
docker exec -i kit-postgres psql -U temporal -d kit -At -c "SELECT COUNT(*) FROM fct_bybit_launchpool_snapshot;"
docker exec -i kit-postgres psql -U temporal -d kit -At -c "SELECT COUNT(*) FROM fct_bybit_easy_earn_snapshot;"
docker exec -i kit-postgres psql -U temporal -d kit -At -c "SELECT COUNT(*) FROM fct_gate_simple_earn_snapshot;"
docker exec -i kit-postgres psql -U temporal -d kit -At -c "SELECT COUNT(*) FROM fct_kucoin_earn_snapshot;"
docker exec -i kit-postgres psql -U temporal -d kit -At -c "SELECT COUNT(*) FROM fct_kucoin_launchpool_snapshot;"
docker exec -i kit-postgres psql -U temporal -d kit -At -c "SELECT COUNT(*) FROM fct_bingx_launchpool_snapshot;"
docker exec -i kit-postgres psql -U temporal -d kit -At -c "SELECT COUNT(*) FROM fct_bitget_launchpool_snapshot;"
docker exec -i kit-postgres psql -U temporal -d kit -At -c "SELECT COUNT(*) FROM fct_bingx_xpool_snapshot;"
docker exec -i kit-postgres psql -U temporal -d kit -At -c "SELECT COUNT(*) FROM fct_gate_loans_snapshot;"
docker exec -i kit-postgres psql -U temporal -d kit -At -c "SELECT COUNT(*) FROM fct_bybit_loans_snapshot;"
docker exec -i kit-postgres psql -U temporal -d kit -At -c "SELECT COUNT(*) FROM fct_kucoin_loans_snapshot;"
docker exec -i kit-postgres psql -U temporal -d kit -At -c "SELECT COUNT(*) FROM fct_whitebit_loans_snapshot;"
Мониторинг quarantine:
docker exec -i kit-postgres psql -U temporal -d kit -At -c "SELECT reason, COUNT(*) FROM asset_mapping_quarantine GROUP BY reason ORDER BY COUNT(*) DESC;"
docker exec -i kit-postgres psql -U temporal -d kit -At -c "SELECT reason, COUNT(*) FROM fct_gate_launchpool_quarantine GROUP BY reason ORDER BY COUNT(*) DESC;"
docker exec -i kit-postgres psql -U temporal -d kit -At -c "SELECT reason, COUNT(*) FROM fct_bybit_launchpool_quarantine GROUP BY reason ORDER BY COUNT(*) DESC;"
docker exec -i kit-postgres psql -U temporal -d kit -At -c "SELECT reason, COUNT(*) FROM fct_bybit_easy_earn_quarantine GROUP BY reason ORDER BY COUNT(*) DESC;"
docker exec -i kit-postgres psql -U temporal -d kit -At -c "SELECT reason, COUNT(*) FROM fct_gate_simple_earn_quarantine GROUP BY reason ORDER BY COUNT(*) DESC;"
docker exec -i kit-postgres psql -U temporal -d kit -At -c "SELECT reason, COUNT(*) FROM fct_kucoin_earn_quarantine GROUP BY reason ORDER BY COUNT(*) DESC;"
docker exec -i kit-postgres psql -U temporal -d kit -At -c "SELECT reason, COUNT(*) FROM fct_kucoin_launchpool_quarantine GROUP BY reason ORDER BY COUNT(*) DESC;"
docker exec -i kit-postgres psql -U temporal -d kit -At -c "SELECT reason, COUNT(*) FROM fct_bingx_launchpool_quarantine GROUP BY reason ORDER BY COUNT(*) DESC;"
docker exec -i kit-postgres psql -U temporal -d kit -At -c "SELECT reason, COUNT(*) FROM fct_bitget_launchpool_quarantine GROUP BY reason ORDER BY COUNT(*) DESC;"
docker exec -i kit-postgres psql -U temporal -d kit -At -c "SELECT reason, COUNT(*) FROM fct_bingx_xpool_quarantine GROUP BY reason ORDER BY COUNT(*) DESC;"
5. Порядок включения расписаний после bootstrap
Включать только после успешного bootstrap шагов 2-4.
Рекомендуемый порядок:
1. sync-gate-launchpool-5m (обязательно)
2. sync-exchange-coins-daily (если используется)
3. sync-networks-catalog-daily (если используется)
4. sync-exchanges-daily (опционально)
Не включайте регулярный launchpool ingestion до успешного выполнения bootstrap для SyncNetworksCatalogWorkflow, SyncExchangeCoinsWorkflow и SyncGateLaunchpoolWorkflow.
6. Документационные сценарии валидации runbook
- Fresh-install: пройти шаги runbook от старта сервисов до включения schedule без дополнительных правок.
- Повторный bootstrap: повторный запуск шагов не должен приводить к разрушению состояния (идемпотентный смысл).
- Race scenario: при
Namespace default is not foundrecovery черезrestart workerдолжен возвращать пайплайн в рабочее состояние. - Quality scenario: запуск launchpool до ExchangeCoins является анти-паттерном (рост quarantine); runbook предотвращает это правильным порядком.