Sequence and Row Kind
Record ordering and row kind answer different questions: which update takes precedence, and whether that update inserts or retracts a row. Configure them alongside the merge engine.
| Option | Use |
|---|---|
sequence.field | Order updates by one or more source fields |
rowkind.field | Decode an operation such as +I or -D from a column |
sequence.snapshot-ordering | Order updates across writers by commit snapshot ID |
Partial updates from independent streams can use sequence groups instead of one shared sequence field.
Sequence Field
By default, Paimon assigns internal sequence numbers to order records during merging. This order
does not necessarily match the order in the source system, especially when updates arrive late
or come from multiple writers. Set sequence.field to a source version or timestamp when the
source defines which update should take precedence.
For example, this Flink SQL table uses the source version to keep the latest order status:
CREATE TABLE order_status (
order_id BIGINT PRIMARY KEY NOT ENFORCED,
status STRING,
source_version BIGINT
) WITH (
'sequence.field' = 'source_version'
);
INSERT INTO order_status VALUES (1, 'shipped', 12), (1, 'paid', 11);
-- After the INSERT job finishes, query in batch mode.
SELECT * FROM order_status;
-- 1, shipped, 12
The default sequence.field.sort-order = ascending merges the largest value last. With the
default deduplicate engine, version 12 wins even if version 11 arrives later. Setting
sequence.field.sort-order = descending reverses that comparison, so the smallest value wins
for deduplicate.
For multiple fields, such as 'sequence.field' = 'update_time,source_offset', Paimon compares
the first field, then uses the next field to break ties. If all configured values are equal,
Paimon falls back to its internal record order. Use a source tie-breaker when the result must
be independent of arrival order.
The merge engine still determines how ordered records are combined. Sequence fields do not
turn an aggregation engine into deduplication, or guarantee a complete historical ordering
for order-dependent field aggregates such as first_value.
Restrictions:
sequence.fieldcannot be combined withmerge-engine = first-row, cross-partition updates, orsequence.snapshot-ordering.- Do not assign an
aggregate-functionto a sequence field itself. GEOMETRY,GEOGRAPHY, and managedBLOBfields cannot be sequence fields.
Row Kind Field
By default, Paimon uses the input row's row kind. Set rowkind.field when the source instead
encodes each operation in a data column. The configured column must have a character-string
type and contain a non-null operation value on every input row.
| Value | Row kind | Meaning |
|---|---|---|
+I | INSERT | Add a row or contribution |
-U | UPDATE_BEFORE | Retract the old row or contribution |
+U | UPDATE_AFTER | Add the updated row or contribution |
-D | DELETE | Retract a row or contribution |
The merge engine determines how each kind affects stored state; see its delete and retraction requirements before choosing an input encoding.
For example, the following Flink SQL table accepts upserts and deletes encoded in op.
Wait for each bounded INSERT job to finish before running the next statement, and run the
queries in batch mode.
CREATE TABLE order_changes (
order_id BIGINT PRIMARY KEY NOT ENFORCED,
status STRING,
source_version BIGINT,
op STRING NOT NULL
) WITH (
'sequence.field' = 'source_version',
'rowkind.field' = 'op'
);
INSERT INTO order_changes VALUES (1, 'paid', 11, '+I');
INSERT INTO order_changes VALUES (1, 'shipped', 12, '+U');
SELECT order_id, status FROM order_changes;
-- 1, shipped
INSERT INTO order_changes VALUES (1, 'shipped', 13, '-D');
SELECT order_id, status FROM order_changes;
-- No rows
The operation column remains a regular stored column; Paimon does not remove it from the
schema. Use the short values in the table above, without surrounding whitespace. Long names
such as INSERT and DELETE are not accepted.
Snapshot Ordering
Use sequence.snapshot-ordering = true when updates from multiple writers should be ordered
by their commit snapshot ID. Records in later snapshots are considered newer, regardless of
the writers' per-record sequence numbers.
For example, with deduplicate, a row committed in snapshot 41 takes precedence over a row
with the same key in snapshot 40, even if the writer of snapshot 40 assigned a larger
internal sequence number. This follows commit order, which may differ from source event
time.
Configure the option when creating the table. This Flink SQL example uses fixed buckets for multiple writers:
CREATE TABLE committed_orders (
order_id BIGINT PRIMARY KEY NOT ENFORCED,
status STRING
) WITH (
'bucket' = '4',
'sequence.snapshot-ordering' = 'true',
'write-only' = 'true'
);
| Requirement or boundary | Meaning |
|---|---|
| Primary-key table; immutable option | Choose this ordering strategy at table creation |
write-only = true for writers | Run a separate dedicated compaction job to preserve snapshot ordering during compaction |
No sequence.field | Source-field ordering and snapshot ordering are mutually exclusive |
| No ordering within one snapshot | Do not rely on this option to choose among multiple versions of the same key in the same snapshot |
Writer buffers can merge versions before flushing, but they do not establish a guaranteed order across files within a snapshot. Size and spill settings do not change this ordering contract.