Skip to content
This repository was archived by the owner on Oct 13, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,7 @@ metastore_db/
python/elastic.jks
python/tls.cert

python/venv**
python/venv**

# Gemini Codegen
gemini-codegen/python/env/
87 changes: 87 additions & 0 deletions gemini-codegen/java/migrateDeltaLakeToIceberg.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Migrating Data from Delta Lake to Iceberg

This document provides instructions on how to run a Spark job to migrate data from a Delta Lake table in Google Cloud Storage (GCS) to an Iceberg table.
---
The text between the line above and line below was written by a human. The rest of the document was created by Gemini. The initial prompt to Gemini was:
```
Create a Spark job in Java to migrate data from deltalake table in GCS to an Iceberg table. Use Timestamp based time travel of deltalake table. Provide instructions to run this job on Serverless Spark in migrateDeltaLakeToIceberg.md and provide a summary of the session in migrateDeltaLakeToIcebergREADME.md
```
Gemini generated the Java app, specifically the file `DeltaLakeToIceberg.java` and the README file. Changes were required to the generated code, including a)specifying spark.sql.extensions and b) using `saveAsTable` instead of `save`. Gemini updated the existing `pom.xml`. A hive metastore URI is specified for the Serverless Spark job. The working gcloud command is:
```
gcloud dataproc batches submit spark --version=2.2 --class=com.customer.app.DeltaLakeToIceberg \
--jars=<GCS-JAR-LOCATION> \
--properties=spark.hadoop.hive.metastore.uris=<metastore_URI> \
-- <GCS-DELTALAKE-TABLE> <catalog>.<warehouse>.<tablename> <date>
```
---
## Prerequisites

* A Dataproc Serverless cluster.
* A GCS bucket for the Iceberg warehouse.
* The JAR file for the Spark job.

## Building the JAR

To build the JAR file, run the following Maven command in your project's root directory:

```bash
mvn clean package
```

This will create a JAR file in the `target` directory named `spark-delta-to-iceberg-migration-1.0-SNAPSHOT.jar`.

## Running the Spark Job

Use the following `gcloud` command to submit the Spark job to Dataproc Serverless:

```bash
gcloud dataproc batches submit spark \
--region=<region> \
--project=<project-id> \
--batch=delta-to-iceberg-migration \
--class=com.customer.app.DeltaLakeToIceberg \
--jars=target/spark-delta-to-iceberg-migration-1.0-SNAPSHOT.jar \
-- \
<delta-table-path> \
<iceberg-table-name> \
<timestamp>
```

### Arguments

* `<region>`: The region for the Dataproc Serverless cluster.
* `<project-id>`: Your Google Cloud project ID.
* `<delta-table-path>`: The GCS path to the Delta Lake table (e.g., `gs://<bucket>/delta-table`).
* `<iceberg-table-name>`: The name of the Iceberg table (e.g., `my_iceberg_table`).
* `<timestamp>`: The timestamp for time travel in the format `yyyy-MM-dd HH:mm:ss`.

### Spark Configuration

You will also need to provide additional configuration to your Spark job for Iceberg to work correctly. Create a file named `spark-properties.conf` with the following content:

```
spark.sql.extensions=org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions
spark.sql.catalog.spark_catalog=org.apache.iceberg.spark.SparkSessionCatalog
spark.sql.catalog.spark_catalog.type=hive
spark.sql.catalog.spark_catalog.warehouse=gs://<your-gcs-bucket>/iceberg-warehouse
```

Then, use the `--properties-file` flag when submitting your job:

```bash
gcloud dataproc batches submit spark \
--region=<region> \
--project=<project-id> \
--batch=delta-to-iceberg-migration \
--class=com.customer.app.DeltaLakeToIceberg \
--jars=target/spark-delta-to-iceberg-migration-1.0-SNAPSHOT.jar \
--properties-file=spark-properties.conf \
-- \
<delta-table-path> \
<iceberg-table-name> \
<timestamp>
```

Replace `<your-gcs-bucket>` with the name of your GCS bucket.

```
29 changes: 29 additions & 0 deletions gemini-codegen/java/migrateDeltaLakeToIcebergREADME.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Summary of the Delta Lake to Iceberg Migration Session

This document summarizes the steps taken to create and run a Spark job for migrating data from a Delta Lake table to an Iceberg table.

## 1. Created the Spark Job

A new Java class, `DeltaLakeToIceberg.java`, was created in `src/main/java/com/customer/app/`. This class contains the main logic for the Spark job, which:

* Accepts the Delta Lake table path, Iceberg table name, and a timestamp as command-line arguments.
* Initializes a Spark session with the necessary Iceberg configurations.
* Reads the Delta Lake table from GCS using the provided timestamp for time travel.
* Writes the data to an Iceberg table.

## 2. Updated the `pom.xml`

The `pom.xml` file was updated to include the following dependencies:

* `org.apache.iceberg:iceberg-spark-runtime-3.5_2.13:1.5.0`
* `io.delta:delta-spark_2.13:3.1.0`

The `<artifactId>` was also changed to `spark-delta-to-iceberg-migration`, and the `<mainClass>` in the `maven-shade-plugin` was updated to `com.customer.app.DeltaLakeToIceberg`.

## 3. Created `migrateDeltaLakeToIceberg.md`

An instruction file, `migrateDeltaLakeToIceberg.md`, was created to provide a guide on how to build and run the Spark job on a Dataproc Serverless cluster. This file includes:

* Instructions for building the JAR file using Maven.
* A `gcloud` command for submitting the Spark job.
* Details on the required command-line arguments and Spark configurations for Iceberg.
67 changes: 67 additions & 0 deletions gemini-codegen/java/migrateJdbcToJdbc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Migrating Data from Postgres to MySQL using a Serverless Spark Job

This document provides instructions on how to run a Spark job to migrate data from a Postgres database to a MySQL database using a serverless Spark environment like Google Cloud Dataproc Serverless.
---
The text between the line above and line below was written by a human. The rest of the document was created by Gemini. The initial prompt to Gemini was:
```
Create a Spark job in Java to to migrate data from a table in a Postgres database to a table in MySQL, both accessible via JDBC; The JDBC URL strings are stored in Google secret manager. The URL string includes the username and password. Read and write data in parallel based on partitioning information that is provided; While writing, write data in batches for efficiency. Use the addInsertionTimeColumn to add a column to the data before writing it to MySQL destination table. Provide instructions to run this job on serverless spark in migrateJdbcToJdbc.md Provide a summary of the session in migrationREADME.md
```
Gemini generated the Java app, specifically the file `PostgresToMySql.java` and the README file. Changes were required to the generated code, including a)specifying drivers for Postgres and MySQL, b)adjusting partition bounds and number of partitions, converting a string argument to integer. At the time of this writing the generated `pom.xml` needed to be updated manually. Serverless for Apache Spark does not require a VPC subnet and the jdbc drivers are in the shaded Jar. The working gcloud command is:
```
gcloud dataproc batches submit spark --class=com.customer.app.PostgresToMySql \
--jars=<bucket-location>/postgres-to-mysql-migration-1.0-SNAPSHOT.jar -- <postgres-table> <mysql-table> \
<postgres-secret> <mysql-secret> <column> <batchsize>
```
---
## Prerequisites

1. **Google Cloud SDK:** Make sure you have the Google Cloud SDK installed and configured on your local machine.
2. **GCP Project:** You need a Google Cloud project with the Dataproc API enabled.
3. **VPC Subnet:** A VPC subnet with Private Google Access enabled is required for the Dataproc Serverless job to access Google Cloud services.
4. **JDBC Drivers:** The JDBC driver JAR files for Postgres and MySQL must be accessible in a Google Cloud Storage bucket.
5. **Secrets:** The JDBC URL strings for both Postgres and MySQL must be stored in Google Secret Manager. The URL should include the username and password.

## Running the Job

1. **Build the JAR:**
Package the application into a fat JAR file using Maven:
```bash
mvn clean package
```

2. **Upload the JAR to GCS:**
Upload the generated JAR file (`target/postgres-to-mysql-migration-1.0-SNAPSHOT.jar`) to a Google Cloud Storage bucket.

3. **Submit the Dataproc Serverless Job:**
Use the `gcloud` command to submit the Spark job to Dataproc Serverless. Replace the placeholders with your specific values.

```bash
gcloud dataproc batches submit spark \
--project=<your-gcp-project-id> \
--region=<your-gcp-region> \
--batch=postgres-to-mysql-migration \
--class=com.customer.app.PostgresToMySql \
--jars=gs://<your-gcs-bucket>/postgres-to-mysql-migration-1.0-SNAPSHOT.jar,gs://<your-gcs-bucket>/postgresql-42.3.3.jar,gs://<your-gcs-bucket>/mysql-connector-java-8.0.28.jar \
--subnet=<your-vpc-subnet> \
-- \
<postgres.table> \
<mysql.table> \
<postgres.secret.id> \
<mysql.secret.id> \
<partition.column> \
<batch.size>
```

### Arguments:
* `<your-gcp-project-id>`: Your Google Cloud project ID.
* `<your-gcp-region>`: The GCP region for the Dataproc job (e.g., `us-central1`).
* `gs://<your-gcs-bucket>/...`: The GCS paths to the application JAR and the JDBC driver JARs.
* `<your-vpc-subnet>`: The name of the VPC subnet to use.
* `<postgres.table>`: The name of the source table in Postgres.
* `<mysql.table>`: The name of the destination table in MySQL.
* `<postgres.secret.id>`: The ID of the secret in Secret Manager containing the Postgres JDBC URL.
* `<mysql.secret.id>`: The ID of the secret in Secret Manager containing the MySQL JDBC URL.
* `<partition.column>`: The name of the column to use for partitioning the read from Postgres.
* `<batch.size>`: The number of records to write in each batch to MySQL.

**Note:** In the `PostgresToMySql.java` file, you need to replace "your-gcp-project-id" in the `getSecret` method with your actual GCP project ID.
22 changes: 22 additions & 0 deletions gemini-codegen/java/migrationREADME.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Postgres to MySQL Migration Spark Job

This project contains a Spark job written in Java to migrate data from a table in a Postgres database to a table in a MySQL database.

## Features

* **Data Migration:** Migrates data from a specified Postgres table to a specified MySQL table.
* **Credential Management:** Securely retrieves JDBC connection strings (including username and password) from Google Secret Manager.
* **Parallel Processing:** Reads data from Postgres in parallel based on a specified partition column to improve performance.
* **Batch Writing:** Writes data to MySQL in batches for improved efficiency and to avoid overwhelming the database.
* **Data Transformation:** Adds an `insertion_time` column to the data with the current timestamp before writing it to the destination table.

## Project Structure

* `src/main/java/com/customer/app/PostgresToMySql.java`: The main application file containing the Spark job logic.
* `src/main/java/com/customer/util/DataframeUtils.java`: A utility class that provides helper functions for data transformation.
* `pom.xml`: The Maven project file that defines dependencies and build settings.
* `migrateJdbcToJdbc.md`: A documentation file with instructions on how to run the Spark job on a serverless Spark environment.

## How to Run

Please refer to the detailed instructions in the [migrateJdbcToJdbc.md](migrateJdbcToJdbc.md) file.
150 changes: 150 additions & 0 deletions gemini-codegen/java/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.customer</groupId>
<artifactId>spark-delta-to-iceberg-migration</artifactId>
<version>1.0-SNAPSHOT</version>

<properties>
<maven.compiler.release>11</maven.compiler.release>
<spark.version>3.5.7</spark.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>libraries-bom</artifactId>
<version>26.73.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<!-- Spark -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.13</artifactId>
<version>${spark.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_2.13</artifactId>
<version>${spark.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.iceberg</groupId>
<artifactId>iceberg-spark-runtime-3.5_2.13</artifactId>
<version>1.5.0</version>
</dependency>
<dependency>
<groupId>io.delta</groupId>
<artifactId>delta-spark_2.13</artifactId>
<version>3.1.0</version>
</dependency>

<!-- JDBC Drivers -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.7.8</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>9.5.0</version>
</dependency>

<!-- Google Cloud Secret Manager -->
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-secretmanager</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.6.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>mozilla/public-suffix-list.txt</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/DEPENDENCIES</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>NOTICE</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformer">
<addHeader>false</addHeader>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ApacheLicenseResourceTransformer">
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.customer.app.DeltaLakeToIceberg</mainClass>
</transformer>
</transformers>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/maven/**</exclude>
<exclude>META-INF/MANIFEST.MF</exclude>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
<exclude>module-info.class</exclude>
<exclude>META-INF/versions/*/module-info.class</exclude>
</excludes>
</filter>
</filters>
<relocations>
<!-- Don't relocate android -->
<!-- Required - Throws NoClassDefFoundError: com/google/protobuf/MessageOrBuilder if removed -->
<relocation>
<pattern>com.google</pattern>
<shadedPattern>com.customer.shaded.com.google</shadedPattern>
</relocation>
<!-- Don't relocate org since org.apache.spark is provided -->
<!-- Take special care of grpc-netty-shaded, it uses the package
io.grpc.netty.shaded.io.grpc.netty, which will cause the
ServicesResourceTransformer to replace both occurrences of io.grpc -->
<relocation>
<pattern>io.grpc.netty.shaded</pattern>
<shadedPattern>com.customer.shaded.io.grpc.netty.shaded</shadedPattern>
</relocation>
<relocation>
<pattern>io</pattern>
<shadedPattern>com.customer.shaded.io</shadedPattern>
<includes>
<include>io.grpc.**</include>
<include>io.opencensus.**</include>
<include>io.perfmark.**</include>
</includes>
</relocation>
<!-- No reason to relocate META-INF for grpc_netty -->
</relocations>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Loading
Loading