<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>odctl on Jaehyeon Kim</title><link>https://jaehyeon.me/tags/odctl/</link><description>Recent content in odctl on Jaehyeon Kim</description><generator>Hugo -- gohugo.io</generator><language>en</language><copyright>Copyright © 2023-2026 Jaehyeon Kim. All Rights Reserved.</copyright><lastBuildDate>Mon, 12 Oct 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://jaehyeon.me/tags/odctl/index.xml" rel="self" type="application/rss+xml"/><item><title>odctl 1.0: Kafka, Flink, Spark, Iceberg, Trino and MLflow on Your Laptop with One Command</title><link>https://jaehyeon.me/blog/2026-10-12-odctl-1-0/</link><pubDate>Mon, 12 Oct 2026 00:00:00 +0000</pubDate><guid>https://jaehyeon.me/blog/2026-10-12-odctl-1-0/</guid><description><![CDATA[<p>Trying a modern data tool on your own machine is rarely about the tool. Writing Iceberg tables from Flink needs a catalog, the catalog needs a database and object storage, Avro on Kafka needs a schema registry, and every piece needs the right connector jars, ports and network settings. Most of an afternoon goes into making them talk to each other before you write a line of your own code.</p>
<p><code>odctl</code> removes that work. It is a command-line tool that runs a set of open-source data and machine learning services on your laptop with Docker, already connected to each other. You say what you want, for example Kafka and Flink, and it starts them together with everything they depend on. It has now reached 1.0, with a documentation site that has a guide for every service.</p>]]></description><enclosure url="https://jaehyeon.me/blog/2026-10-12-odctl-1-0/featured.png" length="226580" type="image/png"/></item><item><title>Keeping Game Leaderboards Up to Date in Real Time with Kafka and Flink SQL</title><link>https://jaehyeon.me/blog/2026-10-02-game-leaderboard-flink-sql/</link><pubDate>Fri, 02 Oct 2026 00:00:00 +0000</pubDate><guid>https://jaehyeon.me/blog/2026-10-02-game-leaderboard-flink-sql/</guid><description>&lt;p>A mobile game produces a score every time a player finishes a round, and players expect the leaderboards to move as they play. In this post, a simulation plays the game and sends each score to Kafka. Four Flink SQL jobs keep four top 10 leaderboards up to date in PostgreSQL, and a web dashboard shows them as they change. Everything runs on your own machine.&lt;/p></description><enclosure url="https://jaehyeon.me/blog/2026-10-02-game-leaderboard-flink-sql/featured.png" length="451195" type="image/png"/></item><item><title>Change Data Capture on a Simulated Online Shop with Debezium and Kafka Connect</title><link>https://jaehyeon.me/blog/2026-10-01-ecommerce-cdc-debezium-kafka-connect/</link><pubDate>Thu, 01 Oct 2026 00:00:00 +0000</pubDate><guid>https://jaehyeon.me/blog/2026-10-01-ecommerce-cdc-debezium-kafka-connect/</guid><description>&lt;p>Most databases change all day: users sign up, orders are placed, and each order moves from one status to the next. Change data capture (CDC) turns those changes into a stream of events that other systems can read as they happen. In this post, a simulated online shop writes to PostgreSQL in real time, Debezium streams every change to Kafka, and a sink connector saves the changes as files in object storage. Everything runs on your own machine.&lt;/p></description><enclosure url="https://jaehyeon.me/blog/2026-10-01-ecommerce-cdc-debezium-kafka-connect/featured.png" length="583526" type="image/png"/></item><item><title>Data Streaming and Machine Learning Projects That Run on Your Laptop</title><link>https://jaehyeon.me/blog/2026-09-30-introducing-benchtop/</link><pubDate>Wed, 30 Sep 2026 00:00:00 +0000</pubDate><guid>https://jaehyeon.me/blog/2026-09-30-introducing-benchtop/</guid><description><![CDATA[<p>Reading about a data tool only goes so far. The hard part is getting several tools to run together and seeing how data moves between them. <a href="https://github.com/jaehyeon-kim/benchtop" target="_blank" rel="noopener noreferrer">Benchtop<i class="fas fa-external-link-square-alt ms-1"></i></a> is a collection of small, hands-on demos for data engineering, stream processing, machine learning, AI engineering and MLOps. Each demo runs locally on the odctl stack, and works from a fresh clone of the repository, with nothing to install beforehand but Docker and uv. Each one builds a working system from open-source tools and explains the ideas behind it, so you learn a tool by running it rather than only reading about it.</p>]]></description><enclosure url="https://jaehyeon.me/blog/2026-09-30-introducing-benchtop/featured.png" length="15991" type="image/png"/></item><item><title>Learning MLOps with a Feature Store: A New Series</title><link>https://jaehyeon.me/blog/2026-09-28-mlops-with-a-feature-store/</link><pubDate>Mon, 28 Sep 2026 00:00:00 +0000</pubDate><guid>https://jaehyeon.me/blog/2026-09-28-mlops-with-a-feature-store/</guid><description><![CDATA[<p>Learning MLOps one tool at a time leaves out the hard part: how features, training and serving fit together as one system. This series learns it by building complete systems. I follow Jim Dowling&rsquo;s <a href="https://www.oreilly.com/library/view/building-machine-learning/9781098165222/" target="_blank" rel="noopener noreferrer"><em>Building Machine Learning Systems with a Feature Store</em><i class="fas fa-external-link-square-alt ms-1"></i></a> and rebuild its hands-on projects with open-source tools, running on my laptop.</p>]]></description><enclosure url="https://jaehyeon.me/blog/2026-09-28-mlops-with-a-feature-store/featured.png" length="516299" type="image/png"/></item><item><title>Building an Agentic Analytics System over an Iceberg Lakehouse</title><link>https://jaehyeon.me/blog/2026-07-18-agentic-analytics-system/</link><pubDate>Sat, 18 Jul 2026 00:00:00 +0000</pubDate><guid>https://jaehyeon.me/blog/2026-07-18-agentic-analytics-system/</guid><description><![CDATA[<p>Generative AI has made conversational analytics feel within reach, yet direct text-to-SQL systems remain hard to operate reliably. A database schema tells you the tables, columns, and types, but it says nothing about which datasets are canonical, which join paths are approved, how a governed metric is calculated, or what the business actually means by &ldquo;revenue&rdquo; or &ldquo;active customer&rdquo;. Ask a language model to infer all of that from raw tables and, sooner or later, it will confidently invent an answer.</p>
<p>I built a proof of concept to explore a more disciplined approach: put a semantic layer between the model and the data, and let the agent reason over governed business concepts instead of guessing at raw schemas. It runs entirely locally on an open-source stack, and it happens to tie together two of my other projects along the way.</p>]]></description><enclosure url="https://jaehyeon.me/blog/2026-07-18-agentic-analytics-system/featured.png" length="160825" type="image/png"/></item><item><title>Running Kafka, Flink, Spark, Trino and Iceberg Locally with One CLI</title><link>https://jaehyeon.me/blog/2026-07-16-odctl-open-data-stack/</link><pubDate>Thu, 16 Jul 2026 00:00:00 +0000</pubDate><guid>https://jaehyeon.me/blog/2026-07-16-odctl-open-data-stack/</guid><description><![CDATA[<p>Anyone who has tried to stand up a realistic data platform on their laptop knows the pain. You want Kafka talking to Flink, Spark writing to Iceberg, Trino querying the result, and maybe a catalog and a lineage tool watching over all of it. What you actually get is an afternoon lost to dependency conflicts, port clashes, and a Docker Compose file that grows a new bug every time you touch it.</p>
<p>I built <code>odctl</code> to make that afternoon disappear. It is a curated collection of open-source data technologies wrapped in a small CLI, and it is now available on PyPI.</p>]]></description><enclosure url="https://jaehyeon.me/blog/2026-07-16-odctl-open-data-stack/featured.png" length="271859" type="image/png"/></item><item><title>Flink Table API - Declarative Analytics for Supplier Stats in Real Time</title><link>https://jaehyeon.me/blog/2025-06-17-kotlin-getting-started-flink-table/</link><pubDate>Tue, 17 Jun 2025 00:00:00 +0000</pubDate><guid>https://jaehyeon.me/blog/2025-06-17-kotlin-getting-started-flink-table/</guid><description><![CDATA[<p>In the last post, we explored the fine-grained control of Flink&rsquo;s DataStream API. Now, we&rsquo;ll approach the same problem from a higher level of abstraction using the <strong>Flink Table API</strong>. This post demonstrates how to build a declarative analytics pipeline that processes our continuous stream of Avro-formatted order events. We will define a <code>Table</code> on top of a <code>DataStream</code> and use SQL-like expressions to perform windowed aggregations. This example highlights the power and simplicity of the Table API for analytical tasks and showcases Flink&rsquo;s seamless integration between its different API layers to handle complex requirements like late data.</p>]]></description><enclosure url="https://jaehyeon.me/blog/2025-06-17-kotlin-getting-started-flink-table/featured.png" length="144113" type="image/png"/></item><item><title>Flink DataStream API - Scalable Event Processing for Supplier Stats</title><link>https://jaehyeon.me/blog/2025-06-10-kotlin-getting-started-flink-datastream/</link><pubDate>Tue, 10 Jun 2025 00:00:00 +0000</pubDate><guid>https://jaehyeon.me/blog/2025-06-10-kotlin-getting-started-flink-datastream/</guid><description><![CDATA[<p>Building on our exploration of stream processing, we now transition from Kafka&rsquo;s native library to <strong>Apache Flink</strong>, a powerful, general-purpose distributed processing engine. In this post, we&rsquo;ll dive into Flink&rsquo;s foundational <strong>DataStream API</strong>. We will tackle the same supplier statistics problem - analyzing a stream of Avro-formatted order events - but this time using Flink&rsquo;s robust features for stateful computation. This example will highlight Flink&rsquo;s sophisticated event-time processing with watermarks and its elegant, built-in mechanisms for handling late-arriving data through side outputs.</p>]]></description><enclosure url="https://jaehyeon.me/blog/2025-06-10-kotlin-getting-started-flink-datastream/featured.png" length="142918" type="image/png"/></item><item><title>Kafka Streams - Lightweight Real-Time Processing for Supplier Stats</title><link>https://jaehyeon.me/blog/2025-06-03-kotlin-getting-started-kafka-streams/</link><pubDate>Tue, 03 Jun 2025 00:00:00 +0000</pubDate><guid>https://jaehyeon.me/blog/2025-06-03-kotlin-getting-started-kafka-streams/</guid><description>&lt;p>A Kotlin application analyses a continuous stream of Avro-formatted order events, calculates supplier statistics in tumbling windows, and handles late-arriving data. This post shifts our focus from basic Kafka clients to real-time stream processing with &lt;strong>Kafka Streams&lt;/strong>. The example demonstrates the power of Kafka Streams for building lightweight, yet robust, stream processing applications directly within your Kafka ecosystem, using event-time processing and custom logic.&lt;/p></description><enclosure url="https://jaehyeon.me/blog/2025-06-03-kotlin-getting-started-kafka-streams/featured.png" length="131804" type="image/png"/></item><item><title>Kafka Clients with Avro - Schema Registry and Order Events</title><link>https://jaehyeon.me/blog/2025-05-27-kotlin-getting-started-kafka-avro-clients/</link><pubDate>Tue, 27 May 2025 00:00:00 +0000</pubDate><guid>https://jaehyeon.me/blog/2025-05-27-kotlin-getting-started-kafka-avro-clients/</guid><description>&lt;p>A Kafka producer that generates mock order data and a consumer that processes these orders are built with Kotlin, Apache Avro for data serialization, and Gradle for build management. This example highlights best practices such as schema management with Avro, robust error handling, and graceful shutdown, providing a solid foundation for your own Kafka-based projects. We&amp;rsquo;ll dive into the build configuration, the Avro schema definition, utility functions for Kafka administration, and the core logic of both the producer and consumer applications.&lt;/p></description><enclosure url="https://jaehyeon.me/blog/2025-05-27-kotlin-getting-started-kafka-avro-clients/featured.png" length="73988" type="image/png"/></item><item><title>Kafka Clients with JSON - Producing and Consuming Order Events</title><link>https://jaehyeon.me/blog/2025-05-20-kotlin-getting-started-kafka-json-clients/</link><pubDate>Tue, 20 May 2025 00:00:00 +0000</pubDate><guid>https://jaehyeon.me/blog/2025-05-20-kotlin-getting-started-kafka-json-clients/</guid><description>&lt;p>A Kafka producer application generates and sends order data, and a Kafka consumer application receives and processes those orders. This post explores that Kotlin-based Kafka project, detailing the construction and operation of both applications. We&amp;rsquo;ll go through each component, from build configuration to message handling, to understand how they work together in an event-driven system.&lt;/p></description><enclosure url="https://jaehyeon.me/blog/2025-05-20-kotlin-getting-started-kafka-json-clients/featured.png" length="97922" type="image/png"/></item></channel></rss>