— «Работай из любой точки России!» — кричали 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.

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

Слайд в статье "Где должна жить бизнес-логика в аналитике (и почему KPI постоянно «плывут»)"
Слайд в статье "Где должна жить бизнес-логика в аналитике (и почему KPI постоянно «плывут»)"

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

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

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

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

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

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

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

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

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

Песочница

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

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

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

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