— «Работай из любой точки России!» — кричали HR-офферы.

— «У нас облачный BI, всё летает!» — вторили тимлиды.

А потом ты выезжаешь за МКАД, садишься в поезд или просто решаешь поработать с веранды на даче, и реальность бьет под дых. Мобильный интернет сегодня — это лотерея. Вчера он был, сегодня его «прикрутили» из-за учений, завтра ты въехал в «белое пятно», а послезавтра твой VPN забанили вместе с протоколом.

И вот ты сидишь, смотришь на крутящийся спиннер в браузере и понимаешь: твоя хваленая облачная IDE превратилась в дорогой скринсейвер. Весь твой BI-стек остался где-то там, за горизонтом событий, куда пакеты не долетают. Схема замерла, ETL превратился в тыкву, а изменения, которые ты вырисовывал последние полчаса, отправились в цифровую вальгаллу.

В этой статье я покажу, как в системе asapBI реализована честная оффлайн работа на примере графического SQL запроса к Clickhouse. Расскажу о том, как под капотом подружились локальное хранилище и тяжелые графы, и почему фраза «интернета нет, но вы моделируйте» — это теперь не издевка, а реальный фича-реквест.

Работа в оффлайне с локальным стеком — это не просто «чтобы работало в лесу». Это принципиально другой уровень контроля над процессом:

  1. Твои данные — твоя крепость (Никто не сотрет)

    В общей dev-базе всегда есть риск, что коллега Вася решит «почистить место» или накатит кривой миграционный скрипт, который снесет твою тестовую таблицу с идеально подобранными кейсами.

    Плюс: В локальном Docker ты сам себе админ. Твои данные живут в томах (volumes), и никто, кроме тебя, их не тронет. Хочешь — храни их годами, хочешь — делай бэкап одной папкой.

  2. Эксперименты без «расстрела»

    Попытка выполнить тяжелый JOIN или сложную View на общем сервере может высадить CPU в 100% и парализовать работу всей команды.

    Плюс: В оффлайне ты можешь «жарить» свой процессор как угодно. Если запрос ушел в бесконечный цикл или съел всю память — упадет только твой контейнер, а не кластер.

  3. Версионность и "Time Machine" через Git

    Когда логика запроса хранится в базе (как обычная View), трудно отследить, кто и когда её поменял.

    Плюс: Твой View — это JSON-файл. Ты можешь создать ветку feature/new-logic, наворотить там схем, а потом сделать diff и посмотреть, что именно изменилось в графе. Это настоящий Code Review для аналитики.

  4. Мы работаем с «цифровым двойником» структуры БД.

    Плюс: Можно крутить связи, менять типы данных и перестраивать логику, не нагружая общий сервер бесконечными ALTER или проверочными SELECT. А если вариант разработки не получился – просто создать среду заново.

Итак, что же в asapBI можно из BI-работ делать оффлайн? Все. Да-да, и «даже на парковке» 😉

Не теряйте время, для оффлайн-работы надо сначала подготовиться. Это мы сделаем за 3 шага:

1. Подключение к корпоративному Git и скачивание проекта в локальную папку.

Удобно делить проекты по направлениям: вот это финансы, это – сбыт. На уровне asapBI такой проект видится в виде папки:

Рис. 1. Локальная папка проекта
Рис. 1. Локальная папка проекта

2. Установка BI-стека

В проекте присутствует файл docker-compose.yml. Этот файл вместе с установленным докером запускает локально нужный нам BI-стек. Обычно этот файл делают админы и кладут в git, но при желании мы сами можем его поправить – например, добавить Airflow.

Пример файла docker-compose.yml для локального запуска PostgreSQL и Clickhouse:

Скрытый текст
services:
  asapbi-postgres:
    image: postgres:15-alpine
    container_name: asapbi-postgres
    restart: unless-stopped
    environment:
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: admin
      POSTGRES_DB: demo
      PGDATA: /var/lib/postgresql/data/pgdata
    ports:
      - "15432:5432"
    volumes:
      - postgres-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U admin -d demo"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 10s
    networks:
      - asapbi-net

  asapbi-clickhouse:
    image: clickhouse/clickhouse-server:24.8-alpine
    container_name: asapbi-clickhouse
    restart: unless-stopped
    environment:
      CLICKHOUSE_USER: admin
      CLICKHOUSE_PASSWORD: admin
      CLICKHOUSE_DB: analytics
    ports:
      - "18123:8123"   # HTTP
      - "19000:9000"   # Native
    volumes:
      - clickhouse-data:/var/lib/clickhouse
      - clickhouse-logs:/var/log/clickhouse-server
    healthcheck:
      test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:8123/ping || exit 1"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s
    networks:
      - asapbi-net

volumes:
  postgres-data:
    driver: local
  clickhouse-data:
    driver: local
  clickhouse-logs:
    driver: local

networks:
  asapbi-net:
    driver: bridge

Предварительно нужно скачать и установить Docker Desktop.

 Теперь запуск и остановка локальных баз данных осуществляется из интерфейса asapBI (при первом старте докер их самостоятельно скачает и установит):

Рис. 2. Запуск и останов баз данных
Рис. 2. Запуск и останов баз данных

Конечно, старт/стоп можно делать и через консоль, а также через сам Docker Desktop.

3. Наполнение баз данных

Итак, базы данных у нас уже запущены. Надо подумать про наполнение – создание таблиц, загрузку данных.

Здесь можно поступить по-разному. Например, взять DBeaver, создать в базах таблицы и наколотить туда данных.

Второй вариант – в скачанном git-проекте могут быть файлы с расширением *.dbcopy-json. Этот файл содержит правила копирования:

Скрытый текст
{
  "description": "Копирование клиентов с подготовкой и очисткой",
  "beforeSQL": [
    "BEGIN;",
    "SET session_replication_role = replica;",
    "CREATE TEMP TABLE IF NOT EXISTS _copy_log (ts timestamptz, msg text);"
  ],
  
  "afterSQL": [
    "SET session_replication_role = DEFAULT;",
    "INSERT INTO _copy_log VALUES (now(), 'Copy completed');",
    "COMMIT;"
  ],

  "tables": [
    {
      "schema": "reference",
      "table": "clients",
      "where": "is_active = true AND created_at >= '2024-01-01'",
      "beforeSQL": [
        "DROP INDEX IF EXISTS idx_clients_region_id",
        "DELETE FROM reference.clients WHERE created_at >= '2024-01-01'"
      ],
      "afterSQL": [        
        "CREATE INDEX IF NOT EXISTS idx_clients_region_id ON reference.clients (region_id)",
        "ANALYZE reference.clients"
      ]
    },
    {
      "schema": "sales",
      "table": "orders",
      "where": "order_date >= '2024-01-01'",
      "beforeSQL": "ALTER TABLE sales.orders DISABLE TRIGGER ALL",
      "afterSQL": "ALTER TABLE sales.orders ENABLE TRIGGER ALL"
    },
    {
      "schema": "analytics",
      "table": "user_sessions",
      "where": "",
      "beforeSQL": null,
      "afterSQL": null
    }
  ]
}

Открыв файл и выбрав базу-источник и локальную базу-приемник, можно скопировать себе часть тестовых данных.

Также в этот файл можно поместить SQL, чтобы при старте именно под вашу задачу создавались таблицы и загружались тестовые данные.

Внимание – безопасники будут сильно против копирования данных из продуктива! Поэтому только тестовые данные с сервера разработки, не имеющие к реальному миру никакого отношения.

Рис. 3. Копирование данных в локальную базу
Рис. 3. Копирование данных в локальную базу

Графический View

А теперь, выбрав запущенный ранее локальный Clickhouse (или другую базу данных), можно строить графические View:

Рис. 4. Графический View
Рис. 4. Графический View

Графический View – это тоже json-файл. Это значит версионность, Code Review и легкий откат.

Если строить SQL-выборки непосредственно в BI-системах - бизнес-логика зашивается на уровне конкретного дашборда. В одном дашборде площадь посчитали одним образом, в другом - другим, а истина вообще где-то рядом. Единой точки правды не получается, и Заказчик удивленно показывает разные цифры в разных дашбордах для одного и того же показателя.

Если писать SQL-выборки на уровне дашборда, то не происходит накопления знаний и фиксации алгоритмов. Вынос алгоритмов из дашборда на уровень View помогает накапливать знания и дает единую точку правды для разных дашбордов при переиспользовании View.

Существует вообще мнение, что алгоритмы расчетов следует фиксировать еще раньше, перед витринами, но это обсуждаемо...

Документирование разработок

Сразу после сохранения документация начинает устаревать – точно так же, как автомобиль, выехавший из салона, тут же теряет часть своей стоимости. Чем дальше, тем больше разъезжается документация и реальные настройки.

А что, если при изменении модели не надо никуда далеко ходить, а править тут же, рядом с объектом? Для этого, например, в той же папке с графической View есть файлы с таким же именем, но с другим расширением - они отображаются под узлом View:

Рис. 5. К View можно прикреплять файлы с документацией
Рис. 5. К View можно прикреплять файлы с документацией

Можно добавлять, создавать любые файлы, которые может открывать VSCode:

Рис. 6. Файл в формате MD
Рис. 6. Файл в формате MD
Рис. 7. Файл в формате drawio
Рис. 7. Файл в формате drawio

При изменении объекта разработчику ничего не стоит тут же открыть документацию и прокомментировать изменения.

Эти все файлы – в текстовом формате, а это значит, что их можно скормить как AI, так и автоматическому сборщику документации.

Таким образом у нас всегда будет актуальная документация на проекте, и пусть под массой стопки бумаг содрогнется стол, когда на него будет положена проектная распечатка 😉

Песочница

Что же у нас получилось? А получилась личная песочница, в которой можно исследовать гипотезы, ошибаться, ронять сервер - и ничего за это не будет.

Можно даже запускать на ночь AI, если наураганит чушь - можно откатить разработку на начало.
Ну а если все хорошо - синхронизировать изменения с общим Git и общими серверами разработки.

🎯 Итог: оффлайн-работа в asapBI — это не просто «работа без интернета». Это полноценная локальная среда разработки с версионностью, изоляцией, безопасностью экспериментов и актуальной документацией. Теперь фраза «работай из любой точки» обретает новый смысл — даже если этой точкой является крыша поезда или палатка на море.

Предыдущие статьи цикла