<?xml version="1.0" encoding="utf-8"?> 
<rss version="2.0"
  xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
  xmlns:atom="http://www.w3.org/2005/Atom">

<channel>

<title>Yuriy Gavrilov: posts tagged Streaming</title>
<link>https://gavrilov.info/tags/streaming/</link>
<description>Welcome to my personal place for love, peace and happiness 🤖 Yuiry Gavrilov</description>
<author></author>
<language>en</language>
<generator>Aegea 11.4 (v4171e)</generator>

<itunes:owner>
<itunes:name></itunes:name>
<itunes:email>yvgavrilov@gmail.com</itunes:email>
</itunes:owner>
<itunes:subtitle>Welcome to my personal place for love, peace and happiness 🤖 Yuiry Gavrilov</itunes:subtitle>
<itunes:image href="https://gavrilov.info/pictures/userpic/userpic-square@2x.jpg?1643451008" />
<itunes:explicit>no</itunes:explicit>

<item>
<title>Мир без Kafka: Почему Kafka не подходит для аналитики реального времени, что идет на смену)</title>
<guid isPermaLink="false">318</guid>
<link>https://gavrilov.info/all/mir-bez-kafka-pochemu-kafka-ne-podhodit-dlya-analitiki-realnogo/</link>
<pubDate>Thu, 12 Feb 2026 13:50:00 +0300</pubDate>
<author></author>
<comments>https://gavrilov.info/all/mir-bez-kafka-pochemu-kafka-ne-podhodit-dlya-analitiki-realnogo/</comments>
<description>
&lt;p&gt;Статья описывает переход от традиционных систем обмена сообщениями, таких как Apache Kafka, к специализированным решениям для потоковой аналитики, таким как &lt;b&gt;Apache Fluss&lt;/b&gt;.&lt;/p&gt;
&lt;p&gt;Основные тезисы:&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;&lt;b&gt;Проблема Kafka:&lt;/b&gt; Kafka — это система хранения на основе *записей* (record-based), не имеющая нативной поддержки схем и аналитических возможностей. Это приводит к избыточному чтению данных и перегрузке сети при аналитических запросах, когда нужны только конкретные колонки, а не всё сообщение целиком.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Эволюция требований:&lt;/b&gt; Рынок перешел от простого перемещения данных (ingestion) к сложной аналитике реального времени и AI, что требует более эффективного хранения и доступа к данным.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Решение (Apache Fluss):&lt;/b&gt;
&lt;ul&gt;
  &lt;li&gt;Табличная структура:** Данные хранятся как таблицы (Log Tables для логов и PK Tables для изменяемых данных), что обеспечивает строгую типизацию.&lt;/li&gt;
  &lt;li&gt;Колоночное хранение:** Использование формата Apache Arrow позволяет читать только нужные колонки (projection pushdown) и эффективнее сжимать данные, что снижает нагрузку на диск и сеть.&lt;/li&gt;
  &lt;li&gt;Интеграция с Lakehouse:** Fluss нативно поддерживает многоуровневое хранение (горячие данные в Fluss, теплые/холодные в S3/Iceberg/Paimon) без лишнего копирования, обеспечивая прозрачный доступ к историческим и оперативным данным.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Вывод:&lt;/b&gt; Fluss в связке с Flink предлагает более дешевую, быструю и удобную архитектуру для современной аналитики реального времени, устраняя недостатки Kafka в этой области.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;b&gt;Ссылка на оригинал:&lt;/b&gt;&lt;br /&gt;
&lt;a href="https://www.ververica.com/blog/a-world-without-kafka"&gt;Why Kafka Falls Short for Real-Time Analytics (and What Comes Next&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;У Apache Kafka был замечательный период: она обеспечивала работу событийно-ориентированных архитектур более десяти лет. Но ландшафт изменился, обнажив явные &lt;b&gt;ограничения Kafka для аналитики в реальном времени&lt;/b&gt; по мере того, как сценарии использования современной &lt;b&gt;потоковой аналитики&lt;/b&gt; и принятия решений становятся всё более требовательными. Kafka все чаще пытаются заставить выполнять функции в &lt;b&gt;архитектуре аналитики реального времени&lt;/b&gt;, для поддержки которых она никогда не проектировалась. Чтобы решить сегодняшние проблемы конвейеров потоковой передачи данных и аналитические требования, необходимы новые возможности. Пришло время для «новичка на районе».&lt;/p&gt;
&lt;p&gt;Во время перехода от пакетной обработки к &lt;b&gt;потоковой передаче данных в реальном времени&lt;/b&gt; значительное внимание и импульс получил проект с открытым исходным кодом, разработанный внутри LinkedIn: &lt;b&gt;Apache Kafka&lt;/b&gt;. Цель состояла в том, чтобы упростить перемещение данных из точки А в точку Б масштабируемым и устойчивым способом, используя модель издатель/подписчик. Kafka позволила компаниям создавать ранние &lt;b&gt;конвейеры потоковой передачи данных&lt;/b&gt; и открыть новый класс событийно-ориентированных сценариев использования. Постоянно растущая экосистема коннекторов и интеграций ускорила внедрение и утвердила Kafka в качестве предпочтительного &lt;b&gt;слоя потокового хранения&lt;/b&gt;. Однако, по мере того как &lt;b&gt;архитектуры аналитики реального времени&lt;/b&gt; эволюционировали за пределы простого приема данных (ingestion), ограничения Kafka для аналитических нагрузок становились всё более очевидными.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://gavrilov.info/pictures/image-233.png" width="1200" height="674" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;С архитектурной точки зрения Kafka — это не аналитический движок. Это устойчивая и масштабируемая &lt;b&gt;система хранения на основе записей (record-based storage system)&lt;/b&gt; для свежих данных в реальном времени — часто называемая «горячим слоем». Следовательно, аналитические нагрузки должны выполняться за пределами кластера Kafka, постоянно перемещая данные между системами хранения и обработки, что увеличивает сетевой трафик и накладные операционные расходы. Кроме того, Kafka нативно не обеспечивает соблюдение схем для данных, публикуемых в топиках.&lt;/p&gt;
&lt;p&gt;Хотя эта гибкость была приемлема для ранних сценариев использования потоковой передачи, современные &lt;b&gt;платформы аналитики реального времени&lt;/b&gt; требуют схем для обеспечения согласованности, управления и качества данных. В качестве компенсации появились реестры схем (Schema Registries) для обеспечения контрактов между издателями и подписчиками, добавляя сложности аналитическим архитектурам на основе Kafka.&lt;/p&gt;
&lt;p&gt;И последнее, но не менее важное (и, возможно, самый важный аспект): Kafka — это система хранения на основе записей. Это хорошо подходит для использования в качестве очереди сообщений, например, для приема данных в реальном времени или событийно-ориентированных архитектур, но имеет значительные ограничения при решении текущих и будущих задач проектов реального времени. Движки обработки, такие как Spark и Flink, должны потреблять все данные топика, даже если требуется только часть данных события (столбцы). Результатом является ненужный сетевой трафик, снижение производительности обработки и чрезмерные требования к хранилищу.&lt;/p&gt;
&lt;p&gt;Компоненты потокового хранения на основе записей по-прежнему будут занимать свое место в архитектуре данных. Такие решения, как Kafka и Pulsar, хорошо подходят для случаев, требующих чтения полных записей. Архитектурные паттерны, основанные на микросервисах, могут использовать вышеуказанные решения для обмена данными, отделяя функции от транспортировки сообщений для повышения производительности, надежности и масштабируемости. Чтение полных записей также полезно для конвейеров приема данных (ingestion pipelines), в которых данные будут храниться в системах долгосрочного хранения, таких как объектное хранилище (Object Storage), для исторических и архивных целей. Узкие места и ограничения возникают, когда они используются для аналитических нагрузок, требующих возможностей, выходящих за рамки простого слоя транспорта данных.&lt;/p&gt;
&lt;h3&gt;Эволюция потоковых данных&lt;/h3&gt;
&lt;p&gt;Сегодняшний разговор движим единственным аспектом: Эволюция. Другими словами, новые потребности требуют новых подходов к управлению данными. Kafka удовлетворила первоначальные потребности в потоковой передаче данных. В этой первой волне в основном доминировали конвейеры приема данных в реальном времени и дискретная (SEP, Simple Event Processing) аналитика. По сути, способность перемещать данные из точки А в точку Б и, в некоторых случаях, выполнять простую подготовку и обработку данных между ними. Kafka, в сочетании со Spark Streaming или специальными коннекторами, справлялась с этими ранними сценариями использования.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://gavrilov.info/pictures/image-234.png" width="1200" height="674" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;Перенесемся вперед: вторая волна привнесла сложность в потоковый конвейер. Помимо дискретной подготовки данных, сценарии использования на этом этапе требовали расширенных аналитических функций, таких как агрегация, обогащение и сложная обработка событий (CEP). Микро-батчинг (micro-batching) оказался недостаточным. Требуется новый архитектурный подход, основанный на колоночном хранении с эффективным проталкиванием проекций (projection pushdown) и прозрачным многоуровневым хранением данных (data tiering), в сочетании с движками обработки с задержкой менее секунды. `Apache Fluss` и `Apache Flink` могут выполнить это обещание и вместе составляют будущее и третью волну по шкале зрелости.&lt;/p&gt;
&lt;p&gt;Каждая техническая статья сегодня упоминает AI/ML. Эта эволюция «третьей волны» позволяет компаниям создавать AI-конвейеры реального времени, которые внедряют передовые аналитические методы (такие как Generative AI) в потоковые данные. Это увеличивает потребность в современных системах хранения данных в реальном времени с расширенными функциями, которые распределяют данные как по быстрым потоковым, так и по историческим слоям, обеспечивая интегрированный, унифицированный доступ к бизнес-данным.&lt;/p&gt;
&lt;h3&gt;Новичок на районе&lt;/h3&gt;
&lt;p&gt;`Apache Fluss` — это современная система хранения потоковых данных в реальном времени для аналитики. Она консолидирует многолетний опыт и уроки, извлеченные из предшественников, отвечая текущим и будущим потребностям организаций. Fluss родился в эпоху, когда для питания моделей машинного обучения требуется больше данных, Лейкхаусы (Lakehouses) являются частью корпоративной экосистемы, а облачная инфраструктура является предпочтительной стратегией для компаний.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://gavrilov.info/pictures/Snimok-ekrana-2026-02-15-v-13.48.31.png" width="696" height="382" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;Но хранение данных — это лишь часть архитектурной головоломки. `Apache Flink` предоставляет возможности и устойчивость для обработки огромных объемов данных в реальном времени с задержкой менее секунды, обеспечивая скорость, необходимую для будущих потоковых приложений. Не ограничиваясь Flink, дополнительные движки обработки и библиотеки разрабатывают интеграции с Fluss, тем самым укрепляя экосистему.&lt;/p&gt;
&lt;p&gt;Ниже приведены основные функции современной аналитики реального времени.&lt;/p&gt;
&lt;h4&gt;Поток как таблица (Stream as Table)&lt;/h4&gt;
&lt;p&gt;Fluss хранит данные как схематизированные таблицы. Этот подход подходит для большинства сценариев использования в реальном времени, включая те, которые опираются как на структурированные, так и на полуструктурированные данные. Структурируя потоковые данные, компании могут улучшить управление, повысить качество данных и гарантировать, что издатели и потребители используют общий язык. Fluss определяет два типа таблиц:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Log Tables (Лог-таблицы)** работают только на добавление (append-only), аналогично топикам Kafka. Такие сценарии использования, как мониторинг логов, кликстримы (clickstreams), показания датчиков, журналы транзакций и другие, являются хорошими примерами данных только для добавления. События неизменяемы и не должны изменяться или обновляться.&lt;/li&gt;
&lt;li&gt;Primary Key (PK) Tables (Таблицы с первичным ключом)** — это изменяемые таблицы, определенные ключом. Записи сначала вставляются, а затем обновляются или удаляются с течением времени в соответствии с журналом изменений (changelog), который они представляют. Таблица PK хранит последние изменения всей таблицы, обеспечивая паттерн доступа «поиск записи» (record lookup). Сценарии использования журнала изменений, такие как балансы счетов, корзина покупок и управление запасами, могут извлечь выгоду из этого подхода. Kafka не может выполнять такое поведение, требуя внешних баз данных типа «ключ-значение» или NoSQL для отслеживания текущего статуса записи, что приводит к сложным и трудным в обслуживании решениям.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://gavrilov.info/pictures/image-235.png" width="1200" height="556" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;Вкратце, PK Tables обеспечивают уникальность записей на основе первичного ключа, операций `INSERT`, `UPDATE` и `DELETE`, а также предоставляют широкие возможности изменения записей. С другой стороны, Log Tables работают только на добавление; обновления записей не требуются.&lt;/p&gt;
&lt;h4&gt;Колоночное хранение (Columnar Storage)&lt;/h4&gt;
&lt;p&gt;То, как Fluss хранит данные на диске, возможно, является наиболее фундаментальным архитектурным сдвигом по сравнению с другими решениями. В отличие от Kafka, Fluss использует формат `Apache Arrow` для хранения данных в колоночном формате, что дает следующие преимущества:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Улучшенное использование хранилища**, так как хранение данных в колоночном формате требует меньше дискового пространства. Степень сжатия зависит от множества характеристик данных, но первоначальные тесты показывают многообещающее улучшение в 5 раз при использовании Apache Arrow в качестве базового формата хранения. Меньше хранилища = меньше затрат. Kafka предоставляет лишь несколько вариантов сжатия данных, которые не сравнимы с теми, что доступны в Apache Arrow «из коробки».&lt;/li&gt;
&lt;li&gt;Эффективные запросы с использованием обрезки столбцов (column pruning).** В общем случае запрашивается или доступно менее половины атрибутов данного бизнес-события, т.е. только те имена столбцов, которые вы добавляете в ваше выражение `SELECT FROM`. Проталкивание проекции (projection pushdown) — это метод, который удаляет ненужные атрибуты (также известный как column pruning) при извлечении данных из системы хранения. Kafka работает по принципу «все или ничего» из-за своего формата хранения на основе записей.&lt;/li&gt;
&lt;li&gt;И колоночное сжатие, и проталкивание проекции улучшат сетевой трафик — перемещение меньшего количества данных приведет к тому, что сетевые администраторы станут счастливее. С Kafka компании постоянно сталкиваются с перегрузкой сети и потенциально высокими расходами на исходящий трафик (egress costs).&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://gavrilov.info/pictures/image-236.png" width="1200" height="674" alt="" /&gt;
&lt;/div&gt;
&lt;h4&gt;Унификация с Lakehouse&lt;/h4&gt;
&lt;p&gt;Kafka была создана в эпоху Data Lake (Озер данных). С самого начала проектирования Fluss создавался для Lakehouse. Это создает большую разницу. Компании поняли, что Озера данных (или во многих случаях «Болота данных» — Data Swamps) трудно поддерживать в рабочем состоянии и окупать инвестиции в лицензии, оборудование и персонал для создания решений больших данных. К счастью, Лейкхаусы преодолевают эти проблемы. Лейкхаусы утверждают, что данные должны быть широко и легко доступны независимо от их возраста. Пакетные события и события реального времени перекрываются, и движки обработки должны иметь возможность прозрачно обращаться к обоим слоям.&lt;/p&gt;
&lt;p&gt;Вот возможности тиринга данных (распределения по уровням) и унифицированного просмотра, которые может предоставить Fluss, в дополнение к слою горячих/свежих данных:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Теплый слой (Warm layer):** для данных возрастом от минут до часов, в основном хранящихся в решениях объектного хранения (Object Storage).&lt;/li&gt;
&lt;li&gt;Холодный слой (Cold layer):** для данных возрастом от дней до лет. Решения Lakehouse, такие как `Apache Paimon` и `Iceberg`, являются предпочтительными платформами для этих исторических данных, питающих модели ML, ретроспективную аналитику и комплаенс.&lt;/li&gt;
&lt;li&gt;Zero-copy data tiering (Тиринг данных без копирования):** старение данных из горячего слоя (таблицы Fluss) в теплые/холодные слои (Object Storage и Lakehouse). Это означает, что доступна единственная копия единицы данных, либо в слое реального времени, либо в историческом слое. Fluss управляет переключением между слоями, облегчая запросы и доступ. Подход Kafka опирается на дублирование данных с помощью задания потребителя/издателя, что приводит к увеличению затрат на хранение и необходимости конвертировать топики Kafka в табличный формат Lakehouse.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://gavrilov.info/pictures/image-237.png" width="1200" height="674" alt="" /&gt;
&lt;/div&gt;
&lt;h3&gt;Светлое будущее впереди&lt;/h3&gt;
&lt;p&gt;Аналитика данных в реальном времени становится краеугольным камнем современных компаний. Цифровые бизнес-модели должны обеспечивать лучший пользовательский опыт и своевременные ответы на взаимодействия с клиентами, что заставляет компании создавать системы для использования и управления данными в реальном времени, создавая увлекательный и впечатляющий («wow») опыт. Действовать сейчас — это не просто вопрос технической осуществимости; для большинства предприятий это становится уникальным преимуществом для выживания в высококонкурентной глобальной рыночной среде.&lt;/p&gt;
&lt;p&gt;Fluss помогает компаниям преодолеть разрыв между мирами реального времени и аналитики, предлагая унифицированный доступ как к свежим данным в реальном времени, так и к историческим, холодным данным. Вкратце, Fluss обеспечивает беспрепятственный доступ к данным независимо от возраста набора данных и упрощает сложные архитектуры аналитики данных, которые тянулись годами, в основном из-за отсутствия наиболее подходящих компонентов и фреймворков.&lt;/p&gt;
&lt;p&gt;В то время как Fluss служит слоем хранения в реальном времени для аналитики, Лейкхаусу предоставляется управление, простота и масштабируемость, которые защищают современные архитектуры в будущем.&lt;/p&gt;
&lt;p&gt;С операционной стороны он предлагает значительные преимущества за счет снижения сложности управления, хранения и обслуживания как данных реального времени, так и пакетных данных. Эта эффективность трансформируется в прямую экономию средств, достигаемую в первую очередь за счет оптимизированного формата таблиц Fluss, двухуровневой системы хранения, основанной на температуре данных, и, наконец, минимизации общего использования ЦП конвейера с помощью проталкивания предикатов (predicate pushdown) и обрезки столбцов. В совокупности эти архитектурные элементы снижают накладные операционные расходы, связанные с обслуживанием платформы, ускоряют внедрение новых сценариев использования и облегчают бесшовную интеграцию с существующей ИТ-инфраструктурой предприятия.&lt;/p&gt;
</description>
</item>

<item>
<title>Создаем Streaming Lakehouse за час: руководство по RisingWave, Lakekeeper и Trino</title>
<guid isPermaLink="false">278</guid>
<link>https://gavrilov.info/all/sozdaem-streaming-lakehouse-za-chas-rukovodstvo-po-risingwave-la/</link>
<pubDate>Sat, 06 Sep 2025 23:03:00 +0300</pubDate>
<author></author>
<comments>https://gavrilov.info/all/sozdaem-streaming-lakehouse-za-chas-rukovodstvo-po-risingwave-la/</comments>
<description>
&lt;p&gt;Вы когда-нибудь мечтали о платформе, где данные, отправленные через простой API-вызов, через секунды становятся доступны для аналитических запросов в вашем озере данных? Мечты сбываются. Эта статья — подробное, основанное на реальном опыте руководство, которое покажет, как построить современный Streaming Lakehouse с нуля.&lt;/p&gt;
&lt;p&gt;Доки, которые пригодились:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/risingwavelabs/risingwave/blob/main/docker/docker-compose.yml"&gt;https://github.com/risingwavelabs/risingwave/blob/main/docker/docker-compose.yml&lt;/a&gt;&lt;br /&gt;
&lt;a href="https://github.com/lakekeeper/lakekeeper/blob/main/examples/minimal/docker-compose.yaml"&gt;https://github.com/lakekeeper/lakekeeper/blob/main/examples/minimal/docker-compose.yaml&lt;/a&gt;&lt;br /&gt;
&lt;a href="https://docs.risingwave.com/iceberg/deliver-to-iceberg#rest-catalog"&gt;https://docs.risingwave.com/iceberg/deliver-to-iceberg#rest-catalog&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Наши главные герои:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;RisingWave&lt;/b&gt;: Потоковая база данных, “сердце” нашего пайплайна. Она будет принимать, преобразовывать и материализовывать данные на лету.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://gavrilov.info/pictures/image-212.png-1.jpg" width="2560" height="868" alt="" /&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Lakekeeper&lt;/b&gt;: Современный REST-каталог для Apache Iceberg. Наш “библиотекарь”, который знает все о структуре данных в озере.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://gavrilov.info/pictures/Snimok-ekrana-2025-09-06-v-22.37.28.png" width="1046" height="270" alt="" /&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Trino&lt;/b&gt;: Мощный движок для федеративных запросов. Наше “окно” в озеро данных для выполнения ad-hoc аналитики.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://gavrilov.info/pictures/Snimok-ekrana-2025-09-06-v-22.39.34.png" width="958" height="446" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;Мы пройдем весь путь: от сравнения технологий и настройки окружения до отправки данных и любования результатами на дашбордах Grafana. И самое главное — мы поделимся всеми “граблями”, на которые наступили, чтобы вы могли их обойти.&lt;/p&gt;
&lt;h3&gt;Глава 1: Почему RisingWave? Взгляд на альтернативы&lt;/h3&gt;
&lt;p&gt;На рынке потоковой обработки есть много инструментов, но все они предлагают разные подходы. Почему для нашей задачи мы выбрали именно RisingWave?&lt;/p&gt;
&lt;p&gt;&lt;b&gt;RisingWave&lt;/b&gt; — это распределенная потоковая база данных, созданная для упрощения обработки данных в реальном времени. Ее ключевая особенность — использование материализованных представлений поверх потоков данных. Вы пишете знакомый SQL, а RisingWave берет на себя всю сложную работу по инкрементальному обновлению результатов с минимальной задержкой.&lt;/p&gt;
&lt;p&gt;Давайте сравним его с популярными альтернативами.&lt;/p&gt;
&lt;h4&gt;Сравнительная таблица&lt;/h4&gt;
&lt;table cellpadding="0" cellspacing="0" border="0" class="e2-text-table"&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;Критерий&lt;/td&gt;
&lt;td style="text-align: center"&gt;RisingWave&lt;/td&gt;
&lt;td style="text-align: center"&gt;Связка Debezium + Flink&lt;/td&gt;
&lt;td style="text-align: center"&gt;Apache SeaTunnel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;&lt;b&gt;Архитектура&lt;/b&gt;&lt;/td&gt;
&lt;td style="text-align: center"&gt;Единая система: хранение состояния (state) и вычисления в одном продукте.&lt;/td&gt;
&lt;td style="text-align: center"&gt;Компонентная: Debezium (CDC), Kafka (очередь), Flink (обработка), отдельное хранилище состояния.&lt;/td&gt;
&lt;td style="text-align: center"&gt;Инструмент для перемещения данных (data mover) с коннекторами.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: right"&gt;&lt;b&gt;Основная задача&lt;/b&gt;&lt;/td&gt;
&lt;td style="text-align: center"&gt;Создание и поддержка инкрементально обновляемых материализованных представлений.&lt;/td&gt;
&lt;td style="text-align: center"&gt;Гибкая, низкоуровневая обработка потоков общего назначения.&lt;/td&gt;
&lt;td style="text-align: center"&gt;Пакетная и потоковая синхронизация данных между разнородными источниками и приемниками.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;&lt;b&gt;Простота использования&lt;/b&gt;&lt;/td&gt;
&lt;td style="text-align: center"&gt;&lt;b&gt;Очень высокая.&lt;/b&gt; Знание SQL — это 90% успеха. Скрывает сложность управления состоянием.&lt;/td&gt;
&lt;td style="text-align: center"&gt;Низкая. Требует экспертизы в каждом компоненте, написания кода на Java/Scala, управления состоянием.&lt;/td&gt;
&lt;td style="text-align: center"&gt;Средняя. Конфигурация через файлы, но требует понимания особенностей каждого коннектора.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: right"&gt;&lt;b&gt;Обработка данных&lt;/b&gt;&lt;/td&gt;
&lt;td style="text-align: center"&gt;SQL-ориентированная. `CREATE MATERIALIZED VIEW ... AS SELECT ...`.&lt;/td&gt;
&lt;td style="text-align: center"&gt;Программная. DataStream API, Table API/SQL. Позволяет писать сложную бизнес-логику.&lt;/td&gt;
&lt;td style="text-align: center"&gt;Декларативная. Определяет `source`, `transform`, `sink`. Менее гибкая для сложных трансформаций.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;&lt;b&gt;Поддержка SQL&lt;/b&gt;&lt;/td&gt;
&lt;td style="text-align: center"&gt;&lt;b&gt;Первоклассная.&lt;/b&gt; Совместимость с PostgreSQL на уровне синтаксиса и протокола.&lt;/td&gt;
&lt;td style="text-align: center"&gt;Хорошая (Flink SQL), но не является основным интерфейсом.&lt;/td&gt;
&lt;td style="text-align: center"&gt;Ограниченная. Используется для простых трансформаций, а не для определения логики потока.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;&lt;b&gt;Управление состоянием&lt;/b&gt;&lt;/td&gt;
&lt;td style="text-align: center"&gt;&lt;b&gt;Встроенное и автоматическое.&lt;/b&gt; Использует облачное хранилище (S3) как персистентный слой.&lt;/td&gt;
&lt;td style="text-align: center"&gt;&lt;b&gt;Ручное.&lt;/b&gt; Требуется настраивать и управлять чекпоинтами и состоянием (например, RocksDB).&lt;/td&gt;
&lt;td style="text-align: center"&gt;Зависит от движка (Flink/Spark). Не является основной функцией самого SeaTunnel.&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;&lt;b&gt;Выводы:&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Связка Debezium + Flink&lt;/b&gt; — это невероятно мощный, но сложный “конструктор”. Он идеален для компаний с большими командами инженеров данных, которым нужна максимальная гибкость для создания кастомной логики.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Apache SeaTunnel&lt;/b&gt; — это отличный “швейцарский нож” для перемещения данных. Его сила — в огромном количестве коннекторов. Он идеален для задач ETL/ELT, когда нужно перелить данные из точки А в точку Б с минимальными трансформациями.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;RisingWave&lt;/b&gt; занимает золотую середину для аналитических задач в реальном времени. Он предлагает простоту и элегантность SQL, скрывая под капотом всю сложность потоковой обработки. Если ваша цель — быстро получить свежие аналитические витрины из потоков данных, RisingWave — ваш выбор.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Глава 2: “Кексы” — фишки RisingWave, которые упрощают жизнь 🍰&lt;/h3&gt;
&lt;p&gt;Что делает RisingWave таким привлекательным на практике?&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;&lt;b&gt;PostgreSQL-совместимость:&lt;/b&gt; Вы можете подключиться к RisingWave любым клиентом, который “говорит” на протоколе Postgres (например, DBeaver, psql). Синтаксис SQL для создания представлений и запросов вам уже знаком.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Все-в-одном для стриминга:&lt;/b&gt; RisingWave объединяет в себе прием данных (коннекторы), их обработку (инкрементальные вычисления) и хранение состояния. Вам не нужно разворачивать и связывать вместе Kafka, Zookeeper, Flink и RocksDB.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Нативные Sink’и и Source’ы:&lt;/b&gt; В нашем примере мы использовали встроенный `webhook` коннектор — не нужно писать отдельный сервис для приема данных! RisingWave нативно умеет работать с Kafka/Redpanda, Kinesis, Pulsar, а также писать данные напрямую в Iceberg, Delta Lake и другие системы.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Инкрементальные вычисления “под капотом”:&lt;/b&gt; Когда вы создаете материализованное представление, RisingWave строит план потоковой обработки. При поступлении новых данных он не пересчитывает все заново, а инкрементально обновляет результат. Это обеспечивает сверхнизкую задержку.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Глава 3: Практика: Строим наш Streaming Lakehouse шаг за шагом&lt;/h3&gt;
&lt;p&gt;Теперь перейдем к самому интересному — воссозданию нашего успешного проекта.&lt;/p&gt;
&lt;h4&gt;Этап 1: Архитектура и подготовка окружения (00:00 – 00:15)&lt;/h4&gt;
&lt;p&gt;Наша архитектура выглядит так:&lt;br /&gt;
`Webhook` → `RisingWave (Source → MView → Sink)` → `Lakekeeper (Catalog) + MinIO (Storage)` ← `Trino (Query)`&lt;/p&gt;
&lt;p&gt;Мы используем два `docker-compose` файла:&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;&lt;b&gt;Для Lakekeeper и его экосистемы&lt;/b&gt; (Postgres, MinIO, Trino): &lt;b&gt;lakekeeper/examples/minimal&lt;/b&gt; &lt;a href="https://github.com/lakekeeper/lakekeeper/tree/main/examples/minimal"&gt;https://github.com/lakekeeper/lakekeeper/tree/main/examples/minimal&lt;/a&gt; .&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Для RisingWave и его окружения&lt;/b&gt;  Postgres для метаданных, MinIO для состояния, Grafana):      &lt;b&gt;risingwave/docker/docker-compose.yml&lt;/b&gt; &lt;a href="https://github.com/risingwavelabs/risingwave/blob/main/docker/docker-compose.yml"&gt;https://github.com/risingwavelabs/risingwave/blob/main/docker/docker-compose.yml&lt;/a&gt; .&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;b&gt;Ключевое действие:&lt;/b&gt; Мы запускаем оба стека, но для RisingWave вносим изменения, чтобы он мог взаимодействовать с Lakekeeper и Trino. Мы объединяем их в одну сеть, добавив в `docker-compose.yml` от RisingWave следующие строки:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;# risingwave/docker/docker-compose.yml

services:
  risingwave-standalone:
    # ...
    # Открываем порт для вебхука, по умолчанию он не открыт наружу
           .....
        --webhook-listen-addr 0.0.0.0:4567 \ 
           .....
    ports:
      - &amp;quot;4566:4566&amp;quot;. 
      # ... другие порты
      - &amp;quot;4567:4567&amp;quot;   # &amp;lt;--- Это важно для рабочего webhook 
    networks:
      - trino_network
# ... и для других сервисов, которые должны общаться с внешним стеком ...

networks:
  trino_network:
    name: minimal_iceberg_net # Имя сети из docker-compose Lakekeeper
    external: true&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;b&gt;Важный момент:&lt;/b&gt; По умолчанию RisingWave не выставляет порт `4567` для вебхуков наружу. Мы добавили его в секцию `ports`, чтобы иметь возможность отправлять `curl` запросы с хост-машины.&lt;/p&gt;
&lt;h4&gt;Этап 2: Настройка каталогов (00:15 – 00:25)&lt;/h4&gt;
&lt;p&gt;“Озеро” без каталога — это просто “болото”. Lakekeeper будет нашим каталогом, а Trino — первым, кто научится им пользоваться.&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;&lt;b&gt;Создаем динамический каталог в Trino:&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;CREATE CATALOG risingwave USING iceberg
    WITH (
        &amp;quot;iceberg.catalog.type&amp;quot; = 'rest',
        &amp;quot;iceberg.rest-catalog.uri&amp;quot; = 'http://lakekeeper:8181/catalog',
        &amp;quot;iceberg.rest-catalog.warehouse&amp;quot; = 'demo',
        &amp;quot;s3.region&amp;quot;= 'dummy',
        &amp;quot;s3.path-style-access&amp;quot; = 'true',
        &amp;quot;s3.endpoint&amp;quot; = 'http://minio:9000',
        &amp;quot;fs.native-s3.enabled&amp;quot; = 'true'
    );&lt;/code&gt;&lt;/pre&gt;&lt;ol start="2"&gt;
&lt;li&gt;&lt;b&gt;Создаем “пустую” таблицу в Trino:&lt;/b&gt; Этот шаг создает метаданные в Lakekeeper. RisingWave будет находить эту таблицу и наполнять ее данными.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;CREATE TABLE risingwave.trino_namespace.product_view_events (
       event_id varchar,
       user_id varchar,
       event_name varchar,
       product_id varchar,
       category varchar,
       price double,
       event_timestamp timestamp(6) with time zone,
       raw_data varchar
    );&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;Этап 3: Магия RisingWave (00:25 – 00:45) 🚀&lt;/h4&gt;
&lt;p&gt;Подключаемся к RisingWave через DBeaver (используя порт `4566` и стандартный драйвер PostgreSQL) и начинаем творить магию.&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;&lt;b&gt;Создаем источник-вебхук:&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;CREATE TABLE wbhtable1 (
      data JSONB
    ) WITH (
      connector = 'webhook'
    ) VALIDATE AS secure_compare(
      headers-&amp;gt;&amp;gt;'authorization',
      'TEST_WEBHOOK'
    );&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Эта команда создает эндпоинт, который принимает JSON и кладет его в таблицу `wbhtable1`. `VALIDATE AS` обеспечивает простую, но эффективную аутентификацию.&lt;/p&gt;
&lt;ol start="2"&gt;
&lt;li&gt;&lt;b&gt;Создаем материализованное представление:&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;CREATE MATERIALIZED VIEW product_view_events AS
    SELECT
      (data-&amp;gt;&amp;gt;'event_id')::VARCHAR AS event_id,
      (data-&amp;gt;&amp;gt;'user_id')::VARCHAR AS user_id,
      (data-&amp;gt;&amp;gt;'event_name')::VARCHAR AS event_name,
      (data-&amp;gt;'properties'-&amp;gt;&amp;gt;'product_id')::VARCHAR AS product_id,
      (data-&amp;gt;'properties'-&amp;gt;&amp;gt;'category')::VARCHAR AS category,
      (data-&amp;gt;'properties'-&amp;gt;&amp;gt;'price')::DOUBLE PRECISION AS price,
      (data-&amp;gt;&amp;gt;'timestamp')::TIMESTAMP WITH TIME ZONE AS event_timestamp,
      data::VARCHAR AS raw_data
    FROM wbhtable1;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Это ядро нашей логики. Мы на лету парсим входящий `JSONB`, приводим типы и создаем структурированное представление `product_view_events`, которое обновляется автоматически.&lt;/p&gt;
&lt;ol start="3"&gt;
&lt;li&gt;&lt;b&gt;Создаем синк (Sink) в Iceberg:&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;CREATE SINK rest_sink FROM product_view_events
    WITH (
        connector = 'iceberg',
        type = 'upsert',
        primary_key = 'event_id',
        catalog.type = 'rest',
        catalog.uri = 'http://lakekeeper:8181/catalog',
        warehouse.path = 'demo',
        database.name = 'trino_namespace',
        table.name = 'product_view_events',
        s3.endpoint = 'http://minio:9000',
        s3.path.style.access = 'true',
        s3.access.key = 'minio-root-user',
        s3.secret.key = 'minio-root-password',
        s3.region = 'dummy'
    );&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;b&gt;“Грабли”, которые мы собрали:&lt;/b&gt; На пути к этому финальному запросу мы столкнулись с несколькими ошибками, которые стоили нам времени. Вот они, чтобы вы не повторяли наших ошибок:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;`catalog.uri`: Должен указывать на полный путь к REST API каталогу, в случае Lakekeeper это `&lt;a href="http://lakekeeper:8181/catalog"&gt;http://lakekeeper:8181/catalog&lt;/a&gt;`.&lt;/li&gt;
&lt;li&gt;`warehouse.path`: Должен содержать &lt;b&gt;логическое имя&lt;/b&gt; хранилища (`demo`), а не его физический путь в S3.&lt;/li&gt;
&lt;li&gt;`s3.region`: &lt;b&gt;Критически важный параметр!&lt;/b&gt; S3-клиент внутри RisingWave требует его обязательного указания, даже для MinIO. Хотя само значение (`us-east-1` или любое другое) для MinIO не принципиально, его отсутствие приводит к ошибке `region is missing` и сбою записи данных.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Этап 4: Запуск и проверка (00:45 – 01:00)&lt;/h4&gt;
&lt;p&gt;Время накормить нашу систему данными! Запускаем в терминале скрипт для генерации и отправки 100 событий, а можно и тысячу. Этот скрипт полностью рабочий и готов к копированию:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;seq 1 100 | xargs -I {} -P 10 bash -c '
  EVENT_ID=$(uuidgen)
  USER_ID=&amp;quot;usr_$(uuidgen | head -c 8)&amp;quot;
  PRODUCT_ID=&amp;quot;prod_$(uuidgen | head -c 8)&amp;quot;
  TIMESTAMP=$(date -u +&amp;quot;%Y-%m-%dT%H:%M:%SZ&amp;quot;)

  curl -s -o /dev/null -X POST \
    http://localhost:4567/webhook/dev/public/wbhtable1 \
    -H &amp;quot;Content-Type: application/json&amp;quot; \
    -H &amp;quot;Authorization: TEST_WEBHOOK&amp;quot; \
    -d &amp;quot;{
          \&amp;quot;event_id\&amp;quot;: \&amp;quot;$EVENT_ID\&amp;quot;,
          \&amp;quot;user_id\&amp;quot;: \&amp;quot;$USER_ID\&amp;quot;,
          \&amp;quot;event_name\&amp;quot;: \&amp;quot;product_viewed\&amp;quot;,
          \&amp;quot;properties\&amp;quot;: {
            \&amp;quot;product_id\&amp;quot;: \&amp;quot;$PRODUCT_ID\&amp;quot;,
            \&amp;quot;category\&amp;quot;: \&amp;quot;electronics\&amp;quot;,
            \&amp;quot;price\&amp;quot;: 9199.99
          },
          \&amp;quot;timestamp\&amp;quot;: \&amp;quot;$TIMESTAMP\&amp;quot;
        }&amp;quot;
'&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;И вот он, момент истины. Идем в DBeaver, открываем подключение к Trino и выполняем:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;select * from risingwave.trino_namespace.product_view_events;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Результат перед вами:&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://gavrilov.info/pictures/Snimok-ekrana-2025-09-06-v-22.22.20.png.jpg" width="2560" height="643" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;Данные, только что сгенерированные и отправленные по HTTP, уже лежат в озере данных в формате Parquet и доступны для анализа. Ура!&lt;/p&gt;
&lt;h3&gt;Глава 4: Наблюдаемость: Смотрим на систему под нагрузкой&lt;/h3&gt;
&lt;p&gt;RisingWave поставляется с готовыми дашбордами для Grafana. Взглянем на них после нашей нагрузки.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;div class="fotorama" data-width="986" data-ratio="2.293023255814"&gt;
&lt;img src="https://gavrilov.info/pictures/Snimok-ekrana-2025-09-06-v-22.18.25.png" width="986" height="430" alt="" /&gt;
&lt;img src="https://gavrilov.info/pictures/Snimok-ekrana-2025-09-06-v-22.20.50.png" width="986" height="454" alt="" /&gt;
&lt;img src="https://gavrilov.info/pictures/Snimok-ekrana-2025-09-06-v-22.20.44.png" width="976" height="454" alt="" /&gt;
&lt;img src="https://gavrilov.info/pictures/Snimok-ekrana-2025-09-06-v-22.20.35.png" width="974" height="452" alt="" /&gt;
&lt;img src="https://gavrilov.info/pictures/Snimok-ekrana-2025-09-06-v-22.20.17.png" width="980" height="446" alt="" /&gt;
&lt;img src="https://gavrilov.info/pictures/Snimok-ekrana-2025-09-06-v-22.19.57.png" width="978" height="446" alt="" /&gt;
&lt;img src="https://gavrilov.info/pictures/Snimok-ekrana-2025-09-06-v-22.19.06.png" width="978" height="450" alt="" /&gt;
&lt;img src="https://gavrilov.info/pictures/Snimok-ekrana-2025-09-06-v-22.18.58.png" width="974" height="456" alt="" /&gt;
&lt;img src="https://gavrilov.info/pictures/Snimok-ekrana-2025-09-06-v-22.18.53.png" width="978" height="448" alt="" /&gt;
&lt;img src="https://gavrilov.info/pictures/Snimok-ekrana-2025-09-06-v-22.20.54.png" width="980" height="462" alt="" /&gt;
&lt;/div&gt;
&lt;div class="e2-text-caption"&gt;Можно листать стрелками --&gt;&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Пропускная способность (Throughput):&lt;/b&gt; Мы видим, как данные проходят через материализованное представление и записываются синком. Пики на графике соответствуют нашей нагрузке.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Задержка барьеров (Barrier Latency):&lt;/b&gt; Это ключевой показатель здоровья потоковой системы. Он показывает время, необходимое для создания контрольной точки (чекпоинта). Значения в десятки миллисекунд говорят о том, что система абсолютно здорова и справляется с нагрузкой без задержек.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Ресурсы (CPU/Memory):&lt;/b&gt; Графики показывают стабильное и предсказуемое потребление ресурсов.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Эти метрики доказывают, что система не просто работает, а работает стабильно и эффективно.&lt;/p&gt;
&lt;h3&gt;Заключение&lt;/h3&gt;
&lt;p&gt;Мы сделали это! Меньше чем за час мы развернули и настроили полноценный Streaming Lakehouse. Мы доказали, что современные инструменты, такие как RisingWave, могут кардинально упростить создание сложных систем обработки данных в реальном времени.&lt;/p&gt;
&lt;p&gt;Путь от ошибки `Table does not exist` до работающего пайплайна был непростым, но каждая решенная проблема углубляла мое понимание системы. Теперь есть не просто набор инструкций, а проверенный в бою рецепт, учитывающий все “подводные камни”.&lt;/p&gt;
&lt;p&gt;Путь к аналитике в реальном времени открыт. Хорошего стриминга и бурного потока с домом у озера, главное что бы избушку не смыло :)&lt;/p&gt;
&lt;p&gt;UPD: Проверил еще пару штук&lt;/p&gt;
&lt;p&gt;Создаем сурс из Кафки&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;CREATE SOURCE kafka_src (
  action VARCHAR
) WITH (
  connector = 'kafka',
  topic = 'query_complete',
  properties.bootstrap.server = 'broker1:29092'
);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;создаем синк в другую кафку&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;CREATE SINK kafka_sink from kafka_src WITH (
  connector = 'kafka',
  topic = 'query_complete',
  properties.bootstrap.server = 'broker2:29092'
) FORMAT PLAIN ENCODE JSON&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Еще вебхук&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;CREATE TABLE wbhtable2 (
      data JSONB
    ) WITH (
      connector = 'webhook'
    ) VALIDATE AS secure_compare(
      headers-&amp;gt;&amp;gt;'authorization',
      'TEST_WEBHOOK'
    );&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Делаем материализацию&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;CREATE MATERIALIZED VIEW events AS
    SELECT
      (data-&amp;gt;&amp;gt;'action')::VARCHAR AS action
    FROM wbhtable2;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;делаем синк из материализации в кафку&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;CREATE SINK kafka_sink2 FROM events WITH (
  connector = 'kafka',
  topic = 'query_complete',
  properties.bootstrap.server = 'broker2:29092'
) FORMAT PLAIN ENCODE JSON (force_append_only='true');&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Без материализации сообщения прилетают так: {“data”:“{\”action\“: \”55555\“}”}&lt;br /&gt;
А с материализацией: {“action”:“99999”}&lt;/p&gt;
&lt;p&gt;Пример запроса&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;curl -s -o /dev/null -X POST \
    http://localhost:4567/webhook/dev/public/wbhtable2 \
    -H &amp;quot;Content-Type: application/json&amp;quot; \
    -H &amp;quot;Authorization: TEST_WEBHOOK&amp;quot; \
    -d &amp;quot;{\&amp;quot;action\&amp;quot;: \&amp;quot;11111\&amp;quot;}&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Еще про s3 подобные архитектуры: &lt;a href="https://gavrilov.info/all/bitva-novyh-arhitektur-sravnivaem-arc-gigapi-i-ducklake/"&gt;https://gavrilov.info/all/bitva-novyh-arhitektur-sravnivaem-arc-gigapi-i-ducklake/&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;&lt;b&gt;Ошибки в версии 2.4 – какая то пакость была, но поставил 2.6.1 и все заработало&lt;/b&gt;&lt;/h2&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;-- 0. устанавливаем последнюю версию risingwave ( 2.6.1 )


-- 1 Создаем вебхук 

CREATE TABLE wbhtable5 (
      data JSONB
    ) WITH (
      connector = 'webhook'
    ) VALIDATE AS secure_compare(
      headers-&amp;gt;&amp;gt;'authorization',
      'TEST_WEBHOOK'
    );

-- 2 Создаем материализацию 

CREATE MATERIALIZED VIEW product_view_events5 AS
    SELECT
      (data-&amp;gt;&amp;gt;'event_id')::VARCHAR AS event_id,
      (data-&amp;gt;&amp;gt;'user_id')::VARCHAR AS user_id,
      (data-&amp;gt;&amp;gt;'event_name')::VARCHAR AS event_name,
      (data-&amp;gt;'properties'-&amp;gt;&amp;gt;'product_id')::VARCHAR AS product_id,
      (data-&amp;gt;'properties'-&amp;gt;&amp;gt;'category')::VARCHAR AS category,
      (data-&amp;gt;'properties'-&amp;gt;&amp;gt;'price')::DOUBLE PRECISION AS price,
      (data-&amp;gt;&amp;gt;'timestamp')::TIMESTAMP WITH TIME ZONE AS event_timestamp,
      data::VARCHAR AS raw_data
    FROM wbhtable5;

-- 3 создаем подключение к iceberg 

CREATE CONNECTION my_iceberg_conn5 WITH (
    type = 'iceberg',
    warehouse.path = 'risi',  -- s3://my-bucket/warehouse/
   -- database.name = 'risi_space',  оказалось не нужна 
    s3.region = 'dummy',
    s3.access.key = 'ЧЧЧ', -- Ваши ключи
    s3.secret.key = 'ЧЧЧ',   -- Ваши ключи
    catalog.type = 'rest',
    s3.endpoint = 'https://gateway.storjshare.io',
    s3.path.style.access = 'true',
    
    -- ИСПОЛЬЗУЕМ ИМЯ СЕРВИСА И ЕГО ВНУТРЕННИЙ ПОРТ!
    catalog.uri = 'http://lakekeeper:8181/catalog'
)

-- 4 Устанавливаем его по умолчанию 

SET iceberg_engine_connection = 'public.my_iceberg_conn5';


-- Создаем таблицу ( обязательно с ключами )

CREATE TABLE public.my_iceberg_table5 (
       event_id VARCHAR PRIMARY KEY,
       user_id varchar,
       event_name varchar,
       product_id varchar,
       category varchar,
       price double,
       event_timestamp Timestamptz,
       raw_data varchar
) ENGINE = iceberg;

 
-- 5 создаем синк 

CREATE SINK to_sales_events5 INTO my_iceberg_table5 AS
SELECT * FROM product_view_events5;

--- Тут можно curl запустить 

seq 1 10 | xargs -I {} -P 10 bash -c '
  EVENT_ID=$(uuidgen)
  USER_ID=&amp;quot;usr_$(uuidgen | head -c 8)&amp;quot;
  PRODUCT_ID=&amp;quot;prod_$(uuidgen | head -c 8)&amp;quot;
  TIMESTAMP=$(date -u +&amp;quot;%Y-%m-%dT%H:%M:%SZ&amp;quot;)

  curl -s -o /dev/null -X POST \
    http://localhost:4567/webhook/dev/public/wbhtable5 \
    -H &amp;quot;Content-Type: application/json&amp;quot; \
    -H &amp;quot;Authorization: TEST_WEBHOOK&amp;quot; \
    -d &amp;quot;{
          \&amp;quot;event_id\&amp;quot;: \&amp;quot;$EVENT_ID\&amp;quot;,
          \&amp;quot;user_id\&amp;quot;: \&amp;quot;$USER_ID\&amp;quot;,
          \&amp;quot;event_name\&amp;quot;: \&amp;quot;product_viewed\&amp;quot;,
          \&amp;quot;properties\&amp;quot;: {
            \&amp;quot;product_id\&amp;quot;: \&amp;quot;$PRODUCT_ID\&amp;quot;,
            \&amp;quot;category\&amp;quot;: \&amp;quot;electronics\&amp;quot;,
            \&amp;quot;price\&amp;quot;: 9199.99
          },
          \&amp;quot;timestamp\&amp;quot;: \&amp;quot;$TIMESTAMP\&amp;quot;
        }&amp;quot;
'


-- 6 проверяем 

SELECT * FROM product_view_events5;

-- 7 проверяем ( появляются не сразу ) 

select * from my_iceberg_table5&lt;/code&gt;&lt;/pre&gt;&lt;div class="e2-text-picture"&gt;
&lt;img src="https://gavrilov.info/pictures/Snimok-ekrana-2025-11-09-v-17.31.56.png" width="2218" height="530" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;И в keeper она есть&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://gavrilov.info/pictures/Snimok-ekrana-2025-11-09-v-17.33.31.png" width="1420" height="1166" alt="" /&gt;
&lt;/div&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://gavrilov.info/pictures/Snimok-ekrana-2025-11-09-v-17.34.29.png" width="2428" height="456" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;И на S3&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://gavrilov.info/pictures/Snimok-ekrana-2025-11-09-v-17.35.37.png" width="1874" height="126" alt="" /&gt;
&lt;/div&gt;
</description>
</item>


</channel>
</rss>