metadata/ directory next to them that records exactly which files make up the table at each commit. In Datris it is selected the same way as parquet or orc: set fileFormat to iceberg on an Object Store or S3 destination. There is no new destination type and no extra service to run.
What an Iceberg table gives you over loose files
Configuration
provider, destinationBucketOverride and credentialsSecret exactly as on the S3 page; the Iceberg-specific fields are the same for both providers.
Field reference
writeToTemporaryLocation is not accepted together with iceberg: commits are already atomic, so it would have nothing to add.
Write modes
Every run that writes rows produces exactly one new snapshot. Readers see either the snapshot before the run or the one after it, never an in-between state.merge is a true upsert: all non-key columns of the incoming row overwrite the existing row, including nulls. This matches keyFields on the PostgreSQL, MongoDB, Snowflake and Databricks destinations, so an agent that already knows the concept can reuse it here. If your source emits partial rows, coalesce them upstream.
Partitioning
partitionBy creates an identity partition on each named column. Data files land under data/<column>=<value>/ inside the prefix, and engines that understand Iceberg partition pruning skip partitions your query does not touch. Every column in partitionBy must exist in the destination schema.
Adding partitioning to, or removing it from, an existing pipeline is refused because the table would have to be rebuilt: delete the pipeline’s destination data first, then re-register it with the new partitionBy.
Schema evolution
Before each write the destination schema is compared with the table’s current schema:- New nullable columns are added automatically. Existing rows read as null for the new column. The column is committed before the data is written, so if the data write then fails, the column exists but has no values yet; the failure message says so.
- Type changes are refused. A column whose type differs from the one already in the table stops the run with a message naming the column and both types.
- Dropping columns is refused. A column present in the table but missing from the destination schema stops the run. Remove it from the table with
deleteBeforeWrite(which recreates the table) or keep it in the schema. - New required columns are refused. A new column that is not nullable stops the run, since existing rows would have no value for it.
Reading the table from outside Datris
The table is fully described by the files under the prefix.metadata/ holds one metadata file per commit plus a version-hint.text naming the current one; data/ holds the Parquet files. Any Iceberg-aware engine that can open the location by path reads the current snapshot without contacting Datris. For example, with the Python Iceberg library:
iceberg_scan('s3://analytics-data/sales/orders') in SQL, or spark.read.format("iceberg").load("s3a://analytics-data/sales/orders"). Point the engine at the same bucket credentials the pipeline uses; for the built-in MinIO that is the MinIO endpoint with path-style access.
Datris itself reads the table through the same query surface as the other formats: the Search tab, the Assistant, the object-store query endpoint, and the query_objectstore MCP tool. For an Iceberg table the result also carries snapshotId (the snapshot the rows were read from, returned as a string because the id is a 64-bit number) and snapshotTimestamp (ISO-8601). Both are null for parquet and orc. Querying a pipeline that has never run returns zero rows rather than an error.
Switching an existing pipeline to Iceberg
A prefix that already holds loose Parquet files or ORC files cannot be converted in place. ChangingfileFormat to or from iceberg on an existing pipeline is refused unless deleteBeforeWrite is set, and a write that finds non-Iceberg objects at the prefix stops with a message asking you to set deleteBeforeWrite or use a new prefix. Either:
- point the pipeline at a new
prefixKey, so the old files stay where they are; or - set
deleteBeforeWrite: true, which removes everything under the prefix and creates the table fresh on the next run. It applies to every run while it stays set: clear it after the first Iceberg run, or each run will drop the table again (re-registering with it cleared passes validation because the pipeline is alreadyicebergon both sides).
parquet and orc pipelines are unaffected; fileFormat still defaults to parquet.
Concurrent runs
Two runs of the same pipeline that would write the same output path are serialised: the second waits for the first to commit, then runs on top of it. This applies to every object-store format, not only Iceberg.Deleting the pipeline
Deleting the pipeline with its destination data removes the prefix, which drops the table (metadata and data together).Not yet available
- Catalog registration. The table is path-based. Registering it in a REST, AWS Glue, or JDBC catalog so that catalog-only engines can query it by name is planned; the data written today does not need to be rewritten when that lands.
- Time travel. Queries read the current snapshot only; reading a table as of an earlier snapshot or timestamp is planned.
- Table maintenance. Expiring old snapshots, compacting small files, and removing orphan files are not yet performed by Datris. Long-lived tables fed by frequent small runs accumulate snapshots and small files; run your engine’s maintenance procedures against the path in the meantime.
- Partition transforms such as day, month, or bucket partitions; only identity partitioning is supported.
- Iceberg as a source. Taps do not read Iceberg tables yet.
