Learning Platform
Глоссарий Troubleshooting
Урок 17.06 · 23 мин
Продвинутый
capstoneduckdb-wasmpublishingbrowser-analytics

Публикация: запрос витрины из DuckDB-WASM в браузере

Пайплайн почти готов: сырые файлы прошли staging, превратились в партиционированную витрину mart_daily_zone в DuckLake и обновляются инкрементально через MERGE. Осталась последняя миля — доставить витрину потребителю. В капстоуне потребитель — это интерактивный дашборд, который работает прямо в браузере, без серверного API.

Здесь смыкаются два урока: DuckDB-WASM из модуля 15 (DuckDB в браузере) и наш пайплайн. Серверная часть готовит данные, браузерная — раздаёт по ним интерактивные запросы. Этот урок — про то, как соединить их: что и как экспортировать из витрины и как DuckDB-WASM это запрашивает на стороне клиента.


Принцип: тяжёлое на сервере, лёгкое в браузере

Прежде чем писать код, зафиксируем правильное разделение. Браузер — ограниченная среда: память вкладки невелика, у клиента может быть слабый телефон. Тащить в браузер всю витрину из сотен тысяч строк, а тем более 41 миллион сырых поездок, неправильно.

Разделение такое: всё тяжёлое — сканирование сырых данных, join, агрегацию — делает серверный пайплайн на DuckDB. Он публикует компактный результат. А браузерный DuckDB-WASM делает только лёгкое — интерактивные срезы и фильтры поверх этого компактного результата.

Разделение работы между сервером и браузером
Сервер: DuckDBТяжёлая часть пайплайна: скан 41 млн сырых поездок, join, агрегация, larger-than-memory расчёт. Результат — компактная витрина.
публикует компактный Parquet
CDN / object storageГотовый Parquet-файл витрины лежит как статический ресурс на CDN или объектном хранилище. Раздаётся по HTTPS.
HTTP range requests
Браузер: DuckDB-WASMЛёгкая часть: интерактивные фильтры и срезы по компактной витрине. Запросы исполняются на вкладке пользователя.

Этот принцип — прямое продолжение того, что мы видели в бенчмарке и в уроке про DuckDB-WASM: инструмент применяется там, где совпадает с масштабом задачи. Серверный DuckDB — для гигабайт сырья, браузерный DuckDB-WASM — для мегабайт готовой витрины.


Экспорт витрины в компактный Parquet

Витрина mart_daily_zone живёт в DuckLake. Для публикации её нужно выгрузить в обычный одиночный Parquet-файл — формат, который DuckDB-WASM читает по HTTP идеально. Делается это командой COPY.

Ключевое слово — компактный. Не нужно публиковать витрину целиком со всеми колонками за все годы. Для дашборда обычно достаточно недавнего периода и только тех колонок, которые он показывает:

-- Экспорт компактного среза витрины для браузера
COPY (
    SELECT trip_date, borough, zone_name, trips, revenue
    FROM lake.mart_daily_zone
    WHERE trip_date >= '2026-01-01'        -- только нужный период
) TO 'publish/dashboard.parquet'
  (FORMAT parquet, COMPRESSION zstd);

-- Результат:
-- 68_240 строк, файл dashboard.parquet ~1.9 МБ.
-- Сравните: исходные 41 млн сырых поездок — десятки ГБ.

Что здесь сделано осознанно:

  • Отбор колонок. В файл идут только 5 колонок, нужных дашборду, а не вся ширина витрины — projection уменьшает размер.
  • Отбор периода. WHERE trip_date >= ... оставляет только актуальный диапазон. История остаётся в лейкхаусе, но в браузер не едет.
  • Сжатие zstd. Parquet и так колоночный, zstd дожимает сильнее — меньше байт по сети до браузера.

Результат — файл около двух мегабайт вместо десятков гигабайт сырья. Такой файл DuckDB-WASM скачает мгновенно, а с учётом HTTP range requests подтянет даже не весь.

TIP

Размер опубликованного файла — это бюджет загрузки браузера. Полезное правило: публикуйте предагрегированную витрину, а не сырьё, и только нужный срез. Если дашборду нужны помесячные итоги, незачем класть в браузер поездочный уровень детализации — пусть финальная агрегация уже сделана на сервере. Чем компактнее файл, тем быстрее холодный старт дашборда.


Запрос витрины из DuckDB-WASM

Теперь браузерная сторона. Опубликованный dashboard.parquet лежит как статический файл на CDN. DuckDB-WASM, инициализированный на странице, запрашивает его по URL — так же, как нативный DuckDB читает локальный Parquet.

-- Внутри DuckDB-WASM на странице: запрос к опубликованной витрине
-- Файл подтягивается по HTTP range requests, не целиком

-- Срез для дашборда: топ зон по выручке за период
SELECT borough, zone_name, sum(revenue) AS revenue, sum(trips) AS trips
FROM 'https://cdn.example.com/publish/dashboard.parquet'
WHERE trip_date BETWEEN '2026-04-01' AND '2026-04-30'
GROUP BY ALL
ORDER BY revenue DESC
LIMIT 10;

-- Результат считается целиком в браузере, без обращения к серверу API:
-- borough   | zone_name       | revenue    | trips
-- Manhattan | Midtown Center  | 2104881.40 | 112033
-- Manhattan | Upper East Side | 1442200.05 | 88714
-- Queens    | JFK Airport     | 1390442.80 | 31002
-- ...

Каждый клик пользователя в дашборде — смена периода, выбор borough, переключение метрики — это новый SQL-запрос к тому же файлу, исполняемый DuckDB-WASM на вкладке. Сервера в этом цикле нет: после загрузки .wasm-модуля и Parquet-файла дашборд работает автономно. Отклик на фильтр — миллисекунды, потому что нет сетевого round-trip.

Если дашборд работает с одним фиксированным датасетом постоянно, его можно положить в OPFS (из урока про DuckDB-WASM) — тогда файл переживёт перезагрузку вкладки и не будет скачиваться заново.


Полный путь данных капстоуна

Урок про публикацию замыкает пайплайн. Соберём весь путь данных от сырого файла до пикселя в браузере — это и есть архитектура капстоуна целиком.

Сквозной путь данных: от сырья до браузера
raw: Parquet + CSVСырые помесячные поездки и грязный справочник зон на объектном хранилище. Иммутабельны.
staging: очистка, типизация
stg_trips, stg_zonesОчищенные типизированные источники. Друг с другом не смешаны, бизнес-логики нет.
marts: join + агрегация
lake.mart_daily_zoneПартиционированная витрина в DuckLake. ACID, снапшоты, инкрементальный MERGE нового месяца.
COPY: компактный срез
dashboard.parquetКомпактный Parquet нужного периода и колонок, сжатый zstd. Лежит на CDN как статика.
DuckDB-WASM: запросы
Дашборд в браузереИнтерактивные срезы исполняются DuckDB-WASM на вкладке пользователя. Серверного API нет.

Обратите внимание на сквозную линию: DuckDB как движок присутствует на обоих концах. На сервере он читает сырьё, чистит, моделирует, материализует витрину в DuckLake. В браузере его WASM-сборка раздаёт интерактивные запросы. Один движок, один SQL-диалект, от десятков гигабайт сырья до интерактивного клика — это и есть «DuckDB everywhere» в применении к реальному пайплайну.


Попробуй сам

Понадобится DuckDB 1.5.x с витриной из предыдущих уроков модуля и любой способ отдать файл по HTTP (локальный статический сервер подойдёт).

Задания:

  1. Выгрузите компактный срез витрины через COPY (SELECT ... WHERE trip_date >= ...) TO 'dashboard.parquet' (FORMAT parquet, COMPRESSION zstd). Сравните размер файла с объёмом исходных сырых данных.
  2. Поэкспериментируйте с компактностью: выгрузите витрину со всеми колонками и за весь период, потом — узкий срез. Сравните размеры файлов и сформулируйте, как отбор колонок и периода влияет на бюджет загрузки браузера.
  3. Отдайте dashboard.parquet по HTTP (например, локальным статическим сервером) и откройте shell.duckdb.org. Выполните в браузерной оболочке агрегирующий запрос к файлу по его URL — убедитесь, что расчёт идёт в браузере.
  4. Откройте вкладку Network в DevTools и проверьте, что DuckDB-WASM скачал не весь файл, а диапазоны байт. Затем нарисуйте полный путь данных капстоуна от сырого файла до браузерного запроса и подпишите роль DuckDB на каждом шаге.

Parquet row groups: как HTTP range requests работают с Parquet
Проверка знанийKnowledge check
Как устроена публикация витрины капстоуна для браузера и почему правильно отдавать в DuckDB-WASM компактный Parquet, а не всю витрину или сырые данные?
ОтветAnswer
Публикация — это последняя миля пайплайна, и она построена на разделении работы: всё тяжёлое (скан 41 миллиона сырых поездок, join, агрегацию, larger-than-memory расчёт) делает серверный пайплайн на DuckDB и материализует витрину в DuckLake, а браузерный DuckDB-WASM делает только лёгкое — интерактивные срезы и фильтры. Серверная сторона выгружает витрину командой COPY в компактный одиночный Parquet-файл: отбираются только нужные дашборду колонки (projection), только актуальный период через WHERE и применяется сжатие zstd. В итоге получается файл порядка двух мегабайт вместо десятков гигабайт сырья. Этот файл лежит как статический ресурс на CDN, а DuckDB-WASM на странице запрашивает его по URL через HTTP range requests — подтягивая даже не весь файл. Каждый клик пользователя (смена периода, выбор borough) — это новый SQL-запрос к тому же файлу, исполняемый на вкладке без обращения к серверу, поэтому отклик измеряется миллисекундами. Отдавать в браузер всю витрину из сотен тысяч строк, а тем более сырые данные, неправильно по двум причинам: память вкладки невелика и у клиента может быть слабое устройство, а холодный старт дашборда тем дольше, чем больше скачиваемый файл — размер опубликованного файла это бюджет загрузки браузера. Поэтому в браузер идёт предагрегированная витрина и только нужный срез, а финальная агрегация уже сделана на сервере. Если дашборд работает с фиксированным датасетом, файл можно положить в OPFS, чтобы он пережил перезагрузку вкладки. Сквозная линия пайплайна — DuckDB как движок присутствует на обоих концах: на сервере читает сырьё и моделирует витрину, в браузере раздаёт запросы своей WASM-сборкой; один движок и один SQL-диалект от сырья до интерактивного клика.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. Как в капстоуне разделена работа между серверным DuckDB и браузерным DuckDB-WASM?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 7