{"id":44581322,"url":"https://github.com/factorhouse/apac-roadshow-2026","last_synced_at":"2026-02-14T05:51:16.740Z","repository":{"id":323415479,"uuid":"1082301795","full_name":"factorhouse/apac-roadshow-2026","owner":"factorhouse","description":"Building Resilient Event-Driven Systems with Kafka and Flink workshop","archived":false,"fork":false,"pushed_at":"2026-02-09T05:49:35.000Z","size":3405,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-09T06:40:52.187Z","etag":null,"topics":["cdc","change-data-capture","data-streaming","debezium","ecommerce","event-driven","factorhouse","flink","instaclustr","kafka","postgresql","workshop"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/factorhouse.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-24T03:25:28.000Z","updated_at":"2026-02-09T05:49:39.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/factorhouse/apac-roadshow-2026","commit_stats":null,"previous_names":["factorhouse/apac-roadshow-2025"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/factorhouse/apac-roadshow-2026","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/factorhouse%2Fapac-roadshow-2026","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/factorhouse%2Fapac-roadshow-2026/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/factorhouse%2Fapac-roadshow-2026/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/factorhouse%2Fapac-roadshow-2026/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/factorhouse","download_url":"https://codeload.github.com/factorhouse/apac-roadshow-2026/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/factorhouse%2Fapac-roadshow-2026/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29438641,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-14T05:24:35.651Z","status":"ssl_error","status_checked_at":"2026-02-14T05:24:34.830Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cdc","change-data-capture","data-streaming","debezium","ecommerce","event-driven","factorhouse","flink","instaclustr","kafka","postgresql","workshop"],"created_at":"2026-02-14T05:51:15.982Z","updated_at":"2026-02-14T05:51:16.732Z","avatar_url":"https://github.com/factorhouse.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🎓 Resilient Event Systems Workshop\n\n## Overview\n\nWelcome to the _Building Resilient Event-Driven Systems with Kafka and Flink_ workshop! This guide provides complete instructions for setting up and running the KartShoppe training platform **from a blank slate**. KartShoppe is a fully-functional, real-time e-commerce application designed to teach modern data engineering principles.\n\nThis session is designed for participants to start with just the source code and progressively build, deploy, and enhance the platform by introducing powerful stream processing capabilities with Apache Kafka and Apache Flink.\n\n![](./images/prod-pop.png)\n\n---\n\n## 🎯 Key Learning Objectives\n\nBy the end of this workshop, you will have hands-on experience with:\n\n- **Event-Driven Architecture:** Understand how to use Kafka as the central nervous system of a modern, decoupled application.\n- **Reactive Microservices:** See how a Quarkus-based backend can consume, process, and serve data from Flink and Kafka, pushing live updates directly to a web UI.\n- **Apache Flink Fundamentals:** Go from zero to building sophisticated, stateful stream processing jobs. You'll learn to implement sources, sinks, transformations, and windowing to solve real business problems.\n- **Stateful Stream Processing:** Implement practical, stateful logic to solve classic e-commerce challenges like real-time inventory management.\n- **Database Change Data Capture (CDC):** Learn to capture row-level changes from a PostgreSQL database in real-time and turn them into an event stream for Flink to process.\n\n---\n\n## 💡 Training Philosophy\n\nThis setup follows a **progressive, hands-on learning approach** designed for maximum impact:\n\n1.  **Start with a Working System:** You'll begin by launching the core KartShoppe application. It's a functional e-commerce site with a UI, API, and message broker, but with a key piece missing: **real-time data processing**.\n\n2.  **Incremental Enhancements:** The workshop guides you through developing and deploying specific Flink jobs. You won't just learn theory; you'll solve a real business problem with each job you write.\n\n3.  **Tangible, Visual Feedback:** As soon as you deploy a Flink job, you will see a new feature come to life in the KartShoppe UI. Watch as inventory counts update in real-time and order statuses change instantly based on user behavior. This immediate feedback loop makes learning concrete and rewarding.\n\n---\n\n## 🌟 Application Highlights\n\n### Overall Architecture\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./images/overview.png\" alt=\"Overview Diagram\"\u003e\n\u003c/p\u003e\n\n- **Frontend App**: The user interface within the browser that interacts with the backend via REST APIs and WebSockets.\n- **Quarkus API**: The backend application that handles core business logic. It writes orders to the Postgres database and produces raw order events into the Kafka cluster. It also consumes processed _inventory_ events from Kafka to update the frontend.\n- **Postgres**: The primary transactional database.\n- **Kafka Cluster**: The central message bus for all real-time events.\n- **Apache Flink Cluster**: A stream processing platform that runs applications for order processing and inventory management in real time.\n- **Tooling (Kpow and Flex)**: Provides monitoring and management capabilities for the Kafka and Flink clusters, respectively.\n\n### Quarkus API\n\nThe Quarkus API serves as the central backend, handling synchronous user-facing interactions and asynchronous event processing. It orchestrates the checkout process, manages real-time data synchronization, and exposes REST endpoints for the frontend.\n\n![](./images/backend-api.png)\n\n#### Core Functionality\n\n- **Checkout and Inventory Event Logic**: The `/checkout` endpoint orchestrates order persistence and determines how inventory updates are triggered based on the `useCdc` frontend flag:\n  - **Direct Event Publishing (`useCdc=false`)**: The API iterates through order items and immediately produces events to the Kafka `order-events` topic using the `orderEventEmitter`. These events are consumed downstream by Flink for real-time calculation.\n  - **CDC-Driven Publishing (`useCdc=true`)**: Direct publishing is skipped. The system relies on the Flink CDC Job to capture the database transaction and drive the inventory workflow.\n\n- **Real-time Product State Management**: The API employs a dual-consumer strategy to build a complete, real-time view of product data:\n  - **Kafka Streams Application**: Consumes the `inventory-events` topic to process partial updates (e.g., stock level changes) and merges them into the in-memory cache.\n  - **Standard Kafka Consumer (`ProductConsumer`)**: Subscribes to the `products` topic to retrieve full static details (e.g., name, description, initial price).\n\n- **Unified Cache and WebSocket Updates**: Upon receiving events from either consumer, the system executes a unified update function that:\n  1.  **Updates Internal Cache**: Modifies the `ProductCacheService` state store to ensure REST API calls return low-latency, up-to-date data.\n  2.  **Pushes to Frontend**: Broadcasts the updated product object to all connected clients via `EcommerceWebsocket` for immediate UI reflection.\n\n#### Key Design Patterns\n\n- **CQRS (Command Query Responsibility Segregation)**: The KTable materialization serves as a highly optimized read model for queries, while Flink jobs handle the complex command and write logic.\n- **Event Sourcing**: Product state is derived dynamically from a continuous stream of inventory events rather than relying solely on direct database queries.\n- **Materialized Views**: The `products-cache` KTable provides a queryable, in-memory view of product inventory, eliminating the need to hammer the database for state.\n- **Stateful Stream Processing**: Utilizes Kafka Streams state stores to maintain robust application state across restarts and failures.\n- **Real-time Cache Invalidation**: Product cache updates are pushed to clients immediately via WebSocket, ensuring eventual consistency between the backend and the UI.\n\n#### Race Condition Prevention\n\nThe implementation specifically addresses a race condition where inventory events might arrive before full product details:\n\n- **Problem**: Inventory events from Flink arrive before the `ProductConsumer` receives the full product objects, leading to potential null pointer exceptions or incomplete data.\n- **Solution**:\n  - **Filter**: `PRODUCT_ADDED` events are filtered out of the stream topology to prevent premature creation.\n  - **Merge Logic**: Partial updates in `ProductCacheService.updateProduct()` only merge into existing products; they do not create new incomplete entries.\n  - **Logging**: Warnings are logged when updates are received for unknown products.\n  - **UI Safeguard**: The system waits for the full product payload from the `products` topic before displaying the item in the UI.\n\n### Flink Applications\n\nThe stream processing layer comprises two composable Flink jobs that process order data and manage product inventory in real-time.\n\n![](./images/flink-apps.png)\n\n#### Order Processing and Inventory Management (`InventoryManagementJobWithOrders`)\n\nThis core stateful processing job consumes product catalog updates and real-time order events to calculate inventory levels, generate alerts, and publish enriched data streams.\n\n**Key Patterns Implemented:**\n\n- **Pattern 01: Hybrid Source for State Bootstrapping**: Uses a `HybridSource` to read a distinct file (`initial-products.json`) to bootstrap the job with the full catalog before seamlessly switching to the Kafka `product-updates` topic for real-time events.\n- **Pattern 02: Co-Processing Multiple Streams**: Utilizes a `CoProcessFunction` to `connect` the product stream and the order stream. This enables unified logic and shared state management across different event types.\n- **Pattern 03: Shared Keyed State**: Maintains `ValueState` keyed by `productId`. This ensures that both catalog updates and order deductions modify the same consistent state for any given product.\n- **Pattern 04: Timers for Event Generation**: Registers processing time timers to monitor product \"staleness.\" If no events occur within a configured window, the `onTimer` callback emits a `STALE_INVENTORY` event to signal that data requires investigation.\n- **Pattern 05: Side Outputs**: Routes business alerts (`LOW_STOCK`, `OUT_OF_STOCK`, `PRICE_DROP`) to dedicated side outputs, keeping the main data flow clean. These are later unioned into a specific alerts topic.\n- **Pattern 06: Data Validation \u0026 Canonicalization**: Parses raw inputs into clean `Product` objects and sinks them to a canonical `products` topic for consumption by other microservices.\n\n**Output Streams:**\n\n- `products`: Enriched, clean product data.\n- `inventory-events`: Main stream of inventory and price changes.\n- `inventory-alerts`: Dedicated stream for generated business alerts.\n- `websocket_fanout`: Copy of inventory events optimized for UI updates.\n\n#### Flink CDC (`OrderCDCJob`)\n\nThis job bridges the transactional database with the event-streaming ecosystem by capturing changes from PostgreSQL in real-time.\n\n**Process Flow:**\n\n1.  **Capture**: Uses Flink CDC (Debezium) to non-intrusively read the PostgreSQL Write-Ahead Log (WAL).\n2.  **Filter**: Isolates only `INSERT` (op: \"c\") events occurring in the `order_items` table.\n3.  **Transform**: Maps the raw database event into a clean JSON message containing only `productId`, `quantity`, `orderId`, and `timestamp`.\n4.  **Publish**: Pushes the standardized events to the `order-events` Kafka topic for downstream consumption.\n\n\u003cbr\u003e\n\n\u003e 💡 **What is Flink CDC?**\n\u003e\n\u003e [Flink CDC](https://nightlies.apache.org/flink/flink-cdc-docs-stable/) is a specialized library that enables the direct ingestion of database changes into Apache Flink jobs.\n\u003e\n\u003e - How It Works: Flink CDC embeds the Debezium engine directly inside the Flink Source function. It automatically performs a consistent snapshot of the existing database tables and then seamlessly switches to reading the transaction logs (e.g., PostgreSQL WAL, MySQL Binlog) to capture real-time updates.\n\u003e - Comparison with Debezium Kafka Connector:\n\u003e   - Architecture: The traditional Debezium approach requires a separate _Kafka Connect_ cluster to pull data from the DB and push it to Kafka topics before Flink can process it (`DB -\u003e Kafka Connect -\u003e Kafka -\u003e Flink`). Flink CDC bypasses this entirely (`DB -\u003e Flink`).\n\u003e   - Infrastructure Efficiency: By running the CDC logic within the Flink TaskManager, it eliminates the need to maintain and monitor a Kafka Connect cluster and reduces the storage overhead of intermediate raw topics.\n\n## 🚀 Before Training Day\n\n### Prerequisites\n\nEnsure students have:\n\n- **Docker Desktop** installed and running\n- **Node.js**: Version `18.x` or higher.\n- **Python**: Version `3.10` or higher.\n  - Note it must include `pip` (package installer) and `venv` (virtual environment support).\n- **8GB RAM** minimum (16GB recommended)\n- **Internet connection** (for downloading dependencies)\n- **macOS or Linux** (Windows with WSL2 works too)\n\n### One-Time Environment Setup\n\n**Run this script ONCE before the training:**\n\n```bash\n./setup-environment.sh\n```\n\nThis installs:\n\n- ✅ SDKMAN (Java version manager)\n- ✅ Java 11 (for building Flink jobs)\n- ✅ Java 17 (for running Quarkus)\n- ✅ Verifies Docker Desktop\n- ✅ Verifies Node.js 18+\n- ✅ Verifies Python 3.10+\n\n**Estimated time:** 5-10 minutes\n\n**Expected output:**\n\n```\n╔════════════════════════════════════════════════════════════════╗\n║              ✨  Setup Completed Successfully!  ✨             ║\n╚════════════════════════════════════════════════════════════════╝\n\nVerified \u0026 Installed Components:\n  ✓ Docker:     Docker version 24.x\n  ✓ Node.js:    v20.x.x\n  ✓ npm:        10.x.x\n  ✓ Python:     Python 3.12.3\n  ✓ SDKMAN:     5.x.x\n  ✓ Java 11:    11.0.25-tem\n  ✓ Java 17:    17.0.13-tem\n```\n\n💡 (Optional) A database client can help you query records in your PostgreSQL database. Recommended options include [DBeaver Community](https://dbeaver.io/) and [pgAdmin](https://www.pgadmin.org/).\n\n---\n\n## 📚 Training Day Schedule\n\n### Setup Instaclustr Instances\n\nFor this workshop, we will set up the core data infrastructure for our application using Instaclustr's managed platform. Your tasks are separated into two parts: creating a Kafka cluster and connecting to a pre-provisioned Postgres database.\n\n⏩ Skip this section if you are using local Kafka and PostgreSQL instances.\n\n#### 1. Your Task: Create an Apache Kafka Cluster\n\nYour first task is to provision a new Apache Kafka cluster. This will serve as the central message bus for all real-time events in our application.\n\n- Please follow the official Instaclustr guide to create your cluster: **[Creating an Apache Kafka Cluster](https://www.instaclustr.com/support/documentation/apache-kafka/getting-started/creating-a-kafka-cluster/)**.\n\nOnce the cluster is provisioned and running, take note of your connection details (especially the Broker Addresses and credentials), as you will need them for the upcoming steps.\n\n💡 Quickly verify if you can reach your Kafka cluster using [netcat](https://linux.die.net/man/1/nc):\n\n```bash\n# Syntax: nc -vz \u003cbootstrap-address\u003e \u003cport\u003e\n\n# Example:\nnc -vz 54.79.220.204 9092\n# Output:\n# Connection to 54.79.220.204 9092 port [tcp/*] succeeded!\n```\n\nThis confirms that the host and port are reachable and that a service is listening.\n\n#### 2. Provided For You: PostgreSQL Database\n\nTo pre-configure logical replication for Flink CDC, a PostgreSQL database has already been provisioned for the workshop.\n\n- You **do not** need to create this yourself.\n- Your workshop instructor will provide you with the necessary connection details (host, port, database name, user, and password).\n\n💡 Quickly verify if you can reach your PostgreSQL cluster using [netcat](https://linux.die.net/man/1/nc):\n\n```bash\n# Syntax: nc -vz \u003chost-url\u003e \u003cport\u003e\n\n# Example:\nnc -vz 15.134.112.202 5432\n# Output:\n# Connection to 15.134.112.202 5432 port [tcp/postgresql] succeeded!\n```\n\nThis confirms that the host and port are reachable and that a service is listening.\n\n### Request Factor House Community License\n\nIn this workshop, we'll use Kpow and Flex to monitor Apache Kafka and Apache Flink clusters. Both tools are covered by a single, free Community license that you need to activate.\n\nPlease follow these two simple steps:\n\n1.  **Generate Your License**: Go to the [Factor House Getting Started](https://account.factorhouse.io/auth/getting_started) page to generate your personal license.\n2.  **Create `license.env` File**: Once generated, copy the license environment variables and paste them into a new file named `license.env`. You can use `license.env.example` as a reference for the correct format.\n\n![](./images/license.png)\n\n### Platform Startup\n\n#### Goal\n\nGet the core KartShoppe platform running **without any Flink jobs**.\n\n#### What's Running?\n\n```\n┌─────────────────────────────────────────────────────────────┐\n│  PLATFORM ARCHITECTURE (No Flink Yet)                       │\n├─────────────────────────────────────────────────────────────┤\n│                                                             │\n│  Frontend (React) ←─ Quinoa ─→ Quarkus API                  │\n│                                     ↓                       │\n│                                WebSockets                   │\n│                                     ↓                       │\n│                                   Kafka                     │\n│                                                             │\n└─────────────────────────────────────────────────────────────┘\n```\n\n\u003cbr\u003e\n\nHere are steps to start and stop the training platform depending on which instances (Instaclustr or local) are used for the workshop.\n\n\u003cdetails open\u003e\n  \u003csummary\u003e\u003cb style=\"font-size: 1.4em;\"\u003e🚀 Running with Instaclustr Managed Services\u003c/b\u003e\u003c/summary\u003e\n\n---\n\n#### 1. Configure Connection Credentials\n\nFirst, we need to tell our application how to connect to your newly created Kafka cluster and the provided PostgreSQL database.\n\n- Open the `.env` file in the project root.\n- Using the connection details from your Instaclustr Kafka cluster and the provided PostgreSQL credentials, fill in the required values.\n- You can use the `.env.remote` file as a template to see which variables are needed.\n\n---\n\n#### 2. Activate the Remote Configuration for the Backend API\n\nThe Quarkus backend API needs a specific configuration file to connect to remote services. This command applies the configuration required for the application to work with Instaclustr services.\n\n```bash\n# This replaces the default properties with the one configured for remote connections\ncp quarkus-api/src/main/resources/application.properties.remote \\\n   quarkus-api/src/main/resources/application.properties\n```\n\n---\n\n#### 3. Create the Kafka Topics\n\nOur Flink jobs need specific topics in Kafka to read from and write to. The following script will set up a Python virtual environment and create them for you.\n\n```bash\n# Create and activate a Python virtual environment\n# On Linux and macOS, Python 3 may be invoked using `python3` instead of `python`.\npython -m venv venv\nsource venv/bin/activate\n\n# Install required Python packages\npip install -r scripts/requirements.txt\n\n# Run the script to create topics on your Instaclustr Kafka cluster\n# After activating a virtual environment,`python` can be used regardless of the system’s Python invocation.\npython scripts/manage_topics.py --action create\n\n# ...\n# [2025-11-18 14:12:33,605] INFO: Topic 'websocket_fanout' created\n# [2025-11-18 14:12:33,605] INFO: Topic 'processing_fanout' created\n# [2025-11-18 14:12:33,605] INFO: Topic 'ecommerce_events' created\n# [2025-11-18 14:12:33,605] INFO: Topic 'ecommerce_processing_fanout' created\n# [2025-11-18 14:12:33,605] INFO: Topic 'product-updates' created\n# [2025-11-18 14:12:33,605] INFO: Topic 'recommendations' created\n# [2025-11-18 14:12:33,605] INFO: Topic 'inventory_updates' created\n# [2025-11-18 14:12:33,605] INFO: Topic 'inventory-events' created\n# [2025-11-18 14:12:33,606] INFO: Topic 'shopping-cart-events' created\n# [2025-11-18 14:12:33,606] INFO: Topic 'basket-patterns' created\n# [2025-11-18 14:12:33,606] INFO: Topic 'order-events' created\n# [2025-11-18 14:12:33,606] INFO: Topic 'product-recommendations' created\n# ...\n```\n\n---\n\n#### 4. Initialize the Database Schema\n\nNext, we'll run a script to create the `orders` and `order_items` tables in your provided PostgreSQL database.\n\n```bash\n# Ensure your virtual environment is still active\nsource venv/bin/activate\n\n# Run the script to create the necessary tables\npython scripts/manage_db.py --action init\n\n# [2025-11-24 11:23:15,539] INFO: Connecting to database 'ecommerce' to initialize schema...\n# [2025-11-24 11:23:15,607] INFO: Applying schema and data from '\u003cpath-to-file\u003e/postgres-init.sql' to 'ecommerce'...\n# [2025-11-24 11:23:15,652] INFO: Database schema and data applied successfully.\n```\n\n---\n\n#### 5. Launch the Core Platform \u0026 Applications\n\nThis step launches all the moving parts of our system. The `start-platform-remote.sh` script performs two key actions:\n\n1.  **Starts Local Services in Docker**: It launches the monitoring tools (Kpow, Flex) and the Flink cluster (JobManager, TaskManager) as Docker containers.\n2.  **Starts Applications Locally**: It runs the Quarkus backend API and the frontend application directly on your machine.\n\n```bash\n./start-platform-remote.sh\n```\n\n**✅ Verification:** Once everything is running, check the following URLs:\n\n- **KartShoppe App**: [http://localhost:8081](http://localhost:8081)\n- **Kpow for Kafka**: [http://localhost:13000](http://localhost:13000)\n- **Flex for Flink**: [http://localhost:13001](http://localhost:13001)\n\n\u003e **Note:** The product page in the KartShoppe UI will be empty. This is expected! Our Flink job, which is responsible for populating the product data, isn't running yet.\n\n![](./images/prod-initial.png)\n\n---\n\n#### 6. Deploy the Inventory Management Flink Job\n\nNow, let's deploy our main Flink job. This job reads the initial product data, calculates inventory, and publishes the results to Kafka, which the UI is listening to.\n\n```bash\n./flink-inventory-with-orders-job.sh\n```\n\n**✅ Verification:** After a few moments, refresh the KartShoppe UI. You should now see the products populated!\n\n\u003e **Try it out!** You can now experiment with adding items to your cart and completing a purchase (leave the \"Use Flink CDC\" box unchecked for now). Watch the inventory levels change in real time.\n\n![](./images/prod-pop.png)\n\n---\n\n#### 7. Deploy the Flink CDC Job\n\nFinally, deploy the second Flink job. This job uses Change Data Capture (CDC) to read order data directly from the database transaction log instead of from a direct Kafka event.\n\n```bash\n./flink-order-cdc-job.sh\n```\n\n**✅ Verification:** To test this new data path, add items to your cart and proceed to checkout.\n\n\u003e **Important:** On the checkout page, be sure to check the **Use Flink CDC** box before completing the purchase.\n\n![](./images/prod-cdc.png)\n\n#### 8. Stopping the Workshop Environment\n\nWhen you finish your session, shut down all local components by running:\n\n```bash\n./stop-platform-remote.sh\n```\n\nThis script stops the local Docker containers and terminates the Quarkus and frontend processes. It **does not** affect any remote Instaclustr services.\n\nIf you also want to remove build artifacts, you can optionally run the cleanup script:\n\n```bash\n./clean.sh\n```\n\nTo release resources in Instaclustr services, run the following commands:\n\n```bash\n# Activate the virtual environment created earlier\nsource venv/bin/activate\n\n# Delete all Kafka topics\npython scripts/manage_topics.py --action delete --all\n\n# Delete database tables and associated resources\npython scripts/manage_db.py --action teardown\n```\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb style=\"font-size: 1.4em;\"\u003e🐋 Running with Local Instances\u003c/b\u003e\u003c/summary\u003e\n\n---\n\n#### 1. Configure Connection Credentials\n\nFirst, we need to tell our application how to connect to the Kafka cluster and PostgreSQL database running in Docker.\n\n```bash\ncp .env.local .env\n```\n\n---\n\n#### 2. Activate the Local Configuration for the Backend API\n\nThe Quarkus backend API needs a specific configuration file to connect to the local instances. This command applies the configuration required for the application to work with the local instances.\n\n```bash\n# This replaces the default properties with the one configured for local connections\ncp quarkus-api/src/main/resources/application.properties.local \\\n   quarkus-api/src/main/resources/application.properties\n```\n\n---\n\n#### 3. Launch the Core Platform\n\nThis step launches all the moving parts of our system. The `start-platform-local.sh` script performs two key actions:\n\n1.  **Starts Local Infrastructure in Docker**: It launches the entire local data platform as Docker containers. This includes:\n    - A Kafka cluster (using Redpanda)\n    - A PostgreSQL database\n    - Monitoring tools (Kpow, Flex)\n    - A Flink cluster (JobManager, TaskManager)\n2.  **Starts Applications Locally**: It runs the Quarkus backend API and the frontend application directly on your machine.\n\n```bash\n./start-platform-local.sh\n```\n\n**✅ Verification:** Once everything is running, check the following URLs:\n\n- **KartShoppe App**: [http://localhost:8081](http-:-//localhost:8081)\n- **Kpow for Kafka**: [http://localhost:13000](http-:-//localhost:13000)\n- **Flex for Flink**: [http://localhost:13001](http-:-//localhost:13001)\n\n\u003e **Note:** The product page in the KartShoppe UI will be empty. This is expected! Our Flink job, which is responsible for populating the product data, isn't running yet.\n\n![](./images/prod-initial.png)\n\n---\n\n#### 4. Deploy the Inventory Management Flink Job\n\nNow, let's deploy our main Flink job. This job reads the initial product data, calculates inventory, and publishes the results to Kafka, which the UI is listening to.\n\n```bash\n./flink-inventory-with-orders-job.sh\n```\n\n**✅ Verification:** After a few moments, refresh the KartShoppe UI. You should now see the products populated!\n\n\u003e **Try it out!** You can now experiment with adding items to your cart and completing a purchase (leave the \"Use Flink CDC\" box unchecked for now). Watch the inventory levels change in real time.\n\n![](./images/prod-pop.png)\n\n---\n\n#### 5. Deploy the Flink CDC Job\n\nFinally, deploy the second Flink job. This job uses Change Data Capture (CDC) to read order data directly from the database transaction log instead of from a direct Kafka event.\n\n```bash\n./flink-order-cdc-job.sh\n```\n\n**✅ Verification:** To test this new data path, add items to your cart and proceed to checkout.\n\n\u003e **Important:** On the checkout page, be sure to check the **Use Flink CDC** box before completing the purchase.\n\n![](./images/prod-cdc.png)\n\n#### 6. Stopping the Workshop Environment\n\nWhen you are finished with your session, you can shut down all the local components by running:\n\n```bash\n./stop-platform-local.sh\n```\n\nThis script will stop the local Docker containers and terminate the Quarkus and frontend processes.\n\n\u003c/details\u003e\n\n---\n\n#### Hands-On Exercise\n\n1. Add 10 items of the same product to your cart\n2. Watch the inventory count decrease in real-time\n3. Check Flink dashboard to see events processed\n4. View Kafka topic to see inventory update messages\n\n---\n\n## 📊 Monitor the Streaming Platform with Kpow and Flex\n\n### Monitoring Kafka with Kpow\n\nKpow provides a powerful window into the Kafka cluster, allowing for the inspection of topics, tracing of messages, and production of new data.\n\n➡️ **Kpow is accessible at:** [http://localhost:13000](http://localhost:13000).\n\n#### Exploring Key Kafka Topics\n\nFirst, it is important to understand the topics that drive the application. In the Kpow UI, navigate to the **Topics** view. While tens of topics are present, these are the most critical for the workshop:\n\n- **`products`**: The canonical topic with the clean, validated state of all products.\n- **`product-updates`**: The input topic for raw, real-time changes to the product catalog.\n- **`order-events`**: Carries commands to deduct product quantity from inventory after a sale.\n- **`inventory-events`**: A detailed audit log of every state change (e.g., price, inventory) for a product.\n- **`inventory-alerts`**: A filtered stream for business-critical notifications like \"low stock.\"\n\n![](./images/key-topics.png)\n\nTo see the initial product catalog that was loaded by the Flink job, the `products` topic can be inspected directly from the topic list. From the topic details view, locate the `products` topic. Click the **menu icon** on the left-hand side of the topic's row and select **Inspect data**.\n\n![](./images/inspect-topic-01.png)\n\nIt navigates to the **Data \u003e Inspect** page with the `products` topic already pre-selected. Simply click the **Search** button to view the messages.\n\n![](./images/inspect-topic-02.png)\n\n#### Tracing a Purchase Event\n\nThis section demonstrates how to trace a single purchase and observe the corresponding events in Kafka.\n\nAfter a purchase of three units of the _FutureTech UltraBook Pro 15 (PROD_0001)_, two new records are expected: one in `order-events` (the command) and one in `inventory-events` (the result).\n\nIn Kpow's **Data Inspect** view:\n\n1.  Select both the `order-events` and `inventory-events` topics.\n2.  Use the **kJQ Filter** to find records related to the specific product. This helps to filter out irrelevant data. Enter the following filter:\n    ```\n    .value.productId == \"PROD_0001\"\n    ```\n3.  Click **Search**.\n\n![](./images/inspect-order.png)\n\nThe two new records related to the purchase will be displayed, showing the command to deduct inventory and the resulting event confirming the new stock level.\n\n#### Manually Updating Inventory\n\nExternal events, like a new stock delivery, can also be simulated. This is done by producing a message directly to the `product-updates` topic. The following steps reset the inventory for the product back to 10.\n\n1.  In Kpow, navigate to **Data \u003e Produce**.\n2.  Select the `product-updates` topic.\n3.  Select `None` and `String` as the key and value deserializers.\n4.  Paste the following JSON into the **Value** field.\n\n    ```json\n    {\n      \"productId\": \"PROD_0001\",\n      \"name\": \"FutureTech UltraBook Pro 15\",\n      \"description\": \"High-performance laptop with Intel i9, 32GB RAM, 1TB SSD. Premium quality from FutureTech.\",\n      \"category\": \"Electronics\",\n      \"brand\": \"FutureTech\",\n      \"price\": 1899.99,\n      \"inventory\": 10,\n      \"imageUrl\": \"https://picsum.photos/400/300?random=1\",\n      \"tags\": [\n        \"laptop\",\n        \"computer\",\n        \"productivity\",\n        \"futuretech\",\n        \"electronics\"\n      ],\n      \"rating\": 4.5,\n      \"reviewCount\": 49\n    }\n    ```\n\n5.  Click **Produce**.\n\n![](./images/create-message.png)\n\n**✅ Verification:** Once the message is produced, the Flink job will process it. After refreshing the KartShoppe UI, the product's inventory will be updated to 10.\n\n![](./images/product-details.png)\n\n---\n\n### Monitoring Flink with Flex\n\nFlex provides deep insights into the Flink cluster, showing job health, metrics, and the dataflow topology.\n\n➡️ **Flex is accessible at:** [http://localhost:13001](http://localhost:13001).\n\nThe main dashboard gives a high-level overview of the Flink cluster, including the two jobs deployed for this workshop.\n\n![](./images/flex-overview.png)\n\n#### Visualizing a Flink Job's Topology\n\nThe dataflow graph for any job can be inspected to understand how data moves through the pipeline.\n\n1.  Navigate to **Jobs \u003e Inspect**.\n2.  Select the `Inventory Management Job`.\n\n![](./images/job-inspect.png)\n\n#### Viewing Logs for Debugging\n\nFlex makes it easy to access the logs from Flink's JobManager and TaskManagers, which is essential for debugging. The logs can be checked to see the output from the Flink apps or to look for any potential errors.\n\n- To view the **JobManager** logs, navigate to **Job Manager \u003e Logs**.\n- For **TaskManager** logs, navigate to **Task Managers**, select a specific manager, and then go to **Inspect \u003e Logs**.\n\n![](./images/jobmanager-log.png)\n\n#### Cancelling a Flink Job\n\nA running Flink job can be stopped at any time from the Flex UI.\n\nTo do this, navigate to **Jobs \u003e Inspect** for the specific job and click the **Cancel** button in the top action bar.\n\n![](./images/cancel-job.png)\n\n## 🎓 Training Tips\n\n### For Instructors\n\n1. **Show, then explain:** Start each Flink job, show the visible change, THEN explain the concepts\n2. **Use the UI constantly:** Keep the browser open, refresh often to show real-time updates\n3. **Monitor Flink dashboard:** Show the job graph, metrics, backpressure (http://localhost:8081)\n4. **Explore Kafka topics:** Use Redpanda Console to see messages flowing (http://localhost:13000)\n5. **Explain incrementally:** Each module builds on the previous - don't overwhelm with all patterns at once\n\n### For Students\n\n1. **Follow along:** Run commands in your own terminal\n2. **Experiment:** Try breaking things! Stop a Flink job, see what happens, restart it\n3. **Ask questions:** Why does this pattern need keyed state? Why use broadcast state here?\n4. **Monitor everything:** Open all dashboards (Flink, Redpanda, Quarkus Dev UI)\n5. **Read the logs:** `tail -f logs/quarkus.log` and Flink job logs show what's happening\n\n---\n\n## 🐛 Troubleshooting\n\n### Platform Won't Start\n\n```bash\n# Check Docker\ndocker info\n\n# Check Java version\njava -version  # Should be 17+\n# if not\nsdk use java 17.0.13-tem\n\n# View logs\ntail -f logs/quarkus.log\ndocker compose logs -f\n```\n\n### Flink Job Fails to Start\n\n```bash\n# Switch to Java 11 for building Flink jobs\nsdk use java 11.0.25-tem\n\n# Rebuild\n./gradlew :flink-inventory:clean :flink-inventory:shadowJar\n\n# Check logs\ntail -f logs/inventory.log\n```\n\n### Port Conflicts\n\n```bash\n# Kill process on port 8081 (Quarkus)\nlsof -ti:8081 | xargs kill -9\n```\n\n### Docker Fails to Start Due to Existing Containers\n\nEach Docker container must have a unique name within a Docker Compose stack. If a container with the same name already exists, Docker Compose will fail to start the stack.\n\nExample error:\n\n```bash\nError response from daemon: Conflict. The container name \"/jobmanager\" is already in use by container \"61f339758f02774d7acdcd3021c4d2b9f8f9653eaea03d126bef121210689517\". You have to remove (or rename) that container to be able to reuse that name.\n```\n\nThis issue can be resolved by stopping and removing the existing Docker Compose environment.\n\n```bash\n## Remote\n# Remove all containers in the stack\ndocker compose -f compose-remote.yml down\n\n# Remove a specific container\ndocker compose -f compose-remote.yml down jobmanager\n\n## Local\n# Remove all containers in the stack\ndocker compose -f compose-local.yml down\n\n# Remove a specific container\ndocker compose -f compose-local.yml down jobmanager\n```\n\n### Clean State Reset\n\n```bash\n## Stop everything\n# remote\n./stop-platform-remote.sh\n# local\n./stop-platform-local.sh\n\n## Clean Docker volumes\n# remote\ndocker compose -f compose-remote.yml down -v\n# local\ndocker compose -f compose-local.yml down -v\n\n## Clean Gradle cache\n./gradlew clean\n# or\n./clean.sh\n\n## Restart\n# remote\n./start-platform-remote.sh\n# local\n./start-platform-local.sh\n```\n\n---\n\n## 📖 Additional Resources\n\n- [Apache Flink Documentation](https://flink.apache.org)\n- [Quarkus Guides](https://quarkus.io/guides/)\n- [Redpanda Documentation](https://docs.redpanda.com)\n- [Ververica Academy](https://www.ververica.com/academy)\n- [Factor House Docs](https://docs.factorhouse.io/)\n- [Instaclustr Documentation](https://www.instaclustr.com/support/documentation/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffactorhouse%2Fapac-roadshow-2026","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffactorhouse%2Fapac-roadshow-2026","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffactorhouse%2Fapac-roadshow-2026/lists"}