ETL jobs

Run an ETL jobPick a runtime and compute, run the job and read the logs.

An ETL job is Python code that reads data from your connections, transforms it and writes the result somewhere else. After this chapter you can create a job, run it, read its log, stop it, and share it with other projects.

Jobs live in a project, on the Jobs tab. A job can also run on a schedule (see Scheduling and run history).

Is a job what you need?#

Use a job when Use something else when
The work should run without you, on a schedule, or repeatedly with the same code You are exploring data. Use a notebook
The result should be written to a lakehouse table, file store or database You only need rows for a chart. Use a dataset
The work is too heavy for a dataset, such as Spark transformations or joins across sources The data fits one SQL query. A dataset on Standard is quicker

You need a project, at least one connection, permission to run ETL jobs and a compute size you may use. Jobs never run on Standard.

Create a job#

  1. In a project, open the Jobs tab and select New Job.
  2. Type a Job Title that follows the name rule.
  3. Select Add.
The Jobs tab. Import Job and New Job sit at the top right.
The Jobs tab. Import Job and New Job sit at the top right.
The New Job dialog asks only for a name.
The New Job dialog asks only for a name.

What happens next: You see Job Added successfully, and the job appears in the list. Select its name to open the editor.

The list shows each job's name, who saved it last and when. The row menu has Rename (Job renamed successfully), Delete (Job deleted successfully) and Export.

The row menu for a job.
The row menu for a job.

Write and run a job#

By the end of this task your job has run and you have read its log.

  1. Select the job name to open the editor.
  2. In the left drawer, open Connections and tick the connections the job uses. Your code can use only the connections you tick.
  3. Write your code (see the examples below). The editor suggests connection, database, table and column names as you type.
  4. Open the runtime picker and choose a Runtime and Compute size. See Runtimes and compute.
  5. Select Run. Groundzero saves the job first. If the save fails, nothing runs.
  6. Watch the Messages pane. It fills with log lines while the job runs.
The editor. Left: Connections, Jobs, Models, Utilities and System Variables. Top right: runtime picker, assistant, output toggle, Run, Save, Close. Bottom: the Messages pane.
The editor. Left: Connections, Jobs, Models, Utilities and System Variables. Top right: runtime picker, assistant, output toggle, Run, Save, Close. Bottom: the Messages pane.
A finished run. The line above the table reads Job completed successfully. The table holds Timestamp and Logs columns.
A finished run. The line above the table reads Job completed successfully. The table holds Timestamp and Logs columns.

How to check it worked: The message ETL job submitted successfully appears, and the log ends with Job completed successfully. If it ends with Job failed. Check the logs for the cause., read the last log lines.

Tip

Tick only the connections the job actually uses. The ticked connections are saved with the job and decide which connections your code can reach.

The editor toolbar#

Control What it does
Runtime picker Opens the Runtime, Compute and GPU Size panel
Assistant (sparkle icon) Opens the code assistant for this job. Type @logs to hand it the latest log lines. See the Analyze, AI and Admin guide
Output pane toggle Shows or hides the Messages pane
Run (play icon) Saves, then starts the job. While a run is active the icon turns into a red Stop
Save (disk icon) Saves code and settings. Greyed out while the code is empty. You see Job saved successfully
Close Returns to the project

The left drawer#

Section Use it to
Connections Tick the connections to use. Each one shows its databases, tables and columns
Jobs Pick other jobs from the same project to run together with this one (sub-jobs)
Models Pick registered models to load before your code starts
Utilities Search ready-made code templates. Hover over one and select the copy icon. You see Template name code copied to clipboard!
System Variables Copy placeholders to paste into code. You see ${NAME} copied to clipboard!
Utilities: search the templates, then copy one into your code.
Utilities: search the templates, then copy one into your code.

Write job code#

Every job starts by creating a session, then reads, transforms and writes through the connection names you ticked.

A Spark job#

python
from GZ.SparkUtils import sparkSession

spark = sparkSession("DailySalesRollup", "WARN")

orders = spark.readJDBC(
    connName="AcmePOSPostgres",
    query="SELECT store_id, order_date, total_amount FROM public.orders",
)

daily = (
    orders.groupBy("store_id", "order_date")
    .sum("total_amount")
    .withColumnRenamed("sum(total_amount)", "revenue")
)

spark.writeGzLakehouse(
    df=daily,
    connName="AcmeLakehouse",
    tableName="sales.daily_sales",
    mode="overwrite",
)

sparkSession(appName, logLevel="WARN") returns a Spark session with extra methods that read from and write to your connections. Give it a meaningful application name.

A DuckDB job#

python
from GZ.DuckDBUtils import duckSession

session = duckSession()
sales = session.readGzLakehouse(
    connName="AcmeLakehouse", tableName="sales.daily_sales"
)
session.register("sales", sales)
top = session.query("SELECT store_id, SUM(revenue) AS r FROM sales GROUP BY 1")
result_df = top.df()

Read and write other systems#

Each connection type has its own read and write methods. You pass the connection name as it appears on the Connections page, never a password.

Connection Read Write or run
Amazon S3 readS3(connName, fileName, format="parquet", schema=None, options=None) writeS3(df, connName, filePath, format, mode, options=None, coalesce=False)
JDBC databases (PostgreSQL, SQL Server, MySQL, Redshift) readJDBC(connName, query, options=None) writeJDBC(df, connName, tableName, mode, options=None); executeJDBC(connName, query)
Snowflake executeSnowflake(connName, query, options=None) writeSnowflake(df, connName, tableName, mode="append", extraOptions=None); executeSnowflakeStatement(connName, statement)
Google BigQuery executeBigQuery(connName, tableName, query=None) writeBigQuery(df, connName, tableName, writeMethod, mode)
Kafka readKafka(...) writeKafka(...)
REST readREST(...) writeREST(...)
GZ Lakehouse executeQuery(connName, sqlQuery, siteName=None) writeGzLakehouse(df, connName, tableName, database=None, mode="overwrite", partitionBy=None, options=None, ...)

Common DuckDB session methods are readGzLakehouse(connName, tableName), readS3File(connName, bucketName, filePath, fileType), register(name, relation) and query(sql). Call .df() on a result to get a pandas DataFrame.

Not sure of the exact syntax? Open Utilities in the left drawer. It has templates for moving data between S3, GCS, Azure Blob, BigQuery, Snowflake, Redshift and GZ Lakehouse, such as S3 to GZLakehouse and Snowflake to S3. Replace each {{PLACEHOLDER}} value and any optional parameters in the comments. You can also ask the code assistant, or your MCP client, to look up the syntax for a connection type.

Note

Template placeholders such as {{S3_CONNECTION_NAME}} are for you to edit before you save. They are not system variables.

Use system variables#

System variables are placeholders in the form ${NAME}. Groundzero fills them in just before your code runs.

Placeholder Value
${STRTTM} Session start time
${ENDTM} Session completion time (set when the code reaches that line)
${SITENM} Site name
${PRJNM} Project name
${JOBNM} Job name
${CLNTNM} Client name
${RTE} Runtime name
${RGN} Region
${USR} User who started the run
${USRGRPS} The user's groups
System Variables in the left drawer.
System Variables in the left drawer.

Warning

Each placeholder is replaced by text that already has single quotes around the value, so you can drop it straight into a SQL statement. For example ${USR} becomes 'maya.chen'. If you need the bare value, remove the quotes in your code.

What your code may not do#

Job code runs in a protected space on the Spark and DuckDB runtimes. This keeps credentials safe.

You cannot What you see Do this instead
Import network and system modules such as socket, urllib, http, ssl, smtplib, asyncio, threading, multiprocessing, pickle, shutil, pathlib, glob or sqlite3 Import of 'name' is not allowed for security reasons Reach outside systems through a connection (REST, S3, a database)
Read secret environment variables The values are hidden Keep secrets in a connection
Read or write files outside /tmp, /home/workspace and /home Access to 'path' is restricted Write to /tmp or to a connection
Call os.system-style functions or sys.exit() … is not allowed for security reasons Let the script end normally

Run jobs together#

Run other jobs along with this one#

  1. Open Jobs in the left drawer.
  2. Pick other jobs from the same project.
  3. Select Run.

Groundzero runs the picked sub-jobs together with the main job and gives each its own saved connections. A run can include up to 255 sub-jobs, and each job can appear once. This needs a Spark, DuckDB or pandas runtime. Otherwise the run fails with Invalid RTE for orchestration.

Load models#

Open Models in the left drawer and pick registered models. They are loaded before your code starts. See Models and MLflow.

Read the log and stop a run#

The Messages pane shows lines about once a second while the job is logging. The download icon above the pane saves the log lines.

You see What it means What to do
Job completed successfully The run finished with status Success Nothing
Job failed. Check the logs for the cause. The run failed Read the last log lines
Job was aborted / Job aborted successfully The run was stopped Run again if you want it to finish
Reconnecting… A log request failed. Groundzero is trying again Wait
Log streaming stopped after 2 minutes without a response. The job may still be running. The retries did not succeed Select Resume log streaming
No new logs for 15 minutes. The job may still be running. The run is quiet Select Resume log streaming
The run could not be found. Groundzero has no record of the run Run the job again
Reconnected to the running job You reloaded the page and Groundzero picked the run up again Nothing

To stop a run, select the red Stop icon. Job aborted successfully confirms it. If a run cannot be stopped, a message gives the reason.

Tip

If you close the browser while a job runs, open the job again. Groundzero picks up the run and carries on showing the log.

If the run does not start#

Message Cause Fix
please select Compute No runtime and compute chosen Choose both in the runtime picker
please select connection No connection ticked Tick a connection in the left drawer
Failed to save job The save did not work, so the run did not start Try again
Failed to run job The run could not be started Try again. If it keeps failing, contact an administrator
Invalid parameters The run request was incomplete, for example no compute Choose a runtime and compute
Invalid RTE. The chosen runtime cannot run jobs Choose a Spark or DuckDB runtime

Share a job#

Published jobs form a shared library for your site.

Publish a job#

  1. In the Jobs list, open the row menu and select Export.
  2. If no published job has that name, you see name exported successfully.
  3. If one exists, the Override Export dialog asks whether to replace it. Choose Override to replace it, or cancel.

Import a job#

  1. On the Jobs tab, select Import Job.
  2. Choose a published job in Select a job and select Import.
  3. If your project already has a job with that name, the Job Name Conflict dialog offers Replace, Keep Both (adds a timestamp to the new name) and Cancel.
Import Job lists the jobs published on your site.
Import Job lists the jobs published on your site.

Administrators can review and delete published jobs, and import them across sites, in the admin console.

Troubleshooting#

Symptom Cause Fix
The log table stays empty after Run The runtime is still starting Wait. The pane reads Running job… until lines arrive
Import of 'x' is not allowed for security reasons The code imports a blocked module Use a connection instead
The job cannot see a table The connection is not ticked, or its tables have not loaded yet Tick the connection and wait for its tables to appear
A run says Success but a table is missing The write used mode="append" or a different tableName than you checked Check tableName and mode