Post

AWS Glue Job Bookmarks Explained: A Practical Guide

In this article, let us understand what AWS Glue job bookmarks are, how they work, when to use them, and most importantly, when not to. If you have ever run a Glue job that processed the same data twice — or worse, missed data entirely — this is for you.

What Are Glue Job Bookmarks?

When you run an ETL job on a schedule, you usually only want to process data that arrived since the last run. Without some tracking mechanism, your job would either reprocess everything (wasteful) or you would need to build your own logic to figure out what is new.

Glue job bookmarks are AWS’s built-in answer to this. When enabled, Glue tracks which files or database rows your job has already processed and skips them on subsequent runs. You toggle a checkbox or set --job-bookmark-option to job-bookmark-enable, and Glue handles the rest.

Under the hood, Glue maintains bookmark state as part of the job definition. After each successful run, it persists information about the last processed data — timestamps for JDBC sources, file modification times for S3 sources, and partition information where applicable. On the next run, the Spark job uses this state to filter only new data.

When Bookmarks Work Well

Bookmarks work best with append-only data sources where you never update or delete records once they land. Think of:

  • S3 buckets receiving daily log files where files are immutable once written
  • JDBC tables with monotonically increasing primary keys or timestamps (like an orders table with an order_id auto-increment column)
  • Streaming-like batch pipelines where data lands as new partitions and you read incrementally

If your pipeline reads from S3 and the files never change after creation, bookmarks are a solid choice. You set them up once and mostly forget about them.

Let Us Walk Through an Example

Let us say we have a Glue job that reads JSON files from an S3 bucket and writes the transformed data to a Glue catalog table in Parquet format. The S3 bucket receives new files every hour.

The job script looks something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import sys
from awsglue.transforms import *
from awsglue.utils import getResolvedOptions
from pyspark.context import SparkContext
from awsglue.context import GlueContext
from awsglue.job import Job

args = getResolvedOptions(sys.argv, ['JOB_NAME'])
sc = SparkContext()
glueContext = GlueContext(sc)
spark = glueContext.spark_session
job = Job(glueContext)
job.init(args['JOB_NAME'], args)

# DynamicFrame with bookmark support
datasource = glueContext.create_dynamic_frame.from_options(
    connection_type="s3",
    connection_options={
        "paths": ["s3://my-bucket/incoming/"],
        "recurse": True
    },
    format="json",
    transformation_ctx="datasource"
)

# Transform and write
apply_mapping = ApplyMapping.apply(
    frame=datasource,
    mappings=[("id", "int", "id", "int"), ("name", "string", "name", "string")]
)

glueContext.write_dynamic_frame.from_catalog(
    frame=apply_mapping,
    database="my_db",
    table_name="processed_data",
    transformation_ctx="write"
)

job.commit()

The key is the transformation_ctx parameter. Glue uses it to tie bookmark state to specific nodes in the job graph. If you change a transformation_ctx name, Glue treats it as a new source and loses track of what it already processed — so be careful with refactoring.

When the job runs the first time, it processes all files. On the second run, Glue looks at the bookmark state, compares file modification timestamps, and only picks up files modified after the last successful job run.

Bookmarks with JDBC Sources

For JDBC sources, bookmarks work differently. Glue uses a column you specify (typically a numeric primary key or a timestamp) to determine what is new.

1
2
3
4
5
6
7
8
9
10
datasource = glueContext.create_dynamic_frame.from_options(
    connection_type="postgresql",
    connection_options={
        "url": "jdbc:postgresql://host:5432/db",
        "dbtable": "public.orders",
        "user": "user",
        "password": "password"
    },
    transformation_ctx="datasource"
)

For this to work, Glue needs a numeric column that strictly increases (like a BIGSERIAL primary key or an updated_at timestamp). The column cannot have gaps that would skip rows — if order IDs are not strictly sequential, you may miss records. Also, bookmarks do not pick up updates to existing rows. They track what is new, not what changed.

Comparing Bookmarks vs Rolling Your Own

ApproachBookmarkingEffort to Set UpHandles UpdatesWorks with Deletes
Glue BookmarkAutomaticMinimalNoNo
Custom watermark columnManual tracking in a control tableModerateYes (updated_at)Yes (soft deletes)
Full reload every runNoneMinimalAlwaysAlways
Change Data Capture (CDC)External tool or DMSHighYesYes

If your source data is purely append-only, bookmarks save you a lot of time. If you need to handle updates or deletes, you are better off building your own watermarking logic or looking into CDC.

Limitations and Things to Watch Out For

1. Bookmarks are tied to the job name. If you clone a job, the new job gets its own bookmark state. If you rename the job, you lose the existing state. This can catch you out when you are cleaning up or reorganising jobs.

2. Changing transformation_ctx resets bookmarks. As mentioned earlier, the transformation_ctx parameter is the anchor for bookmark state. Change it and Glue treats the data source as brand new, reprocessing everything from scratch. This is fine if you know about it, but painful if you discover it because a production job suddenly processed 6 months of data.

3. Job failures and retries. Bookmark state is only persisted when the job succeeds. If your job fails mid-way, the next run picks up from the last successful bookmark — it does not track partial progress within a run. This means if your job fails after processing 80% of new files, those 80% get reprocessed on the retry. Make sure your transformations are idempotent or your destination handles duplicates.

4. Only works with Glue DynamicFrame APIs. Bookmarks work with create_dynamic_frame.from_options and create_dynamic_frame.from_catalog. If you use native Spark DataFrames (spark.read.parquet(...)) instead of Glue DynamicFrames, bookmarks do nothing. The bookmark logic lives in the Glue DynamicFrame layer, not in Spark itself.

5. Not all formats support bookmarks equally. Bookmarks support JSON, CSV, Parquet, Avro, and ORC on S3. For JDBC, they rely on the key column you specify. Custom or nested formats may not work as expected — test with a representative sample before relying on this in production.

What Changes in Production

In a production use case, you would want:

  • Monitoring. Set up a CloudWatch alarm that triggers if a job processes zero records — bookmarks might be stuck or data might have stopped flowing for a real reason.
  • Reset strategy. Have a documented process and possibly a script to reset bookmarks if needed. The AWS console lets you do this, but you do not want the on-call person figuring it out at 3 AM.
  • Idempotent writes. Even with bookmarks, design your write step so that duplicate processing does not corrupt the destination. Overwrite semantics or upsert logic using a key gives you a safety net.
  • Test bookmark behavior on job updates. Before updating a job in production, run it in a dev environment with the same bookmark state (or a copy of it) to make sure your changes do not accidentally trigger a full reload.

When to Skip Bookmarks Altogether

There are cases where bookmarks are more trouble than they are worth. Skip them if your source data gets frequent updates and deletes, you are joining multiple sources where only some are append-only, or you are already using a data format that provides its own incremental tracking (Delta Lake, Iceberg, Hudi). If you need precise control over what gets processed and when, maintaining your own watermark column or control table is a few extra lines of code but saves you from debugging bookmark state issues at odd hours.

Glue job bookmarks are a convenient feature for simple, append-only ETL workloads. They work well when your data model matches their assumptions and you understand what resets them. The moment your use case involves updates, deletes, or complex multi-source joins, you are likely better served by managing incremental logic yourself. Like most AWS features, they are a good fit for the 80% case — just make sure your use case is in that 80%.

This post is licensed under CC BY 4.0 by the author.