Search docs.molo17.com
    Configuration guide Connectors

    Set up an Amazon Redshift target with S3 bulk loads

    The AWS RedShift agent writes to a Redshift cluster or a Redshift Serverless workgroup. For a snapshot it can use a bulk path: it writes the rows to a compressed CSV file in an S3 bucket, loads the file into a staging table with the Redshift COPY command, and then applies the staging table to your table. Live changes use standard writes. This guide prepares the database user and the S3 bucket, creates the target, maps the types, runs a bulk snapshot and live changes, and shows the errors that a new setup is most likely to raise, including one that cuts text without an error.

    In this guide

    You will create a Redshift user and a schema with the grants the agent needs, and an S3 bucket with an IAM policy for the access key. You will create the target connection with the S3 settings, add an entity and let Gluesync create the table, choose a type and a length for every column in the Field mapping dialog, run a snapshot with Bulk load, and check the rows in Redshift, the files in S3 and the load in the Core Hub log. You will then follow live changes, and recover from the failures that are most likely on a new setup.

    Quick information

    Goal

    Replicate a PostgreSQL table to Amazon Redshift with a bulk snapshot through S3, then keep it in sync with change data capture (CDC)

    Audience

    Data engineers and AWS administrators who own a Redshift cluster or workgroup

    Level

    Intermediate

    Time required

    About 60 minutes

    Area

    Amazon Redshift → SQL and S3; Control Plane → Pipelines → AWS RedShift target → Base settings and Advanced settings; entity → Field mapping and start options

    Applies to

    Gluesync 2.3

    Before you begin

    Make sure that:

    1. You have a Redshift provisioned cluster or a Redshift Serverless workgroup, and you can run SQL on it as an administrator. Redshift is a target only: you need another database as the source. This guide was tested with Redshift Serverless in eu-central-1 and a PostgreSQL 16 source;

    2. Core Hub can open a connection to the Redshift endpoint on port 5439. The security group, the VPC and the public access setting of the endpoint must allow the Core Hub host;

    3. You have an S3 bucket in the same region as Redshift, and an IAM user whose access key you can use. The agent signs in to Redshift with a database user and a password. The access key is used only for S3, and Gluesync passes the same key to the COPY command;

    4. You can sign in to the Control Plane with a role that can create pipelines;

    5. You have a source table with a primary key. This guide uses a PostgreSQL table public.events with 100,000 rows (id bigint primary key, kind varchar(20), val integer, note text, amount numeric(12,2), flag boolean, created_at timestamp, seen_at timestamptz, born date) and a second table with one column of each less common type.

    The messages quoted in this guide were captured in the Control Plane and in the Core Hub log against Redshift Serverless. Names in angle brackets stand for your own values. The options of the agent are in the AWS RedShift target reference.

    Guide steps

    Create the Redshift user and the schema

    SQL

    Gluesync does not need a superuser. Connect to the target database as an administrator and run the statements below. Replace the names and the password.

    1. Create the user and a schema that the user owns. The agent creates the target tables there, and an owner can create, change, truncate and drop its own tables:

      CREATE USER gluesync_user PASSWORD '<a strong password>';
      CREATE SCHEMA gluesync_data AUTHORIZATION gluesync_user;
    2. Let the user create its staging tables. The bulk path creates one staging table for each entity, and it creates it without a schema name. The table lands in the first schema of the search path of the user, which is public with the default search path (in the test, a staging table named pu_…​ appeared in public). Grant CREATE on that schema:

      GRANT USAGE, CREATE ON SCHEMA public TO gluesync_user;
    3. If you load into tables that already exist, and the user does not own them, grant the rights on each of them:

      GRANT SELECT, INSERT, UPDATE, DELETE ON <schema>.<table> TO gluesync_user;

      The Empty the table first option of a snapshot runs TRUNCATE, which needs the ownership of the table, or the TRUNCATE privilege on it. The bulk path also runs ANALYZE on its staging table, which the user owns.

    4. Check what the user can do:

      SELECT has_schema_privilege('gluesync_user', 'gluesync_data', 'CREATE') AS data_schema,
             has_schema_privilege('gluesync_user', 'public', 'CREATE') AS staging_schema;
    Expected result

    Both columns return true. Redshift grants the right to create temporary tables to every user through the PUBLIC group, and the agent uses temporary tables for standard writes. If your administrators revoked it, grant TEMP on the database to the user.

    Create the S3 bucket and the IAM policy

    CLI

    The bulk path needs a bucket where Core Hub writes the files and Redshift reads them. Create the bucket in the same region as your Redshift endpoint, then give the IAM user the three rights that the agent and the COPY command use. Replace <bucket>.

    1. Create the bucket, if you do not have one:

      aws s3api create-bucket --bucket <bucket> --region eu-central-1 \
        --create-bucket-configuration LocationConstraint=eu-central-1
    2. Attach this policy to the IAM user whose access key you will give to Gluesync:

      {
        "Version": "2012-10-17",
        "Statement": [
          {
            "Effect": "Allow",
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3:::<bucket>"
          },
          {
            "Effect": "Allow",
            "Action": ["s3:PutObject", "s3:GetObject"],
            "Resource": "arn:aws:s3:::<bucket>/gluesync-bulk/*"
          }
        ]
      }

      s3:ListBucket is for the check that Gluesync runs on the bucket, s3:PutObject is for the upload, and s3:GetObject is for the read of the COPY command, which runs with the same key. The agent always writes under the prefix gluesync-bulk/, and you cannot change it.

    3. The agent never deletes the files it writes, so add a lifecycle rule that removes them. Without it the bucket grows with every bulk load:

      aws s3api put-bucket-lifecycle-configuration --bucket <bucket> --lifecycle-configuration '{
        "Rules": [{
          "ID": "expire-gluesync-bulk",
          "Status": "Enabled",
          "Filter": {"Prefix": "gluesync-bulk/"},
          "Expiration": {"Days": 1}
        }]
      }'
    Expected result

    The bucket exists in the region of Redshift, and the IAM user can list it and write and read under gluesync-bulk/. The reference page lists the Redshift rights redshift:GetClusterCredentials and redshift:DescribeClusters: the agent does not use them, because it connects with the database user and the password.

    Create the pipeline and the Redshift target

    UI

    1. In the Control Plane, select Create your first pipeline, or the + button of the pipeline list. The Agents setup wizard opens;

    2. In 1. Setup source, choose the PostgreSQL CDC agent tile and fill Base settings with your source database. Select Test and continue, then Next. The PostgreSQL WAL source guide prepares the source;

    3. In 2. Setup target(s), choose the AWS RedShift agent tile and fill Base settings:

      • Host name / Address: the endpoint, such as <workgroup>.<account>.<region>.redshift-serverless.amazonaws.com for Serverless, or the cluster endpoint without the port and the database;

      • Port: 5439;

      • Database name, Username and Password: the database and the user of step 1;

      • AWS S3 bucket name (used by bulk operations): <bucket>;

      • AWS Region for S3 bucket: the region of the bucket, such as eu-central-1. When the field has no value, the agent uses us-east-1;

      • AWS Access Key ID for S3 and AWS Secret Access Key for S3: the key of the IAM user;

    4. Under Security, set Enable TLS to encrypt the connection. It is off by default. With Enable TLS on and Trust server certificate off, the connection to Redshift Serverless was accepted and live changes kept flowing;

    5. Select Test and continue. The target appears with a green check mark. Select Next, enter a Pipeline name and select Finish.

    Edit target page of the AWS RedShift agent with host
    Figure 1. The Redshift target connection with the S3 settings. The values are examples.
    Expected result

    The pipeline appears in the HEALTHY group of the pipeline list. A saved connection does not prove the S3 settings: the check of the bucket runs when you save the connection while an entity with Bulk load exists, and when such an entity starts. Step 8 shows what the check says when a setting is wrong.

    There is no Schema, Workgroup or S3 Key Prefix field. The schema comes from the Target Schema of each entity, and the workgroup is part of the endpoint host name. The key names for the REST API are in the reference.

    Add the entity and let Gluesync create the table

    UI

    1. In the pipeline, select Entities. The Add entities page opens with the Object browser. Expand the source schema public, select the check box of the table events, and then select the mapping icon of the table in the Selected entities list. The Field mapping dialog opens;

    2. Under Target, choose the Target Schema. In Target Table type the table name and choose Create table "<name>". The dialog shows New table <name> will be created on confirm. and a proposed target type for every column;

    3. The text column shows the error Missing required field, because its Length is 0. Read the next section before you set it;

    4. Select Confirm. The Create table dialog shows the Read-only SQL statement. Select Create table <name> to run it in Redshift;

    5. Back on the Add entities page, select Confirm and continue.

    The statement has this form:

    CREATE TABLE "<schema>"."events" (
        "id" bigint,
        "kind" character varying(20),
        "val" integer,
        "note" text,
        "amount" numeric(12, 2),
        "flag" boolean,
        "created_at" timestamp without time zone,
        "seen_at" timestamp with time zone,
        "born" date,
        PRIMARY KEY ("id")
    )
    Create table dialog with a read-only CREATE TABLE statement for a table with a bigint key
    Figure 2. The create table statement that Core Hub proposes.

    In Redshift, text is varchar(256). The statement says "note" text, and Redshift created character varying(256) for the column, whatever Length you typed. A source value longer than 256 bytes is then refused on a standard write, and silently cut on a bulk load (see step 8). For text that can be longer, change the Target type of the column to varchar and set Length to the size you need, up to 65,535. Redshift counts the length of a varchar in bytes, so a text with accented letters or Asian characters needs more than its number of characters.

    Expected result

    The entity appears in the pipeline on Hold, and the table exists in Redshift. A column of type varchar(20) stays varchar(20), and numeric(12,2) stays numeric(12,2). PRIMARY KEY is declared, but Redshift does not enforce it: in the test, an INSERT of an existing key was accepted. Gluesync uses the key to find rows, so keep other writers away from the table.

    See Target table creation for the dialog.

    Choose a type for the columns that need one

    UI

    Most types map to the Redshift type with the same name. A few need a decision, and two cannot be loaded at all. The table below lists what the Field mapping dialog proposed for a PostgreSQL source, and what to do.

    Field mapping dialog with source types uuid
    Figure 3. The proposed types of a table with one column of each type. Columns that need a length show an error until you set it.
    1. For uuid and inet, the proposal is varchar with Length 0. Set Length to 36 for uuid and to 45 for inet;

    2. For jsonb and json, the proposal is varchar. To query the document with the Redshift SUPER functions, choose super as Target type and set Length to -1. In the test, c_json.a returned the value of the key a, and is_object(c_json) returned true;

    3. For bytea and for array columns, clear the check box of the column in the Field mapping dialog, so that Gluesync does not copy it. The proposal is varbyte, and an array shows a Data type mismatch detected warning. Step 8 shows the error that you get if you keep them.

    Source type Redshift type Note

    smallint, integer, bigint

    smallint, integer, bigint

    Values written unchanged

    real, double precision

    real, double precision

    Values written unchanged

    numeric(p,s)

    numeric(p,s)

    numeric(38,10) kept its precision and scale. Redshift allows up to 38 digits

    char(n)

    character(n)

    Padded with blanks to n, so ab is stored as ab plus three blanks

    varchar(n)

    character varying(n)

    Length kept

    text

    text, which is varchar(256)

    Choose varchar with a length for longer text

    boolean

    boolean

    true and false

    date, time

    date, time without time zone

    Values written unchanged

    timestamp, timestamptz

    timestamp without time zone, timestamp with time zone

    timestamp(3) kept its milliseconds: 2026-01-02 03:04:05.678

    uuid, inet

    varchar

    Set the length

    json, jsonb

    varchar, or super if you choose it

    super stores an object that you can query

    bytea, arrays

    varbyte is proposed

    Not loadable: clear the check box of the column

    Expected result

    The Error tab of the dialog shows 0 errors and Confirm is enabled. After the snapshot of step 6, a table with these types contained 123e4567-e89b-12d3-a456-426614174000 in the uuid column, an object in the super column, the numeric(38,10) value 12345678901234567890.1234567890, and a NULL in every column of a source row that had none.

    See Data type mapping for the Gluesync types behind this table.

    Run a snapshot with the bulk load

    UI + SQL

    Bulk load is a setting of the snapshot. With it off, the agent writes JDBC batches. With it on, it writes the rows to a gzip CSV file in S3, loads the file into the staging table with COPY, and applies it to the target table.

    1. In the pipeline, find the entity. On Hold, select the start options button, the one next to the play button. The Snapshot (copy-only) dialog opens;

    2. Keep Keep and merge under Data already in the target;

    3. Under Speed select Custom. In the Writing column, under During the copy, select Bulk load. The diagram shows Bulk and a Staging table;

    4. Select Start snapshot.

    Snapshot copy-only dialog with Speed Custom
    Figure 4. The Writing column with Bulk load for the copy. The dialog only offers During the copy: live changes are in step 7.

    Check the rows in Redshift:

    SELECT count(*), min(id), max(id) FROM <schema>.events;

    Check the staging file in S3. The agent leaves it there, which is why step 2 adds a lifecycle rule:

    aws s3 ls s3://<bucket>/gluesync-bulk/ --recursive

    Then read the Core Hub log. The lines of the load are at INFO level:

    docker logs <core hub container> 2>&1 | grep -E "RedshiftCsvUploader|RedshiftBulkCopyExecutor"
    Expected result

    The count equals the source count, 100000. S3 holds one file named gluesync-bulk/<agent id>/<entity id>/<uuid>.csv.gz, about 2 MB for 100,000 rows of this table. The log shows the steps of one load:

    COPY command completed: 100000 rows loaded into "pu_eve_<entity id>"

    Staging operation counts: I=0, U=100000, D=0 (metadata rows=100000)

    INSERT completed in <n> ms for <schema>.events (rows=100000)

    Bulk SQL operations completed (delete/insert): deleted=0, inserted=100000. Committing.

    The staging table is named after the first two characters of the schema, the first three of the table and the entity id. The snapshot rows are counted as updates (U): Gluesync deletes the matching keys from the target table and inserts the staged rows, because Redshift has no MERGE that it uses here.

    On Redshift Serverless the final commit of a load can be slow. In the test, 100,000 rows loaded in about 10 seconds, and the commit alone took 9.9 seconds, with the warning Slow final post-DML commit detected: 9906 ms (threshold=5000 ms) for <schema>.events. The warning is a measure, not a failure. The threshold is the Bulk lock diagnostics slow final commit threshold (ms) property.

    Follow live changes and read the results

    UI + SQL

    The copy-only snapshot leaves the entity on Hold. Start it to follow the changes, then change the source and read the target.

    1. Select the play button of the entity. The Sync changes (CDC) dialog opens. Under Start from choose Now, because the rows are already in the target, and select Start entity. The entity becomes Active. Under Writing the dialog shows Standard and This target has no bulk load: live changes always use standard writes;

    2. On the source, insert, update and delete one row each:

      INSERT INTO events VALUES (200001, 'new', 1, 'cdc insert', 12.34, true, now(), now(), current_date);
      UPDATE events SET val = -1, note = 'cdc update' WHERE id = 5;
      DELETE FROM events WHERE id = 6;
    3. After a few seconds, read the three keys and the count in Redshift:

      SELECT id, kind, val, note FROM <schema>.events WHERE id IN (5, 6, 200001) ORDER BY id;
      SELECT count(*), count(DISTINCT id) FROM <schema>.events;
    Expected result

    The first query returns the row 5 with val -1 and note cdc update, and the row 200001, and no row 6. The second query returns 100000 and 100000: one row was added, one removed, and no key is duplicated.

    Gluesync applies a batch of changes with an UPDATE of the rows that exist and an INSERT of the missing ones, so an update of a row that is not in the target creates it. A delete runs as a DELETE on the key. Redshift is built for large loads, and every statement goes through the leader node, so a long stream of single-row statements is slower than one large batch. Read Tune replication throughput for the settings of the pipeline.

    Fix the errors you are most likely to meet

    UI + SQL

    Each error below was reproduced on the target. The first three come from the check of the S3 settings. They appear as Failed to save when you select Test and continue on a target that has an entity with Bulk load. The wrong value stays saved, so fix it and select Test and continue again.

    1. Set AWS S3 bucket name (used by bulk operations) to a name that does not exist. The banner says The connection was saved, but agent <id> of pipeline <id> did not accept the new credentials: S3 bucket '<bucket>' not found. Please verify the bucket name. Fix: type the right name;

    2. Set a wrong AWS Secret Access Key for S3. The banner says …​ Access denied to S3 bucket '<bucket>'. Please check your AWS credentials and bucket permissions. Fix: use the key of the IAM user of step 2, and check its policy;

    3. Set AWS Region for S3 bucket to a region other than the one of the bucket. The banner says …​ S3 error (status 400) while accessing bucket '<bucket>': Bad Request (Service: S3, Status Code: 400, Request ID: <id>, Extended Request ID: <id>). Fix: use the region of the bucket;

    4. Add a source row whose text column is longer than the target column, with a standard write. For note as varchar(256), a 300-character value puts the entity in the Error group, with the notification SQL Error and ERROR: Value too long for character type. The log has context: Value too long for type character varying(256). Fix: widen the column in Redshift. The entity retries on its own and no restart is needed. In the test, the row was in Redshift within 45 seconds of the ALTER:

      ALTER TABLE <schema>.events ALTER COLUMN note TYPE varchar(2000);
    5. Load the same value with Bulk load and there is no error: the COPY command runs with TRUNCATECOLUMNS, which cuts the value to the size of the column. In the test, a 300-character value arrived as 256 characters, and the row count matched. Compare the longest value on both sides after a bulk load, and widen a column before the load if the source can hold more;

    6. Keep a bytea or an array column in the entity. Confirm and continue shows The entity could not be saved. and Unsupported type for columns <column> with type unsupported, <column> with type unsupported in entity <schema>.<table> for <schema>.<table>. Redshift reports a varbyte column as binary varying, which the agent does not know, so the column cannot be used. Fix: clear the check box of these columns in the Field mapping dialog. Mapping them to a varchar is refused with Incompatible data types;

    7. To delete an entity, pause it first: Delete stays disabled while it runs. Deleting an entity drops its staging table, and leaves the target table and the files in S3.

    Entity list with the notification SQL Error: Value too long for character type for a Redshift entity in the Error group
    Figure 5. A standard write of a value that is too long: the entity is in the Error group and the SQL Error notification shows the Redshift message.
    Expected result

    After each fix, the target is saved without a banner, and the entity loads again. A deleted entity leaves no staging table, so SELECT table_name FROM information_schema.tables WHERE table_name LIKE 'pu%' lists only the staging tables of the entities that you still use.

    Bulk and throughput settings

    The target has these properties, under Advanced settings with Add property. The names are the labels of the Control Plane:

    Setting Default When to change it

    Max concurrent S3 uploads

    4

    Raise it when many entities load at once and the upload to S3 is the slow step

    Target slice count for parallel load optimization

    4

    A load of 200,000 rows or 100 MB or more is cut into this number of gzip files and loaded with a manifest, so that the slices of Redshift read them in parallel. Set it to 1 to always write one file

    Disable truncate on target table before snapshot inserts

    false

    Stops the agent from truncating the target table before the first snapshot rows. Read the reference before you change it

    Enable automatic compression analysis on COPY

    off

    Runs the COPY command with COMPUPDATE ON. The default is COMPUPDATE OFF

    Enable automatic statistics update on COPY

    off

    Runs the COPY command with STATUPDATE ON. The default is STATUPDATE OFF

    Use ALTER TABLE APPEND for insert-only batches (requires identical DISTSTYLE/DISTKEY/SORTKEY between staging and target)

    off

    Replaces the final INSERT by ALTER TABLE ... APPEND for a batch that only has inserts. The snapshot of the test counted its rows as updates (U), so read the line Staging operation counts of the log before you rely on it

    Enable temporary bulk lock diagnostics, Bulk lock diagnostics row limit (10), Bulk lock diagnostics slow final commit threshold (ms) (5000)

    off

    Switch them on with MOLO17 Support when commits wait on locks

    Read Tune replication throughput for the settings that apply to every target.

    Verify the result

    You have set up the Redshift target if:

    • The user can create objects in the data schema and in the schema of the staging tables;

    • The IAM user can list the bucket, and write and read under gluesync-bulk/, and a lifecycle rule expires the files;

    • Test and continue on the target ends without a banner, and AWS Region for S3 bucket is the region of the bucket;

    • The row count of the target equals the source after the bulk snapshot, and the log shows COPY command completed;

    • A CDC insert, update and delete reach the target, and no key is duplicated;

    • No column of the entity is bytea or an array, and every long text column is a varchar that is wide enough.

    Troubleshooting

    Problem Possible cause What to do

    Failed to save with S3 bucket '<bucket>' not found. Please verify the bucket name.

    The bucket name is wrong

    Correct AWS S3 bucket name (used by bulk operations) and select Test and continue

    Failed to save with Access denied to S3 bucket '<bucket>'. Please check your AWS credentials and bucket permissions.

    The access key or the secret is wrong, or the IAM user cannot list the bucket

    Check the key, and the s3:ListBucket, s3:PutObject and s3:GetObject rights of the policy in step 2

    Failed to save with S3 error (status 400) while accessing bucket '<bucket>': Bad Request

    AWS Region for S3 bucket is not the region of the bucket

    Set the region of the bucket. The default is us-east-1

    ERROR: Value too long for character type, with Value too long for type character varying(256)

    A text column became varchar(256), or a varchar is shorter than the source value

    ALTER TABLE …​ ALTER COLUMN …​ TYPE varchar(<n>). The entity retries on its own. See the truncation guide

    Rows arrive in Redshift with a cut text and there is no error

    Bulk load runs COPY with TRUNCATECOLUMNS

    Widen the column before the load, and compare the longest value of each text column on both sides

    Missing required field on a varchar or text column in Field mapping

    Length is 0

    Type a length. For uuid use 36, for inet use 45, and for super use -1

    The entity could not be saved. with Unsupported type for columns …​

    The entity has a bytea or an array column, mapped to varbyte

    Clear the check box of the column in Field mapping

    COPY command failed with SQLState in the Core Hub log

    A file that Redshift cannot read, or an S3 right that is missing for the COPY command

    Read the lines Recent Redshift SYS_LOAD_ERROR_DETAIL rows that follow in the log. They come from the system table sys_load_error_detail of Redshift

    The warning Slow final post-DML commit detected

    The commit of a load waits in the commit queue of Redshift, which is common on Serverless

    Load fewer, larger batches, or raise the threshold of the warning. Write to MOLO17 Support if every load waits

    The bucket grows

    The agent never deletes its files

    Add the lifecycle rule of step 2

    Delete is disabled for an entity

    The entity is running or in error

    Pause it, then delete it

    Contact MOLO17 Support if you need assistance with a Redshift target.