From fd257a6cbdade34ad66e43191a61b82d23db5ff0 Mon Sep 17 00:00:00 2001 From: Duke Dhal Date: Mon, 15 Sep 2025 15:25:56 +0530 Subject: [PATCH 01/17] fix: contribution --- docs/community/contributing.mdx | 2 +- docs/community/setting-up-a-dev-env.mdx | 274 ++++++++++++++++++++++++ docs/connectors/mongodb/index.mdx | 4 +- sidebars.js | 1 + 4 files changed, 279 insertions(+), 2 deletions(-) create mode 100644 docs/community/setting-up-a-dev-env.mdx diff --git a/docs/community/contributing.mdx b/docs/community/contributing.mdx index 7bad5d19..4f1d70bb 100644 --- a/docs/community/contributing.mdx +++ b/docs/community/contributing.mdx @@ -1,5 +1,5 @@ --- -title: How to contribute +title: Contribute to OLake description: Contributing to OLake guide sidebar_position: 1 --- diff --git a/docs/community/setting-up-a-dev-env.mdx b/docs/community/setting-up-a-dev-env.mdx new file mode 100644 index 00000000..40effa23 --- /dev/null +++ b/docs/community/setting-up-a-dev-env.mdx @@ -0,0 +1,274 @@ +--- +title: Setting up a Development Environment +description: Setting up a Development Environment in OLake guide +sidebar_position: 3 +--- + +# Setting up a Development Environment + +The documentation in this section is a bit of knowlegde required to run OLake for developement purposes. +:::note +Now we have evolved to recommend and support docker compose more actively as the main way to run OLake for development and preserve your sanity. Most people should stick to the first few sections - ("Fork & Clone", "docker compose" and "Installing Dev Tools") +::: + +## Fork and Clone +First, [fork the repository on GitHub](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/about-forks), then clone it. + +Second, you can clone the main repository directly, and raise pull required accordingly. + +There are basically 3 repositories in which you can contribute to. + +- ### OLake CLI +```bash +git clone git@github.com:datazip-inc/olake.git +``` + +- ### OLake UI +```bash +git clone git@github.com:datazip-inc/olake-ui.git +``` + +- ### OLake Helm +```bash +git clone git@github.com:datazip-inc/olake-helm.git +``` + +## docker compose + +In order to test you code, you must have your source and destination configured. + +As of now, OLake offers 5 sources whose local setup via docker compose is provided below: +- #### Postgres - **[docker compose](/docs/connectors/postgres/setup/local)** +- #### MongoDB - **[docker compose](/docs/connectors/mongodb/setup/local)** +- #### MySQL - **[docker compose](/docs/connectors/mysql/setup/local)** +- #### Oracle (docker compose WIP) +- #### Kafka (docker compose WIP) + +OLake destination docker compose: +- #### Iceberg (using JDBC catalog) - [docker compose](https://github.com/datazip-inc/olake/blob/master/destination/iceberg/local-test/docker-compose.yml) +- #### Parquet docker compose + +```yml title="docker-compose.yml" +version: '3.1' + +services: + minio: + image: minio/minio:RELEASE.2025-04-03T14-56-28Z + container_name: minio + environment: + - MINIO_ROOT_USER=admin + - MINIO_ROOT_PASSWORD=password + - MINIO_DOMAIN=minio + networks: + default: + aliases: + - warehouse.minio + ports: + - "9001:9001" + - "9000:9000" + volumes: + - ./data/minio-data:/data + command: [ "server", "/data", "--console-address", ":9001" ] + + mc: + depends_on: + - minio + image: minio/mc:RELEASE.2025-04-03T17-07-56Z + container_name: minio-mc + networks: + - default + environment: + - AWS_ACCESS_KEY_ID=admin + - AWS_SECRET_ACCESS_KEY=password + - AWS_REGION=us-east-1 + entrypoint: | + /bin/sh -c " + until (/usr/bin/mc config host add minio http://minio:9000 admin password) do echo '...waiting...' && sleep 1; done; + if ! /usr/bin/mc ls minio/warehouse > /dev/null 2>&1; then + /usr/bin/mc mb minio/warehouse; + /usr/bin/mc policy set public minio/warehouse; + fi; + tail -f /dev/null + " +``` + +Paste this above docker compose in a file and run: +```bash +docker compose -f {file-location} up -d +``` + +## Setup up Source + + + +**This setup is using the above provided docker compose for Postgres.** +```json title="source.json" +{ + "host": "primary_postgres", + "port": 5432, + "database": "main", + "username": "main", + "password": "password", + "jdbc_url_params": {}, + "ssl": { + "mode": "disable" + }, + "update_method": { + "replication_slot": "postgres_slot", + "intial_wait_time": 120 + }, + "reader_batch_size": 1000, + "default_mode": "cdc", + "max_threads": 10 +} +``` + + + + + + + + +You can use your own Oracle configuration. The docker compose is **WIP**. + + +You can use your own Kafka configuration. The docker compose is **WIP**. + + + +## Setup up Destination + + +**This setup is using the above provided docker compose for Iceberg.** +```json title="destination.json" +{ + "type": "ICEBERG", + "writer": { + "catalog_type": "jdbc", + "jdbc_url": "jdbc:postgresql://localhost:5432/iceberg", + "jdbc_username": "iceberg", + "jdbc_password": "password", + "iceberg_s3_path": "s3a://warehouse", + "s3_endpoint": "http://localhost:9000", + "s3_use_ssl": false, + "s3_path_style": true, + "aws_access_key": "admin", + "aws_secret_key": "password", + "iceberg_db": "olake_iceberg", + "aws_region": "us-east-1" + } +} +``` + + +**This setup is using the above provided docker compose for S3 Parquet.** +```json title="destination.json" +{ + "type": "PARQUET", + "writer": { + "s3_bucket": "warehouse", + "s3_region": "us-east-1", + "s3_access_key": "admin", + "s3_secret_key": "password", + "s3_endpoint": "http://localhost:9000", + "s3_path": "" + } +} +``` + + +To access S3-compatible MinIO, click on [`http://localhost:9001/login`](http://localhost:9001/login), with username = `admin` and password = `password`. + +## Commands +:::info +Below given commands are the mandatory commands required to run OLake. For more information, refer to this: [`OLake commands`](/docs/community/commands-and-flags) +::: + +- ### Discover + Initially you have to run the discover command to get the possible streams of the source. It requires source name and source config path, with command type `discover`. + ```bash + ./build.sh driver-{source-name} discover --config {source.json path} + ``` + This will generate streams.json, which will have all the streams of the source. + +- ### Sync + After doing required changes in the streams, sync command has to be run. + ```bash + ./build.sh driver-{source-name} sync --config {source.json path} --catalog {stream.json path} --destination {destination.json path} + ``` + :::note + This above command will generate state.json, having the persisted state of the source and stats.json, having the statistics of the sync. + ::: + If want to run sync with state, run: + ```bash + ./build.sh driver-{source-name} sync --config {source.json path} --catalog {stream.json path} --destination {destination.json path} --state {state.json path} + ``` + +## Debug Mode +If you don't want to run the sync commands after every change, we have this debugger mode for Go side of code. +:::caution +If using the Iceberg as destination, you have to generate the jar file for Java side of code. To generate the jar file either run the sync command once or run `mvn clean package -DskipTests`. +::: + +**Assumptions: You are using [VSCode](https://code.visualstudio.com/download) to run OLake locally.** + +### Steps to Debug +- Make a directory `.vscode` (inside OLake project, at root location) if not already created. +- Create a file named `launch.json` inside the `.vscode` directory and paste the beflow config. + +```json title=".vscode/launch.json" +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Go Code", + "type": "go", + "request": "launch", + "program": "{PATH_TO_UPDATE}/drivers/{source-name}/main.go", + "mode": "auto", + "args": [ + "sync", + "--config", + "{PATH_TO_UPDATE}/drivers/{source-name}/examples/source.json", + "--catalog", + "{PATH_TO_UPDATE}/drivers/{source-name}/examples/streams.json", + "--destination", + "{PATH_TO_UPDATE}/drivers/{source-name}/examples/destination.json", + // "--state", + // "{PATH_TO_UPDATE}/drivers/{source-name}/examples/state.json", + ] + } + ] +} +``` + +### Params: +| key | value(s) | +| --- | --- | +|`mode` | `auto`, `debug` | +| `args` | `sync` , `discover`, `check` | + +Update `PATH_TO_UPDATE` with the location where OLake project lives inside your system. For example: + +```json +"program": "/Users/john/Desktop/projects/olake/drivers/mongodb/main.go", +... + "--config", + "/Users/john/Desktop/projects/olake/drivers/mongodb/examples/source.json", +... +``` +Now, setup debug points in the codebase and click "Launch Go Code". + +![Debug](/img/docs/getting-started/debug.webp) + + + + + + + + + + + diff --git a/docs/connectors/mongodb/index.mdx b/docs/connectors/mongodb/index.mdx index f0ffe45f..a89907a3 100644 --- a/docs/connectors/mongodb/index.mdx +++ b/docs/connectors/mongodb/index.mdx @@ -34,7 +34,9 @@ For CDC mode, MongoDB must meet the following requirements: ::: -**To set up MongoDB for CDC, please refer to the [MongoDB and Atlas CDC Setup](/docs/connectors/mongodb/cdc_setup) guide.** +To set up MongoDB for CDC, please refer to the **[MongoDB and Atlas CDC Setup](/docs/connectors/mongodb/cdc_setup)** guide. + +For local setup, follow **[MongoDB via Docker Compose](/docs/connectors/mongodb/setup/local)**. ### Connection Prerequisites diff --git a/sidebars.js b/sidebars.js index 23a6d3b1..103488df 100644 --- a/sidebars.js +++ b/sidebars.js @@ -140,6 +140,7 @@ const docSidebar = { sectionHeader("COMMUNITY"), 'community/contributing', 'community/issues-and-prs', + 'community/setting-up-a-dev-env', 'community/code-of-conduct', 'community/channels', From 9f8e531a3c227d6bb435b82c878036ce8496aac2 Mon Sep 17 00:00:00 2001 From: Nayan Joshi Date: Mon, 15 Sep 2025 16:41:53 +0530 Subject: [PATCH 02/17] Modified the contribution.mdx and added the source.json for remaining connectors --- docs/community/contributing.mdx | 181 ++++++------------------ docs/community/setting-up-a-dev-env.mdx | 37 +++++ 2 files changed, 78 insertions(+), 140 deletions(-) diff --git a/docs/community/contributing.mdx b/docs/community/contributing.mdx index 4f1d70bb..86ca9493 100644 --- a/docs/community/contributing.mdx +++ b/docs/community/contributing.mdx @@ -1,5 +1,5 @@ --- -title: Contribute to OLake +title: How to contribute description: Contributing to OLake guide sidebar_position: 1 --- @@ -11,166 +11,67 @@ If you are a student contributor and new to data engineering domain, finish the - [General Terminologies](../resources/terminologies) - [OLake Terminologies](../resources/olake-terminologies) -For Contributing to OLake, refer the guide [here](https://github.com/datazip-inc/olake/blob/master/CONTRIBUTING.md) (detailed Contributing guide comming soon) +Core contributors and community members engage in the following public channels: -A good start will be to explore: -- [first good issues](https://github.com/datazip-inc/olake/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22) -- [beginner issues](https://github.com/datazip-inc/olake/issues?q=is%3Aissue%20state%3Aopen%20label%3Abeginner) - -See the issues and if its looks interesting enough to you, comment below them and one of our maintainers will get in touch with you. - -You can also refer to the [projects](https://github.com/orgs/datazip-inc/projects/5) section in GitHub to see who all are working on which issues. - -Refer [here](../connectors/mongodb/setup/local) to build mongodb replicaset. - -:::info -Please join OLake slack community's `#contributing-to-olake` channel for all the information regarding contributions. Join [OLake Community Slack](https://olake.io/slack) here. -::: - -### Clone the repo - -```bash -git clone git@github.com:datazip-inc/olake.git -``` - -### Create a Pull Request - -#### Make A Branch -- Please create a separate branch for each issue that you're working on. Do not make changes to the default branch (e.g. master, staging) of your fork. +- [OLake Community Slack](https://olake.io/slack) - Join the community's `#contributing-to-olake` channel for all the information regarding contributions. +- [GitHub Issues](https://github.com/datazip-inc/olake/issues) - Report bugs, request features, and claim tasks; browse issues by label to find work you’d like to tackle and comment to assign yourself. +- [GitHub Projects Board](https://github.com/orgs/datazip-inc/projects/5) - You can also refer to the projects section in GitHub to see who all are working on which issues. -```bash -git checkout -b -``` +## Orientation -To check your current branch, +Here's a list of repositories that contain OLake-related packages: -```bash -git branch -``` +- [datazip-inc/olake](https://github.com/datazip-inc/olake) is the main repository containing the core OLake replication engine and CLI tools +- [datazip-inc/olake-ui](https://github.com/datazip-inc/olake-frontend) contains the web-based user interface for managing OLake jobs and configurations +- [datazip-inc/olake-docs](https://github.com/datazip-inc/olake-docs) contains the documentation website and guides -To get back to an existing branch +## Types of Contributions -```bash -git checkout -``` +### Report Bug -or +The best way to report a bug is to file an issue on GitHub. Please include: -```bash -git switch -``` - -Read more about common git commands: [Beginners guide](https://blog.devgenius.io/git-bash-commands-d636bbc4282f) | [Advanced git guide](https://zriyansh.medium.com/advanced-git-commands-no-one-told-you-about-999bdde02fad) - -### Add, verify and push changes -#### 1. Add your changes -```bash -git add . -``` - -#### 2. Verify -Perform `git status` or `git diff` to see what all changes occured, which all files are ready to be committed (or staged) - -#### 3. Commit -After you are done with all the changes, perform - -```sh -git commit -am "write a descriptive message here about the nature of changes / addition of code" -``` +- Your operating system name and version +- OLake version +- Source and destination database details +- Detailed steps to reproduce the bug +- Any details about your local setup that might be helpful in troubleshooting :::info -We accept verified as well as un-verified Commits. You can verify your commits via signing gpg keys. Pull Requests with non-verified commits will be accepted as well. Follow [this guide by GitLab](https://docs.gitlab.com/user/project/repository/signed_commits/gpg/) to set up commit signing. +**Where to report issues:** +- **OLake CLI/Core issues**: Report in [datazip-inc/olake](https://github.com/datazip-inc/olake/issues) +- **OLake UI issues**: Report in [datazip-inc/olake-ui](https://github.com/datazip-inc/olake-frontend/issues) +- **Documentation issues**: Report in [datazip-inc/olake-docs](https://github.com/datazip-inc/olake-docs/issues) ::: -Next, take a pull (in case any new code has been added, this will help you avoid merge conflicts) by doing `git pull` and then `git push origin `. +### Submit Ideas or Feature Requests +The best way is to start a Discussion thread on GitHub: -#### Push Your Code ASAP -- Push your code as soon as you can. Follow the "early and often" rule. -- Make a pull request as soon as you can and mark the title with a "[WIP]". You can create a draft pull request. +Open a GitHub Discussion in the Ideas category to propose features or enhancements. Describe the desired behavior in detail, including examples where helpful. Keep the proposal tightly scoped so it’s easy to review and implement. This is a volunteer-driven project—well-defined, thoughtful ideas are always appreciated. -#### Describe Your Pull Request -- Use the format specified in pull request template for the repository. Populate the pull request descriptioncompletely for maximum verbosity. - - Tag the actual issue number by replacing #[issue_number] e.g. #42. This closes the issue when your PR is merged. - - Tag the actual issue author by replacing @[author] e.g. @issue_author. This brings the reporter of the issue into the conversation. - - Mark the tasks off your checklist by adding an x in the [ ] e.g. [x]. This checks off the boxes in your to-do list. The more boxes you check, the better. +### Fix Bugs -- Describe your change in detail. Too much detail is better than too little. -- Describe how you tested your change. -- Check the Preview tab to make sure the Markdown is correctly rendered and that all tags and references are linked. If not, go back and edit the Markdown. +Look through the GitHub issues. Issues tagged with `#bug` are open to whoever wants to implement them. -### Request Review -- Once your PR is ready, remove the "[WIP]" from the title and/or change it from a draft PR to a regular PR. -- If a specific reviewer is not assigned automatically, please request a review from the project maintainer. +### Implement Features -## Debugging OLake +Look through the GitHub issues. Issues tagged with `#feature` are open to whoever wants to implement them. -### Assumptions -1. You are using [VSCode](https://code.visualstudio.com/download) to run OLake locally. -2. You have cloned the project in a suitable directory. +### A good start will be to explore: +- [first good issues](https://github.com/datazip-inc/olake/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22) +- [beginner issues](https://github.com/datazip-inc/olake/issues?q=is%3Aissue%20state%3Aopen%20label%3Abeginner) -```bash -git clone git@github.com:datazip-inc/olake.git -``` +See the issues and if its looks interesting enough to you, comment below them and one of our maintainers will get in touch with you. -:::caution -Please read [getting started with OLake](../getting-started/quickstart) guide before proceeding to follow along this guide -::: +## Types of contributors +OLake recognizes two primary contributor roles, each with distinct responsibilities and privileges within the project: -## Steps to Debug -1. Make a directory `.vscode` (inside OLake project, at root location) if not already created. -2. Create a file named `launch.json` inside the `.vscode` directory and paste the beflow config. - - -```json title=".vscode/launch.json" -{ - "version": "0.2.0", - "configurations": [ - { - "name": "Launch Go Code", - "type": "go", - "request": "launch", - "program": "{PATH_TO_UPDATE}/drivers/mongodb/main.go", - "mode": "auto", - "args": [ - "sync", - "--config", - "{PATH_TO_UPDATE}/drivers/mongodb/examples/source.json", - "--catalog", - "{PATH_TO_UPDATE}/drivers/mongodb/examples/streams.json", - "--destination", - "{PATH_TO_UPDATE}/drivers/mongodb/examples/destination.json", - // "--state", - // "{PATH_TO_UPDATE}/drivers/mongodb/examples/state.json", - ] - } - ] -} -``` - -### Params: -| key | value(s) | -| --- | --- | -|`mode` | `auto`, `debug` | -| `args` | `sync` , `discover` | - -Update `PATH_TO_UPDATE` with the location where OLake project lives inside your system. For example: - -```json -"program": "/Users/john/Desktop/projects/olake/drivers/mongodb/main.go", -... - "--config", - "/Users/john/Desktop/projects/olake/drivers/mongodb/examples/source.json", -... -``` -Now, setup debug points in the codebase and click "Launch Go Code". - -![Debug](/img/docs/getting-started/debug.webp) - - - +### Project Maintainers -:::info -Refer [here](https://olake.io/docs/writers/iceberg/troubleshooting#vscode-debug-configuration) fir more details on how to debug OLake's Iceberg writer using VSCode. -::: +Project Maintainers guide OLake by sorting and prioritizing issues, reviewing and merging pull requests in all repositories, and coordinating releases. + +### Contributors + +Contributors include anyone who engages with OLake by submitting code, reporting bugs, writing documentation, proposing improvements, or helping on community channels. All contributions—large or small—are welcome. \ No newline at end of file diff --git a/docs/community/setting-up-a-dev-env.mdx b/docs/community/setting-up-a-dev-env.mdx index 40effa23..f6adbcb3 100644 --- a/docs/community/setting-up-a-dev-env.mdx +++ b/docs/community/setting-up-a-dev-env.mdx @@ -126,8 +126,45 @@ docker compose -f {file-location} up -d +**This setup is using the above provided docker compose for MongoDB.** + +```json title="source.json" +{ + "hosts": ["primary_mongo:27017"], + "username": "admin", + "password": "password", + "authdb": "admin", + "replica_set": "rs0", + "read_preference": "secondaryPreferred", + "srv": false, + "database": "reddit", + "max_threads": 5, + "backoff_retry_count": 4, + "chunking_strategy": "" +} +``` + + +**This setup is using the above provided docker compose for MySQL.** + +```json title="source.json" +{ + "hosts": "primary_mysql", + "username": "root", + "password": "password", + "database": "main", + "port": 3306, + "update_method": { + "initial_wait_time": 10 + }, + "tls_skip_verify": true, + "max_threads": 5, + "backoff_retry_count": 4 +} +``` + You can use your own Oracle configuration. The docker compose is **WIP**. From 9d53d2b6f84714ab81fe49f4d4768baadf5c1c58 Mon Sep 17 00:00:00 2001 From: Nayan Joshi Date: Tue, 16 Sep 2025 16:14:54 +0530 Subject: [PATCH 03/17] Checked and modified all the source connectors for the contribution section --- docs/community/setting-up-a-dev-env.mdx | 23 +++++++++++++++++------ docs/connectors/mongodb/setup/local.mdx | 2 +- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/docs/community/setting-up-a-dev-env.mdx b/docs/community/setting-up-a-dev-env.mdx index f6adbcb3..ca26eec6 100644 --- a/docs/community/setting-up-a-dev-env.mdx +++ b/docs/community/setting-up-a-dev-env.mdx @@ -104,8 +104,8 @@ docker compose -f {file-location} up -d **This setup is using the above provided docker compose for Postgres.** ```json title="source.json" { - "host": "primary_postgres", - "port": 5432, + "host": "localhost", + "port": 5431, "database": "main", "username": "main", "password": "password", @@ -118,7 +118,6 @@ docker compose -f {file-location} up -d "intial_wait_time": 120 }, "reader_batch_size": 1000, - "default_mode": "cdc", "max_threads": 10 } ``` @@ -130,7 +129,7 @@ docker compose -f {file-location} up -d ```json title="source.json" { - "hosts": ["primary_mongo:27017"], + "hosts": ["localhost:27017"], "username": "admin", "password": "password", "authdb": "admin", @@ -140,7 +139,7 @@ docker compose -f {file-location} up -d "database": "reddit", "max_threads": 5, "backoff_retry_count": 4, - "chunking_strategy": "" + "chunking_strategy": "splitVector" } ``` @@ -151,7 +150,7 @@ docker compose -f {file-location} up -d ```json title="source.json" { - "hosts": "primary_mysql", + "hosts": "localhost", "username": "root", "password": "password", "database": "main", @@ -174,6 +173,12 @@ You can use your own Kafka configuration. The docker compose is **WIP**. +:::note + +If using the OLake UI (running in Docker), replace `localhost` with `host.docker.internal` as the host. For the OLake CLI (running on your local machine), using `localhost` is correct. + +::: + ## Setup up Destination @@ -217,6 +222,12 @@ You can use your own Kafka configuration. The docker compose is **WIP**. To access S3-compatible MinIO, click on [`http://localhost:9001/login`](http://localhost:9001/login), with username = `admin` and password = `password`. +:::note + +If using the OLake UI (running in Docker), replace `localhost` with `host.docker.internal` as the host. For the OLake CLI (running on your local machine), using `localhost` is correct. + +::: + ## Commands :::info Below given commands are the mandatory commands required to run OLake. For more information, refer to this: [`OLake commands`](/docs/community/commands-and-flags) diff --git a/docs/connectors/mongodb/setup/local.mdx b/docs/connectors/mongodb/setup/local.mdx index 78f860d4..ecfb5a15 100644 --- a/docs/connectors/mongodb/setup/local.mdx +++ b/docs/connectors/mongodb/setup/local.mdx @@ -36,7 +36,7 @@ This compose file does the following: - **Custom Entry Points**: Uses custom entry points for the data loader to ensure it waits for the MongoDB instance to be ready before attempting to load data. :::info -The `host.docker.internal` is used to refer to the host machine from within the Docker container. This is useful for connecting to services running on the host from within a container. +The `host.docker.internal` is used to refer to the host machine from within the Docker container. This is useful for connecting to services running on the host from within a container. If running on the local machine then replace `host.docker.internal` with `localhost`. ::: ```yaml title="docker-compose.yml" From 9b1199bbd8b45126d15d0f15e19530500e1d7655 Mon Sep 17 00:00:00 2001 From: Nayan Joshi Date: Tue, 16 Sep 2025 21:02:17 +0530 Subject: [PATCH 04/17] Added all the necessary information for the cli commands, also mentioned the steps for how to raise a PR --- docs/community/contributing.mdx | 64 +++++++++- docs/community/issues-and-prs.mdx | 152 +++++++++++++----------- docs/community/setting-up-a-dev-env.mdx | 65 +++++++++- 3 files changed, 205 insertions(+), 76 deletions(-) diff --git a/docs/community/contributing.mdx b/docs/community/contributing.mdx index 86ca9493..382179f6 100644 --- a/docs/community/contributing.mdx +++ b/docs/community/contributing.mdx @@ -1,5 +1,5 @@ --- -title: How to contribute +title: Contribute to OLake description: Contributing to OLake guide sidebar_position: 1 --- @@ -24,9 +24,20 @@ Here's a list of repositories that contain OLake-related packages: - [datazip-inc/olake](https://github.com/datazip-inc/olake) is the main repository containing the core OLake replication engine and CLI tools - [datazip-inc/olake-ui](https://github.com/datazip-inc/olake-frontend) contains the web-based user interface for managing OLake jobs and configurations - [datazip-inc/olake-docs](https://github.com/datazip-inc/olake-docs) contains the documentation website and guides +- [datazip-inc/olake-helm](https://github.com/datazip-inc/olake-helm) contains official Helm charts for deploying OLake on Kubernetes. ## Types of Contributions +:::info + +Before making any significant changes and before filing a new issue, please check [existing open](https://github.com/datazip-inc/olake/issues?q=is%3Aopen+is%3Aissue), or [recently closed](https://github.com/datazip-inc/olake/issues?q=is%3Aissue+is%3Aclosed) issues to make sure somebody else hasn't already reported the issue. Please try to include as much information as you can. + +**Pointers-** +- If you find any **bugs** → please create an [**issue.**](https://github.com/datazip-inc/olake/issues/new?assignees=&labels=&template=bug_report.md&title=) +- If you want to build any **new feature** → please create an [issue with the label **`enhancement`**.](https://github.com/datazip-inc/olake/issues/new?template=new-feature.md) + +::: + ### Report Bug The best way to report a bug is to file an issue on GitHub. Please include: @@ -35,6 +46,7 @@ The best way to report a bug is to file an issue on GitHub. Please include: - OLake version - Source and destination database details - Detailed steps to reproduce the bug +- Any modifications you have made relevant to the bug - Any details about your local setup that might be helpful in troubleshooting :::info @@ -42,13 +54,20 @@ The best way to report a bug is to file an issue on GitHub. Please include: - **OLake CLI/Core issues**: Report in [datazip-inc/olake](https://github.com/datazip-inc/olake/issues) - **OLake UI issues**: Report in [datazip-inc/olake-ui](https://github.com/datazip-inc/olake-frontend/issues) - **Documentation issues**: Report in [datazip-inc/olake-docs](https://github.com/datazip-inc/olake-docs/issues) +- **OLake Helm issues**: Report in [datazip-inc/olake-helm](https://github.com/datazip-inc/olake-helm) ::: ### Submit Ideas or Feature Requests -The best way is to start a Discussion thread on GitHub: +The best way to start is by discussing your idea in the OLake Slack community first. This helps get early feedback, gauge interest, and refine your proposal with input form other community members. -Open a GitHub Discussion in the Ideas category to propose features or enhancements. Describe the desired behavior in detail, including examples where helpful. Keep the proposal tightly scoped so it’s easy to review and implement. This is a volunteer-driven project—well-defined, thoughtful ideas are always appreciated. +After the Slack discussion, open a GitHub Discussion in the Ideas category to propose features or enhancements. Describe the desired behavior in detail, including examples where helpful. Keep the proposal tightly scoped so it’s easy to review and implement. This is a volunteer-driven project—well-defined, thoughtful ideas are always appreciated. + +When submitting your proposal, consider including: + +- Requirement — what kind of use case are you trying to solve? +- Proposal — what do you suggest to solve the problem or improve the existing situation? +- Any open questions to address❓ ### Fix Bugs @@ -58,6 +77,14 @@ Look through the GitHub issues. Issues tagged with `#bug` are open to whoever wa Look through the GitHub issues. Issues tagged with `#feature` are open to whoever wants to implement them. +### Complete Tasks + +Look through the GitHub issues. Issues tagged with `#task` are open to whoever wants to work on them. + +### Reporting Security Issues + +Please do not create a public GitHub issue. If you've found a security issue, please email us directly at [hello@olake.io](mailto:hello@olake.io) instead of raising an issue. + ### A good start will be to explore: - [first good issues](https://github.com/datazip-inc/olake/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22) - [beginner issues](https://github.com/datazip-inc/olake/issues?q=is%3Aissue%20state%3Aopen%20label%3Abeginner) @@ -74,4 +101,33 @@ Project Maintainers guide OLake by sorting and prioritizing issues, reviewing an ### Contributors -Contributors include anyone who engages with OLake by submitting code, reporting bugs, writing documentation, proposing improvements, or helping on community channels. All contributions—large or small—are welcome. \ No newline at end of file +Contributors include anyone who engages with OLake by submitting code, reporting bugs, writing documentation, proposing improvements, or helping on community channels. All contributions—large or small—are welcome. + +## Goodies + +We know how much you love swags and stickers, for each PR merged (again, not typo fixes or documentation fixes) we will send over a Tshirt and stickers to you. Take a sneak peak below. + +**Note:** PR qualifying for goodies is subject to value it adds and the OLake team approval. + +
+ {/* On mobile: single column; on md and above: three columns */} +
+ Shirt Front + Shirt Back + Stickers +
+
+ +Thank You! \ No newline at end of file diff --git a/docs/community/issues-and-prs.mdx b/docs/community/issues-and-prs.mdx index 06faf8df..500a6102 100644 --- a/docs/community/issues-and-prs.mdx +++ b/docs/community/issues-and-prs.mdx @@ -1,46 +1,19 @@ --- -title: Issues and PR +title: How to Raise a PR description: Issues and Pull Request Guidelines sidebar_position: 2 --- -# Issues and Pull Request Guidelines - -Hi there! We're thrilled that you'd like to contribute to this project, thank you for your interest. Whether it's a bug report, new feature, correction, or additional documentation, we greatly value feedback and contributions from our community. - -Please read through this document before submitting any issues or pull requests to ensure we have all the necessary information to effectively respond to your bug report or contribution. - -- We accept contributions made to the OLake [staging branch](https://github.com/datazip-inc/olake/tree/staging) - ## General Guidelines -Before making any significant changes and before filing a new issue, please check [existing open](https://github.com/datazip-inc/olake/issues?q=is%3Aopen+is%3Aissue), or [recently closed](https://github.com/datazip-inc/olake/issues?q=is%3Aissue+is%3Aclosed) issues to make sure somebody else hasn't already reported the issue. Please try to include as much information as you can. - -Details like these are incredibly useful: -- Requirement - what kind of use case are you trying to solve? -- Proposal - what do you suggest to solve the problem or improve the existing situation? -- Any open questions to address❓ - -If you are reporting a bug, details like these are incredibly useful: -- A reproducible test case or series of steps. -- The version of our code being used. -- Any modifications you've made relevant to the bug🐞. -- Anything unusual about your environment or deployment. -- Discussing your proposed changes ahead of time will make the contribution process smooth for everyone 🙌. - -## Issues - -### Report a Bug - -Bug reports help us make OLake better for everyone. We provide a preconfigured template for reporting bugs to make it very clear what information we need. To avoid duplicate reports, check if the bug was [already reported](https://github.com/datazip-inc/olake/issues?q=is%3Aissue%20is%3Aopen%20label%3Abug) before raising a new one. - -### Request new Features or a Connector +Please read through this document before submitting any issues or pull requests to ensure we have all the necessary information to effectively respond to your bug report or contribution. -Requesting new features is an essential way to contribute. Your input helps us understand your needs and priorities, enabling us to enhance the functionality and versatility of OLake. [Starting a Github Discussion](https://github.com/datazip-inc/olake/discussions) is the best way to do it. -### Reporting Security Issues +:::warning -Please do not create a public GitHub issue. If you've found a security issue, please email us directly at [hello@olake.io](mailto:hello@olake.io) instead of raising an issue. +- All contributions must target the OLake [staging branch](https://github.com/datazip-inc/olake/tree/staging). Create feature branches from staging and open PRs against staging. +- Your pull request must be accompanied by a signed Contributor License Agreement (CLA) corresponding to the OLake component (CLI, UI, Helm, or Docs) you are contributing to; PRs without a signed CLA will not be accepted. +::: ## Pull Request (PR) guidelines @@ -81,14 +54,87 @@ GitHub provides additional document on [forking a repository](https://help.githu You can always reach out to `hello@olake.io` to understand more about the repo and product. We are very responsive over email and [slack community](https://olake.io/slack). -### Pointers: -- If you find any **bugs** → please create an [**issue.**](https://github.com/datazip-inc/olake/issues/new?assignees=&labels=&template=bug_report.md&title=) -- If you find anything **missing** in documentation → you can create an issue with the label **`documentation`**. -- If you want to build any **new feature** → please create an [issue with the label **`enhancement`**.](https://github.com/datazip-inc/olake/issues/new?template=new-feature.md) -- If you want to **discuss** something about the product, start a new [**discussion**.](https://github.com/datazip-inc/olake/discussions) +## Steps to raise a PR + +### 1. Clone the repo + +```bash +git clone git@github.com:datazip-inc/olake.git +``` + +### 2. Create a Pull Request + +#### Make A Branch +- Please create a separate branch for each issue that you're working on. Do not make changes to the default branch (e.g. master, staging) of your fork. + +```bash +git checkout -b +``` + +To check your current branch, + +```bash +git branch +``` + +To get back to an existing branch + +```bash +git checkout +``` + +or + +```bash +git switch +``` + +### 3. Add, verify and push changes + +#### 1. Add your changes + +```bash +git add . +``` + +#### 2. verify + +Perform `git status` or `git diff` to see what all changes occured, which all files are ready to be committed (or staged) + +#### 3. Commit your changes + +After you are done with all the changes, perform + +```bash +git commit -am "write a descriptive message here about the nature of changes / addition of code" +``` + +:::note + +We accept verified as well as un-verified Commits. You can verify your commits via signing gpg keys. Pull Requests with non-verified commits will be accepted as well. Follow [this guide by GitLab](https://docs.gitlab.com/user/project/repository/signed_commits/gpg/) to set up commit signing. + +::: + +Next, take a pull (in case any new code has been added, this will help you avoid merge conflicts) by doing `git pull` and then `git push origin `. + +**Push Your Code ASAP** +- Push your code as soon as you can. Follow the "early and often" rule. +- Make a pull request as soon as you can and mark the title with a "[WIP]". You can create a draft pull request. + +**Describe Your Pull Request** +- Use the format specified in pull request template for the repository. Populate the pull request descriptioncompletely for maximum verbosity. + - Tag the actual issue number by replacing #[issue_number] e.g. #42. This closes the issue when your PR is merged. + - Tag the actual issue author by replacing @[author] e.g. @issue_author. This brings the reporter of the issue into the conversation. + - Mark the tasks off your checklist by adding an x in the [ ] e.g. [x]. This checks off the boxes in your to-do list. The more boxes you check, the better. +- Describe your change in detail. Too much detail is better than too little. +- Describe how you tested your change. +- Check the Preview tab to make sure the Markdown is correctly rendered and that all tags and references are linked. If not, go back and edit the Markdown. +### Request Review +- Once your PR is ready, remove the "[WIP]" from the title and/or change it from a draft PR to a regular PR. +- If a specific reviewer is not assigned automatically, please request a review from the project maintainer. -### Conventions to follow when submitting Commits and Pull Request(s). +## Conventions to follow when submitting Commits and Pull Request(s). We try to follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/), more specifically the commits and PRs **should have type specifiers** prefixed in the name. [This](https://www.conventionalcommits.org/en/v1.0.0/#specification) should give you a better idea. @@ -109,31 +155,3 @@ There are many other ways to get involved with the community and to participate Again, Feel free to ping us on `#contributing-to-olake` on our slack community if you need any help on this :) -## Goodies - -We know how much you love swags and stickers, for each PR merged (again, not typo fixes or documentation fixes) we will send over a Tshirt and stickers to you. Take a sneak peak below. - -**Note:** PR qualifying for goodies is subject to value it adds and the OLake team approval. - -
- {/* On mobile: single column; on md and above: three columns */} -
- Shirt Front - Shirt Back - Stickers -
-
- -Thank You! \ No newline at end of file diff --git a/docs/community/setting-up-a-dev-env.mdx b/docs/community/setting-up-a-dev-env.mdx index ca26eec6..387cc844 100644 --- a/docs/community/setting-up-a-dev-env.mdx +++ b/docs/community/setting-up-a-dev-env.mdx @@ -11,6 +11,13 @@ The documentation in this section is a bit of knowlegde required to run OLake fo Now we have evolved to recommend and support docker compose more actively as the main way to run OLake for development and preserve your sanity. Most people should stick to the first few sections - ("Fork & Clone", "docker compose" and "Installing Dev Tools") ::: +**Pre-requisites:** + +Before setting up and running OLake, ensure you have the following installed on your system: +- Java (OpenJDK 11 or later recommended) +- Go (Golang) (version 1.18 or later recommended) +- Node.js (for Chalk and UI tooling; version 16.x LTS or later recommended) + ## Fork and Clone First, [fork the repository on GitHub](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/about-forks), then clone it. @@ -33,14 +40,20 @@ git clone git@github.com:datazip-inc/olake-ui.git git clone git@github.com:datazip-inc/olake-helm.git ``` -## docker compose +## Docker Compose In order to test you code, you must have your source and destination configured. As of now, OLake offers 5 sources whose local setup via docker compose is provided below: - #### Postgres - **[docker compose](/docs/connectors/postgres/setup/local)** + Runs Postgres 15 configured for CDC (logical replication with a replication slot) and loads a small sample table for quick verification. + - #### MongoDB - **[docker compose](/docs/connectors/mongodb/setup/local)** + Starts a single-node replica set with keyfile authentication and imports sample Reddit data for local development and testing. + - #### MySQL - **[docker compose](/docs/connectors/mysql/setup/local)** + Launches MySQL 8.0 with binlog-based CDC enabled, initializes a replication user, and loads sample data with a binlog checker. + - #### Oracle (docker compose WIP) - #### Kafka (docker compose WIP) @@ -97,7 +110,16 @@ Paste this above docker compose in a file and run: docker compose -f {file-location} up -d ``` -## Setup up Source +## Setup Up + +:::note + +Make sure both the `source.json` and the `destination.json` are present in the same working directory. +For instance, you can create a folder and place both the files there. + +::: + +### 1. Source @@ -179,7 +201,7 @@ If using the OLake UI (running in Docker), replace `localhost` with `host.docker ::: -## Setup up Destination +### 2. Destination **This setup is using the above provided docker compose for Iceberg.** @@ -230,7 +252,8 @@ If using the OLake UI (running in Docker), replace `localhost` with `host.docker ## Commands :::info -Below given commands are the mandatory commands required to run OLake. For more information, refer to this: [`OLake commands`](/docs/community/commands-and-flags) +- Below given commands are the mandatory commands required to run OLake. For more information, refer to this: [`OLake commands`](/docs/community/commands-and-flags) +- To run OLake CLI commands mentioned below, you must be inside the root directory of the olake repository which we get after cloning `datazip-inc/olake.git` . ::: - ### Discover @@ -240,19 +263,51 @@ Below given commands are the mandatory commands required to run OLake. For more ``` This will generate streams.json, which will have all the streams of the source. + After streams.json is created, you can modify it to select which streams to sync, enable or disable normalization, and configure partitioning. This lets you customize sync behavior before running sync. - ### Sync After doing required changes in the streams, sync command has to be run. + + This generates two files: + + - state.json: stores the persisted sync state + - stats.json: contains sync statistics + ```bash ./build.sh driver-{source-name} sync --config {source.json path} --catalog {stream.json path} --destination {destination.json path} ``` :::note This above command will generate state.json, having the persisted state of the source and stats.json, having the statistics of the sync. ::: - If want to run sync with state, run: + If want to run sync with state (i.e. with CDC enabled), run: ```bash ./build.sh driver-{source-name} sync --config {source.json path} --catalog {stream.json path} --destination {destination.json path} --state {state.json path} ``` + ### Query Data after the Sync + + After running the sync command, you can query your data using the Spark Iceberg service available at `localhost:8888`. + + For example, run the following SQL commands to explore your synced data: + + ```json title="sql" + %%sql + SHOW DATABASES; + + SELECT * FROM {destination_database_name}.{table_name}; + ``` + + ### Sync with State enabled + + When running sync with state (CDC) mode enabled, you can verify Change Data Capture functionality by: + 1. Inserting records in the source database and seeing them appear in the destination table. + 2. Updating records in the source and observing corresponding updates downstream. + 3. Deleting records in the source and confirming deletions reflect in the destination. + + When CDC is enabled, the destination table will have an additional column op_type indicating operation type: + - `"r"` for inserted records + - `"u"` for updated records + - `"d"` for deletions (with other row columns as NULL) + ## Debug Mode If you don't want to run the sync commands after every change, we have this debugger mode for Go side of code. :::caution From 611d640ec9d3d6dfb8f50717ddfb98b85babefc0 Mon Sep 17 00:00:00 2001 From: Nayan Joshi Date: Wed, 17 Sep 2025 11:40:27 +0530 Subject: [PATCH 05/17] Fixed some grammatical errors in the document --- docs/community/setting-up-a-dev-env.mdx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/community/setting-up-a-dev-env.mdx b/docs/community/setting-up-a-dev-env.mdx index 387cc844..90641f59 100644 --- a/docs/community/setting-up-a-dev-env.mdx +++ b/docs/community/setting-up-a-dev-env.mdx @@ -16,12 +16,12 @@ Now we have evolved to recommend and support docker compose more actively as the Before setting up and running OLake, ensure you have the following installed on your system: - Java (OpenJDK 11 or later recommended) - Go (Golang) (version 1.18 or later recommended) -- Node.js (for Chalk and UI tooling; version 16.x LTS or later recommended) +- Node.js (for Chalk; version 16.x LTS or later recommended) ## Fork and Clone First, [fork the repository on GitHub](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/about-forks), then clone it. -Second, you can clone the main repository directly, and raise pull required accordingly. +Second, you can clone the main repository directly, and raise pull request accordingly. There are basically 3 repositories in which you can contribute to. @@ -42,7 +42,7 @@ git clone git@github.com:datazip-inc/olake-helm.git ## Docker Compose -In order to test you code, you must have your source and destination configured. +In order to test your code, you must have your source and destination configured. As of now, OLake offers 5 sources whose local setup via docker compose is provided below: - #### Postgres - **[docker compose](/docs/connectors/postgres/setup/local)** @@ -304,7 +304,7 @@ If using the OLake UI (running in Docker), replace `localhost` with `host.docker 3. Deleting records in the source and confirming deletions reflect in the destination. When CDC is enabled, the destination table will have an additional column op_type indicating operation type: - - `"r"` for inserted records + - `"c"` for inserted records - `"u"` for updated records - `"d"` for deletions (with other row columns as NULL) From d3fa71c89d400cb3d022f7e42634d85f7e4329f1 Mon Sep 17 00:00:00 2001 From: Duke Dhal Date: Wed, 17 Sep 2025 19:19:31 +0530 Subject: [PATCH 06/17] fix: docker --- docs/community/setting-up-a-dev-env.mdx | 425 +++++++++++++++++++++--- 1 file changed, 380 insertions(+), 45 deletions(-) diff --git a/docs/community/setting-up-a-dev-env.mdx b/docs/community/setting-up-a-dev-env.mdx index ca26eec6..725e2551 100644 --- a/docs/community/setting-up-a-dev-env.mdx +++ b/docs/community/setting-up-a-dev-env.mdx @@ -33,69 +33,404 @@ git clone git@github.com:datazip-inc/olake-ui.git git clone git@github.com:datazip-inc/olake-helm.git ``` -## docker compose +## Docker Compose -In order to test you code, you must have your source and destination configured. +In order to test your code, you must have your source and destination setup and configured. -As of now, OLake offers 5 sources whose local setup via docker compose is provided below: -- #### Postgres - **[docker compose](/docs/connectors/postgres/setup/local)** -- #### MongoDB - **[docker compose](/docs/connectors/mongodb/setup/local)** -- #### MySQL - **[docker compose](/docs/connectors/mysql/setup/local)** -- #### Oracle (docker compose WIP) -- #### Kafka (docker compose WIP) +To get this done, run the given below docker compose files for source and destination in your local machine. -OLake destination docker compose: -- #### Iceberg (using JDBC catalog) - [docker compose](https://github.com/datazip-inc/olake/blob/master/destination/iceberg/local-test/docker-compose.yml) -- #### Parquet docker compose +### Source docker compose ```yml title="docker-compose.yml" -version: '3.1' - services: - minio: - image: minio/minio:RELEASE.2025-04-03T14-56-28Z - container_name: minio + # ------------------------- + # Postgres (primary + loader) + # ------------------------- + primary_postgres: + container_name: primary_postgres # Set an explicit name for the container. + image: postgres:15 # Use the official Postgres version 15 image. + profiles: ["postgres"] + hostname: primary_postgres # Define the hostname within the network. + ports: + - "5431:5432" # Map port 5432 inside the container to 5431 on the host. + environment: + POSTGRES_USER: main # Set the default Postgres username. + POSTGRES_PASSWORD: password # Set the password for the Postgres user. + POSTGRES_DB: main # Create a default database named "main". + # Custom command to install the wal2json plugin for logical decoding and then start Postgres. + command: > + bash -c "apt-get update && apt-get install -y postgresql-15-wal2json && exec docker-entrypoint.sh postgres -c wal_level=logical -c max_wal_senders=10 -c max_replication_slots=10 -c wal_sender_timeout=0" + # Explanation of the command: + # 1. Update the package list. + # 2. Install the postgresql-15-wal2json package, which is used to convert WAL (Write-Ahead Logging) + # into JSON format for logical replication. + # 3. Use exec to run the default docker-entrypoint script provided by the Postgres image. + # 4. Start Postgres with additional configuration parameters: + # - wal_level=logical: Enable logical replication. + # - max_wal_senders=10: Allow up to 10 concurrent WAL sender processes. + # - max_replication_slots=10: Allow up to 10 replication slots for logical decoding. + # - wal_sender_timeout=0: Disable sender timeout so long-running snapshots do not drop the connection. + volumes: + - pg-data:/var/lib/postgresql/data # Use a named volume (pg-data) to persist Postgres data. + networks: + - pg-cluster # Connect the container to the custom network "pg-cluster". + healthcheck: + test: ["CMD", "pg_isready", "-U", "main", "-d", "main"] # Healthcheck command to check if Postgres is ready. + interval: 10s # Run the healthcheck every 10 seconds. + timeout: 5s # Set a timeout of 5 seconds for the healthcheck command. + retries: 10 # Retry the healthcheck up to 10 times before declaring unhealthy. + + # Data loader service to perform operations against the Postgres database. + pg-data-loader: + image: postgres:15 # Use the same version of Postgres to ensure compatible client tools. + container_name: pg-data-loader # Set a custom name for clarity. + profiles: ["postgres"] environment: - - MINIO_ROOT_USER=admin - - MINIO_ROOT_PASSWORD=password - - MINIO_DOMAIN=minio + PGUSER: main # Set the default Postgres user for the client. + PGPASSWORD: password # Set the password for the Postgres user. + PGDATABASE: main # Connect to the "main" database. + depends_on: + primary_postgres: + condition: service_healthy # Wait until the primary_postgres service passes its health check. + entrypoint: > + bash -c " + echo \"Waiting for Postgres to be ready...\"; + # Poll until Postgres is reachable. + until pg_isready -h primary_postgres -p 5432 -U main -d main; do + echo \"Waiting...\"; + sleep 2; + done; + echo \"Creating test table sample_data...\"; + # Execute a SQL command to create a table if it doesn't exist. + psql -h primary_postgres -U main -d main -c \"CREATE TABLE IF NOT EXISTS sample_data (id SERIAL PRIMARY KEY, str_col TEXT, num_col INT);\"; + echo \"Inserting one test row...\"; + # Insert a sample row into the table. + psql -h primary_postgres -U main -d main -c \"INSERT INTO sample_data (str_col, num_col) VALUES ('Hello world', 123);\"; + echo \"Creating logical replication slot...\"; + # Create a logical replication slot using the wal2json output plugin. + psql -h primary_postgres -U main -d main -c \"SELECT * FROM pg_create_logical_replication_slot('postgres_slot', 'wal2json');\"; + echo \"Done. Data and replication slot should now exist.\" + " + # Explanation of the entrypoint: + # - Wait until Postgres is ready to accept connections using pg_isready. + # - Create a table named "sample_data" with three columns: id, str_col, and num_col. + # - Insert a sample row into the table. + # - Create a logical replication slot named "postgres_slot" using the wal2json plugin. + restart: "no" # Do not automatically restart the container after completion. + networks: + - pg-cluster # Connect to the same custom network "pg-cluster". + + # ------------------------- + # MongoDB (init keyfile + primary + loader) + # ------------------------- + init-keyfile: + image: mongo:8.0 # Use MongoDB version 8.0 as the base image. + container_name: init_keyfile # Explicit container name for easier identification. + profiles: ["mongo"] + command: > # Execute a shell command on container startup. + sh -c " + # Check if the keyfile does not already exist. + if [ ! -f /etc/mongodb/pki/keyfile ]; then + echo 'Generating keyfile...'; + # Generate a random keyfile using OpenSSL with base64 encoding and save it to the expected location. + # Then set the file permission to read-only (400) for security. + openssl rand -base64 756 > /etc/mongodb/pki/keyfile && chmod 400 /etc/mongodb/pki/keyfile; + else + # If the keyfile already exists, output a message. + echo 'Keyfile already exists.'; + fi + " + volumes: + - mongo-keyfile-vol:/etc/mongodb/pki # Mount the volume that stores the keyfile. networks: - default: - aliases: - - warehouse.minio + - mongo-cluster # Connect this container to the defined mongo-cluster network. + restart: "no" # This container should not restart automatically. + + # Primary MongoDB container that sets up a replica set and creates an admin user. + primary_mongo: + container_name: primary_mongo # Set an explicit name for the primary MongoDB container. + image: mongo:8.0 # Use MongoDB version 8.0 as the container image. + profiles: ["mongo"] + hostname: primary_mongo # Set the hostname within the container network. ports: - - "9001:9001" - - "9000:9000" + - "27017:27017" # Expose port 27017 for MongoDB connections (host:container mapping). + depends_on: + - init-keyfile # Ensure the keyfile initialization service runs before this service. volumes: - - ./data/minio-data:/data - command: [ "server", "/data", "--console-address", ":9001" ] + - mongo-keyfile-vol:/etc/mongodb/pki # Mount the volume to share the generated keyfile. + command: | # Execute a series of shell commands using a multi-line script. + bash -c ' + echo "Waiting for keyfile..." + # Wait until the keyfile is available before proceeding. + while [ ! -f /etc/mongodb/pki/keyfile ]; do sleep 1; done + + echo "Keyfile found, starting mongod without authentication first..." + # Start MongoDB in the background with replication enabled without initially requiring authentication. + mongod --replSet rs0 --bind_ip_all --port 27017 & + + # Store the process ID of the started mongod instance. + MONGO_PID=$! + + echo "Waiting for MongoDB to start..." + # Poll the MongoDB process until it is ready to accept connections. + until mongosh --port 27017 --eval "db.runCommand({ ping: 1 })" >/dev/null 2>&1; do + sleep 2 + done + + echo "Initializing replica set..." + # Initialize the replica set with a single member and set its host address. + # Note: host.docker.internal provides a host network reference. + mongosh --port 27017 --eval "rs.initiate({_id: \"rs0\", members: [{_id: 0, host: \"host.docker.internal:27017\"}]})" + + echo "Waiting for replica set to initialize..." + # Allow some time for the replica set configuration to propagate. + sleep 5 + + echo "Creating admin user..." + # Create an admin user with root privileges in the admin database. + mongosh --port 27017 --eval " + db = db.getSiblingDB(\"admin\"); + db.createUser({ + user: \"admin\", + pwd: \"password\", + roles: [{ role: \"root\", db: \"admin\" }] + }); + " + + echo "Stopping MongoDB to restart with authentication..." + # Stop the previously started MongoDB instance by killing its process. + kill $MONGO_PID + wait $MONGO_PID + + echo "Starting MongoDB with authentication..." + # Restart MongoDB ensuring that authentication is enabled by providing the keyfile. + exec mongod --replSet rs0 --bind_ip_all --port 27017 --keyFile /etc/mongodb/pki/keyfile + ' + healthcheck: + test: ["CMD", "mongosh", "--port", "27017", "--eval", "db.adminCommand('ping')"] # Healthcheck command to verify MongoDB is reachable. + interval: 10s # Check health status every 10 seconds. + timeout: 10s # Timeout if no response is received within 10 seconds. + retries: 10 # Attempt up to 10 retries before marking the container as unhealthy. + networks: + - mongo-cluster # Connect this container to the mongo-cluster network. - mc: + # Data loader service that imports sample Reddit JSON data into the MongoDB. + mongo_data-loader: + image: mongo:8.0 # Use MongoDB image to leverage mongoimport tool. + container_name: mongo_data-loader # Explicit container name for clarity. + profiles: ["mongo"] depends_on: - - minio - image: minio/mc:RELEASE.2025-04-03T17-07-56Z - container_name: minio-mc + primary_mongo: + condition: service_healthy # Wait for the primary MongoDB service to be healthy before starting. + entrypoint: | # Custom entrypoint script to run the data loading commands. + bash -c ' + echo "Waiting for MongoDB admin user to be ready..." + # Keep checking until the MongoDB admin user is available and accepting connections. + until mongosh --host primary_mongo --username "admin" --password "password" --authenticationDatabase admin --eval "db.runCommand({ ping: 1 })" >/dev/null 2>&1; do + echo "Waiting for admin authentication to be ready..." + sleep 2 + done + + # Update package lists and install additional utilities (curl, wget, jq) needed for fetching and processing data. + apt-get update && apt-get install -y curl wget jq + + echo "Downloading Sample Reddit data..." + # Download sample Reddit data in JSON format from a remote GitHub repository into a temporary file. + curl -s "https://raw.githubusercontent.com/datazip-inc/olake-docs/refs/heads/master/static/reddit.json" >> /tmp/reddit.json + + echo "Importing Sample Reddit data into the reddit database, funny collection..." + # Use mongoimport to load the JSON data into the MongoDB database named "reddit" and collection named "funny". + mongoimport --host primary_mongo --username "admin" --password "password" --authenticationDatabase admin --db reddit --collection funny --file /tmp/reddit.json --jsonArray + + echo "Sample Reddit data import complete!" + ' + restart: "no" # The container will not restart automatically after the data is loaded. networks: - - default + - mongo-cluster # Connect to the mongo-cluster network. + + # ------------------------- + # MySQL (primary + init user + loaders) + # ------------------------- + primary_mysql: + container_name: primary_mysql # Name the container "primary_mysql" for easy reference. + image: mysql:8.0 # Use the MySQL 8.0 image. + profiles: ["mysql"] + hostname: primary_mysql # Set the container hostname to "primary_mysql". + ports: + - "3306:3306" # Expose port 3306 on both host and container. environment: - - AWS_ACCESS_KEY_ID=admin - - AWS_SECRET_ACCESS_KEY=password - - AWS_REGION=us-east-1 - entrypoint: | - /bin/sh -c " - until (/usr/bin/mc config host add minio http://minio:9000 admin password) do echo '...waiting...' && sleep 1; done; - if ! /usr/bin/mc ls minio/warehouse > /dev/null 2>&1; then - /usr/bin/mc mb minio/warehouse; - /usr/bin/mc policy set public minio/warehouse; - fi; - tail -f /dev/null - " + MYSQL_ROOT_PASSWORD: password # Root password for MySQL. + MYSQL_DATABASE: main # Create a default database named "main" at startup. + # Enable Change Data Capture (CDC) by setting necessary MySQL replication options. + command: + - "--server-id=1" # Set a unique server identifier for replication. + - "--log-bin=mysql-bin" # Enable binary logging (needed for replication and CDC). + - "--binlog-format=ROW" # Use ROW format to record every change in each row. + - "--local-infile=1" # Enable local data loading for importing files. + - "--binlog_expire_logs_seconds=604800" # Set binary log expiration to 7 days (604800 seconds). + - "--skip-host-cache" # Disable host cache for DNS resolution. + - "--skip-name-resolve" # Disable DNS host name resolution to improve performance. + + volumes: + - mysql-data:/var/lib/mysql # Mount persistent storage volume for MySQL data. + networks: + - mysql-cluster # Connect the container to the custom network "mysql-cluster". + healthcheck: + test: [ + "CMD", + "mysqladmin", + "ping", + "-h", + "localhost", + "-u", + "root", + "-ppassword", + ] # Healthcheck command to ensure MySQL is responsive. + interval: 10s # Check health every 10 seconds. + timeout: 5s # Each health check attempt must complete within 5 seconds. + retries: 10 # Allow up to 10 retries before marking the container as unhealthy. + + # Service to initialize the replication (CDC) user. + init-cdc-user: + image: mysql:8.0 # Use the same MySQL 8.0 image for consistency. + container_name: init_cdc_user # Name the container "init_cdc_user" for identification. + profiles: ["mysql"] + depends_on: + - primary_mysql # Ensure the primary MySQL service starts before creating the user. + entrypoint: > + bash -c "echo 'Creating replication user...'; + sleep 10; # Wait for MySQL to finish initial setup. + mysql -h primary_mysql -P 3306 -u root -ppassword -e \" + CREATE USER IF NOT EXISTS 'cdc_user'@'%' IDENTIFIED BY 'cdc_password'; + GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'cdc_user'@'%'; + FLUSH PRIVILEGES;\"; + echo 'Setting global binlog_row_metadata to FULL...'; + mysql -h primary_mysql -P 3306 -u root -ppassword -e \"SET GLOBAL binlog_row_metadata = 'FULL';\"; + echo 'Replication user created and global binlog_row_metadata set.'" + networks: + - mysql-cluster # Connect to the same MySQL network. + restart: "no" # Do not restart this container automatically. + + # Service to load sample data into the main database. + mysql-data-loader: + image: ubuntu:20.04 # Use Ubuntu 20.04 for running the data loading commands. + container_name: mysql-data-loader # Name the container for clarity. + profiles: ["mysql"] + depends_on: + primary_mysql: + condition: service_healthy # Wait until the primary MySQL container passes its healthcheck. + entrypoint: > + bash -c "apt-get update -qq && apt-get install -y mysql-client && \ + echo 'Waiting for MySQL to be ready...'; \ + until mysqladmin ping -h primary_mysql -P 3306 -u root -ppassword; do echo 'Waiting...'; sleep 2; done; \ + echo 'Creating table sample_table...'; \ + mysql -h primary_mysql -P 3306 -u root -ppassword main -e 'CREATE TABLE IF NOT EXISTS sample_table (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255));'; \ + echo 'Inserting sample data...'; \ + mysql -h primary_mysql -P 3306 -u root -ppassword main -e 'INSERT INTO sample_table (name) VALUES (\"sample_data\");'; \ + echo 'Data insertion complete!'; \ + tail -f /dev/null" + + restart: "no" # Do not restart this container after execution. + networks: + - mysql-cluster # Use the same network for connectivity. + + # Service to check for the existence and functionality of MySQL binary logs. + binlog-checker: + image: ubuntu:20.04 # Use Ubuntu 20.04 to run the binary log checking commands. + container_name: binlog_checker # Name this container for identification. + profiles: ["mysql"] + depends_on: + - primary_mysql # Ensure primary MySQL is running before checking binlogs. + entrypoint: > + bash -c "export DEBIAN_FRONTEND=noninteractive && \ + apt-get update -qq && \ + apt-get install -y mysql-server-core-8.0 && \ + # Terminate any automatically started mysqld process in this container (if present). + pkill mysqld || true && \ + sleep 10 && \ + echo 'Listing MySQL binaries in /usr/bin:' && ls -l /usr/bin/mysql* && \ + echo 'Attempting to run mysqlbinlog:' && \ + # Attempt to execute mysqlbinlog command on the first binary log file. + /usr/bin/mysqlbinlog /var/lib/mysql/mysql-bin.000001 || echo 'mysqlbinlog not found!'" # If mysqlbinlog is unavailable, print a message. + volumes: + - mysql-data:/var/lib/mysql # Mount the MySQL data volume to access binary logs. + networks: + - mysql-cluster # Connect to the same custom network. + restart: "no" # Do not restart this container automatically. + + +# ------------------------- +# Networks +# ------------------------- +networks: + pg-cluster: + mongo-cluster: + mysql-cluster: + +# ------------------------- +# Volumes (persistent storage) +# ------------------------- +volumes: + pg-data: + mongo-keyfile-vol: + mysql-data: ``` -Paste this above docker compose in a file and run: +### To start the source containers on your machine: +- **Postgres:** + ```bash + docker compose -f {docker compose file path} --profile postgres up -d + ``` +- **MongoDB:** + ```bash + docker compose -f {docker compose file path} --profile mongo up -d + ``` +- **MySQL:** + ```bash + docker compose -f {docker compose file path} --profile mysql up -d + ``` + +:::tip +- Replace `{docker compose file path}` with the file path of your docker compose. +- To start all the source containers together: ```bash -docker compose -f {file-location} up -d +docker compose -f {docker compose file path} --profile postgres --profile mongo --profile mysql up -d ``` +::: + + +:::info +In the above docker-compose.yml file, containers for sources are provided, which also includes the data-loading in respective source. \ +*For sources Oracle and Kafka, docker-compose is currently work in progress.* + +For more information, on individual source setup please follow the links given below: +- **[Postgres](/docs/connectors/postgres/)** +- **[MongoDB](/docs/connectors/mongodb/)** +- **[MySQL](/docs/connectors/mysql/)** +- **[Oracle](/docs/connectors/oracle/)** +- **[Kafka](/docs/connectors/kafka/)** +::: + +### Destination docker compose + +For both Iceberg and S3 Parquet can be used by spinning up the below docker compose file. +- **[Docker compose for using Iceberg and S3 Parquet as Destination](https://github.com/datazip-inc/olake/blob/master/destination/iceberg/local-test/docker-compose.yml)** + +### To start the destination containers on your machine: +- **Iceberg:** + ```bash + docker compose -f {docker compose file path} --profile iceberg up -d + ``` +- **S3 Parquet:** + ```bash + docker compose -f {docker compose file path} up -d + ``` + +:::info +For more information, on individual destination setup please follow the links given below: +- **[Iceberg](/docs/writers/iceberg/catalog/rest)** +- **[S3 Parquet](/docs/writers/parquet/config)** +::: ## Setup up Source From 43a28f855ec30b86fa5b205d3f1f766e4b4d13f0 Mon Sep 17 00:00:00 2001 From: Nayan Joshi Date: Wed, 17 Sep 2025 21:22:24 +0530 Subject: [PATCH 07/17] Final changes done to the contribution page based on new source and destination docker compose files --- docs/community/setting-up-a-dev-env.mdx | 33 +++++++++++---------- src/theme/DocPaginator/footerNavigations.js | 28 ++++++++++++----- 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/docs/community/setting-up-a-dev-env.mdx b/docs/community/setting-up-a-dev-env.mdx index c6899afa..0ffb095d 100644 --- a/docs/community/setting-up-a-dev-env.mdx +++ b/docs/community/setting-up-a-dev-env.mdx @@ -407,7 +407,7 @@ docker compose -f {docker compose file path} --profile postgres --profile mongo :::info -In the above docker-compose.yml file, containers for sources are provided, which also includes the data-loading in respective source. \ +In the above docker-compose.yml file, containers for all the sources are provided, which also includes the data-loading in respective source. \ *For sources Oracle and Kafka, docker-compose is currently work in progress.* For more information, on individual source setup please follow the links given below: @@ -457,7 +457,7 @@ For more information, on individual destination setup please follow the links gi }, "update_method": { "replication_slot": "postgres_slot", - "intial_wait_time": 120 + "initial_wait_time": 120 }, "reader_batch_size": 1000, "max_threads": 10 @@ -584,13 +584,10 @@ If using the OLake UI (running in Docker), replace `localhost` with `host.docker This will generate streams.json, which will have all the streams of the source. After streams.json is created, you can modify it to select which streams to sync, enable or disable normalization, and configure partitioning. This lets you customize sync behavior before running sync. -- ### Sync - After doing required changes in the streams, sync command has to be run. - - This generates two files: - - state.json: stores the persisted sync state - - stats.json: contains sync statistics + If you want to learn more about the streams.json file and how to modify it, refer [here](/docs/install/docker-cli#streams-config) +- ### Sync + After doing required changes in the streams, sync command has to be run. ```bash ./build.sh driver-{source-name} sync --config {source.json path} --catalog {stream.json path} --destination {destination.json path} @@ -598,11 +595,17 @@ If using the OLake UI (running in Docker), replace `localhost` with `host.docker :::note This above command will generate state.json, having the persisted state of the source and stats.json, having the statistics of the sync. ::: - If want to run sync with state (i.e. with CDC enabled), run: + If want to run sync with state (i.e. with CDC/Incremental enabled), run: ```bash ./build.sh driver-{source-name} sync --config {source.json path} --catalog {stream.json path} --destination {destination.json path} --state {state.json path} ``` + :::tip + + Follow the [Docker CLI](/docs/install/docker-cli) guide for a comprehensive understanding of OLake CLI commands and its usage. + + ::: + ### Query Data after the Sync After running the sync command, you can query your data using the Spark Iceberg service available at `localhost:8888`. @@ -613,20 +616,20 @@ If using the OLake UI (running in Docker), replace `localhost` with `host.docker %%sql SHOW DATABASES; - SELECT * FROM {destination_database_name}.{table_name}; + SELECT * FROM {catalog_name}.{destination_database_name}.{table_name}; ``` ### Sync with State enabled - When running sync with state (CDC) mode enabled, you can verify Change Data Capture functionality by: - 1. Inserting records in the source database and seeing them appear in the destination table. - 2. Updating records in the source and observing corresponding updates downstream. - 3. Deleting records in the source and confirming deletions reflect in the destination. + When running sync with state mode enabled, you can verify Change Data Capture/Incremental functionality by following this example steps below: + 1. Inserting 2 records in the source database and confirm that both the records are replicated in the destination table. + 2. Updating 1 record in the source database and observing corresponding update in the destination table. + 3. Deleting 2 records in the source database and confirming the records reflect the deletion in the destination. When CDC is enabled, the destination table will have an additional column op_type indicating operation type: - `"c"` for inserted records - `"u"` for updated records - - `"d"` for deletions (with other row columns as NULL) + - `"d"` for deletions (with other row columns as NONE) ## Debug Mode If you don't want to run the sync commands after every change, we have this debugger mode for Go side of code. diff --git a/src/theme/DocPaginator/footerNavigations.js b/src/theme/DocPaginator/footerNavigations.js index 76637856..95c7c2ce 100644 --- a/src/theme/DocPaginator/footerNavigations.js +++ b/src/theme/DocPaginator/footerNavigations.js @@ -410,7 +410,7 @@ export const paginationConfig = { permalink: '/docs' }, next: { - title: 'Issues and PRs', + title: 'How to Raise a PR', permalink: '/docs/community/issues-and-prs' } }, @@ -418,20 +418,32 @@ export const paginationConfig = { // Issues and PRs '/docs/community/issues-and-prs': { previous: { - title: 'How to contribute', + title: 'Contribute to Olake', permalink: '/docs/community/contributing' }, next: { - title: 'Install Docker Compose CLI', - permalink: '/docs/install/docker-cli' + title: 'Seting up a Development Environment', + permalink: '/docs/community/setting-up-a-dev-env' + } + }, + + // Setting up a Development Environment + '/docs/community/setting-up-a-dev-env': { + previous: { + title: 'How to Raise a PR', + permalink: '/docs/community/issues-and-prs' + }, + next: { + title: 'Code of Conduct', + permalink: '/docs/community/code-of-conduct' } }, // Code of Conduct '/docs/community/code-of-conduct': { previous: { - title: 'Introduction', - permalink: '/docs' + title: 'Setting up a Development Environment', + permalink: '/docs/community/setting-up-a-dev-env' }, next: { title: 'Channels', @@ -442,8 +454,8 @@ export const paginationConfig = { // Channels '/docs/community/channels': { previous: { - title: 'Introduction', - permalink: '/docs' + title: 'Code of Conduct', + permalink: '/docs/community/code-of-conduct' }, next: { title: 'Release Notes', From 52a3b484b74a13b03b376cc5ca6cfc79e1d41515 Mon Sep 17 00:00:00 2001 From: Nayan Joshi Date: Thu, 18 Sep 2025 12:04:18 +0530 Subject: [PATCH 08/17] Modified the CLA content --- docs/community/issues-and-prs.mdx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/community/issues-and-prs.mdx b/docs/community/issues-and-prs.mdx index 500a6102..f2e3e4db 100644 --- a/docs/community/issues-and-prs.mdx +++ b/docs/community/issues-and-prs.mdx @@ -12,9 +12,14 @@ Please read through this document before submitting any issues or pull requests :::warning - All contributions must target the OLake [staging branch](https://github.com/datazip-inc/olake/tree/staging). Create feature branches from staging and open PRs against staging. -- Your pull request must be accompanied by a signed Contributor License Agreement (CLA) corresponding to the OLake component (CLI, UI, Helm, or Docs) you are contributing to; PRs without a signed CLA will not be accepted. + ::: +### Olake Contributor Agreement: + +To contribute to this project, we need you to sign the [Contributor License Agreement (“CLA”)](https://docs.google.com/forms/d/e/1FAIpQLSdze2q6gn81fmbIp2bW5cIpAXcpv7Y5OQjQyXflNvoYWiO4OQ/viewform) for the first commit you make. By agreeing to the CLA we can add you to list of approved contributors and review the changes proposed by you. + + ## Pull Request (PR) guidelines From 23a631beb3cc9008d1a30f668471f9a8d747023f Mon Sep 17 00:00:00 2001 From: Duke Dhal Date: Thu, 18 Sep 2025 12:15:59 +0530 Subject: [PATCH 09/17] fix: changes --- docs/community/setting-up-a-dev-env.mdx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/docs/community/setting-up-a-dev-env.mdx b/docs/community/setting-up-a-dev-env.mdx index 0ffb095d..12b71c77 100644 --- a/docs/community/setting-up-a-dev-env.mdx +++ b/docs/community/setting-up-a-dev-env.mdx @@ -424,15 +424,12 @@ For both Iceberg and S3 Parquet can be used by spinning up the below docker comp - **[Docker compose for using Iceberg and S3 Parquet as Destination](https://github.com/datazip-inc/olake/blob/master/destination/iceberg/local-test/docker-compose.yml)** ### To start the destination containers on your machine: -- **Iceberg:** - ```bash - docker compose -f {docker compose file path} --profile iceberg up -d - ``` -- **S3 Parquet:** ```bash docker compose -f {docker compose file path} up -d ``` +If want to use Hive Catalog for Iceberg as destination, use: `docker compose -f {docker compose file path} --profile hive up -d` + :::info For more information, on individual destination setup please follow the links given below: - **[Iceberg](/docs/writers/iceberg/catalog/rest)** From 6a142bea32e5539a24f79d4450e7883f23d43ecf Mon Sep 17 00:00:00 2001 From: Duke Dhal Date: Thu, 18 Sep 2025 19:58:29 +0530 Subject: [PATCH 10/17] fix: docker compose for source added --- docs/community/docker-compose.yml | 332 ++++++++++++++++++++++++++++++ 1 file changed, 332 insertions(+) create mode 100644 docs/community/docker-compose.yml diff --git a/docs/community/docker-compose.yml b/docs/community/docker-compose.yml new file mode 100644 index 00000000..7eec92e1 --- /dev/null +++ b/docs/community/docker-compose.yml @@ -0,0 +1,332 @@ +services: + # ------------------------- + # Postgres (primary + loader) + # ------------------------- + primary_postgres: + container_name: primary_postgres # Set an explicit name for the container. + image: postgres:15 # Use the official Postgres version 15 image. + profiles: ["postgres"] + hostname: primary_postgres # Define the hostname within the network. + ports: + - "5431:5432" # Map port 5432 inside the container to 5431 on the host. + environment: + POSTGRES_USER: main # Set the default Postgres username. + POSTGRES_PASSWORD: password # Set the password for the Postgres user. + POSTGRES_DB: main # Create a default database named "main". + # Custom command to install the wal2json plugin for logical decoding and then start Postgres. + command: > + bash -c "apt-get update && apt-get install -y postgresql-15-wal2json && exec docker-entrypoint.sh postgres -c wal_level=logical -c max_wal_senders=10 -c max_replication_slots=10 -c wal_sender_timeout=0" + # Explanation of the command: + # 1. Update the package list. + # 2. Install the postgresql-15-wal2json package, which is used to convert WAL (Write-Ahead Logging) + # into JSON format for logical replication. + # 3. Use exec to run the default docker-entrypoint script provided by the Postgres image. + # 4. Start Postgres with additional configuration parameters: + # - wal_level=logical: Enable logical replication. + # - max_wal_senders=10: Allow up to 10 concurrent WAL sender processes. + # - max_replication_slots=10: Allow up to 10 replication slots for logical decoding. + # - wal_sender_timeout=0: Disable sender timeout so long-running snapshots do not drop the connection. + volumes: + - pg-data:/var/lib/postgresql/data # Use a named volume (pg-data) to persist Postgres data. + networks: + - pg-cluster # Connect the container to the custom network "pg-cluster". + healthcheck: + test: ["CMD", "pg_isready", "-U", "main", "-d", "main"] # Healthcheck command to check if Postgres is ready. + interval: 10s # Run the healthcheck every 10 seconds. + timeout: 5s # Set a timeout of 5 seconds for the healthcheck command. + retries: 10 # Retry the healthcheck up to 10 times before declaring unhealthy. + + # Data loader service to perform operations against the Postgres database. + pg-data-loader: + image: postgres:15 # Use the same version of Postgres to ensure compatible client tools. + container_name: pg-data-loader # Set a custom name for clarity. + profiles: ["postgres"] + environment: + PGUSER: main # Set the default Postgres user for the client. + PGPASSWORD: password # Set the password for the Postgres user. + PGDATABASE: main # Connect to the "main" database. + depends_on: + primary_postgres: + condition: service_healthy # Wait until the primary_postgres service passes its health check. + entrypoint: > + bash -c " + echo \"Waiting for Postgres to be ready...\"; + # Poll until Postgres is reachable. + until pg_isready -h primary_postgres -p 5432 -U main -d main; do + echo \"Waiting...\"; + sleep 2; + done; + echo \"Creating test table sample_data...\"; + # Execute a SQL command to create a table if it doesn't exist. + psql -h primary_postgres -U main -d main -c \"CREATE TABLE IF NOT EXISTS sample_data (id SERIAL PRIMARY KEY, str_col TEXT, num_col INT);\"; + echo \"Inserting one test row...\"; + # Insert a sample row into the table. + psql -h primary_postgres -U main -d main -c \"INSERT INTO sample_data (str_col, num_col) VALUES ('Hello world', 123);\"; + echo \"Creating logical replication slot...\"; + # Create a logical replication slot using the wal2json output plugin. + psql -h primary_postgres -U main -d main -c \"SELECT * FROM pg_create_logical_replication_slot('postgres_slot', 'wal2json');\"; + echo \"Done. Data and replication slot should now exist.\" + " + # Explanation of the entrypoint: + # - Wait until Postgres is ready to accept connections using pg_isready. + # - Create a table named "sample_data" with three columns: id, str_col, and num_col. + # - Insert a sample row into the table. + # - Create a logical replication slot named "postgres_slot" using the wal2json plugin. + restart: "no" # Do not automatically restart the container after completion. + networks: + - pg-cluster # Connect to the same custom network "pg-cluster". + + # ------------------------- + # MongoDB (init keyfile + primary + loader) + # ------------------------- + init-keyfile: + image: mongo:8.0 # Use MongoDB version 8.0 as the base image. + container_name: init_keyfile # Explicit container name for easier identification. + profiles: ["mongo"] + command: > # Execute a shell command on container startup. + sh -c " + # Check if the keyfile does not already exist. + if [ ! -f /etc/mongodb/pki/keyfile ]; then + echo 'Generating keyfile...'; + # Generate a random keyfile using OpenSSL with base64 encoding and save it to the expected location. + # Then set the file permission to read-only (400) for security. + openssl rand -base64 756 > /etc/mongodb/pki/keyfile && chmod 400 /etc/mongodb/pki/keyfile; + else + # If the keyfile already exists, output a message. + echo 'Keyfile already exists.'; + fi + " + volumes: + - mongo-keyfile-vol:/etc/mongodb/pki # Mount the volume that stores the keyfile. + networks: + - mongo-cluster # Connect this container to the defined mongo-cluster network. + restart: "no" # This container should not restart automatically. + + # Primary MongoDB container that sets up a replica set and creates an admin user. + primary_mongo: + container_name: primary_mongo # Set an explicit name for the primary MongoDB container. + image: mongo:8.0 # Use MongoDB version 8.0 as the container image. + profiles: ["mongo"] + hostname: primary_mongo # Set the hostname within the container network. + ports: + - "27017:27017" # Expose port 27017 for MongoDB connections (host:container mapping). + depends_on: + - init-keyfile # Ensure the keyfile initialization service runs before this service. + volumes: + - mongo-keyfile-vol:/etc/mongodb/pki # Mount the volume to share the generated keyfile. + command: | # Execute a series of shell commands using a multi-line script. + bash -c ' + echo "Waiting for keyfile..." + # Wait until the keyfile is available before proceeding. + while [ ! -f /etc/mongodb/pki/keyfile ]; do sleep 1; done + + echo "Keyfile found, starting mongod without authentication first..." + # Start MongoDB in the background with replication enabled without initially requiring authentication. + mongod --replSet rs0 --bind_ip_all --port 27017 & + + # Store the process ID of the started mongod instance. + MONGO_PID=$! + + echo "Waiting for MongoDB to start..." + # Poll the MongoDB process until it is ready to accept connections. + until mongosh --port 27017 --eval "db.runCommand({ ping: 1 })" >/dev/null 2>&1; do + sleep 2 + done + + echo "Initializing replica set..." + # Initialize the replica set with a single member and set its host address. + # Note: host.docker.internal provides a host network reference. + mongosh --port 27017 --eval "rs.initiate({_id: \"rs0\", members: [{_id: 0, host: \"host.docker.internal:27017\"}]})" + + echo "Waiting for replica set to initialize..." + # Allow some time for the replica set configuration to propagate. + sleep 5 + + echo "Creating admin user..." + # Create an admin user with root privileges in the admin database. + mongosh --port 27017 --eval " + db = db.getSiblingDB(\"admin\"); + db.createUser({ + user: \"admin\", + pwd: \"password\", + roles: [{ role: \"root\", db: \"admin\" }] + }); + " + + echo "Stopping MongoDB to restart with authentication..." + # Stop the previously started MongoDB instance by killing its process. + kill $MONGO_PID + wait $MONGO_PID + + echo "Starting MongoDB with authentication..." + # Restart MongoDB ensuring that authentication is enabled by providing the keyfile. + exec mongod --replSet rs0 --bind_ip_all --port 27017 --keyFile /etc/mongodb/pki/keyfile + ' + healthcheck: + test: ["CMD", "mongosh", "--port", "27017", "--eval", "db.adminCommand('ping')"] # Healthcheck command to verify MongoDB is reachable. + interval: 10s # Check health status every 10 seconds. + timeout: 10s # Timeout if no response is received within 10 seconds. + retries: 10 # Attempt up to 10 retries before marking the container as unhealthy. + networks: + - mongo-cluster # Connect this container to the mongo-cluster network. + + # Data loader service that imports sample Reddit JSON data into the MongoDB. + mongo_data-loader: + image: mongo:8.0 # Use MongoDB image to leverage mongoimport tool. + container_name: mongo_data-loader # Explicit container name for clarity. + profiles: ["mongo"] + depends_on: + primary_mongo: + condition: service_healthy # Wait for the primary MongoDB service to be healthy before starting. + entrypoint: | # Custom entrypoint script to run the data loading commands. + bash -c ' + echo "Waiting for MongoDB admin user to be ready..." + # Keep checking until the MongoDB admin user is available and accepting connections. + until mongosh --host primary_mongo --username "admin" --password "password" --authenticationDatabase admin --eval "db.runCommand({ ping: 1 })" >/dev/null 2>&1; do + echo "Waiting for admin authentication to be ready..." + sleep 2 + done + + # Update package lists and install additional utilities (curl, wget, jq) needed for fetching and processing data. + apt-get update && apt-get install -y curl wget jq + + echo "Downloading Sample Reddit data..." + # Download sample Reddit data in JSON format from a remote GitHub repository into a temporary file. + curl -s "https://raw.githubusercontent.com/datazip-inc/olake-docs/refs/heads/master/static/reddit.json" >> /tmp/reddit.json + + echo "Importing Sample Reddit data into the reddit database, funny collection..." + # Use mongoimport to load the JSON data into the MongoDB database named "reddit" and collection named "funny". + mongoimport --host primary_mongo --username "admin" --password "password" --authenticationDatabase admin --db reddit --collection funny --file /tmp/reddit.json --jsonArray + + echo "Sample Reddit data import complete!" + ' + restart: "no" # The container will not restart automatically after the data is loaded. + networks: + - mongo-cluster # Connect to the mongo-cluster network. + + # ------------------------- + # MySQL (primary + init user + loaders) + # ------------------------- + primary_mysql: + container_name: primary_mysql # Name the container "primary_mysql" for easy reference. + image: mysql:8.0 # Use the MySQL 8.0 image. + profiles: ["mysql"] + hostname: primary_mysql # Set the container hostname to "primary_mysql". + ports: + - "3306:3306" # Expose port 3306 on both host and container. + environment: + MYSQL_ROOT_PASSWORD: password # Root password for MySQL. + MYSQL_DATABASE: main # Create a default database named "main" at startup. + # Enable Change Data Capture (CDC) by setting necessary MySQL replication options. + command: + - "--server-id=1" # Set a unique server identifier for replication. + - "--log-bin=mysql-bin" # Enable binary logging (needed for replication and CDC). + - "--binlog-format=ROW" # Use ROW format to record every change in each row. + - "--local-infile=1" # Enable local data loading for importing files. + - "--binlog_expire_logs_seconds=604800" # Set binary log expiration to 7 days (604800 seconds). + - "--skip-host-cache" # Disable host cache for DNS resolution. + - "--skip-name-resolve" # Disable DNS host name resolution to improve performance. + + volumes: + - mysql-data:/var/lib/mysql # Mount persistent storage volume for MySQL data. + networks: + - mysql-cluster # Connect the container to the custom network "mysql-cluster". + healthcheck: + test: [ + "CMD", + "mysqladmin", + "ping", + "-h", + "localhost", + "-u", + "root", + "-ppassword", + ] # Healthcheck command to ensure MySQL is responsive. + interval: 10s # Check health every 10 seconds. + timeout: 5s # Each health check attempt must complete within 5 seconds. + retries: 10 # Allow up to 10 retries before marking the container as unhealthy. + + # Service to initialize the replication (CDC) user. + init-cdc-user: + image: mysql:8.0 # Use the same MySQL 8.0 image for consistency. + container_name: init_cdc_user # Name the container "init_cdc_user" for identification. + profiles: ["mysql"] + depends_on: + - primary_mysql # Ensure the primary MySQL service starts before creating the user. + entrypoint: > + bash -c "echo 'Creating replication user...'; + sleep 10; # Wait for MySQL to finish initial setup. + mysql -h primary_mysql -P 3306 -u root -ppassword -e \" + CREATE USER IF NOT EXISTS 'cdc_user'@'%' IDENTIFIED BY 'cdc_password'; + GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'cdc_user'@'%'; + FLUSH PRIVILEGES;\"; + echo 'Setting global binlog_row_metadata to FULL...'; + mysql -h primary_mysql -P 3306 -u root -ppassword -e \"SET GLOBAL binlog_row_metadata = 'FULL';\"; + echo 'Replication user created and global binlog_row_metadata set.'" + networks: + - mysql-cluster # Connect to the same MySQL network. + restart: "no" # Do not restart this container automatically. + + # Service to load sample data into the main database. + mysql-data-loader: + image: ubuntu:20.04 # Use Ubuntu 20.04 for running the data loading commands. + container_name: mysql-data-loader # Name the container for clarity. + profiles: ["mysql"] + depends_on: + primary_mysql: + condition: service_healthy # Wait until the primary MySQL container passes its healthcheck. + entrypoint: > + bash -c "apt-get update -qq && apt-get install -y mysql-client && \ + echo 'Waiting for MySQL to be ready...'; \ + until mysqladmin ping -h primary_mysql -P 3306 -u root -ppassword; do echo 'Waiting...'; sleep 2; done; \ + echo 'Creating table sample_table...'; \ + mysql -h primary_mysql -P 3306 -u root -ppassword main -e 'CREATE TABLE IF NOT EXISTS sample_table (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255));'; \ + echo 'Inserting sample data...'; \ + mysql -h primary_mysql -P 3306 -u root -ppassword main -e 'INSERT INTO sample_table (name) VALUES (\"sample_data\");'; \ + echo 'Data insertion complete!'; \ + tail -f /dev/null" + + restart: "no" # Do not restart this container after execution. + networks: + - mysql-cluster # Use the same network for connectivity. + + # Service to check for the existence and functionality of MySQL binary logs. + binlog-checker: + image: ubuntu:20.04 # Use Ubuntu 20.04 to run the binary log checking commands. + container_name: binlog_checker # Name this container for identification. + profiles: ["mysql"] + depends_on: + - primary_mysql # Ensure primary MySQL is running before checking binlogs. + entrypoint: > + bash -c "export DEBIAN_FRONTEND=noninteractive && \ + apt-get update -qq && \ + apt-get install -y mysql-server-core-8.0 && \ + # Terminate any automatically started mysqld process in this container (if present). + pkill mysqld || true && \ + sleep 10 && \ + echo 'Listing MySQL binaries in /usr/bin:' && ls -l /usr/bin/mysql* && \ + echo 'Attempting to run mysqlbinlog:' && \ + # Attempt to execute mysqlbinlog command on the first binary log file. + /usr/bin/mysqlbinlog /var/lib/mysql/mysql-bin.000001 || echo 'mysqlbinlog not found!'" # If mysqlbinlog is unavailable, print a message. + volumes: + - mysql-data:/var/lib/mysql # Mount the MySQL data volume to access binary logs. + networks: + - mysql-cluster # Connect to the same custom network. + restart: "no" # Do not restart this container automatically. + + +# ------------------------- +# Networks +# ------------------------- +networks: + pg-cluster: + mongo-cluster: + mysql-cluster: + +# ------------------------- +# Volumes (persistent storage) +# ------------------------- +volumes: + pg-data: + mongo-keyfile-vol: + mysql-data: \ No newline at end of file From 5930ce0c6bddf2fc758ee5cab215be4fffd2c949 Mon Sep 17 00:00:00 2001 From: Nayan Joshi Date: Fri, 19 Sep 2025 02:21:20 +0530 Subject: [PATCH 11/17] Modified the setting up a develeopment environment section and contribution section --- docs/community/contributing.mdx | 56 +- docs/community/issues-and-prs.mdx | 43 +- docs/community/setting-up-a-dev-env.mdx | 590 +++++------------- sidebars.js | 2 +- .../architecture_diagram.png | Bin 0 -> 124458 bytes .../olake_architecture.png | Bin 0 -> 153571 bytes 6 files changed, 185 insertions(+), 506 deletions(-) create mode 100644 static/img/community/setting-up-a-dev-env/architecture_diagram.png create mode 100644 static/img/community/setting-up-a-dev-env/olake_architecture.png diff --git a/docs/community/contributing.mdx b/docs/community/contributing.mdx index 382179f6..ee00f01f 100644 --- a/docs/community/contributing.mdx +++ b/docs/community/contributing.mdx @@ -32,13 +32,11 @@ Here's a list of repositories that contain OLake-related packages: Before making any significant changes and before filing a new issue, please check [existing open](https://github.com/datazip-inc/olake/issues?q=is%3Aopen+is%3Aissue), or [recently closed](https://github.com/datazip-inc/olake/issues?q=is%3Aissue+is%3Aclosed) issues to make sure somebody else hasn't already reported the issue. Please try to include as much information as you can. -**Pointers-** -- If you find any **bugs** → please create an [**issue.**](https://github.com/datazip-inc/olake/issues/new?assignees=&labels=&template=bug_report.md&title=) -- If you want to build any **new feature** → please create an [issue with the label **`enhancement`**.](https://github.com/datazip-inc/olake/issues/new?template=new-feature.md) - ::: -### Report Bug +### Reporting Issues + +### 1. Report Bug The best way to report a bug is to file an issue on GitHub. Please include: @@ -51,13 +49,30 @@ The best way to report a bug is to file an issue on GitHub. Please include: :::info **Where to report issues:** -- **OLake CLI/Core issues**: Report in [datazip-inc/olake](https://github.com/datazip-inc/olake/issues) -- **OLake UI issues**: Report in [datazip-inc/olake-ui](https://github.com/datazip-inc/olake-frontend/issues) -- **Documentation issues**: Report in [datazip-inc/olake-docs](https://github.com/datazip-inc/olake-docs/issues) -- **OLake Helm issues**: Report in [datazip-inc/olake-helm](https://github.com/datazip-inc/olake-helm) + + + +Report in [datazip-inc/olake](https://github.com/datazip-inc/olake/issues) + + +Report in [datazip-inc/olake-ui](https://github.com/datazip-inc/olake-frontend/issues) + + +Report in [datazip-inc/olake-docs](https://github.com/datazip-inc/olake-docs/issues) + + +Report in [datazip-inc/olake-helm](https://github.com/datazip-inc/olake-helm) + + + ::: -### Submit Ideas or Feature Requests +### 2. Reporting Security Issues + +Please do not create a public GitHub issue. If you've found a security issue, please email us directly at [hello@olake.io](mailto:hello@olake.io) instead of raising an issue. + + +### 3. Submit Ideas or Feature Requests The best way to start is by discussing your idea in the OLake Slack community first. This helps get early feedback, gauge interest, and refine your proposal with input form other community members. @@ -69,27 +84,18 @@ When submitting your proposal, consider including: - Proposal — what do you suggest to solve the problem or improve the existing situation? - Any open questions to address❓ -### Fix Bugs +### Contributing to Solve an Issues -Look through the GitHub issues. Issues tagged with `#bug` are open to whoever wants to implement them. - -### Implement Features - -Look through the GitHub issues. Issues tagged with `#feature` are open to whoever wants to implement them. - -### Complete Tasks - -Look through the GitHub issues. Issues tagged with `#task` are open to whoever wants to work on them. - -### Reporting Security Issues - -Please do not create a public GitHub issue. If you've found a security issue, please email us directly at [hello@olake.io](mailto:hello@olake.io) instead of raising an issue. +If you’d like to contribute code or docs, check existing GitHub issues and pick one that fits your interest: +- **Fix Bugs** → Work on issues labeled bug +- **Implement Features** → Work on issues labeled feature +- **Complete Tasks** → Work on issues labeled task ### A good start will be to explore: - [first good issues](https://github.com/datazip-inc/olake/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22) - [beginner issues](https://github.com/datazip-inc/olake/issues?q=is%3Aissue%20state%3Aopen%20label%3Abeginner) -See the issues and if its looks interesting enough to you, comment below them and one of our maintainers will get in touch with you. +See the issues and if its looks interesting enough to you, comment on it and one of our maintainers will reply to you on the same issue. ## Types of contributors diff --git a/docs/community/issues-and-prs.mdx b/docs/community/issues-and-prs.mdx index f2e3e4db..0f0168da 100644 --- a/docs/community/issues-and-prs.mdx +++ b/docs/community/issues-and-prs.mdx @@ -15,31 +15,8 @@ Please read through this document before submitting any issues or pull requests ::: -### Olake Contributor Agreement: - -To contribute to this project, we need you to sign the [Contributor License Agreement (“CLA”)](https://docs.google.com/forms/d/e/1FAIpQLSdze2q6gn81fmbIp2bW5cIpAXcpv7Y5OQjQyXflNvoYWiO4OQ/viewform) for the first commit you make. By agreeing to the CLA we can add you to list of approved contributors and review the changes proposed by you. - - - ## Pull Request (PR) guidelines -Contributions via pull requests are much appreciated. Once the approach is agreed upon ✅, make your changes and open a Pull Request(s). -Before sending us a pull request, please ensure that, - -- Fork the olake repo on GitHub, clone it on your machine. -- Create a branch with your changes. -- You are working against the latest source on the `staging` branch. -- Modify the source; please focus only on the specific change you are contributing. -- Commit to your fork using clear commit messages. -- Send us a pull request, answering any default questions in the pull request interface. -- Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. -- Once you've pushed your commits to GitHub, make sure that your branch can be auto-merged (there are no merge conflicts). If not, on your computer, merge master into your branch, resolve any merge conflicts, make sure everything still runs correctly and passes all the tests, and then push up those changes. -- Once the change has been approved and merged, we will inform you in a comment. - - -GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and -[creating a pull request](https://help.github.com/articles/creating-a-pull-request/). - **Note:** Unless your change is small, **please** consider submitting different Pull Request(s): * 1️⃣ First PR should include the overall structure of the new component: @@ -70,7 +47,7 @@ git clone git@github.com:datazip-inc/olake.git ### 2. Create a Pull Request #### Make A Branch -- Please create a separate branch for each issue that you're working on. Do not make changes to the default branch (e.g. master, staging) of your fork. +- Please create a separate branch for each issue that you're working on. Always checkout the `staging` branch first, and create your feature/fix branch from there. ```bash git checkout -b @@ -114,12 +91,6 @@ After you are done with all the changes, perform git commit -am "write a descriptive message here about the nature of changes / addition of code" ``` -:::note - -We accept verified as well as un-verified Commits. You can verify your commits via signing gpg keys. Pull Requests with non-verified commits will be accepted as well. Follow [this guide by GitLab](https://docs.gitlab.com/user/project/repository/signed_commits/gpg/) to set up commit signing. - -::: - Next, take a pull (in case any new code has been added, this will help you avoid merge conflicts) by doing `git pull` and then `git push origin `. **Push Your Code ASAP** @@ -127,7 +98,7 @@ Next, take a pull (in case any new code has been added, this will help you avoid - Make a pull request as soon as you can and mark the title with a "[WIP]". You can create a draft pull request. **Describe Your Pull Request** -- Use the format specified in pull request template for the repository. Populate the pull request descriptioncompletely for maximum verbosity. +- Use the format specified in pull request template for the repository. Populate the pull request description completely for maximum verbosity. - Tag the actual issue number by replacing #[issue_number] e.g. #42. This closes the issue when your PR is merged. - Tag the actual issue author by replacing @[author] e.g. @issue_author. This brings the reporter of the issue into the conversation. - Mark the tasks off your checklist by adding an x in the [ ] e.g. [x]. This checks off the boxes in your to-do list. The more boxes you check, the better. @@ -150,13 +121,3 @@ e.g. If you are submitting a fix for an issue in frontend, the PR name should be - Feel free to ping us on `#contributing-to-olake` on our slack community if you need any help. -There are many other ways to get involved with the community and to participate in this project: - -- Use the product, submitting GitHub issues when a problem is found. -- Help code review pull requests and participate in issue threads. -- Submit a new feature request as an issue. -- Help answer questions on forums such as Stack Overflow and OLake Community Slack Channel. -- Tell others about the project on Twitter, your blog, etc. - -Again, Feel free to ping us on `#contributing-to-olake` on our slack community if you need any help on this :) - diff --git a/docs/community/setting-up-a-dev-env.mdx b/docs/community/setting-up-a-dev-env.mdx index 0ffb095d..4916d1a3 100644 --- a/docs/community/setting-up-a-dev-env.mdx +++ b/docs/community/setting-up-a-dev-env.mdx @@ -7,439 +7,121 @@ sidebar_position: 3 # Setting up a Development Environment The documentation in this section is a bit of knowlegde required to run OLake for developement purposes. -:::note -Now we have evolved to recommend and support docker compose more actively as the main way to run OLake for development and preserve your sanity. Most people should stick to the first few sections - ("Fork & Clone", "Docker Compose" and "Debug Mode") -::: **Pre-requisites:** Before setting up and running OLake, ensure you have the following installed on your system: -- Java (OpenJDK 11 or later recommended) -- Go (Golang) (version 1.18 or later recommended) -- Node.js (for Chalk; version 16.x LTS or later recommended) -## Fork and Clone -First, [fork the repository on GitHub](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/about-forks), then clone it. + + -Second, you can clone the main repository directly, and raise pull request accordingly. + + -There are basically 3 repositories in which you can contribute to. +Please follow the link to install Java 21 on Windows: [JDK 21](https://www.oracle.com/in/java/technologies/downloads/#jdk21-windows) -- ### OLake CLI + + + ```bash -git clone git@github.com:datazip-inc/olake.git +brew install openjdk@21 ``` + + -- ### OLake UI + + + + + + + +Please follow the link to install Golang 1.25 on Windows: [Go 1.25](https://go.dev/dl/) + + + + ```bash -git clone git@github.com:datazip-inc/olake-ui.git +brew install go@1.25 ``` + + + + + + + + + + +Please follow the link to install Node.js 22.19.0 on Windows: [Node.js 22.19.0](https://nodejs.org/en/download) -- ### OLake Helm + + + ```bash -git clone git@github.com:datazip-inc/olake-helm.git +brew install node@22 ``` + + -## Docker Compose - -In order to test your code, you must have your source and destination setup and configured. - -To get this done, run the given below docker compose files for source and destination in your local machine. - -### Source docker compose - -```yml title="docker-compose.yml" -services: - # ------------------------- - # Postgres (primary + loader) - # ------------------------- - primary_postgres: - container_name: primary_postgres # Set an explicit name for the container. - image: postgres:15 # Use the official Postgres version 15 image. - profiles: ["postgres"] - hostname: primary_postgres # Define the hostname within the network. - ports: - - "5431:5432" # Map port 5432 inside the container to 5431 on the host. - environment: - POSTGRES_USER: main # Set the default Postgres username. - POSTGRES_PASSWORD: password # Set the password for the Postgres user. - POSTGRES_DB: main # Create a default database named "main". - # Custom command to install the wal2json plugin for logical decoding and then start Postgres. - command: > - bash -c "apt-get update && apt-get install -y postgresql-15-wal2json && exec docker-entrypoint.sh postgres -c wal_level=logical -c max_wal_senders=10 -c max_replication_slots=10 -c wal_sender_timeout=0" - # Explanation of the command: - # 1. Update the package list. - # 2. Install the postgresql-15-wal2json package, which is used to convert WAL (Write-Ahead Logging) - # into JSON format for logical replication. - # 3. Use exec to run the default docker-entrypoint script provided by the Postgres image. - # 4. Start Postgres with additional configuration parameters: - # - wal_level=logical: Enable logical replication. - # - max_wal_senders=10: Allow up to 10 concurrent WAL sender processes. - # - max_replication_slots=10: Allow up to 10 replication slots for logical decoding. - # - wal_sender_timeout=0: Disable sender timeout so long-running snapshots do not drop the connection. - volumes: - - pg-data:/var/lib/postgresql/data # Use a named volume (pg-data) to persist Postgres data. - networks: - - pg-cluster # Connect the container to the custom network "pg-cluster". - healthcheck: - test: ["CMD", "pg_isready", "-U", "main", "-d", "main"] # Healthcheck command to check if Postgres is ready. - interval: 10s # Run the healthcheck every 10 seconds. - timeout: 5s # Set a timeout of 5 seconds for the healthcheck command. - retries: 10 # Retry the healthcheck up to 10 times before declaring unhealthy. - - # Data loader service to perform operations against the Postgres database. - pg-data-loader: - image: postgres:15 # Use the same version of Postgres to ensure compatible client tools. - container_name: pg-data-loader # Set a custom name for clarity. - profiles: ["postgres"] - environment: - PGUSER: main # Set the default Postgres user for the client. - PGPASSWORD: password # Set the password for the Postgres user. - PGDATABASE: main # Connect to the "main" database. - depends_on: - primary_postgres: - condition: service_healthy # Wait until the primary_postgres service passes its health check. - entrypoint: > - bash -c " - echo \"Waiting for Postgres to be ready...\"; - # Poll until Postgres is reachable. - until pg_isready -h primary_postgres -p 5432 -U main -d main; do - echo \"Waiting...\"; - sleep 2; - done; - echo \"Creating test table sample_data...\"; - # Execute a SQL command to create a table if it doesn't exist. - psql -h primary_postgres -U main -d main -c \"CREATE TABLE IF NOT EXISTS sample_data (id SERIAL PRIMARY KEY, str_col TEXT, num_col INT);\"; - echo \"Inserting one test row...\"; - # Insert a sample row into the table. - psql -h primary_postgres -U main -d main -c \"INSERT INTO sample_data (str_col, num_col) VALUES ('Hello world', 123);\"; - echo \"Creating logical replication slot...\"; - # Create a logical replication slot using the wal2json output plugin. - psql -h primary_postgres -U main -d main -c \"SELECT * FROM pg_create_logical_replication_slot('postgres_slot', 'wal2json');\"; - echo \"Done. Data and replication slot should now exist.\" - " - # Explanation of the entrypoint: - # - Wait until Postgres is ready to accept connections using pg_isready. - # - Create a table named "sample_data" with three columns: id, str_col, and num_col. - # - Insert a sample row into the table. - # - Create a logical replication slot named "postgres_slot" using the wal2json plugin. - restart: "no" # Do not automatically restart the container after completion. - networks: - - pg-cluster # Connect to the same custom network "pg-cluster". - - # ------------------------- - # MongoDB (init keyfile + primary + loader) - # ------------------------- - init-keyfile: - image: mongo:8.0 # Use MongoDB version 8.0 as the base image. - container_name: init_keyfile # Explicit container name for easier identification. - profiles: ["mongo"] - command: > # Execute a shell command on container startup. - sh -c " - # Check if the keyfile does not already exist. - if [ ! -f /etc/mongodb/pki/keyfile ]; then - echo 'Generating keyfile...'; - # Generate a random keyfile using OpenSSL with base64 encoding and save it to the expected location. - # Then set the file permission to read-only (400) for security. - openssl rand -base64 756 > /etc/mongodb/pki/keyfile && chmod 400 /etc/mongodb/pki/keyfile; - else - # If the keyfile already exists, output a message. - echo 'Keyfile already exists.'; - fi - " - volumes: - - mongo-keyfile-vol:/etc/mongodb/pki # Mount the volume that stores the keyfile. - networks: - - mongo-cluster # Connect this container to the defined mongo-cluster network. - restart: "no" # This container should not restart automatically. - - # Primary MongoDB container that sets up a replica set and creates an admin user. - primary_mongo: - container_name: primary_mongo # Set an explicit name for the primary MongoDB container. - image: mongo:8.0 # Use MongoDB version 8.0 as the container image. - profiles: ["mongo"] - hostname: primary_mongo # Set the hostname within the container network. - ports: - - "27017:27017" # Expose port 27017 for MongoDB connections (host:container mapping). - depends_on: - - init-keyfile # Ensure the keyfile initialization service runs before this service. - volumes: - - mongo-keyfile-vol:/etc/mongodb/pki # Mount the volume to share the generated keyfile. - command: | # Execute a series of shell commands using a multi-line script. - bash -c ' - echo "Waiting for keyfile..." - # Wait until the keyfile is available before proceeding. - while [ ! -f /etc/mongodb/pki/keyfile ]; do sleep 1; done - - echo "Keyfile found, starting mongod without authentication first..." - # Start MongoDB in the background with replication enabled without initially requiring authentication. - mongod --replSet rs0 --bind_ip_all --port 27017 & - - # Store the process ID of the started mongod instance. - MONGO_PID=$! - - echo "Waiting for MongoDB to start..." - # Poll the MongoDB process until it is ready to accept connections. - until mongosh --port 27017 --eval "db.runCommand({ ping: 1 })" >/dev/null 2>&1; do - sleep 2 - done - - echo "Initializing replica set..." - # Initialize the replica set with a single member and set its host address. - # Note: host.docker.internal provides a host network reference. - mongosh --port 27017 --eval "rs.initiate({_id: \"rs0\", members: [{_id: 0, host: \"host.docker.internal:27017\"}]})" - - echo "Waiting for replica set to initialize..." - # Allow some time for the replica set configuration to propagate. - sleep 5 - - echo "Creating admin user..." - # Create an admin user with root privileges in the admin database. - mongosh --port 27017 --eval " - db = db.getSiblingDB(\"admin\"); - db.createUser({ - user: \"admin\", - pwd: \"password\", - roles: [{ role: \"root\", db: \"admin\" }] - }); - " - - echo "Stopping MongoDB to restart with authentication..." - # Stop the previously started MongoDB instance by killing its process. - kill $MONGO_PID - wait $MONGO_PID - - echo "Starting MongoDB with authentication..." - # Restart MongoDB ensuring that authentication is enabled by providing the keyfile. - exec mongod --replSet rs0 --bind_ip_all --port 27017 --keyFile /etc/mongodb/pki/keyfile - ' - healthcheck: - test: ["CMD", "mongosh", "--port", "27017", "--eval", "db.adminCommand('ping')"] # Healthcheck command to verify MongoDB is reachable. - interval: 10s # Check health status every 10 seconds. - timeout: 10s # Timeout if no response is received within 10 seconds. - retries: 10 # Attempt up to 10 retries before marking the container as unhealthy. - networks: - - mongo-cluster # Connect this container to the mongo-cluster network. - - # Data loader service that imports sample Reddit JSON data into the MongoDB. - mongo_data-loader: - image: mongo:8.0 # Use MongoDB image to leverage mongoimport tool. - container_name: mongo_data-loader # Explicit container name for clarity. - profiles: ["mongo"] - depends_on: - primary_mongo: - condition: service_healthy # Wait for the primary MongoDB service to be healthy before starting. - entrypoint: | # Custom entrypoint script to run the data loading commands. - bash -c ' - echo "Waiting for MongoDB admin user to be ready..." - # Keep checking until the MongoDB admin user is available and accepting connections. - until mongosh --host primary_mongo --username "admin" --password "password" --authenticationDatabase admin --eval "db.runCommand({ ping: 1 })" >/dev/null 2>&1; do - echo "Waiting for admin authentication to be ready..." - sleep 2 - done - - # Update package lists and install additional utilities (curl, wget, jq) needed for fetching and processing data. - apt-get update && apt-get install -y curl wget jq - - echo "Downloading Sample Reddit data..." - # Download sample Reddit data in JSON format from a remote GitHub repository into a temporary file. - curl -s "https://raw.githubusercontent.com/datazip-inc/olake-docs/refs/heads/master/static/reddit.json" >> /tmp/reddit.json - - echo "Importing Sample Reddit data into the reddit database, funny collection..." - # Use mongoimport to load the JSON data into the MongoDB database named "reddit" and collection named "funny". - mongoimport --host primary_mongo --username "admin" --password "password" --authenticationDatabase admin --db reddit --collection funny --file /tmp/reddit.json --jsonArray - - echo "Sample Reddit data import complete!" - ' - restart: "no" # The container will not restart automatically after the data is loaded. - networks: - - mongo-cluster # Connect to the mongo-cluster network. - - # ------------------------- - # MySQL (primary + init user + loaders) - # ------------------------- - primary_mysql: - container_name: primary_mysql # Name the container "primary_mysql" for easy reference. - image: mysql:8.0 # Use the MySQL 8.0 image. - profiles: ["mysql"] - hostname: primary_mysql # Set the container hostname to "primary_mysql". - ports: - - "3306:3306" # Expose port 3306 on both host and container. - environment: - MYSQL_ROOT_PASSWORD: password # Root password for MySQL. - MYSQL_DATABASE: main # Create a default database named "main" at startup. - # Enable Change Data Capture (CDC) by setting necessary MySQL replication options. - command: - - "--server-id=1" # Set a unique server identifier for replication. - - "--log-bin=mysql-bin" # Enable binary logging (needed for replication and CDC). - - "--binlog-format=ROW" # Use ROW format to record every change in each row. - - "--local-infile=1" # Enable local data loading for importing files. - - "--binlog_expire_logs_seconds=604800" # Set binary log expiration to 7 days (604800 seconds). - - "--skip-host-cache" # Disable host cache for DNS resolution. - - "--skip-name-resolve" # Disable DNS host name resolution to improve performance. - - volumes: - - mysql-data:/var/lib/mysql # Mount persistent storage volume for MySQL data. - networks: - - mysql-cluster # Connect the container to the custom network "mysql-cluster". - healthcheck: - test: [ - "CMD", - "mysqladmin", - "ping", - "-h", - "localhost", - "-u", - "root", - "-ppassword", - ] # Healthcheck command to ensure MySQL is responsive. - interval: 10s # Check health every 10 seconds. - timeout: 5s # Each health check attempt must complete within 5 seconds. - retries: 10 # Allow up to 10 retries before marking the container as unhealthy. - - # Service to initialize the replication (CDC) user. - init-cdc-user: - image: mysql:8.0 # Use the same MySQL 8.0 image for consistency. - container_name: init_cdc_user # Name the container "init_cdc_user" for identification. - profiles: ["mysql"] - depends_on: - - primary_mysql # Ensure the primary MySQL service starts before creating the user. - entrypoint: > - bash -c "echo 'Creating replication user...'; - sleep 10; # Wait for MySQL to finish initial setup. - mysql -h primary_mysql -P 3306 -u root -ppassword -e \" - CREATE USER IF NOT EXISTS 'cdc_user'@'%' IDENTIFIED BY 'cdc_password'; - GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'cdc_user'@'%'; - FLUSH PRIVILEGES;\"; - echo 'Setting global binlog_row_metadata to FULL...'; - mysql -h primary_mysql -P 3306 -u root -ppassword -e \"SET GLOBAL binlog_row_metadata = 'FULL';\"; - echo 'Replication user created and global binlog_row_metadata set.'" - networks: - - mysql-cluster # Connect to the same MySQL network. - restart: "no" # Do not restart this container automatically. - - # Service to load sample data into the main database. - mysql-data-loader: - image: ubuntu:20.04 # Use Ubuntu 20.04 for running the data loading commands. - container_name: mysql-data-loader # Name the container for clarity. - profiles: ["mysql"] - depends_on: - primary_mysql: - condition: service_healthy # Wait until the primary MySQL container passes its healthcheck. - entrypoint: > - bash -c "apt-get update -qq && apt-get install -y mysql-client && \ - echo 'Waiting for MySQL to be ready...'; \ - until mysqladmin ping -h primary_mysql -P 3306 -u root -ppassword; do echo 'Waiting...'; sleep 2; done; \ - echo 'Creating table sample_table...'; \ - mysql -h primary_mysql -P 3306 -u root -ppassword main -e 'CREATE TABLE IF NOT EXISTS sample_table (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255));'; \ - echo 'Inserting sample data...'; \ - mysql -h primary_mysql -P 3306 -u root -ppassword main -e 'INSERT INTO sample_table (name) VALUES (\"sample_data\");'; \ - echo 'Data insertion complete!'; \ - tail -f /dev/null" - - restart: "no" # Do not restart this container after execution. - networks: - - mysql-cluster # Use the same network for connectivity. - - # Service to check for the existence and functionality of MySQL binary logs. - binlog-checker: - image: ubuntu:20.04 # Use Ubuntu 20.04 to run the binary log checking commands. - container_name: binlog_checker # Name this container for identification. - profiles: ["mysql"] - depends_on: - - primary_mysql # Ensure primary MySQL is running before checking binlogs. - entrypoint: > - bash -c "export DEBIAN_FRONTEND=noninteractive && \ - apt-get update -qq && \ - apt-get install -y mysql-server-core-8.0 && \ - # Terminate any automatically started mysqld process in this container (if present). - pkill mysqld || true && \ - sleep 10 && \ - echo 'Listing MySQL binaries in /usr/bin:' && ls -l /usr/bin/mysql* && \ - echo 'Attempting to run mysqlbinlog:' && \ - # Attempt to execute mysqlbinlog command on the first binary log file. - /usr/bin/mysqlbinlog /var/lib/mysql/mysql-bin.000001 || echo 'mysqlbinlog not found!'" # If mysqlbinlog is unavailable, print a message. - volumes: - - mysql-data:/var/lib/mysql # Mount the MySQL data volume to access binary logs. - networks: - - mysql-cluster # Connect to the same custom network. - restart: "no" # Do not restart this container automatically. - - -# ------------------------- -# Networks -# ------------------------- -networks: - pg-cluster: - mongo-cluster: - mysql-cluster: - -# ------------------------- -# Volumes (persistent storage) -# ------------------------- -volumes: - pg-data: - mongo-keyfile-vol: - mysql-data: + + + + + + + +Please follow the link to install Docker on Windows: [Docker Desktop](https://docs.docker.com/desktop/setup/install/windows-install/) + + + + +```bash + brew install --cask docker ``` + + + + + + + + +## 1. Source and Destination Setup + +Use the following command to quickly spin up the source (Postgres/MongoDB/MySQL) and destination (Iceberg/Parquet Writer) services using Docker Compose. This will download the required docker-compose files and start the containers in the background. -### To start the source containers on your machine: -- **Postgres:** - ```bash - docker compose -f {docker compose file path} --profile postgres up -d - ``` -- **MongoDB:** - ```bash - docker compose -f {docker compose file path} --profile mongo up -d - ``` -- **MySQL:** - ```bash - docker compose -f {docker compose file path} --profile mysql up -d - ``` - -:::tip -- Replace `{docker compose file path}` with the file path of your docker compose. -- To start all the source containers together: ```bash -docker compose -f {docker compose file path} --profile postgres --profile mongo --profile mysql up -d +sh -c 'curl -fsSL https://raw.githubusercontent.com/datazip-inc/olake-docs/fix/contribution/docs/community/docker-compose.yml -o docker-compose.source.yml && \ +curl -fsSL https://raw.githubusercontent.com/datazip-inc/olake/fix/contribution-md/destination/iceberg/local-test/docker-compose.yml -o docker-compose.destination.yml && \ +docker compose -f docker-compose.source.yml --profile mongo -f docker-compose.destination.yml up -d' ``` -::: +## 2. OLake CLI Setup -:::info -In the above docker-compose.yml file, containers for all the sources are provided, which also includes the data-loading in respective source. \ -*For sources Oracle and Kafka, docker-compose is currently work in progress.* - -For more information, on individual source setup please follow the links given below: -- **[Postgres](/docs/connectors/postgres/)** -- **[MongoDB](/docs/connectors/mongodb/)** -- **[MySQL](/docs/connectors/mysql/)** -- **[Oracle](/docs/connectors/oracle/)** -- **[Kafka](/docs/connectors/kafka/)** -::: +The diagram below illustrates the high-level architecture of OLake, showing how data flows from various sources to different destinations: -### Destination docker compose +![OLake Architecture](/img/community/setting-up-a-dev-env/architecture_diagram.png) + +### Fork and Clone +First, [fork the repository on GitHub](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/about-forks), then clone it. -For both Iceberg and S3 Parquet can be used by spinning up the below docker compose file. -- **[Docker compose for using Iceberg and S3 Parquet as Destination](https://github.com/datazip-inc/olake/blob/master/destination/iceberg/local-test/docker-compose.yml)** +Second, you can clone the main repository directly, and raise pull request accordingly. -### To start the destination containers on your machine: -- **Iceberg:** - ```bash - docker compose -f {docker compose file path} --profile iceberg up -d - ``` -- **S3 Parquet:** - ```bash - docker compose -f {docker compose file path} up -d - ``` +```bash +git clone git@github.com:datazip-inc/olake.git +``` -:::info -For more information, on individual destination setup please follow the links given below: -- **[Iceberg](/docs/writers/iceberg/catalog/rest)** -- **[S3 Parquet](/docs/writers/parquet/config)** +:::note +- To run OLake CLI commands mentioned below, you must be inside the root directory of the olake repository which we get after cloning `datazip-inc/olake.git`. +```bash +nayan@Nayans-MacBook-Pro olake % +``` +- Create `source.json` and `destination.json` files inside this directory. ::: -## Setup up Source +### Set up Source @@ -515,13 +197,8 @@ You can use your own Kafka configuration. The docker compose is **WIP**. -:::note - -If using the OLake UI (running in Docker), replace `localhost` with `host.docker.internal` as the host. For the OLake CLI (running on your local machine), using `localhost` is correct. - -::: +### Set up Destination -### 2. Destination **This setup is using the above provided docker compose for Iceberg.** @@ -564,40 +241,36 @@ If using the OLake UI (running in Docker), replace `localhost` with `host.docker To access S3-compatible MinIO, click on [`http://localhost:9001/login`](http://localhost:9001/login), with username = `admin` and password = `password`. -:::note -If using the OLake UI (running in Docker), replace `localhost` with `host.docker.internal` as the host. For the OLake CLI (running on your local machine), using `localhost` is correct. -::: +### Commands -## Commands :::info -- Below given commands are the mandatory commands required to run OLake. For more information, refer to this: [`OLake commands`](/docs/community/commands-and-flags) -- To run OLake CLI commands mentioned below, you must be inside the root directory of the olake repository which we get after cloning `datazip-inc/olake.git` . +- To better understand the workflow, let’s walk through an example using **Postgres** as the source and the destination as **Iceberg**. ::: - ### Discover Initially you have to run the discover command to get the possible streams of the source. It requires source name and source config path, with command type `discover`. + ```bash - ./build.sh driver-{source-name} discover --config {source.json path} + ./build.sh driver-postgres discover --config ./source.json ``` - This will generate streams.json, which will have all the streams of the source. - - After streams.json is created, you can modify it to select which streams to sync, enable or disable normalization, and configure partitioning. This lets you customize sync behavior before running sync. If you want to learn more about the streams.json file and how to modify it, refer [here](/docs/install/docker-cli#streams-config) -- ### Sync - After doing required changes in the streams, sync command has to be run. + +- ### Sync + + This below command will generate state.json, having the persisted state of the source and stats.json, having the statistics of the sync. + + Initial sync with no state: ```bash - ./build.sh driver-{source-name} sync --config {source.json path} --catalog {stream.json path} --destination {destination.json path} + ./build.sh driver-postgres sync --config ./source.json --catalog ./streams.json --destination ./destination.json ``` - :::note - This above command will generate state.json, having the persisted state of the source and stats.json, having the statistics of the sync. - ::: + If want to run sync with state (i.e. with CDC/Incremental enabled), run: ```bash - ./build.sh driver-{source-name} sync --config {source.json path} --catalog {stream.json path} --destination {destination.json path} --state {state.json path} + ./build.sh driver-postgres sync --config ./source.json --catalog ./streams.json --destination ./destination.json --state ./state.json ``` :::tip @@ -606,30 +279,69 @@ If using the OLake UI (running in Docker), replace `localhost` with `host.docker ::: - ### Query Data after the Sync +- ### Query Data after the Sync After running the sync command, you can query your data using the Spark Iceberg service available at `localhost:8888`. For example, run the following SQL commands to explore your synced data: - ```json title="sql" + ```bash %%sql SHOW DATABASES; - SELECT * FROM {catalog_name}.{destination_database_name}.{table_name}; + SELECT * FROM olake_iceberg.olake_iceberg.sample_data; ``` - ### Sync with State enabled +- ### Sync with State enabled When running sync with state mode enabled, you can verify Change Data Capture/Incremental functionality by following this example steps below: - 1. Inserting 2 records in the source database and confirm that both the records are replicated in the destination table. - 2. Updating 1 record in the source database and observing corresponding update in the destination table. - 3. Deleting 2 records in the source database and confirming the records reflect the deletion in the destination. - - When CDC is enabled, the destination table will have an additional column op_type indicating operation type: - - `"c"` for inserted records - - `"u"` for updated records - - `"d"` for deletions (with other row columns as NONE) + 1. Inserting 2 records in the source database and confirm that both the records are replicated in the destination table. To perform this run the below command below in the terminal: + ```bash + docker exec -it primary_postgres psql -U main -d main -c "INSERT INTO public.sample_data (id, num_col, str_col) VALUES (10, 100, 'First record'), (20, 200, 'Second record');" + ``` + + Now run the sync command with state mode enabled (mentioned above) so that the new records are replicated to the destination. + + After the sync is completed, you can execute the below command using the Spark Iceberg service to verify that the records are replicated: + ```bash + %%sql + SELECT * FROM olake_iceberg.olake_iceberg.sample_data; + ``` + You can notice that the op_type for these records will indicate `"c"` for created. + + + 2. Updating 1 record in the source database and observing corresponding update in the destination table. To perform this run the following command below in the terminal: + + ```bash + docker exec -it primary_postgres psql -U main -d main -c "UPDATE public.sample_data SET num_col = 150, str_col = 'First record updated' WHERE id = 1;" + ``` + + Now run the sync command with state mode enabled (mentioned above) so that the new records are replicated to the destination. + + After the sync is completed, you can execute the below command using the Spark Iceberg service to verify that the records are replicated: + ```bash + %%sql + SELECT * FROM olake_iceberg.olake_iceberg.sample_data; + ``` + You can notice that the op_type for this record will indicate `"u"` for updated. + + 3. Deleting 2 records in the source database and confirming the records reflect the deletion in the destination. To perform this run the following commands below in psql terminal: + + ```json title="sql" + docker exec -it primary_postgres psql -U main -d main -c "DELETE FROM public.sample_data WHERE id IN (1, 10);" + ``` + Now run the sync command with state mode enabled (mentioned above) so that the new records are replicated to the destination. + + After the sync is completed, you can execute the below command using the Spark Iceberg service to verify that the records are replicated: + ```bash + %%sql + SELECT * FROM olake_iceberg.olake_iceberg.sample_data; + ``` + You can notice that the op_type for these records will indicate `"d"` for deleted (with other fields as NONE) + +## 3. OLake UI Setup + +**UI setup: Work in progress (coming soon).** ## Debug Mode If you don't want to run the sync commands after every change, we have this debugger mode for Go side of code. diff --git a/sidebars.js b/sidebars.js index 103488df..923c45e4 100644 --- a/sidebars.js +++ b/sidebars.js @@ -139,8 +139,8 @@ const docSidebar = { // Community sectionHeader("COMMUNITY"), 'community/contributing', - 'community/issues-and-prs', 'community/setting-up-a-dev-env', + 'community/issues-and-prs', 'community/code-of-conduct', 'community/channels', diff --git a/static/img/community/setting-up-a-dev-env/architecture_diagram.png b/static/img/community/setting-up-a-dev-env/architecture_diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..e991f720945cfc6cb7618050fd7b4a20ff9e2683 GIT binary patch literal 124458 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ℑnpM&ZF<8dwrvd)%3!WE6DrjND9lOV*uecea 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 z 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 Og0wdj{HM7q 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 literal 0 HcmV?d00001 From b4cf7629036a930260b181d87c29ab6f5d94adbe Mon Sep 17 00:00:00 2001 From: Shubham Baldava Date: Fri, 19 Sep 2025 13:12:14 +0530 Subject: [PATCH 12/17] fix: Minor spelling mistakes and formatting fixes. With some other improvements --- docs/community/setting-up-a-dev-env.mdx | 217 +++++++++++++----------- 1 file changed, 122 insertions(+), 95 deletions(-) diff --git a/docs/community/setting-up-a-dev-env.mdx b/docs/community/setting-up-a-dev-env.mdx index 4916d1a3..b3acb72d 100644 --- a/docs/community/setting-up-a-dev-env.mdx +++ b/docs/community/setting-up-a-dev-env.mdx @@ -6,7 +6,7 @@ sidebar_position: 3 # Setting up a Development Environment -The documentation in this section is a bit of knowlegde required to run OLake for developement purposes. +The documentation in this section is a bit of knowledge required to run OLake for development purposes. **Pre-requisites:** @@ -16,70 +16,112 @@ Before setting up and running OLake, ensure you have the following installed on - - -Please follow the link to install Java 21 on Windows: [JDK 21](https://www.oracle.com/in/java/technologies/downloads/#jdk21-windows) - - - ```bash brew install openjdk@21 ``` - + +```bash +sudo apt update +sudo apt install openjdk-21-jdk +``` - + - - +Please follow the link to install Java 21 on Windows: [JDK 21](https://www.oracle.com/in/java/technologies/downloads/#jdk21-windows) -Please follow the link to install Golang 1.25 on Windows: [Go 1.25](https://go.dev/dl/) + + - + + + + ```bash brew install go@1.25 ``` - + +```bash +sudo snap install go --classic +# or download from https://go.dev/dl/ and follow installation instructions +``` - + - - +Please follow the link to install Golang 1.25 on Windows: [Go 1.25](https://go.dev/dl/) -Please follow the link to install Node.js 22.19.0 on Windows: [Node.js 22.19.0](https://nodejs.org/en/download) + + - + + + + ```bash brew install node@22 ``` - + +```bash +curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - +sudo apt-get install -y nodejs +``` - + - - +Please follow the link to install Node.js 22.19.0 on Windows: [Node.js 22.19.0](https://nodejs.org/en/download) -Please follow the link to install Docker on Windows: [Docker Desktop](https://docs.docker.com/desktop/setup/install/windows-install/) + + + + + ```bash - brew install --cask docker +brew install --cask docker ``` + + + +```bash +# Add Docker's official GPG key: +sudo apt-get update +sudo apt-get install ca-certificates curl +sudo install -m 0755 -d /etc/apt/keyrings +sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc +sudo chmod a+r /etc/apt/keyrings/docker.asc + +# Add the repository to Apt sources: +echo \ + "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \ + $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ + sudo tee /etc/apt/sources.list.d/docker.list > /dev/null +sudo apt-get update + +# Install Docker packages: +sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin +``` + + + + +Please follow the link to install Docker on Windows: [Docker Desktop](https://docs.docker.com/desktop/setup/install/windows-install/) + @@ -105,23 +147,20 @@ The diagram below illustrates the high-level architecture of OLake, showing how ![OLake Architecture](/img/community/setting-up-a-dev-env/architecture_diagram.png) ### Fork and Clone -First, [fork the repository on GitHub](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/about-forks), then clone it. -Second, you can clone the main repository directly, and raise pull request accordingly. +Clone the OLake repository and navigate to the project directory: ```bash -git clone git@github.com:datazip-inc/olake.git +git clone git@github.com:datazip-inc/olake.git && cd olake ``` :::note -- To run OLake CLI commands mentioned below, you must be inside the root directory of the olake repository which we get after cloning `datazip-inc/olake.git`. -```bash -nayan@Nayans-MacBook-Pro olake % -``` -- Create `source.json` and `destination.json` files inside this directory. +- For contributing, you can [fork the repository on GitHub](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/about-forks) first, then clone your fork. +- All OLake CLI commands mentioned below must be run from this root directory. +- You'll create `source.json` and `destination.json` configuration files in the steps below. ::: -### Set up Source +### Set up Source (source.json) @@ -147,7 +186,7 @@ nayan@Nayans-MacBook-Pro olake % ``` - + **This setup is using the above provided docker compose for MongoDB.** @@ -192,12 +231,12 @@ nayan@Nayans-MacBook-Pro olake % You can use your own Oracle configuration. The docker compose is **WIP**. - + You can use your own Kafka configuration. The docker compose is **WIP**. -### Set up Destination +### Set up Destination (destination.json) @@ -239,39 +278,37 @@ You can use your own Kafka configuration. The docker compose is **WIP**. ``` + To access S3-compatible MinIO, click on [`http://localhost:9001/login`](http://localhost:9001/login), with username = `admin` and password = `password`. -### Commands +### Commands to run OLake code :::info - To better understand the workflow, let’s walk through an example using **Postgres** as the source and the destination as **Iceberg**. ::: - ### Discover - Initially you have to run the discover command to get the possible streams of the source. It requires source name and source config path, with command type `discover`. + Initially, you have to run the discover command to get the possible streams of the source. It requires the source name and source config path, with command type `discover`. ```bash ./build.sh driver-postgres discover --config ./source.json ``` - If you want to learn more about the streams.json file and how to modify it, refer [here](/docs/install/docker-cli#streams-config) + If you want to learn more about the `streams.json` file and how to modify it, refer to the [Docker CLI guide](/docs/install/docker-cli#streams-config). - ### Sync - This below command will generate state.json, having the persisted state of the source and stats.json, having the statistics of the sync. + This command syncs the data from Postgres to Iceberg (also registers tables in the catalog). + + The command below will generate state.json, having the persisted state of the source and stats.json, having the statistics of the sync. Initial sync with no state: ```bash ./build.sh driver-postgres sync --config ./source.json --catalog ./streams.json --destination ./destination.json ``` - - If want to run sync with state (i.e. with CDC/Incremental enabled), run: - ```bash - ./build.sh driver-postgres sync --config ./source.json --catalog ./streams.json --destination ./destination.json --state ./state.json - ``` :::tip @@ -279,13 +316,21 @@ To access S3-compatible MinIO, click on [`http://localhost:9001/login`](http://l ::: + :::note + + **Code Flow for Each Source**: + Example: `drivers/postgres/main.go` → `drivers/postgres/internal/postgres.go` → other internal modules + + While running the build command, you can add print statements to debug the flow. If you prefer using a debugger with VSCode, please follow the [Debug Mode](#debug-mode) section below. + ::: + - ### Query Data after the Sync - After running the sync command, you can query your data using the Spark Iceberg service available at `localhost:8888`. + After running the sync command, you can query your data using the Spark Iceberg service available at [`localhost:8888`](http://localhost:8888). For example, run the following SQL commands to explore your synced data: - ```bash + ```sql %%sql SHOW DATABASES; @@ -294,66 +339,52 @@ To access S3-compatible MinIO, click on [`http://localhost:9001/login`](http://l - ### Sync with State enabled - When running sync with state mode enabled, you can verify Change Data Capture/Incremental functionality by following this example steps below: - 1. Inserting 2 records in the source database and confirm that both the records are replicated in the destination table. To perform this run the below command below in the terminal: + You can run the sync with `--state` and path to the `state.json` to only sync new/updated/deleted records. + + When running sync with state mode enabled, you can verify Change Data Capture/Incremental functionality by following these example steps: + 1. **Insert Records**: Insert 2 records in the source database and confirm that both records are replicated in the destination table. Run the following command: ```bash docker exec -it primary_postgres psql -U main -d main -c "INSERT INTO public.sample_data (id, num_col, str_col) VALUES (10, 100, 'First record'), (20, 200, 'Second record');" ``` - Now run the sync command with state mode enabled (mentioned above) so that the new records are replicated to the destination. - - After the sync is completed, you can execute the below command using the Spark Iceberg service to verify that the records are replicated: - ```bash - %%sql - SELECT * FROM olake_iceberg.olake_iceberg.sample_data; + Now run sync with state (CDC/Incremental enabled): + ```bash + ./build.sh driver-postgres sync --config ./source.json --catalog ./streams.json --destination ./destination.json --state ./state.json ``` - You can notice that the op_type for these records will indicate `"c"` for created. + After the sync is completed, execute the SQL query from the previous section on `localhost:8888`. You should see 2 additional rows with `op_type` indicating `"c"` for created. - 2. Updating 1 record in the source database and observing corresponding update in the destination table. To perform this run the following command below in the terminal: - ```bash + 2. **Update Record**: Update 1 record in the source database and observe the corresponding update in the destination table. Run the following command: + + ```sql title="sql" docker exec -it primary_postgres psql -U main -d main -c "UPDATE public.sample_data SET num_col = 150, str_col = 'First record updated' WHERE id = 1;" ``` - - Now run the sync command with state mode enabled (mentioned above) so that the new records are replicated to the destination. + Run the sync command with state (as shown above), then execute the SQL query on `localhost:8888`. - After the sync is completed, you can execute the below command using the Spark Iceberg service to verify that the records are replicated: - ```bash - %%sql - SELECT * FROM olake_iceberg.olake_iceberg.sample_data; - ``` You can notice that the op_type for this record will indicate `"u"` for updated. - 3. Deleting 2 records in the source database and confirming the records reflect the deletion in the destination. To perform this run the following commands below in psql terminal: + 3. **Delete Records**: Delete 2 records in the source database and confirm the records reflect the deletion in the destination. Run the following command: - ```json title="sql" + ```sql title="sql" docker exec -it primary_postgres psql -U main -d main -c "DELETE FROM public.sample_data WHERE id IN (1, 10);" ``` - Now run the sync command with state mode enabled (mentioned above) so that the new records are replicated to the destination. - - After the sync is completed, you can execute the below command using the Spark Iceberg service to verify that the records are replicated: - ```bash - %%sql - SELECT * FROM olake_iceberg.olake_iceberg.sample_data; - ``` + Run the sync command with state (as shown above), then execute the SQL query on `localhost:8888`. + You can notice that the op_type for these records will indicate `"d"` for deleted (with other fields as NONE) -## 3. OLake UI Setup - -**UI setup: Work in progress (coming soon).** -## Debug Mode -If you don't want to run the sync commands after every change, we have this debugger mode for Go side of code. +### 2.1 VSCODE Debug Mode for OLake CLI +If you don't want to run the sync commands after every change, you can use this debugger mode for the Go side of the code. :::caution If using the Iceberg as destination, you have to generate the jar file for Java side of code. To generate the jar file either run the sync command once or run `mvn clean package -DskipTests`. ::: -**Assumptions: You are using [VSCode](https://code.visualstudio.com/download) to run OLake locally.** +**Prerequisites**: You must be using [VSCode](https://code.visualstudio.com/download) to run OLake locally. ### Steps to Debug -- Make a directory `.vscode` (inside OLake project, at root location) if not already created. -- Create a file named `launch.json` inside the `.vscode` directory and paste the beflow config. +- Create a `.vscode` directory inside the OLake project root if it doesn't already exist. +- Create a file named `launch.json` inside the `.vscode` directory and paste the below config. ```json title=".vscode/launch.json" { @@ -368,13 +399,13 @@ If using the Iceberg as destination, you have to generate the jar file for Java "args": [ "sync", "--config", - "{PATH_TO_UPDATE}/drivers/{source-name}/examples/source.json", + "{PATH_TO_UPDATE}/source.json", "--catalog", - "{PATH_TO_UPDATE}/drivers/{source-name}/examples/streams.json", + "{PATH_TO_UPDATE}/streams.json", "--destination", - "{PATH_TO_UPDATE}/drivers/{source-name}/examples/destination.json", + "{PATH_TO_UPDATE}/destination.json", // "--state", - // "{PATH_TO_UPDATE}/drivers/{source-name}/examples/state.json", + // "{PATH_TO_UPDATE}/state.json", ] } ] @@ -387,7 +418,7 @@ If using the Iceberg as destination, you have to generate the jar file for Java |`mode` | `auto`, `debug` | | `args` | `sync` , `discover`, `check` | -Update `PATH_TO_UPDATE` with the location where OLake project lives inside your system. For example: +Update `PATH_TO_UPDATE` with the absolute path where the OLake project is located on your system. For example: ```json "program": "/Users/john/Desktop/projects/olake/drivers/mongodb/main.go", @@ -396,17 +427,13 @@ Update `PATH_TO_UPDATE` with the location where OLake project lives inside your "/Users/john/Desktop/projects/olake/drivers/mongodb/examples/source.json", ... ``` -Now, setup debug points in the codebase and click "Launch Go Code". +Now, set up debug points in the codebase and click "Launch Go Code". ![Debug](/img/docs/getting-started/debug.webp) - +## 3. OLake UI Setup - - - - - +**UI setup**: Please follow the setup instructions at [https://github.com/datazip-inc/olake-ui/](https://github.com/datazip-inc/olake-ui/) \ No newline at end of file From 69c5b05c6e0f400576fdf68f2dc8b30615b28aeb Mon Sep 17 00:00:00 2001 From: Shubham Baldava Date: Fri, 19 Sep 2025 14:58:29 +0530 Subject: [PATCH 13/17] fix: modified minor thing in helm chart doc --- docs/install/kubernetes.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/install/kubernetes.mdx b/docs/install/kubernetes.mdx index 62b4abda..3ecb6f3e 100644 --- a/docs/install/kubernetes.mdx +++ b/docs/install/kubernetes.mdx @@ -215,6 +215,7 @@ External PostgreSQL databases can be used instead of the built-in postgresql dep **Requirements:** - PostgreSQL 12+ with `btree_gin` extension enabled +- You need to enable this with command `CREATE EXTENSION IF NOT EXISTS btree_gin;`, then run `\dx` to verify if its enabled. - Both OLake and Temporal databases created on the PostgreSQL instance - Network connectivity from Kubernetes cluster to PostgreSQL instance From bcc0f257a392a3778a43ea2b51cf12fcf2121beb Mon Sep 17 00:00:00 2001 From: Shubham Baldava Date: Fri, 19 Sep 2025 14:59:54 +0530 Subject: [PATCH 14/17] fix: modified minor thing in helm chart doc --- docs/install/kubernetes.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/install/kubernetes.mdx b/docs/install/kubernetes.mdx index 3ecb6f3e..16c86176 100644 --- a/docs/install/kubernetes.mdx +++ b/docs/install/kubernetes.mdx @@ -215,7 +215,7 @@ External PostgreSQL databases can be used instead of the built-in postgresql dep **Requirements:** - PostgreSQL 12+ with `btree_gin` extension enabled -- You need to enable this with command `CREATE EXTENSION IF NOT EXISTS btree_gin;`, then run `\dx` to verify if its enabled. +- This can be enabled with `CREATE EXTENSION IF NOT EXISTS btree_gin;`, then run `\dx` to verify if its enabled. - Both OLake and Temporal databases created on the PostgreSQL instance - Network connectivity from Kubernetes cluster to PostgreSQL instance From 8bc7a389bbfdea46664a8542a5f94cdbebb92ccc Mon Sep 17 00:00:00 2001 From: Shubham Baldava Date: Fri, 19 Sep 2025 15:26:52 +0530 Subject: [PATCH 15/17] fix: spellings fixed --- blog/2025-01-07-olake-architecture.mdx | 2 +- ...ow-to-set-up-postgresql-cdc-on-aws-rds.mdx | 2 +- blog/2025-05-08-olake-airflow-on-ec2.mdx | 20 +++++++-------- ...25-08-29-deploying-olake-on-kubernetes.mdx | 4 +-- ...ache-hive-vs-apache-iceberg-comparison.mdx | 2 +- docs/community/issues-and-prs.mdx | 2 +- docs/connectors/mongodb/index.mdx | 10 ++++---- docs/connectors/oracle/index.mdx | 6 ++--- docs/connectors/postgres/index.mdx | 6 ++--- docs/release/v0.1.0-v0.1.1.mdx | 2 +- docs/release/v0.1.6-v0.1.8.mdx | 4 +-- docs/understanding/terminologies/olake.mdx | 24 +++++++++--------- iceberg/2025-02-21-mor-vs-cow.mdx | 2 +- ...rg-partitioning-and-writing-strategies.mdx | 10 ++++---- src/data/meetup/6th-meetup.json | 2 +- ...tributed-stream-processing-in-practice.tsx | 2 +- .../2025/15/{parition.png => partition.png} | Bin 17 files changed, 50 insertions(+), 50 deletions(-) rename static/img/blog/2025/15/{parition.png => partition.png} (100%) diff --git a/blog/2025-01-07-olake-architecture.mdx b/blog/2025-01-07-olake-architecture.mdx index 5fb14fc7..d151f720 100644 --- a/blog/2025-01-07-olake-architecture.mdx +++ b/blog/2025-01-07-olake-architecture.mdx @@ -15,7 +15,7 @@ update: [18.02.2025] When building [OLake](https://olake.io/), our goal was simple: *Fastest DB to Data LakeHouse (Apache Iceberg to start) data pipeline.* -Checkout GtiHub repository for OLake - [https://github.com/datazip-inc/olake](https://github.com/datazip-inc/olake) +Checkout GitHub repository for OLake - [https://github.com/datazip-inc/olake](https://github.com/datazip-inc/olake) Over time, many of us who’ve worked with data pipelines have dealt with the toil of building one-off ETL scripts, battling performance bottlenecks, or worrying about vendor lock-in. diff --git a/blog/2025-04-23-how-to-set-up-postgresql-cdc-on-aws-rds.mdx b/blog/2025-04-23-how-to-set-up-postgresql-cdc-on-aws-rds.mdx index 16974be3..3ace6039 100644 --- a/blog/2025-04-23-how-to-set-up-postgresql-cdc-on-aws-rds.mdx +++ b/blog/2025-04-23-how-to-set-up-postgresql-cdc-on-aws-rds.mdx @@ -249,7 +249,7 @@ Essential monitoring includes: ### **Does OLake take care of Full-historical snapshot/replication before CDC? How fast is it?** -OLake has fastest optimised historical load: +OLake has fastest optimized historical load: - OLake has Historical-load + CDC mode for this - Tables are chunked into smaller pieces to make it parallel and recoverable from failures - Any new table additions is also taken care of automatically. diff --git a/blog/2025-05-08-olake-airflow-on-ec2.mdx b/blog/2025-05-08-olake-airflow-on-ec2.mdx index 51c14618..ac351517 100644 --- a/blog/2025-05-08-olake-airflow-on-ec2.mdx +++ b/blog/2025-05-08-olake-airflow-on-ec2.mdx @@ -14,7 +14,7 @@ tags: [olake] At OLake, we're building tools to make data integration seamless. Today, we're excited to show you how to leverage your existing Apache Airflow setup to automate OLake data synchronization tasks directly on your EC2 Server! -Olake is designed to efficiently sync data from various sources to your chosen destinations. This guide provides an Airflow DAG (Directed Acyclic Graph) that orchestrates the Olake sync command by provisioning a dedicated EC2 instance, executing Olake within a Docker container and handling configuration and state persistence through Amazon S3. +OLake is designed to efficiently sync data from various sources to your chosen destinations. This guide provides an Airflow DAG (Directed Acyclic Graph) that orchestrates the OLake sync command by provisioning a dedicated EC2 instance, executing OLake within a Docker container and handling configuration and state persistence through Amazon S3. This post assumes you already have: @@ -46,7 +46,7 @@ Before deploying the DAG, ensure the following are in place: * Click on the + icon to `Add a new record ` * Select the `Connection Type` to be `Amazon Web Services ` * Enter a `Connection Id` (this would be later used in `AWS_CONNECTION_ID` variable in the DAG) - * **(Important)** Either enter `AWS Access Key Id` and `AWS Secret Access Key` or user can just attach an AWS IAM Role to the Airflow instance (with sufficient permissions as below code snippet). If no Access Keys are used, default boto3 behaviour is used. + * **(Important)** Either enter `AWS Access Key Id` and `AWS Secret Access Key` or user can just attach an AWS IAM Role to the Airflow instance (with sufficient permissions as below code snippet). If no Access Keys are used, default boto3 behavior is used. * Click **Save**. @@ -132,7 +132,7 @@ Before deploying the DAG, ensure the following are in place: ``` -* **SSH Connection (`SSH_CONNECTION_ID` in the DAG):** This connection allows Airflow to securely connect to the dynamically created EC2 instance to execute the Olake setup and run commands. +* **SSH Connection (`SSH_CONNECTION_ID` in the DAG):** This connection allows Airflow to securely connect to the dynamically created EC2 instance to execute the OLake setup and run commands. * Still in the Airflow UI (`Admin` -> `Connections`), click the `+` icon to add another new record. * Set the **Connection Type** to **SSH**. * Enter a **Connection Id** (e.g., `ssh_ec2_olake`). This exact ID will be used for the `SSH_CONNECTION_ID` variable in your DAG. @@ -172,11 +172,11 @@ Before deploying the DAG, ensure the following are in place: -#### 3. **Amazon S3 Setup for Olake Configurations and State:** -* **S3 Bucket (`S3_BUCKET_NAME` in the DAG):** Create an S3 bucket where Olake's configuration files and persistent state file will be stored. -* **S3 Prefix for Configurations (`S3_PREFIX` in the DAG):** Decide on a "folder" (S3 prefix) within your bucket where your Olake configuration files will reside (e.g., `olake/projectA/configs/`). +#### 3. **Amazon S3 Setup for OLake Configurations and State:** +* **S3 Bucket (`S3_BUCKET_NAME` in the DAG):** Create an S3 bucket where OLake's configuration files and persistent state file will be stored. +* **S3 Prefix for Configurations (`S3_PREFIX` in the DAG):** Decide on a "folder" (S3 prefix) within your bucket where your OLake configuration files will reside (e.g., `olake/projectA/configs/`). -* **Upload Olake Configuration Files:** Before running the DAG, you must upload your Olake `source.json`, `streams.json`, and `destination.json` files to the S3 bucket under the prefix you defined. The DAG's SSH script will sync these files to the EC2 instance. Please visit[ OLake Docs](https://olake.io/docs) website to learn how the[ source](https://olake.io/docs/connectors/overview) and[ destinations](https://olake.io/docs/writers/overview) can be set up. +* **Upload OLake Configuration Files:** Before running the DAG, you must upload your OLake `source.json`, `streams.json`, and `destination.json` files to the S3 bucket under the prefix you defined. The DAG's SSH script will sync these files to the EC2 instance. Please visit[ OLake Docs](https://olake.io/docs) website to learn how the[ source](https://olake.io/docs/connectors/overview) and[ destinations](https://olake.io/docs/writers/overview) can be set up. We need to generate `streams.json` beforehand using the OLake `discover` command against your source database. * Streams Generation Guides: @@ -185,9 +185,9 @@ We need to generate `streams.json` beforehand using the OLake `discover` command * The content of this file will be placed within the `streams.json` file. #### 4. **EC2 Instance IAM Role (`IAM_ROLE_NAME` in the DAG):** -The EC2 instances launched by Airflow (which will act as the worker nodes for Olake) need their own set of permissions to perform their tasks. This is achieved by assigning them an IAM Instance Profile. This instance profile must have an attached IAM policy granting permissions to: -* Access Amazon S3 to download Olake configuration files. -* Access Amazon S3 to read and write the Olake state file. +The EC2 instances launched by Airflow (which will act as the worker nodes for OLake) need their own set of permissions to perform their tasks. This is achieved by assigning them an IAM Instance Profile. This instance profile must have an attached IAM policy granting permissions to: +* Access Amazon S3 to download OLake configuration files. +* Access Amazon S3 to read and write the OLake state file. ```json # s3_access_policy.json diff --git a/blog/2025-08-29-deploying-olake-on-kubernetes.mdx b/blog/2025-08-29-deploying-olake-on-kubernetes.mdx index effc19eb..a61165be 100644 --- a/blog/2025-08-29-deploying-olake-on-kubernetes.mdx +++ b/blog/2025-08-29-deploying-olake-on-kubernetes.mdx @@ -160,7 +160,7 @@ global: olake.io/workload-type: "memory-optimized" 456: olake.io/workload-type: "general-purpose" - # Default scheduling behaviour + # Default scheduling behavior 789: {} ``` @@ -170,7 +170,7 @@ A typical enterprise scenario can be considered: a massive customer transactions Without node mapping, both operations might be scheduled on the same node by Kubernetes, causing memory contention. Or worse, the memory-hungry sync job might be put on a small node where an out-of-memory error would cause it to fail. -With JobID-based mapping, the heavy sync is necessarily landed on a node with label `olake.io/workload-type: "memory-optimized"` where completion is achieved in 30 minutes instead of timing out. The other sync job are run happily on smaller, cheaper nodes, finishing without waste. +With JobID-based mapping, the heavy sync is necessarily landed on a node with label `olake.io/workload-type: "memory-optimized"` where completion is achieved in 30 minutes instead of timing out. The other sync jobs are run happily on smaller, cheaper nodes, finishing without waste. ### The Progressive Advantage diff --git a/blog/2025-09-15-apache-hive-vs-apache-iceberg-comparison.mdx b/blog/2025-09-15-apache-hive-vs-apache-iceberg-comparison.mdx index 64d77fc7..34183b97 100644 --- a/blog/2025-09-15-apache-hive-vs-apache-iceberg-comparison.mdx +++ b/blog/2025-09-15-apache-hive-vs-apache-iceberg-comparison.mdx @@ -111,7 +111,7 @@ Iceberg was built with **schema evolution in mind**. It tracks columns by unique ### Partitioning -![Partitioning Comparison](/img/blog/2025/15/parition.png) +![Partitioning Comparison](/img/blog/2025/15/partition.png) Partitioning is another area where Hive shows its **Hadoop heritage**. Data in Hive is partitioned by creating directories for each partition value, such as year=2023/month=01/day=15. While this can improve query performance when filters align with the partition structure, it puts enormous pressure on design decisions made at table creation time. A poorly chosen partitioning strategy can create millions of tiny directories and files, degrading performance and requiring painful rewrites to fix. diff --git a/docs/community/issues-and-prs.mdx b/docs/community/issues-and-prs.mdx index 39f57ca4..7e623a99 100644 --- a/docs/community/issues-and-prs.mdx +++ b/docs/community/issues-and-prs.mdx @@ -81,7 +81,7 @@ git add . #### 2. verify -Perform `git status` or `git diff` to see what all changes occured, which all files are ready to be committed (or staged) +Perform `git status` or `git diff` to see what all changes occurred, which all files are ready to be committed (or staged) #### 3. Commit your changes diff --git a/docs/connectors/mongodb/index.mdx b/docs/connectors/mongodb/index.mdx index 0599f2a6..12ac232b 100644 --- a/docs/connectors/mongodb/index.mdx +++ b/docs/connectors/mongodb/index.mdx @@ -50,12 +50,12 @@ For local setup, follow **[MongoDB via Docker Compose](/docs/connectors/mongodb/ - + ### 1. Navigate to the Source Configuration Page 1. Complete the [OLake UI Setup Guide](/docs/getting-started/olake-ui) -2. After logging in to the OlakeUI, select the `Sources` tab from the left sidebar. +2. After logging in to the OLake UI, select the `Sources` tab from the left sidebar. 3. Click **`Create Source`** on the top right corner. 4. Select **MongoDB** from the connector dropdown 5. Provide a name for this source. @@ -63,7 +63,7 @@ For local setup, follow **[MongoDB via Docker Compose](/docs/connectors/mongodb/ ### 2. Provide Configuration Details - Enter MongoDB credentials. -![Olake UI MongoDB Source Setup](/img/connectors/mongodb/mongodb-ui-setup.webp) +![OLake UI MongoDB Source Setup](/img/connectors/mongodb/mongodb-ui-setup.webp) | Field | Description | Example Value | | ----------------------------- | -------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | @@ -86,11 +86,11 @@ For local setup, follow **[MongoDB via Docker Compose](/docs/connectors/mongodb/ - + ### 1. Create Configuration File - - Once the Olake CLI is setup, create a folder to store configuration files such as `source.json` and `destination.json`. + - Once the OLake CLI is setup, create a folder to store configuration files such as `source.json` and `destination.json`. ### 2. Provide Configuration Details diff --git a/docs/connectors/oracle/index.mdx b/docs/connectors/oracle/index.mdx index bd6b32ae..15b22220 100644 --- a/docs/connectors/oracle/index.mdx +++ b/docs/connectors/oracle/index.mdx @@ -28,12 +28,12 @@ The OLake Oracle Source connector supports two synchronization modes. It offers - + ### 1. Navigate to the Source Configuration Page 1. Complete the [OLake UI Setup Guide](/docs/getting-started/olake-ui) -2. After logging in to the OlakeUI, select the `Sources` tab from the left sidebar. +2. After logging in to the OLake UI, select the `Sources` tab from the left sidebar. 3. Click **`Create Source`** on the top right corner. 4. Select **Oracle** from the connector dropdown 5. Provide a name for this source. @@ -75,7 +75,7 @@ Oracle Database automatically converts unquoted lowercase usernames to uppercase ### 1. Create Configuration File - - Once the Olake CLI is setup, create a folder to store configuration files such as `source.json` and `destination.json`. + - Once the OLake CLI is setup, create a folder to store configuration files such as `source.json` and `destination.json`. The `source.json` file for oracle must contain these mandatory fields. diff --git a/docs/connectors/postgres/index.mdx b/docs/connectors/postgres/index.mdx index 13060faf..23f8f3da 100644 --- a/docs/connectors/postgres/index.mdx +++ b/docs/connectors/postgres/index.mdx @@ -38,12 +38,12 @@ The OLake Postgres Source connector supports multiple synchronization modes. It - + ### 1. Navigate to the Source Configuration Page 1. Complete the [OLake UI Setup Guide](/docs/getting-started/olake-ui) -2. After logging in to the OlakeUI, select the `Sources` tab from the left sidebar. +2. After logging in to the OLake UI, select the `Sources` tab from the left sidebar. 3. Click **`Create Source`** on the top right corner. 4. Select **Postgres** from the connector dropdown 5. Provide a name for this source. @@ -81,7 +81,7 @@ The OLake Postgres Source connector supports multiple synchronization modes. It ### 1. Create Configuration File - - Once the Olake CLI is setup, create a folder to store configuration files such as `source.json` and `destination.json`. + - Once the OLake CLI is setup, create a folder to store configuration files such as `source.json` and `destination.json`. The `source.json` file for postgres must contain these mandatory fields. diff --git a/docs/release/v0.1.0-v0.1.1.mdx b/docs/release/v0.1.0-v0.1.1.mdx index 06319d27..a0af8617 100644 --- a/docs/release/v0.1.0-v0.1.1.mdx +++ b/docs/release/v0.1.0-v0.1.1.mdx @@ -17,7 +17,7 @@ June 13 – June 18, 2025 1. **Driver Releaser -**
Launches the OLake Driver Releaser tool for packaging and distributing OLake connectors, making driver updates seamless across environments. -2. **Strict CDC Sync Mode -**
Adds a new mode that applies only change events and skips any full‑refresh backfill during syncs, guaranteeing CDC‑only behaviour. This reduces load on sources/targets and avoids accidental re‑snapshots in continuous pipelines. +2. **Strict CDC Sync Mode -**
Adds a new mode that applies only change events and skips any full‑refresh backfill during syncs, guaranteeing CDC‑only behavior. This reduces load on sources/targets and avoids accidental re‑snapshots in continuous pipelines. 3. **Discover with Merge -**
Schema discovery now merges results into an existing streams.json so prior selections and settings are preserved while new streams are added. This minimizes manual edits when onboarding new tables or evolving schemas. diff --git a/docs/release/v0.1.6-v0.1.8.mdx b/docs/release/v0.1.6-v0.1.8.mdx index 694896fa..f2059bf2 100644 --- a/docs/release/v0.1.6-v0.1.8.mdx +++ b/docs/release/v0.1.6-v0.1.8.mdx @@ -7,7 +7,7 @@ July 17 – July 30, 2025 1. **Incremental Sync: MongoDB and Oracle -**
Added incremental synchronisation support for MongoDB and Oracle sources. This adds change‑only replication for both the sources so OLake transfers new/updated documents since the last run, reducing latency and data volume for recurring pipelines. -2. **Oracle Connector Filter & Chunking -**
Added filter support and optimised chunking strategy for the Oracle connector. This ensures query-level filtering and an optimized chunking strategy to the Oracle connector, ensuring only relevant rows are fetched and evenly sized data chunks maximize parallel throughput. +2. **Oracle Connector Filter & Chunking -**
Added filter support and optimized chunking strategy for the Oracle connector. This ensures query-level filtering and an optimized chunking strategy to the Oracle connector, ensuring only relevant rows are fetched and evenly sized data chunks maximize parallel throughput. 3. **Oracle multi cursor support for incremental Sync -**
OLake incremental sync can now be configured with a primary and secondary cursor column of the same datatype, where the secondary is used only if the primary cursor value is NULL, reducing missed changes in sparse or null‑heavy tables. @@ -29,4 +29,4 @@ July 17 – July 30, 2025 2. **Discovery Cursor Fix -**
Fixes merging of cursor fields in the new discover flow so schema and cursor metadata are recorded consistently. This avoids missing or duplicated cursor information when building stream definitions. -3. **Postgres CDC Reliability -**
Improved Postgres CDC behaviour by advancing LSN during full load when cache is invalid. \ No newline at end of file +3. **Postgres CDC Reliability -**
Improved Postgres CDC behavior by advancing LSN during full load when cache is invalid. \ No newline at end of file diff --git a/docs/understanding/terminologies/olake.mdx b/docs/understanding/terminologies/olake.mdx index fa5d5076..b044c4dd 100644 --- a/docs/understanding/terminologies/olake.mdx +++ b/docs/understanding/terminologies/olake.mdx @@ -60,7 +60,7 @@ Normalization must be enabled at the schema configuration step per table or stre :::
Olake Partition
@@ -70,14 +70,14 @@ Normalization must be enabled at the schema configuration step per table or stre
Olake Partition output
*After state: Sync done, when normalization is on.*
Olake Partition output
@@ -90,7 +90,7 @@ Normalization must be enabled at the schema configuration step per table or stre ### 2. Sync Modes -Sync modes in Olake define the strategy used to replicate data from a source system to a destination. Each mode represents a different approach to data synchronization, with specific behaviours, guarantees, and performance characteristics. +Sync modes in OLake define the strategy used to replicate data from a source system to a destination. Each mode represents a different approach to data synchronization, with specific behaviors, guarantees, and performance characteristics. OLake supports 4 distinct sync modes: @@ -104,7 +104,7 @@ OLake supports 4 distinct sync modes: A delta-sync strategy that only processes new or changed records since the last sync. Requires primary (mandatory) and secondary cursor (optional) fields for change detection. Similar to CDC sync, an initial full-refresh takes place in this as well. :::info Cursor fields are columns in the source table used to track the last synced records. \ - Olake allows setting up to two cursor fields: + OLake allows setting up to two cursor fields: - **Primary Cursor:** This is the mandatory cursor field through which the changes in the records are captured and compared. - **Secondary Cursor:** In case primary cursor's value is null, then the value of secondary cursor is considered if provided. ::: @@ -114,7 +114,7 @@ OLake supports 4 distinct sync modes:
Olake Partition output
@@ -137,7 +137,7 @@ The data filter feature allows selective ingestion from source databases by appl
Olake Partition output
@@ -153,7 +153,7 @@ For more information, refer to [Schema Evolution Feature](/docs/features?tab=sch
Olake Partition output
@@ -174,14 +174,14 @@ Unlike traditional systems like Hive, Iceberg's approach uses "hidden partitioni *For input as given below,*
Olake Partition
-*As seen below, partition on created_at field, tranformation using 'day' has been done.* +*As seen below, partition on created_at field, transformation using 'day' has been done.*
Olake Partition output
@@ -221,7 +221,7 @@ User has to start with job creation, which will be followed with source configur
Olake Partition output
diff --git a/iceberg/2025-02-21-mor-vs-cow.mdx b/iceberg/2025-02-21-mor-vs-cow.mdx index edb5b613..fc7bdbf6 100644 --- a/iceberg/2025-02-21-mor-vs-cow.mdx +++ b/iceberg/2025-02-21-mor-vs-cow.mdx @@ -113,7 +113,7 @@ While positional deletes are efficient to read but slower to write, equality del ### Performance Considerations -The key to maintaining good performance with Merge-on-Read tables lies in regular compaction. Over time, as more changes accumulate in delete files and new data files, query performance can start to degrade. Compaction helps by periodically combining all these changes with the base files, creating a fresh, optimised set of data files. +The key to maintaining good performance with Merge-on-Read tables lies in regular compaction. Over time, as more changes accumulate in delete files and new data files, query performance can start to degrade. Compaction helps by periodically combining all these changes with the base files, creating a fresh, optimized set of data files. ### When to Choose Merge-on-Read diff --git a/iceberg/2025-06-24-iceberg-partitioning-and-writing-strategies.mdx b/iceberg/2025-06-24-iceberg-partitioning-and-writing-strategies.mdx index 54f5b394..9aabc2c6 100644 --- a/iceberg/2025-06-24-iceberg-partitioning-and-writing-strategies.mdx +++ b/iceberg/2025-06-24-iceberg-partitioning-and-writing-strategies.mdx @@ -9,7 +9,7 @@ tags: [iceberg, partitioning] ![iceberg-partitioning-and-writing-strategies](/img/blog/cover/iceberg-partitioning-and-writing-strategies-cover.webp) -Ever wondered how partitioning in big table formats like Apache Iceberg works out? And what partitioned writing strategies Iceberg can assist you with during ETL? Iceberg handles data partitioning very [differently from any other data lake format (hive paritioning)](/iceberg/hive-partitioning-vs-iceberg-partitioning). +Ever wondered how partitioning in big table formats like Apache Iceberg works out? And what partitioned writing strategies Iceberg can assist you with during ETL? Iceberg handles data partitioning very [differently from any other data lake format (hive partitioning)](/iceberg/hive-partitioning-vs-iceberg-partitioning). In this blog, we will dive into: @@ -56,7 +56,7 @@ AND region IN ('US', 'EU') AND order_amount > 1000 The query engine reads the latest `metadata.json`, and identifies the current `snapshot-id` and retrieves the snapshot’s `manifest-list` location. Then loads the partition specification to understand the partitioning, here `[order_date, region]` from the metadata along with the schema of the table. -- Here `0007-hash.metadata.json` depicts the most recent state of the table, and in case you are familiar with using Apache Iceberg, you should know that `0000-hash.metadata.json` is created when the table is first initialised. +- Here `0007-hash.metadata.json` depicts the most recent state of the table, and in case you are familiar with using Apache Iceberg, you should know that `0000-hash.metadata.json` is created when the table is first initialized. Then it moves on to read the manifest-list, its current snapshot, here `snap-0001-hash.avro`. It provides it with the high-level statistics for each manifest file. The engine examines partition bounds i.e., `lower_bound` and `upper_bound`, across all partitions in that manifest. @@ -81,7 +81,7 @@ Here, Again, you see that without even reading individual `data-file-n.parquet` file, we decide whether to read it or to skip it. This is called **Partition Pruning**, an extremely important partitioning feature of Iceberg as it gifts it with huge efficiency gains. As, here itself, you can see it reads only 1 data file out of all 4 data files, trust me, this comes out to be a huge boon in production level. -Along with it, there is a very important concept of **Scan Planning** with filters like **bloom** which transforms the SQL query into a highly optimised, parallel execution plan, but that would go out of scope for this blog, so won’t be discussing about it. +Along with it, there is a very important concept of **Scan Planning** with filters like **bloom** which transforms the SQL query into a highly optimized, parallel execution plan, but that would go out of scope for this blog, so won’t be discussing about it. ## Real World Scale: Manifest Explosion @@ -111,7 +111,7 @@ It creates a dedicated Rolling File Writer for each `PartitionKey` and maps it i ![iceberg-partitioning-and-writing-strategies-4](/img/blog/2025/06/iceberg-partitioning-and-writing-strategies-4.webp) -It is a memory-efficient, sequential partition writing strategy that operates under the fundamental constraint of pre-clustered data ordering. This represents a low-memory, high-throughput approach optimised for scenarios where incoming data is already sorted by partition specification and partition values, enabling single-active-writer semantics with minimal memory footprint. +It is a memory-efficient, sequential partition writing strategy that operates under the fundamental constraint of pre-clustered data ordering. This represents a low-memory, high-throughput approach optimized for scenarios where incoming data is already sorted by partition specification and partition values, enabling single-active-writer semantics with minimal memory footprint. With each spec, all records belonging to the same partition value combination must be contiguous. It has O(1) memory usage regardless of partition count with only one file handle and buffer active at any time and supports PartitionSpec evolution through multi-spec capability, but it requires upstream clustering/sorting of data and cannot handle streaming data. @@ -139,7 +139,7 @@ Thus summarising, Partitioning is the heart of Iceberg’s performance, scalability, and metadata efficiency. -Whether you're querying massive datasets or writing from high-volume streams, Iceberg's PartitionSpec, Manifest Lists, and Writing Strategies give you the tools to build highly optimised data pipelines. +Whether you're querying massive datasets or writing from high-volume streams, Iceberg's PartitionSpec, Manifest Lists, and Writing Strategies give you the tools to build highly optimized data pipelines. And remember: all of this happens _without you having to worry about folders, MSCK repairs, or schema breakage_. diff --git a/src/data/meetup/6th-meetup.json b/src/data/meetup/6th-meetup.json index 1f7898a1..0cc9b81c 100644 --- a/src/data/meetup/6th-meetup.json +++ b/src/data/meetup/6th-meetup.json @@ -1,5 +1,5 @@ { - "summary": "The sixth OLake community meetup (28 April 2025) centred on a real-world production story from PhysicsWallah and a deeper dive into OLake’s roadmap. Guest speaker Adish Jain walked the community through PhysicsWallah migration from a Redshift warehouse to an Iceberg-based lakehouse, the pains they faced with Debezium, and how OLake solved them with faster, resumable full loads, direct Iceberg ingestion, and automatic schema evolution. A live demo showed MongoDB-to-Iceberg ingestion running in Kubernetes. Shubham Baldava then unpacked OLake’s Golang + Java architecture, explained plans to shift the Iceberg writer to Go/Rust for lower memory use, previewed an upcoming UI, and announced mid-level SMT transformations arriving within three months.", + "summary": "The sixth OLake community meetup (28 April 2025) centered on a real-world production story from PhysicsWallah and a deeper dive into OLake’s roadmap. Guest speaker Adish Jain walked the community through PhysicsWallah migration from a Redshift warehouse to an Iceberg-based lakehouse, the pains they faced with Debezium, and how OLake solved them with faster, resumable full loads, direct Iceberg ingestion, and automatic schema evolution. A live demo showed MongoDB-to-Iceberg ingestion running in Kubernetes. Shubham Baldava then unpacked OLake’s Golang + Java architecture, explained plans to shift the Iceberg writer to Go/Rust for lower memory use, previewed an upcoming UI, and announced mid-level SMT transformations arriving within three months.", "chaptersAndTopics": [ { "title": "Introduction and Agenda", diff --git a/src/pages/webinar/w-8-distributed-stream-processing-in-practice.tsx b/src/pages/webinar/w-8-distributed-stream-processing-in-practice.tsx index 4d2b761c..bc6053a9 100644 --- a/src/pages/webinar/w-8-distributed-stream-processing-in-practice.tsx +++ b/src/pages/webinar/w-8-distributed-stream-processing-in-practice.tsx @@ -27,7 +27,7 @@ const hosts = [ { name: "Hasan Geren", role: "Data Engineer @ ProcurePro", - bio: "Hasan's career includes Data Engineering, where he has: • Designed and optimised 𝘀𝗰𝗮𝗹𝗮𝗯𝗹𝗲 𝗱𝗮𝘁𝗮𝗯𝗮𝘀𝗲𝘀 and cloud storage architectures. • Built 𝗹𝗼𝘄-𝗹𝗮𝘁𝗲𝗻𝗰𝘆 𝗱𝗮𝘁𝗮 𝗽𝗶𝗽𝗲𝗹𝗶𝗻𝗲𝘀 to support real-time applications and analytics dashboards. • Developed AI/ML-based solutions, including 𝗟𝗦𝗧𝗠 𝗺𝗼𝗱𝗲𝗹𝘀 and 𝗿𝗲𝗰𝗼𝗺𝗺𝗲𝗻𝗱𝗮𝘁𝗶𝗼𝗻 𝘀𝘆𝘀𝘁𝗲𝗺𝘀 to enhance user engagement. • Collaborated across teams to drive actionable insights, ensuring data solutions align with business goals.", + bio: "Hasan's career includes Data Engineering, where he has: • Designed and optimized 𝘀𝗰𝗮𝗹𝗮𝗯𝗹𝗲 𝗱𝗮𝘁𝗮𝗯𝗮𝘀𝗲𝘀 and cloud storage architectures. • Built 𝗹𝗼𝘄-𝗹𝗮𝘁𝗲𝗻𝗰𝘆 𝗱𝗮𝘁𝗮 𝗽𝗶𝗽𝗲𝗹𝗶𝗻𝗲𝘀 to support real-time applications and analytics dashboards. • Developed AI/ML-based solutions, including 𝗟𝗦𝗧𝗠 𝗺𝗼𝗱𝗲𝗹𝘀 and 𝗿𝗲𝗰𝗼𝗺𝗺𝗲𝗻𝗱𝗮𝘁𝗶𝗼𝗻 𝘀𝘆𝘀𝘁𝗲𝗺𝘀 to enhance user engagement. • Collaborated across teams to drive actionable insights, ensuring data solutions align with business goals.", image: "/img/authors/hasan.webp", linkedin: "https://www.linkedin.com/in/hasan-geren/", }, diff --git a/static/img/blog/2025/15/parition.png b/static/img/blog/2025/15/partition.png similarity index 100% rename from static/img/blog/2025/15/parition.png rename to static/img/blog/2025/15/partition.png From 72b1e0d529c1d6f400b8f35384f780ac9c35e334 Mon Sep 17 00:00:00 2001 From: Shubham Baldava Date: Fri, 19 Sep 2025 15:31:22 +0530 Subject: [PATCH 16/17] fix: spellings fixed --- airflow/olake_sync_from_source.py | 10 ++++---- airflow/olake_sync_from_source_ec2.py | 2 +- blog/2025-05-08-olake-airflow-on-ec2.mdx | 24 +++++++++---------- ...lding-open-data-lakehouse-from-scratch.mdx | 4 ++-- blog/authors.yml | 2 +- docs/connectors/mongodb/cdc_setup.mdx | 2 +- docs/connectors/mysql/index.mdx | 2 +- docs/getting-started/playground.mdx | 8 +++---- docs/install/docker-cli.mdx | 2 +- docs/release/v0.1.0-v0.1.1.mdx | 2 +- docs/understanding/terminologies/olake.mdx | 2 +- docs/writers/iceberg/azure.mdx | 16 ++++++------- docs/writers/parquet/troubleshoot.mdx | 2 +- iceberg/2025-05-08-olake-iceberg-athena.mdx | 8 +++---- iceberg/2025-05-28-olake-glue-snowflake.mdx | 6 ++--- src/theme/DocPaginator/footerNavigations.js | 2 +- 16 files changed, 47 insertions(+), 47 deletions(-) diff --git a/airflow/olake_sync_from_source.py b/airflow/olake_sync_from_source.py index 3db5eb4e..14e99bf3 100644 --- a/airflow/olake_sync_from_source.py +++ b/airflow/olake_sync_from_source.py @@ -14,11 +14,11 @@ # This connection tells Airflow how to authenticate with your K8s cluster. KUBERNETES_CONN_ID = "kubernetes_default" # <-- EDIT THIS LINE -# !!! IMPORTANT: Set this to the Kubernetes namespace where Olake pods should run !!! +# !!! IMPORTANT: Set this to the Kubernetes namespace where OLake pods should run !!! # Ensure ConfigMaps and the PVC exist or will be created in this namespace. TARGET_NAMESPACE = "olake" # <-- EDIT THIS LINE -# !!! IMPORTANT: Set this to the correct Olake image for your source database !!! +# !!! IMPORTANT: Set this to the correct OLake image for your source database !!! # Find images at: https://hub.docker.com/u/olakego # Examples: "olakego/source-mongodb:latest", "olakego/source-mysql:latest", "olakego/source-postgres:latest" OLAKE_IMAGE = "olakego/source-db:latest" # <-- EDIT THIS LINE @@ -54,9 +54,9 @@ # Generic tags tags=["kubernetes", "olake", "etl", "sync"], doc_md=""" - ### Olake Sync DAG + ### OLake Sync DAG - This DAG runs the Olake `sync` command using pre-created ConfigMaps + This DAG runs the OLake `sync` command using pre-created ConfigMaps for source, destination, and streams configuration. It ensures a persistent volume claim exists before running the sync task. @@ -249,7 +249,7 @@ def create_pvc_with_hook(**context): ), ], - # Use the container's default entrypoint (should be the Olake binary) + # Use the container's default entrypoint (should be the OLake binary) cmds=None, # Pass arguments for the 'sync' command arguments=[ diff --git a/airflow/olake_sync_from_source_ec2.py b/airflow/olake_sync_from_source_ec2.py index 8142dff2..a3b2f4b9 100644 --- a/airflow/olake_sync_from_source_ec2.py +++ b/airflow/olake_sync_from_source_ec2.py @@ -237,7 +237,7 @@ def run_olake_docker_via_ssh(ti, ssh_conn_id, command): fi echo "INFO: State file uploaded successfully." -# Now check the Olake exit code +# Now check the OLake exit code if [ $OLAKE_EXIT_CODE -ne 0 ]; then echo "ERROR: ETL job failed with exit code $OLAKE_EXIT_CODE." exit $OLAKE_EXIT_CODE diff --git a/blog/2025-05-08-olake-airflow-on-ec2.mdx b/blog/2025-05-08-olake-airflow-on-ec2.mdx index ac351517..d512d11a 100644 --- a/blog/2025-05-08-olake-airflow-on-ec2.mdx +++ b/blog/2025-05-08-olake-airflow-on-ec2.mdx @@ -290,7 +290,7 @@ OLAKE_IMAGE = "DOCKER_IMAGE_NAME" ## Recap of Values to Change: -To ensure the DAG runs correctly in your environment, you **must** update the following placeholder variables in the `olake_sync_from_source_ec2.py` (or your DAG file name) with your specific AWS and Olake details: +To ensure the DAG runs correctly in your environment, you **must** update the following placeholder variables in the `olake_sync_from_source_ec2.py` (or your DAG file name) with your specific AWS and OLake details: @@ -303,7 +303,7 @@ To ensure the DAG runs correctly in your environment, you **must** update the fo ### **EC2 Instance Configuration:** * `AMI_ID`: Replace with the actual AMI ID of a container-ready image (with Docker/containerd, aws-cli, jq) in your chosen `AWS_REGION_NAME`. -* `INSTANCE_TYPE`: (Optional) Select an appropriate EC2 instance type based on your Olake workload's resource needs (e.g., `t3.medium`, `m5.large`, or an ARM equivalent like `t4g.medium`). \ +* `INSTANCE_TYPE`: (Optional) Select an appropriate EC2 instance type based on your OLake workload's resource needs (e.g., `t3.medium`, `m5.large`, or an ARM equivalent like `t4g.medium`). \ The AMI tag we have hardcoded is EKS supported Ubuntu image with containerd and aws-cli pre-installed which are very crucial for the DAG to work. Another point to note is that since Graviton powered machines are cheaper compared to x86 machines, so the AMI already uses ARM architecture AMI. * `KEY_NAME`: Enter the name of the EC2 Key Pair you want to associate with the launched instances. This is the same key we have used while setting up the SSH Connection. * `SUBNET_ID`: Provide the ID of the VPC subnet where the EC2 instance should be launched. @@ -311,11 +311,11 @@ The AMI tag we have hardcoded is EKS supported Ubuntu image with containerd and * `IAM_ROLE_NAME`: Enter the **name** (not the ARN) of the IAM Instance Profile that grants the EC2 instance necessary permissions (primarily S3 access). * `DEFAULT_EC2_USER`: Change this if the default SSH username for your chosen `AMI_ID` is different from `ubuntu` (e.g., `ec2-user` for Amazon Linux). -### **ETL Configuration (S3 & Olake):** +### **ETL Configuration (S3 & OLake):** -* `S3_BUCKET_NAME`: The name of your S3 bucket where Olake configurations and state will be stored. -* `S3_BUCKET_PREFIX`: The "folder" path (prefix) within your S3 bucket for Olake files (e.g., `olake/projectA/configs/`). Remember the trailing slash if it's part of your intended structure. -* `OLAKE_IMAGE`: The full name of the Olake Docker image you want to use (e.g., `olakego/source-postgres:latest`, `olakego/source-mysql:latest`, `olakego/source-mongodb:latest`). +* `S3_BUCKET_NAME`: The name of your S3 bucket where OLake configurations and state will be stored. +* `S3_BUCKET_PREFIX`: The "folder" path (prefix) within your S3 bucket for OLake files (e.g., `olake/projectA/configs/`). Remember the trailing slash if it's part of your intended structure. +* `OLAKE_IMAGE`: The full name of the OLake Docker image you want to use (e.g., `olakego/source-postgres:latest`, `olakego/source-mysql:latest`, `olakego/source-mongodb:latest`). ### Deploying the DAG to Airflow @@ -325,7 +325,7 @@ The AMI tag we have hardcoded is EKS supported Ubuntu image with containerd and 2. Place the file into the `dags` folder recognized by your Airflow instance. The location of this folder depends on your Airflow setup. 3. Airflow automatically scans this folder. Wait a minute or two, and the DAG named `olake_sync_from_source` should appear in the Airflow UI. You might need to unpause it (toggle button on the left) if it loads in a paused state. -### Running Your Dynamic Olake Sync on EC2 +### Running Your Dynamic OLake Sync on EC2 1. **Access Airflow UI:** Navigate to your Airflow web UI. 2. **Find and Unpause DAG:** Locate the DAG, likely named `olake_sync_from_source` (or whatever `dag_id` you've set). If it's paused, click the toggle to unpause it. @@ -333,15 +333,15 @@ The AMI tag we have hardcoded is EKS supported Ubuntu image with containerd and 4. **Monitor the Run:** Click on the DAG run instance to view its progress in the Graph, Gantt, or Tree view. You will see the following sequence of tasks: * `create_ec2_instance_task`: This task will begin first, using the AWS connection to launch a new EC2 instance according to your DAG's configuration (AMI, instance type, networking, IAM role). Airflow will wait for this instance to be in a 'running' state. * `get_instance_ip_task`: Once the instance is running, this Python task will execute. It queries AWS to get the IP address or DNS name of the new EC2 instance, making it available for the next task. It also includes a pause to allow the SSH service on the new instance to become fully available. - * `run_olake_docker_task`: This is the core task where Olake runs. It will: + * `run_olake_docker_task`: This is the core task where OLake runs. It will: * Connect to the newly created EC2 instance via SSH using the configured SSH connection. * Execute the shell commands defined in `olake_ssh_command` within your DAG. This script prepares the EC2 instance by: * Creating necessary directories. - * Downloading your Olake configuration files and the latest state file from S3. - * Pulling the specified Olake Docker image using `ctr image pull`. - * Running the Olake `sync` process inside a Docker container using `ctr run ... /home/olake sync ...`. + * Downloading your OLake configuration files and the latest state file from S3. + * Pulling the specified OLake Docker image using `ctr image pull`. + * Running the OLake `sync` process inside a Docker container using `ctr run ... /home/olake sync ...`. * Uploading the updated state file back to S3 upon successful completion. - * You can click on this task instance in the Airflow UI and view its logs. These logs will contain the **real-time STDOUT and STDERR** from the SSH session on the EC2 instance, including the output from the Olake Docker container. This is where you'll see Olake's synchronization progress and any potential errors from the Olake process itself. + * You can click on this task instance in the Airflow UI and view its logs. These logs will contain the **real-time STDOUT and STDERR** from the SSH session on the EC2 instance, including the output from the OLake Docker container. This is where you'll see OLake's synchronization progress and any potential errors from the OLake process itself. * `terminate_ec2_instance_task`: After the `run_olake_docker_task` completes (whether it succeeds or fails, due to `trigger_rule=TriggerRule.ALL_DONE`), this final task will execute. It securely terminates the EC2 instance that was launched for this DAG run, ensuring you don't incur unnecessary AWS charges. ![olake-airflow-on-ec2-3](/img/blog/2025/05/olake-airflow-on-ec2-3.webp) diff --git a/blog/2025-08-12-building-open-data-lakehouse-from-scratch.mdx b/blog/2025-08-12-building-open-data-lakehouse-from-scratch.mdx index f03eb665..af1bdc47 100644 --- a/blog/2025-08-12-building-open-data-lakehouse-from-scratch.mdx +++ b/blog/2025-08-12-building-open-data-lakehouse-from-scratch.mdx @@ -39,7 +39,7 @@ Here's where things get really interesting. Unlike traditional ETL pipelines tha ## Step 1: Setting Up OLake - CDC Engine -Olake has one of its unique offerings the OLake UI, which we will be using for our setup. This is a user-friendly control center for managing data pipelines without relying heavily on CLI commands. It allows you to configure sources, destinations, and jobs visually, making the setup more accessible and less error-prone. Many organizations actively use OLake UI to reduce manual CLI work, streamline CDC pipelines, and adopt a no-code-friendly approach. +OLake has one of its unique offerings the OLake UI, which we will be using for our setup. This is a user-friendly control center for managing data pipelines without relying heavily on CLI commands. It allows you to configure sources, destinations, and jobs visually, making the setup more accessible and less error-prone. Many organizations actively use OLake UI to reduce manual CLI work, streamline CDC pipelines, and adopt a no-code-friendly approach. For our setup, we will be working with the OLake UI. We'll start by cloning the repository from GitHub and bringing it up using Docker Compose. Once the UI is running, it will serve as our control hub for creating and monitoring all CDC pipelines. @@ -79,7 +79,7 @@ Once it's running, go ahead at http://localhost:8000, olake-ui and use these cre ![olake-login](/img/blog/2025/10/olake-login.webp) -**You are greeted with Olake UI!** +**You are greeted with OLake UI!** ![olake-ui](/img/blog/2025/10/olakeui.webp) diff --git a/blog/authors.yml b/blog/authors.yml index d17a5932..ac89773b 100644 --- a/blog/authors.yml +++ b/blog/authors.yml @@ -85,7 +85,7 @@ akshay: duke: page: true name: Duke - title: Olake Maintainer + title: OLake Maintainer image_url: /img/authors/duke.webp socials: linkedin: dukedhal diff --git a/docs/connectors/mongodb/cdc_setup.mdx b/docs/connectors/mongodb/cdc_setup.mdx index f0eee03c..af9e1e5c 100644 --- a/docs/connectors/mongodb/cdc_setup.mdx +++ b/docs/connectors/mongodb/cdc_setup.mdx @@ -47,7 +47,7 @@ This guide covers setting up Change Data Capture (CDC) for both self-hosted Mong **Applicable for both MongoDB (Self-Hosted) and Atlas.** -Olake needs a user that can: +OLake needs a user that can: - Read/write your application database (to ingest data). - Read from the local database (where oplog is stored). diff --git a/docs/connectors/mysql/index.mdx b/docs/connectors/mysql/index.mdx index 0ff99a54..8d92cf31 100644 --- a/docs/connectors/mysql/index.mdx +++ b/docs/connectors/mysql/index.mdx @@ -48,7 +48,7 @@ The OLake MySQL Source connector supports multiple sync modes. It also offers fe - + ### 1. Navigate to the Source Configuration Page diff --git a/docs/getting-started/playground.mdx b/docs/getting-started/playground.mdx index 9c111811..8704f0a9 100644 --- a/docs/getting-started/playground.mdx +++ b/docs/getting-started/playground.mdx @@ -96,7 +96,7 @@ This will spin up: Once the stack is up and running (especially after init-mysql-tasks and olake-app are healthy/started): -- Olake Application UI: http://localhost:8000 +- OLake Application UI: http://localhost:8000 Default credentials: @@ -118,9 +118,9 @@ Once the stack is up and running (especially after init-mysql-tasks and olake-ap SELECT * FROM weather LIMIT 10; ``` This will display the first 10 rows of the weather table. -### 5. Interacting with Olake +### 5. Interacting with OLake - 1. Log in to the Olake UI at http://localhost:8000 using the default credentials. + 1. Log in to the OLake UI at http://localhost:8000 using the default credentials. 2. Create and Configure a Job: Create a Job to define and run the data pipeline: On the main page, click on the "Create your first Job" button @@ -162,7 +162,7 @@ Once the stack is up and running (especially after init-mysql-tasks and olake-ap * **Iceberg Database (example)**: weather - * **S3 Endpoint (for Iceberg data files written by Olake workers)**: http://host.docker.internal:9090 + * **S3 Endpoint (for Iceberg data files written by OLake workers)**: http://host.docker.internal:9090 * **AWS Region**: us-east-1 diff --git a/docs/install/docker-cli.mdx b/docs/install/docker-cli.mdx index 5fe0224a..5411be95 100644 --- a/docs/install/docker-cli.mdx +++ b/docs/install/docker-cli.mdx @@ -233,7 +233,7 @@ The `stats.json` file remains available after sync completion. ## Logs -During a sync, Olake automatically creates an olake.log file inside a folder named `sync_` (for example: sync_2025-02-17_11-41-40). +During a sync, OLake automatically creates an olake.log file inside a folder named `sync_` (for example: sync_2025-02-17_11-41-40). This folder is created in the same directory as the configuration files. - The olake.log file contains a complete record of all logs generated while the command is running. diff --git a/docs/release/v0.1.0-v0.1.1.mdx b/docs/release/v0.1.0-v0.1.1.mdx index a0af8617..95f0ba4c 100644 --- a/docs/release/v0.1.0-v0.1.1.mdx +++ b/docs/release/v0.1.0-v0.1.1.mdx @@ -1,4 +1,4 @@ -# Olake (v0.1.0 – v0.1.1) +# OLake (v0.1.0 – v0.1.1) June 13 – June 18, 2025 ## 🎯 What's New diff --git a/docs/understanding/terminologies/olake.mdx b/docs/understanding/terminologies/olake.mdx index b044c4dd..67763e23 100644 --- a/docs/understanding/terminologies/olake.mdx +++ b/docs/understanding/terminologies/olake.mdx @@ -198,7 +198,7 @@ Unlike traditional systems like Hive, Iceberg's approach uses "hidden partitioni ### 6. Job Configuration -The job configuration property refers to the options that defines job’s name, schedule, and execution in the Olake’s system. \ +The job configuration property refers to the options that defines job’s name, schedule, and execution in the OLake’s system. \ User has to start with job creation, which will be followed with source configuration, then destination configuration, checking and enabling relevant streams from the schema for sync, and then finally in job configuration, job name and frequency has to be set. - **Frequency Options:** diff --git a/docs/writers/iceberg/azure.mdx b/docs/writers/iceberg/azure.mdx index 33ee04d2..56828880 100644 --- a/docs/writers/iceberg/azure.mdx +++ b/docs/writers/iceberg/azure.mdx @@ -35,7 +35,7 @@ To follow this guide, you will need: First, we need to set up the storage foundation and credentials on Azure. All these steps are performed in the Azure Portal. 1. **Create Service Principal:** Create a new **App Registration**. This will be our application's identity. - **Name**: `Olake Warehouse (Development)` + **Name**: `OLake Warehouse (Development)` **Redirect URI**: Leave empty a. When the App Registration is created, select "**Manage**" -> "**Certificates & secrets**" and create a "**New client secret**". Note down the secrets "**Value**". @@ -75,7 +75,7 @@ Now, let's get the Lakekeeper REST Catalog running using its official Docker Com This will start the Lakekeeper REST catalog service and its required PostgreSQL backend. Lakekeeper's API will now be available on your local machine at `http://localhost:8181`. ### Step 3: Add the Azure Warehouse to Lakekeeper -Before Olake can use Lakekeeper, we must configure Lakekeeper to be aware of our ADLS Gen2 storage. +Before OLake can use Lakekeeper, we must configure Lakekeeper to be aware of our ADLS Gen2 storage. 1. **Access the Lakekeeper UI:** Open your browser and navigate to `http://localhost:8181`. 2. **Add a New Warehouse:** Find the section for adding a new storage profile or warehouse. @@ -83,7 +83,7 @@ Before Olake can use Lakekeeper, we must configure Lakekeeper to be aware of our * **Warehouse Name:** `olake_warehouse` * **Storage Type:** Select "Azure". * **Credential Type:** Select "Client Credentials". - * **Client ID:** The `Application (client) ID` of the `Olake Warehouse (Development)` App Registration from Step 1. + * **Client ID:** The `Application (client) ID` of the `OLake Warehouse (Development)` App Registration from Step 1. * **Client Secret:** The "Value" of the client secret that we noted down previously in Step 1. * **Tenant ID:** The `Directory (tenant) ID` from the Applications Overview page from Step 1. * **Account Name:** olakehouse @@ -94,7 +94,7 @@ Before Olake can use Lakekeeper, we must configure Lakekeeper to be aware of our Lakekeeper Warehouse Configuration ### Step 4: Set Up the OLake Environment -Now, let's get the Olake UI running using its official Docker Compose setup. +Now, let's get the OLake UI running using its official Docker Compose setup. 1. **Get the OLake Docker Compose file:** Follow the instructions at the [OLake Getting Started](/docs/getting-started/quickstart) to get the `docker-compose.yml` file. 2. **Start OLake:** Follow the steps from the docs and remember to modify the directory in `docker-compose.yml` where OLake's persistent data and configuration will be stored, once done, run the following command: @@ -105,10 +105,10 @@ Now, let's get the Olake UI running using its official Docker Compose setup. This will start the OLake UI along with Temporal and PostgreSQL services. OLake’s UI will now be available on your local machine at http://localhost:8000. -### Step 5: Configure the Iceberg Destination in the Olake UI -With all services running, we will now connect Olake to Lakekeeper and Azure. +### Step 5: Configure the Iceberg Destination in the OLake UI +With all services running, we will now connect OLake to Lakekeeper and Azure. -1. **Log in to Olake UI:** Open your browser and navigate to http://localhost:8000. +1. **Log in to OLake UI:** Open your browser and navigate to http://localhost:8000. 2. **Navigate to Destinations:** Go to the **Destinations** page and click **"Create Destination"**, then select **Apache Iceberg**. 3. **Fill in the Endpoint config:** * **Catalog Type**: `REST Catalog` @@ -118,7 +118,7 @@ With all services running, we will now connect Olake to Lakekeeper and Azure. * **S3 Endpoint**: `https://olakehouse.dfs.core.windows.net` * **AWS Access Key**: Leave empty. * **AWS Secret Key**: Leave empty. -4. **Save and Test** the destination to ensure Olake can communicate with both Lakekeeper and Azure. +4. **Save and Test** the destination to ensure OLake can communicate with both Lakekeeper and Azure. ![azure-adls-6](/img/docs/adls/azure-adls-6.webp) diff --git a/docs/writers/parquet/troubleshoot.mdx b/docs/writers/parquet/troubleshoot.mdx index 607a18ff..23283df4 100644 --- a/docs/writers/parquet/troubleshoot.mdx +++ b/docs/writers/parquet/troubleshoot.mdx @@ -30,7 +30,7 @@ Symptom: failed to write test file to S3: AccessDenied: Access Denied OR failed ``` **Cause:** -- The AWS credentials supplied to Olake do **not** have the minimum set of S3 actions or the bucket ARN is incorrect. +- The AWS credentials supplied to OLake do **not** have the minimum set of S3 actions or the bucket ARN is incorrect. **Resolution:** - Make sure the IAM user / role has the JSON policy shown in the [IAM Permissions](/docs/writers/parquet/permission) page attached. diff --git a/iceberg/2025-05-08-olake-iceberg-athena.mdx b/iceberg/2025-05-08-olake-iceberg-athena.mdx index c5979b7b..b1abf03e 100644 --- a/iceberg/2025-05-08-olake-iceberg-athena.mdx +++ b/iceberg/2025-05-08-olake-iceberg-athena.mdx @@ -18,7 +18,7 @@ Let's dive in. ## Why Iceberg ? -When designing Olake, choosing Apache Iceberg as the destination format was a clear decision. Why? Because Iceberg isn't just another way to put files in S3; it's a leap forward that brings much-needed reliability and advanced capabilities to data lakes. Many in the industry see iceberg as the future standard for data engineering because it tackles fundamental challenges when working with data on object storage head-on: +When designing OLake, choosing Apache Iceberg as the destination format was a clear decision. Why? Because Iceberg isn't just another way to put files in S3; it's a leap forward that brings much-needed reliability and advanced capabilities to data lakes. Many in the industry see iceberg as the future standard for data engineering because it tackles fundamental challenges when working with data on object storage head-on: ### 1. Reliable Transactions: @@ -55,7 +55,7 @@ Together, these tools allow you to build an end-to-end pipeline from your databa In this Blog we will be diving into the following sections 1. Configuring AWS Glue catalog & S3 permissions -2. Replicating database data to Apache Iceberg using Olake +2. Replicating database data to Apache Iceberg using OLake 3. Querying iceberg data using Amazon Athena ## Step 1: Configure AWS Glue Catalog and S3 Permissions @@ -112,7 +112,7 @@ By setting up these basic AWS resources and granting OLake the necessary permiss OLake integrates with AWS Glue to automatically register iceberg tables, making them queryable in Athena. -## Step 2: Data Ingestion from Database to Apache Iceberg Using Olake +## Step 2: Data Ingestion from Database to Apache Iceberg Using OLake ### 2.1 Install Docker Ensure Docker is installed and running on your local or cloud environment. OLake is Docker-based, making setup fast and portable @@ -160,7 +160,7 @@ Once OLake has synced data into Iceberg tables and registered them with Glue, yo - On the left sidebar under Data Source, select `AwsDataCatalog` -- Expand the relevant database (as mentioned in the Olake writer cofig file) and tables section to see the Iceberg tables synced by Olake +- Expand the relevant database (as mentioned in the OLake writer cofig file) and tables section to see the Iceberg tables synced by OLake ![olake-iceberg-athena-2](/img/blog/2025/05/olake-iceberg-athena-2.webp) diff --git a/iceberg/2025-05-28-olake-glue-snowflake.mdx b/iceberg/2025-05-28-olake-glue-snowflake.mdx index feb9beeb..6cd40d22 100644 --- a/iceberg/2025-05-28-olake-glue-snowflake.mdx +++ b/iceberg/2025-05-28-olake-glue-snowflake.mdx @@ -121,7 +121,7 @@ By setting up these basic AWS resources and granting OLake the necessary permiss OLake integrates with AWS Glue to automatically register Iceberg tables, making them queryable in Snowflake. -### **Step 2: Data Ingestion from Database to Apache Iceberg Using Olake** +### **Step 2: Data Ingestion from Database to Apache Iceberg Using OLake** **2.1 Install Docker** @@ -140,7 +140,7 @@ You’ll need two configuration files * [source.json](https://olake.io/docs/connectors/postgres/config) file with your database connection details * [destination.json](https://olake.io/docs/writers/iceberg/catalog/glue) file with your Writer ([Apache Iceberg](https://olake.io/docs/writers/iceberg/catalog/overview)) connection details -You can read here for detailed version[ Olake Configuration](https://olake.io/docs/getting-started/postgres) +You can read here for detailed version[ OLake Configuration](https://olake.io/docs/getting-started/postgres) **2.4 Run the[ Discover](https://olake.io/docs/connectors/postgres/overview) command** to generate a `streams.json` file, which contains the list of available data streams from your source database. You can refer to the official[ OLake documentation](https://olake.io/docs) for complete documentation @@ -163,7 +163,7 @@ This approach is especially useful for organizations that maintain large dataset Similarly, Iceberg tables store both data and metadata externally, typically in cloud object storage. With Snowflake’s support for external volumes, you can query Iceberg tables directly, leveraging Snowflake’s performance while benefiting from open table formats and flexible storage. You can read more here[ External Tables in Snowflake](https://docs.snowflake.com/en/user-guide/tables-external-intro). -Since we're using Olake to ingest data into Iceberg and managing metadata through the AWS Glue Catalog, Snowflake recognises these as external tables. To query them, we first need to mount the underlying storage as an external volume in Snowflake. +Since we're using OLake to ingest data into Iceberg and managing metadata through the AWS Glue Catalog, Snowflake recognises these as external tables. To query them, we first need to mount the underlying storage as an external volume in Snowflake. **3.1. Configure an external volume** diff --git a/src/theme/DocPaginator/footerNavigations.js b/src/theme/DocPaginator/footerNavigations.js index 206eb284..0e960293 100644 --- a/src/theme/DocPaginator/footerNavigations.js +++ b/src/theme/DocPaginator/footerNavigations.js @@ -418,7 +418,7 @@ export const paginationConfig = { // Issues and PRs '/docs/community/issues-and-prs': { previous: { - title: 'Contribute to Olake', + title: 'Contribute to OLake', permalink: '/docs/community/contributing' }, next: { From 3bf6acf21e71842b8dbd406d1304186cb3dd8428 Mon Sep 17 00:00:00 2001 From: Shubham Baldava Date: Mon, 22 Sep 2025 18:59:22 +0530 Subject: [PATCH 17/17] fix: made more fixes --- docs/community/setting-up-a-dev-env.mdx | 1 - docusaurus.config.js | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/community/setting-up-a-dev-env.mdx b/docs/community/setting-up-a-dev-env.mdx index 2b4b8733..a496b717 100644 --- a/docs/community/setting-up-a-dev-env.mdx +++ b/docs/community/setting-up-a-dev-env.mdx @@ -727,7 +727,6 @@ Alternatively, you can generate the jar file by running the ./build.sh sync comm |`mode` | `auto`, `debug` | | `args` | `sync` , `discover`, `check` | -Update `PATH_TO_UPDATE` with the absolute path where the OLake project is located on your system. For example: Update `workspaceFolder` with the absolute path where the OLake project is located on your system. For example: ```json diff --git a/docusaurus.config.js b/docusaurus.config.js index ab05922c..7dc30992 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -33,6 +33,7 @@ const config = { }, future: { + v4: true, experimental_faster: true },