Hands-on demo for building a lakehouse with OLake: sync MySQL CDC data into Apache Iceberg on MinIO, then query the same tables with Trino and Spark.
git clone https://github.com/datazip-inc/workshops.git
cd workshopsThis repo includes docker-compose-trino.yml and the trino/etc/ config used in the steps below.
The OLake Playground lives in the official OLake GitHub repo. Use it to spin up MySQL, MinIO, Iceberg REST catalog, Spark, and the OLake UI — then create a pipeline and sync data to Iceberg.
Follow the playground docs until you have synced your data and queried it with Spark. Once that is done, come back here to query the same Iceberg tables with Trino.
Trino connects to the existing MinIO and Iceberg REST catalog on olake-network — it does not start MinIO or the catalog itself.
docker compose -f docker-compose-trino.yml up -dVerify Trino is healthy:
curl http://localhost:8090/v1/infoYou should see "state":"ACTIVE".
Replace job_weather with the namespace matching your OLake job name (e.g. if your job is named job, database is weather, schema is job_weather).
# List schemas
docker exec -it olake-trino-coordinator trino \
--catalog iceberg --execute "SHOW SCHEMAS;"
# List tables
docker exec -it olake-trino-coordinator trino \
--catalog iceberg --schema job_weather --execute "SHOW TABLES;"
# Query synced data
docker exec -it olake-trino-coordinator trino \
--catalog iceberg --schema job_weather \
--execute "SELECT * FROM weather LIMIT 10;"Open http://localhost:3000 — login: admin / password
If needed, edit the OLake Demo connection:
- Host:
host.docker.internal - Port:
8090 - Catalog:
iceberg - Schema:
job_weather
Run:
SELECT * FROM job_weather.weather LIMIT 10;This section loads a larger TPCH dataset into MySQL, syncs it to Iceberg via OLake, then runs continuous updates to simulate CDC while syncing changes incrementally.
- Python 3.10+
- MySQL running (from the Spark stack)
Install dependencies:
pip install -r requirements-postgres-to-mysql.txtGenerate TPCH data and load it into MySQL:
python3 duckdb_to_mysql.pyThis uses DuckDB to generate a full TPCH dataset at scale factor 10 (~10 GB total), then loads only partsupp into MySQL (tpch.partsupp, ~8M rows).
Verify:
docker exec -it primary_mysql mysql -u root -ppassword -e "
USE tpch;
SELECT COUNT(*) FROM partsupp;
SELECT * FROM partsupp LIMIT 5;
"In OLake UI (http://localhost:8000, admin / password), create a new Job named tpch_job.
| Field | Value |
|---|---|
| Connector | MySQL |
| Host | host.docker.internal |
| Port | 3306 |
| Database | tpch |
| Username | root |
| Password | password |
Select partsupp and enable Normalisation.
| Field | Value |
|---|---|
| Connector | Apache Iceberg |
| Catalog Type | REST |
| REST Catalog URI | http://host.docker.internal:8181 |
| S3 Path | s3://warehouse/tpch_job/ |
| S3 Endpoint | http://host.docker.internal:9000 |
| S3 Access Key | minio |
| S3 Secret Key | minio123 |
| AWS Region | us-east-1 |
Click Sync now and wait for completion. Iceberg table: tpch_job.partsupp.
In a separate terminal:
python3 continuous_update_partsupp.pyThis adds +10 to ps_supplycost on 500,000 rows every minute, generating MySQL binlog / CDC events.
Example output:
[run 1] Updating first 500,000 rows in tpch.partsupp (+10 to ps_supplycost) ...
[run 1] Done. Rows updated: 500,000 | Time taken: 3.2s
Sleeping 56.8s until next run ...
Set the OLake job frequency to Every minute, or click Sync now each minute during the demo.
OLake will pick up the MySQL CDC changes and merge them into the Iceberg table on MinIO.
OLake Fusion is the maintenance tool for Iceberg tables. It is available in the OLake UI under the Maintenance section.
-
Open Maintenance in OLake UI to launch Fusion.
-
Import the catalog
Since you already synced data using OLake, import the existing catalog using the dropdown (no manual catalog setup needed).Important: Fusion cannot access the Docker network, so do not use
host.docker.internalfor catalog endpoints. Use your machine's local IP instead.Get your local IP (macOS):
ipconfig getifaddr en0
When importing or editing the catalog in Fusion, replace
host.docker.internalwith this IP (e.g. for the REST catalog URI and S3 endpoint). -
View your table
Go to the Tables section, select the imported catalog, then choose the schema where your table lives (e.g.tpch_job).
Your table (e.g.partsupp) will appear along with table metrics. -
Configure compaction
Open the Configure tab for the table:- Select Lite compaction
- Set a custom cron schedule to run every 2 minutes:
*/2 * * * *
Fusion will run Lite compaction on that schedule automatically.
| File | Purpose |
|---|---|
docker-compose-trino.yml |
Trino + SQLPad (connects to existing MinIO/Iceberg stack) |
trino/etc/ |
Trino config (Iceberg REST catalog + MinIO S3) |
postgres_to_mysql.py |
Generate TPCH SF=10 data; load partsupp into MySQL |
continuous_update_partsupp.py |
Update 500k rows/min in MySQL for CDC testing |
requirements-postgres-to-mysql.txt |
Python dependencies (duckdb, pymysql) |
After duckdb_to_mysql.py completes and the data is loaded into MySQL, remove the local DuckDB working directory to free disk space (~5 GB):
rm -rf tpch_workdirTrino can't connect / ENOTFOUND
Ensure Trino stack is up and base stacks are running:
docker compose -f docker-compose-trino.yml up -d
curl http://localhost:8090/v1/infoSQLPad connection error
Use host.docker.internal:8090 as the Trino host in SQLPad settings.
OLake can't connect to MySQL
Use host.docker.internal (not localhost) from OLake workers.
Lock wait timeout on MySQL updates
Use the included continuous_update_partsupp.py — it uses fast LIMIT-based updates.
- Clone this repo → follow OLake Playground until data is synced and queried in Spark
- Part 1 (continued):
docker compose -f docker-compose-trino.yml up -d→ query the same data in Trino - Part 2: Load TPCH
partsupp→ sync via OLake → run continuous updates → sync every minute → configure Lite compaction in Fusion (every 2 mins)