From 8899243995240dc2c9b7eedd4b6661b627a040b9 Mon Sep 17 00:00:00 2001 From: Balaji632 Date: Mon, 30 Dec 2024 14:03:29 +0530 Subject: [PATCH 1/4] blog post on debezium logminer with oracleDB --- .../2024-12-30-debezium-oracledb-logminer.md | 313 ++++++++++++++++++ 1 file changed, 313 insertions(+) create mode 100644 _posts/2024-12-30-debezium-oracledb-logminer.md diff --git a/_posts/2024-12-30-debezium-oracledb-logminer.md b/_posts/2024-12-30-debezium-oracledb-logminer.md new file mode 100644 index 0000000000..96b2064ea6 --- /dev/null +++ b/_posts/2024-12-30-debezium-oracledb-logminer.md @@ -0,0 +1,313 @@ +--- +layout: post +title: "Debezium with Oracle DB and LogMiner for Change Data Capture on Confluent Platform" +author: Balaji K +categories: + [Platform Engineering, Data, Infrastructure, Kafka, Kubernetes] +image: assets/blog-images/platform-strategy.svg +featured: true +hidden: true +cat_box_title: Contact Us +ctas: + - title: Get In Touch + description: "Have questions or need assistance? Our team is here to help" + url: "/contact/" + +teaser: Platform Engineering is dead. Long live platform engineering! +toc: true +--- + +# Introduction + +In the evolving landscape of real-time data processing, Change Data Capture (CDC) is essential for keeping data systems synchronized. Debezium, combined with the Confluent Platform, provides a robust solution for streaming database changes directly into Apache Kafka. When integrating with Oracle Database, Debezium leverages Oracle LogMiner to capture and publish database transactions efficiently. +In this blog, we'll walk you through configuring Debezium’s Oracle connector within the Confluent Platform. You'll learn how to set up LogMiner, configure Debezium, and stream changes into your Kafka topics seamlessly. This guide covers: + + • Preparing your Oracle Database for CDC with LogMiner. + • Configuring the Debezium connector on Confluent Platform. + • Validating and monitoring the data flow. + +By the end, you'll have a powerful CDC pipeline ready to capture and stream changes from Oracle to Kafka, enabling real-time data integration and analytics. Let's dive in! + + +# Confluent Platform Overview + +Confluent Platform is a streaming platform that enables user to store, and manage data as real-time streams. It enabled transformations through stream processing, simplifies enterprise operations at scale. Confluent Platform can be downloaded and managed by user. This is a specialized distribution system of Kafka with many commercial features built into Kafka brokers that function as Confluent Server. Confluent facilitates the development of a whole new class of contemporary, event-driven applications, provides a universal data pipeline, and opens up potent new use cases with complete performance, scalability, and dependability. + + +# Pre-requisite + + • Internet Connection + • OS supporting Confluent Platform (link) + • Docker Engine 1.11 or higher installed + + +# Setting up Confluent Platform + + 1. Download Oracle JDBC Driver from Oracle JDBC Driver page. You need to download zipped JDBC driver for Oracle DB 21c. Extract the zip file. + 2. Download the Confluent Platform KRaft all-in-one Docker Compose file using command: + + $> wget https://raw.githubusercontent.com/confluentinc/cp-all-in-one/7.8.0-post/cp-all-in-one-kraft/docker-compose.yml + + + 3. Update docker-compose.yaml file with below changes: + + kafka-connect: + image: debezium/connect:2.7.3.Final + hostname: kafka-connect + container_name: kafka-connect + depends_on: + - broker + - schema-registry + ports: + - "8083:8083" + environment: + BOOTSTRAP_SERVERS: 'broker:29092' + GROUP_ID: 1 + CONFIG_STORAGE_TOPIC: "my_connect_configs" + OFFSET_STORAGE_TOPIC: "my_connect_offset" + STATUS_STORAGE_TOPIC: "my_connect_status" + #KAFKA_CLASSPATH: /opt/oracle/ojdbc8.jar + LD_LIBRARY_PATH: /kafka/instantclient_21_16 + volumes: + - ./basic/instantclient_21_16:/kafka/instantclient_21_16 + + oracle-db: + image: heartu41/oracle19c + container_name: oracle + ports: + - "1521:1521" + environment: + ORACLE_PWD: "Oracle123" + ORACLE_SID: "ORCLCDB" + ENABLE_ARCHIVELOG: true + ENABLE_FORCE_LOGGING: true + + volumes: + - ./oracleDB/oradata:/opt/oracle/oradata + + 4. Create directory “oracleDB/oradata and change ownership to 54321 + ''' + $> mkdir -p oracleDB/oradata + $> sudo chown -R oracleDB/oradata 54321:54321 + ''' + 5. Start Confluent Platform stack in detach mode: + + ''' + $> docker compose up -d + Each component of Confluent Platform starts in separate container. + + Creating broker ... done + Creating schema-registry ... done + Creating rest-proxy ... done + Creating connect ... done + Creating ksqldb-server ... done + Creating control-center ... done + Creating ksql-datagen ... done + Creating ksqldb-cli ... done + Creating oracle ... done + + ''' + + You can verify if all services are up and running using command “docker compose ps -a” + +# Configuring Oracle Database + + When Oracle DB container starts for the first time, there are no initial configuration and database exists. Hence database will be installed and configuration will be started. This process may take 10-20 mins (approx). You can verify readiness of Oracle DB from container logs (docker compose logs oracle) and should see message: + + ''' + ############################# + DATABASE IS READY TO USE! + ############################# + ''' + + + In order to incorporate changes from an Oracle database, a number of database configurations are required: + + +# Enable Archive logs + + The Oracle container registry image used in the Install Oracle section may not have archive logging enabled. If you use another image or a pre-existing environment, you should check whether archive logging is enabled. + + ''' + $> docker compose exec oracle bash + $> sqlplus ‘/ as sysdba’ + + SQL> SELECT LOG_MODE FROM V$DATABASE + + ''' + + If the column contains ARCHIVELOG, then archive logging is enabled. If the column contains the value NOARCHIVELOG, archive logging isn’t enabled, and further configuration is necessary. + + Execute the following SQL commands inside the SQL*Plus terminal window: + + ''' + ALTER SYSTEM SET db_recovery_file_dest_size = 10G; + ALTER SYSTEM SET db_recovery_file_dest = '/opt/oracle/oradata/ORCLCDB' scope=spfile; + SHUTDOWN IMMEDIATE + STARTUP MOUNT + ALTER DATABASE ARCHIVELOG; + ALTER DATABASE OPEN; + ARCHIVE LOG LIST; + ''' + Output of last executed SQL command should show Archive mode for Database log mode. + ''' + Database log mode Archive Mode + Automatic archival Enabled + Archive destination USE_DB_RECOVERY_FILE_DEST + Oldest online log sequence 1 + Next log sequence to archive 3 + Current log sequence 3 + ''' + + + +# Redo Logs + + Oracle Redo logs are the transactional logs. Using the same terminal window, execute below listed SQL command: + + ''' + SQL> SELECT GROUP#, MEMBER FROM V$LOGFILE ORDER BY 1, 2; + + GROUP# MEMBER + ---------- --------------------------------------------------- + 1 /opt/oracle/oradata/ORCLCDB/redo01.log + 2 /opt/oracle/oradata/ORCLCDB/redo02.log + 3 /opt/oracle/oradata/ORCLCDB/redo03.log + + + ALTER DATABASE CLEAR LOGFILE GROUP 1; + ALTER DATABASE DROP LOGFILE GROUP 1; + ALTER DATABASE ADD LOGFILE GROUP 1 ('/opt/oracle/oradata/ORCLCDB/redo01.log') size 400M REUSE; + ALTER SYSTEM SWITCH LOGFILE; + + ''' + +# Supplemental Logging + + Database supplementary logging needs to be enabled at the very least for Debezium to communicate with LogMiner, handle chained rows, and work with different storage configurations. + + ''' + SQL> ALTER DATABASE ADD SUPPLEMENTAL LOG DATA + ''' + + +# Users Setup in Oracle DB + + To capture change events, the Debezium connector needs to establish a JDBC connection to the Oracle database and interact with LogMiner APIs. This requires a dedicated database user account with specific permissions to access LogMiner and read data from the target tables. + + In the same SQLplus terminal window, create table spaces: + + + ''' + CONNECT sys/oraclepw@ORCLCDB as sysdba; + CREATE TABLESPACE logminer_tbs DATAFILE '/opt/oracle/oradata/ORCLCDB/logminer_tbs.dbf' SIZE 25M REUSE AUTOEXTEND ON MAXSIZE UNLIMITED; + + CONNECT sys/oraclepw@ORCLPDB1 as sysdba; + CREATE TABLESPACE logminer_tbs DATAFILE '/opt/oracle/oradata/ORCLCDB/ORCLPDB1/logminer_tbs.dbf' SIZE 25M REUSE AUTOEXTEND ON MAXSIZE UNLIMITED; + ''' + +> NOTE – Replace oraclepw with the password set in docker compose file. +> + ''' + CONNECT sys/oraclepw@ORCLCDB as sysdba; + CREATE USER c##dbzuser IDENTIFIED BY dbz DEFAULT TABLESPACE LOGMINER_TBS QUOTA UNLIMITED ON LOGMINER_TBS CONTAINER=ALL; + + + GRANT CREATE SESSION TO c##dbzuser CONTAINER=ALL; + GRANT SET CONTAINER TO c##dbzuser CONTAINER=ALL; + GRANT SELECT ON V_$DATABASE TO c##dbzuser CONTAINER=ALL; + GRANT FLASHBACK ANY TABLE TO c##dbzuser CONTAINER=ALL; + GRANT SELECT ANY TABLE TO c##dbzuser CONTAINER=ALL; + GRANT SELECT_CATALOG_ROLE TO c##dbzuser CONTAINER=ALL; + GRANT EXECUTE_CATALOG_ROLE TO c##dbzuser CONTAINER=ALL; + GRANT SELECT ANY TRANSACTION TO c##dbzuser CONTAINER=ALL; + GRANT SELECT ANY DICTIONARY TO c##dbzuser CONTAINER=ALL; + GRANT LOGMINING TO c##dbzuser CONTAINER=ALL; + + GRANT CREATE TABLE TO c##dbzuser CONTAINER=ALL; + GRANT LOCK ANY TABLE TO c##dbzuser CONTAINER=ALL; + GRANT CREATE SEQUENCE TO c##dbzuser CONTAINER=ALL; + + GRANT EXECUTE ON DBMS_LOGMNR TO c##dbzuser CONTAINER=ALL; + GRANT EXECUTE ON DBMS_LOGMNR_D TO c##dbzuser CONTAINER=ALL; + + GRANT SELECT ON V_$LOG TO c##dbzuser CONTAINER=ALL; + GRANT SELECT ON V_$LOG_HISTORY TO c##dbzuser CONTAINER=ALL; + GRANT SELECT ON V_$LOGMNR_LOGS TO c##dbzuser CONTAINER=ALL; + GRANT SELECT ON V_$LOGMNR_CONTENTS TO c##dbzuser CONTAINER=ALL; + GRANT SELECT ON V_$LOGMNR_PARAMETERS TO c##dbzuser CONTAINER=ALL; + GRANT SELECT ON V_$LOGFILE TO c##dbzuser CONTAINER=ALL; + GRANT SELECT ON V_$ARCHIVED_LOG TO c##dbzuser CONTAINER=ALL; + GRANT SELECT ON V_$ARCHIVE_DEST_STATUS TO c##dbzuser CONTAINER=ALL; + GRANT SELECT ON V_$TRANSACTION TO c##dbzuser CONTAINER=ALL; + + EXIT; + + ''' + +# Create Initial Test Data + + Connect to Oracle DB using command: + + ''' + $> sqlplus ‘/ as sysdba’ + SQL> connect c##dbzuser/dbz + SQL> alter session set container=ORCLPDB1; + ''' + + Create table and some initial sample data. + + ''' + CREATE TABLE customers (id number(9,0) primary key, name varchar2(50)); INSERT INTO customers VALUES (1001, 'Jane Doe'); + INSERT INTO customers VALUES (1002, 'Bob Willy'); + INSERT INTO customers VALUES (1003, 'Eddie Murphy'); + INSERT INTO customers VALUES (1004, 'Anne Mary'); + COMMIT; + + ''' + + Set the table’s supplemental log level: + + ''' + ALTER TABLE customers ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS; + + ''' + +# Deploy Debezium Oracle Connector + + Create a source connector file “source_logMiner.json” with configuration: + + ''' + { + "name": "debezium-log-miner", + "config": { + "connector.class": "io.debezium.connector.oracle.OracleConnector", + "tasks.max": "1", + "topic.prefix" : "server1", + "database.hostname": "oracle-db", + "database.port": "1521", + "database.user": "c##dbzuser", + "database.password": "dbz", + "database.dbname": "ORCLCDB", + "database.pdb.name": "ORCLPDB1", + "database.server.name": "server1", + "table.include.list": "C##DBZUSER.CUSTOMERS", + "database.history.kafka.bootstrap.servers": "broker:29092", + "database.history.kafka.topic": "schema-changes", + "schema.history.internal.kafka.bootstrap.servers" : "broker:29092", + "schema.history.internal.kafka.topic": "schema-changes.new_table" + } +} +''' + +Save above configuration and deploy the source connector + +''' +$> curl -i -X POST -H "Accept:application/json" \ -H "Content-Type:application/json" \ localhost:8083/connectors \ -d @r source_logMiner.json | jq +''' + +Once the source connector registration is successful, open Confluent Platform Control Center using http://:9021 in a browser. Navigate to Overview → Topics. You should see a topic “server1.C__DBZUSER.CUSTOMERS’ listed there. Click on the topic and verify the contents of topic. + + +This concludes deployment of Oracle Debezium connector which captures changes in the table CUSTOMERS. + From 2a75bb79e82ab016904feab879f239e44f4b9565 Mon Sep 17 00:00:00 2001 From: Balaji632 Date: Wed, 29 Jan 2025 12:04:13 +0530 Subject: [PATCH 2/4] update blog post with review comments --- .../2024-12-30-debezium-oracledb-logminer.md | 20 ++++++++++++------ .../debezium-architecture.png | Bin 0 -> 41365 bytes 2 files changed, 13 insertions(+), 7 deletions(-) create mode 100644 assets/blog-images/oracleCDC-debezium-connector/debezium-architecture.png diff --git a/_posts/2024-12-30-debezium-oracledb-logminer.md b/_posts/2024-12-30-debezium-oracledb-logminer.md index 96b2064ea6..cf3f930805 100644 --- a/_posts/2024-12-30-debezium-oracledb-logminer.md +++ b/_posts/2024-12-30-debezium-oracledb-logminer.md @@ -3,7 +3,7 @@ layout: post title: "Debezium with Oracle DB and LogMiner for Change Data Capture on Confluent Platform" author: Balaji K categories: - [Platform Engineering, Data, Infrastructure, Kafka, Kubernetes] + [Data Processing, Data Streaming, Confluent Kafka, Connectors] image: assets/blog-images/platform-strategy.svg featured: true hidden: true @@ -13,13 +13,16 @@ ctas: description: "Have questions or need assistance? Our team is here to help" url: "/contact/" -teaser: Platform Engineering is dead. Long live platform engineering! toc: true --- # Introduction In the evolving landscape of real-time data processing, Change Data Capture (CDC) is essential for keeping data systems synchronized. Debezium, combined with the Confluent Platform, provides a robust solution for streaming database changes directly into Apache Kafka. When integrating with Oracle Database, Debezium leverages Oracle LogMiner to capture and publish database transactions efficiently. + + +![Image-1](../assets/blog-images/oracleCDC-debezium-connector/debezium-architecture.png) + In this blog, we'll walk you through configuring Debezium’s Oracle connector within the Confluent Platform. You'll learn how to set up LogMiner, configure Debezium, and stream changes into your Kafka topics seamlessly. This guide covers: • Preparing your Oracle Database for CDC with LogMiner. @@ -34,7 +37,7 @@ By the end, you'll have a powerful CDC pipeline ready to capture and stream chan Confluent Platform is a streaming platform that enables user to store, and manage data as real-time streams. It enabled transformations through stream processing, simplifies enterprise operations at scale. Confluent Platform can be downloaded and managed by user. This is a specialized distribution system of Kafka with many commercial features built into Kafka brokers that function as Confluent Server. Confluent facilitates the development of a whole new class of contemporary, event-driven applications, provides a universal data pipeline, and opens up potent new use cases with complete performance, scalability, and dependability. -# Pre-requisite +# Pre-requisites • Internet Connection • OS supporting Confluent Platform (link) @@ -44,7 +47,9 @@ Confluent Platform is a streaming platform that enables user to store, and manag # Setting up Confluent Platform 1. Download Oracle JDBC Driver from Oracle JDBC Driver page. You need to download zipped JDBC driver for Oracle DB 21c. Extract the zip file. - 2. Download the Confluent Platform KRaft all-in-one Docker Compose file using command: + + 2. Download the Confluent Platform KRaft all-in-one Docker Compose file using below command. The docker compose file is a configuration file used to quickly set up a local Confluent Platform environment with all the essential components like Kafka, Schema Registry, and Connect, specifically utilizing the "KRaft" mode for managing Kafka cluster metadata allowing user to easily run a full Confluent Platform instance in a single Docker command using the "cp-all-in-one" image with KRaft enabled. + $> wget https://raw.githubusercontent.com/confluentinc/cp-all-in-one/7.8.0-post/cp-all-in-one-kraft/docker-compose.yml @@ -112,7 +117,7 @@ Confluent Platform is a streaming platform that enables user to store, and manag # Configuring Oracle Database - When Oracle DB container starts for the first time, there are no initial configuration and database exists. Hence database will be installed and configuration will be started. This process may take 10-20 mins (approx). You can verify readiness of Oracle DB from container logs (docker compose logs oracle) and should see message: + When Oracle DB container starts for the first time, there are no initial configuration and database exists. Hence database will be installed and configuration will be started. This process may take 10-20 mins (approximately). You can verify readiness of Oracle DB from container logs (docker compose logs oracle) and should see message: ''' ############################# @@ -163,7 +168,7 @@ Confluent Platform is a streaming platform that enables user to store, and manag # Redo Logs - Oracle Redo logs are the transactional logs. Using the same terminal window, execute below listed SQL command: + Oracle Redo logs are the transactional logs. Using the same terminal window, execute below listed SQL command to determine filenames, location of redo logs and recreate log group with size of 400 MB using same log file. ''' SQL> SELECT GROUP#, MEMBER FROM V$LOGFILE ORDER BY 1, 2; @@ -309,5 +314,6 @@ $> curl -i -X POST -H "Accept:application/json" \ -H "Content-Type:application/j Once the source connector registration is successful, open Confluent Platform Control Center using http://:9021 in a browser. Navigate to Overview → Topics. You should see a topic “server1.C__DBZUSER.CUSTOMERS’ listed there. Click on the topic and verify the contents of topic. -This concludes deployment of Oracle Debezium connector which captures changes in the table CUSTOMERS. + +In this blog, we successfully deployed the Oracle Debezium connector to capture real-time changes from the CUSTOMERS table. From setting up the environment to configuring Debezium and validating CDC events in Kafka, we demonstrated a seamless data streaming pipeline. This integration enables reliable change data capture, ensuring efficient data synchronization between Oracle and downstream consumers. With this setup in place, you can now extend it further by adding transformations, integrating with analytics platforms, or scaling for enterprise workloads. diff --git a/assets/blog-images/oracleCDC-debezium-connector/debezium-architecture.png b/assets/blog-images/oracleCDC-debezium-connector/debezium-architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..6a7209261f1aee4cdd14b90ad2738727476551cd GIT binary patch literal 41365 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 literal 0 HcmV?d00001 From ccb325ff344efc82a6d5f9d2a8165ea3d07f1175 Mon Sep 17 00:00:00 2001 From: Balaji632 Date: Wed, 29 Jan 2025 12:12:34 +0530 Subject: [PATCH 3/4] updated formatting --- .../2024-12-30-debezium-oracledb-logminer.md | 66 ++++++++++--------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/_posts/2024-12-30-debezium-oracledb-logminer.md b/_posts/2024-12-30-debezium-oracledb-logminer.md index cf3f930805..f05b6ae438 100644 --- a/_posts/2024-12-30-debezium-oracledb-logminer.md +++ b/_posts/2024-12-30-debezium-oracledb-logminer.md @@ -55,7 +55,7 @@ Confluent Platform is a streaming platform that enables user to store, and manag 3. Update docker-compose.yaml file with below changes: - +``` kafka-connect: image: debezium/connect:2.7.3.Final hostname: kafka-connect @@ -89,15 +89,15 @@ Confluent Platform is a streaming platform that enables user to store, and manag volumes: - ./oracleDB/oradata:/opt/oracle/oradata - + ``` 4. Create directory “oracleDB/oradata and change ownership to 54321 - ''' + ``` $> mkdir -p oracleDB/oradata $> sudo chown -R oracleDB/oradata 54321:54321 - ''' + ``` 5. Start Confluent Platform stack in detach mode: - ''' + ``` $> docker compose up -d Each component of Confluent Platform starts in separate container. @@ -111,7 +111,7 @@ Confluent Platform is a streaming platform that enables user to store, and manag Creating ksqldb-cli ... done Creating oracle ... done - ''' + ``` You can verify if all services are up and running using command “docker compose ps -a” @@ -119,11 +119,11 @@ Confluent Platform is a streaming platform that enables user to store, and manag When Oracle DB container starts for the first time, there are no initial configuration and database exists. Hence database will be installed and configuration will be started. This process may take 10-20 mins (approximately). You can verify readiness of Oracle DB from container logs (docker compose logs oracle) and should see message: - ''' + ``` ############################# DATABASE IS READY TO USE! ############################# - ''' + ``` In order to incorporate changes from an Oracle database, a number of database configurations are required: @@ -133,19 +133,19 @@ Confluent Platform is a streaming platform that enables user to store, and manag The Oracle container registry image used in the Install Oracle section may not have archive logging enabled. If you use another image or a pre-existing environment, you should check whether archive logging is enabled. - ''' + ``` $> docker compose exec oracle bash $> sqlplus ‘/ as sysdba’ SQL> SELECT LOG_MODE FROM V$DATABASE - ''' + ``` If the column contains ARCHIVELOG, then archive logging is enabled. If the column contains the value NOARCHIVELOG, archive logging isn’t enabled, and further configuration is necessary. Execute the following SQL commands inside the SQL*Plus terminal window: - ''' + ``` ALTER SYSTEM SET db_recovery_file_dest_size = 10G; ALTER SYSTEM SET db_recovery_file_dest = '/opt/oracle/oradata/ORCLCDB' scope=spfile; SHUTDOWN IMMEDIATE @@ -153,16 +153,17 @@ Confluent Platform is a streaming platform that enables user to store, and manag ALTER DATABASE ARCHIVELOG; ALTER DATABASE OPEN; ARCHIVE LOG LIST; - ''' + ``` Output of last executed SQL command should show Archive mode for Database log mode. - ''' + + ``` Database log mode Archive Mode Automatic archival Enabled Archive destination USE_DB_RECOVERY_FILE_DEST Oldest online log sequence 1 Next log sequence to archive 3 Current log sequence 3 - ''' + ``` @@ -170,7 +171,7 @@ Confluent Platform is a streaming platform that enables user to store, and manag Oracle Redo logs are the transactional logs. Using the same terminal window, execute below listed SQL command to determine filenames, location of redo logs and recreate log group with size of 400 MB using same log file. - ''' + ``` SQL> SELECT GROUP#, MEMBER FROM V$LOGFILE ORDER BY 1, 2; GROUP# MEMBER @@ -185,15 +186,15 @@ Confluent Platform is a streaming platform that enables user to store, and manag ALTER DATABASE ADD LOGFILE GROUP 1 ('/opt/oracle/oradata/ORCLCDB/redo01.log') size 400M REUSE; ALTER SYSTEM SWITCH LOGFILE; - ''' + ``` # Supplemental Logging Database supplementary logging needs to be enabled at the very least for Debezium to communicate with LogMiner, handle chained rows, and work with different storage configurations. - ''' + ``` SQL> ALTER DATABASE ADD SUPPLEMENTAL LOG DATA - ''' + ``` # Users Setup in Oracle DB @@ -203,17 +204,17 @@ Confluent Platform is a streaming platform that enables user to store, and manag In the same SQLplus terminal window, create table spaces: - ''' + ``` CONNECT sys/oraclepw@ORCLCDB as sysdba; CREATE TABLESPACE logminer_tbs DATAFILE '/opt/oracle/oradata/ORCLCDB/logminer_tbs.dbf' SIZE 25M REUSE AUTOEXTEND ON MAXSIZE UNLIMITED; CONNECT sys/oraclepw@ORCLPDB1 as sysdba; CREATE TABLESPACE logminer_tbs DATAFILE '/opt/oracle/oradata/ORCLCDB/ORCLPDB1/logminer_tbs.dbf' SIZE 25M REUSE AUTOEXTEND ON MAXSIZE UNLIMITED; - ''' + ``` > NOTE – Replace oraclepw with the password set in docker compose file. > - ''' + ``` CONNECT sys/oraclepw@ORCLCDB as sysdba; CREATE USER c##dbzuser IDENTIFIED BY dbz DEFAULT TABLESPACE LOGMINER_TBS QUOTA UNLIMITED ON LOGMINER_TBS CONTAINER=ALL; @@ -248,41 +249,41 @@ Confluent Platform is a streaming platform that enables user to store, and manag EXIT; - ''' + ``` # Create Initial Test Data Connect to Oracle DB using command: - ''' + ``` $> sqlplus ‘/ as sysdba’ SQL> connect c##dbzuser/dbz SQL> alter session set container=ORCLPDB1; - ''' + ``` Create table and some initial sample data. - ''' + ``` CREATE TABLE customers (id number(9,0) primary key, name varchar2(50)); INSERT INTO customers VALUES (1001, 'Jane Doe'); INSERT INTO customers VALUES (1002, 'Bob Willy'); INSERT INTO customers VALUES (1003, 'Eddie Murphy'); INSERT INTO customers VALUES (1004, 'Anne Mary'); COMMIT; - ''' + ``` Set the table’s supplemental log level: - ''' + ``` ALTER TABLE customers ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS; - ''' + ``` # Deploy Debezium Oracle Connector Create a source connector file “source_logMiner.json” with configuration: - ''' + ``` { "name": "debezium-log-miner", "config": { @@ -303,17 +304,18 @@ Confluent Platform is a streaming platform that enables user to store, and manag "schema.history.internal.kafka.topic": "schema-changes.new_table" } } -''' +``` Save above configuration and deploy the source connector -''' +``` $> curl -i -X POST -H "Accept:application/json" \ -H "Content-Type:application/json" \ localhost:8083/connectors \ -d @r source_logMiner.json | jq -''' +``` Once the source connector registration is successful, open Confluent Platform Control Center using http://:9021 in a browser. Navigate to Overview → Topics. You should see a topic “server1.C__DBZUSER.CUSTOMERS’ listed there. Click on the topic and verify the contents of topic. +# Summary In this blog, we successfully deployed the Oracle Debezium connector to capture real-time changes from the CUSTOMERS table. From setting up the environment to configuring Debezium and validating CDC events in Kafka, we demonstrated a seamless data streaming pipeline. This integration enables reliable change data capture, ensuring efficient data synchronization between Oracle and downstream consumers. With this setup in place, you can now extend it further by adding transformations, integrating with analytics platforms, or scaling for enterprise workloads. From 25154a65e10e3fafd81215e83a24e7c8a05fcc46 Mon Sep 17 00:00:00 2001 From: Balaji632 Date: Wed, 29 Jan 2025 12:14:07 +0530 Subject: [PATCH 4/4] updated formatting in pre-requisite --- _posts/2024-12-30-debezium-oracledb-logminer.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/_posts/2024-12-30-debezium-oracledb-logminer.md b/_posts/2024-12-30-debezium-oracledb-logminer.md index f05b6ae438..4d5e76d7f7 100644 --- a/_posts/2024-12-30-debezium-oracledb-logminer.md +++ b/_posts/2024-12-30-debezium-oracledb-logminer.md @@ -39,9 +39,9 @@ Confluent Platform is a streaming platform that enables user to store, and manag # Pre-requisites - • Internet Connection - • OS supporting Confluent Platform (link) - • Docker Engine 1.11 or higher installed + - Internet Connection + - OS supporting Confluent Platform (link) + - Docker Engine 1.11 or higher installed # Setting up Confluent Platform