amogh-jahagirdar commented on code in PR #16025:
URL: https://github.com/apache/iceberg/pull/16025#discussion_r3994014757
##########
format/spec.md:
##########
@@ -656,15 +662,33 @@ A data or delete file is associated with a sort order by
the sort order's id wit
### Manifests
-A manifest is an immutable Avro file that lists data files or delete files,
along with each file’s partition data tuple, metrics, and tracking information.
One or more manifest files are used to store a [snapshot](#snapshots), which
tracks all of the files in a table at some point in time. Manifests are tracked
by a [manifest list](#manifest-lists) for each table snapshot.
+A manifest is an immutable file that lists data files or delete files, along
with each file’s partition data, metrics, and tracking information. One or more
manifest files are used to store a [snapshot](#snapshots), which tracks all of
the files in a table at some point in time. Manifests are tracked by a snapshot
root for each table snapshot. In v4, the snapshot root is a root manifest that
may track data files in addition to leaf manifest files.
A manifest is a valid Iceberg data file: files must use valid Iceberg formats,
schemas, and column projection.
-A manifest may store either data files or delete files, but not both because
manifests that contain delete files are scanned first during job planning.
Whether a manifest is a data manifest or a delete manifest is stored in
manifest metadata.
Review Comment:
Again, I really tried not having to change so much existing content but the
issue here is I don't think there's a really elegant way other than saying "In
v4, there's a root manifest and there are leaf manifests, which can be v4
leaves which contain data files and colocated DVs or prior manifests" etc
basically enumerating via prose. We also want to limit the "Since v4"
statements since that's a place for churn. So what I did instead was just
create a table.
Now, alternatively, we could have just made this section be on "leaf
manifests" and alternate the existing manifest list, but the issue with that is
it ends up basically duplicating the table and just shifting requirements there.
So in my head its best for this just to have a table clearly describing
possible contents, and then tab off manifest, where v1-v3 stays as is, and v4
describes manifests and the different invariants for root/leaf.
##########
format/spec.md:
##########
@@ -693,27 +716,14 @@ The schema of a manifest file is defined by the
`manifest_entry` struct, which c
| | _optional_ | **`4 file_sequence_number`** | `long`
| File sequence number indicating
when the file was added. Inherited when null and status is 1 (added). |
| _required_ | _required_ | **`2 data_file`** | `data_file`
`struct` (see below) | File path, partition tuple,
metrics, ... |
-The manifest entry fields are used to keep track of the snapshot in which
files were added or logically deleted. The `data_file` struct, defined below,
is nested inside the manifest entry so that it can be easily passed to job
planning without the manifest entry fields.
+ The manifest entry fields are used to keep track of the snapshot in which
files were added or logically deleted. The `data_file` struct, defined below,
is nested inside the manifest entry so that it can be easily passed to job
planning without the manifest entry fields.
-When a file is added to the dataset, its manifest entry should store the
snapshot ID in which the file was added and set status to 1 (added).
+ When a file is added to the dataset, its manifest entry should store the
snapshot ID in which the file was added and set status to 1 (added).
-When a file is replaced or deleted from the dataset, its manifest entry fields
store the snapshot ID in which the file was deleted and status 2 (deleted). The
file may be deleted from the file system when the snapshot in which it was
deleted is garbage collected, assuming that older snapshots have also been
garbage collected [1].
+ When a file is replaced or deleted from the dataset, its manifest entry
fields store the snapshot ID in which the file was deleted and status 2
(deleted).
-Iceberg v2 adds data and file sequence numbers to the entry and makes the
snapshot ID optional. Values for these fields are inherited from manifest
metadata when `null`. That is, if the field is `null` for an entry, then the
entry must inherit its value from the manifest file's metadata, stored in the
manifest list.
-The `sequence_number` field represents the data sequence number and must never
change after a file is added to the dataset. The data sequence number
represents a relative age of the file content and should be used for planning
which delete files apply to a data file.
-The `file_sequence_number` field represents the sequence number of the
snapshot that added the file and must also remain unchanged upon assigning at
commit. The file sequence number can't be used for pruning delete files as the
data within the file may have an older data sequence number.
-The data and file sequence numbers are inherited only if the entry status is 1
(added). If the entry status is 0 (existing) or 2 (deleted), the entry must
include both sequence numbers explicitly.
+ **`data_file` struct (field 2)**
-Notes:
-
-1. Technically, data files can be deleted when the last snapshot that contains
the file as “live” data is garbage collected. But this is harder to detect and
requires finding the diff of multiple snapshots. It is easier to track what
files are deleted in a snapshot and delete them when that snapshot expires. It
is not recommended to add a deleted file back to a table. Adding a deleted file
can lead to edge cases where incremental deletes can break table snapshots.
-2. Manifest list files are required in v2, so that the `sequence_number` and
`snapshot_id` to inherit are always available.
Review Comment:
I've been trying to avoid moving around existing content as much as possible
but to try and organize the manifest_entry section so that it's more shared for
v1-v3 and v4, where the only difference is around tracking fields being placed
into the v4 tracking file AND data_file basically being what we call
tracked_file, I had to shuffle this a bit. I also moved common prose across all
format versions so that it's below the tables.
##########
format/spec.md:
##########
@@ -693,27 +725,14 @@ The schema of a manifest file is defined by the
`manifest_entry` struct, which c
| | _optional_ | **`4 file_sequence_number`** | `long`
| File sequence number indicating
when the file was added. Inherited when null and status is 1 (added). |
| _required_ | _required_ | **`2 data_file`** | `data_file`
`struct` (see below) | File path, partition tuple,
metrics, ... |
-The manifest entry fields are used to keep track of the snapshot in which
files were added or logically deleted. The `data_file` struct, defined below,
is nested inside the manifest entry so that it can be easily passed to job
planning without the manifest entry fields.
+ The manifest entry fields are used to keep track of the snapshot in which
files were added or logically deleted. The `data_file` struct, defined below,
is nested inside the manifest entry so that it can be easily passed to job
planning without the manifest entry fields.
-When a file is added to the dataset, its manifest entry should store the
snapshot ID in which the file was added and set status to 1 (added).
+ When a file is added to the dataset, its manifest entry should store the
snapshot ID in which the file was added and set status to 1 (added).
-When a file is replaced or deleted from the dataset, its manifest entry fields
store the snapshot ID in which the file was deleted and status 2 (deleted). The
file may be deleted from the file system when the snapshot in which it was
deleted is garbage collected, assuming that older snapshots have also been
garbage collected [1].
Review Comment:
"The file may be deleted from the file system when the snapshot in which it
was deleted is garbage collected, assuming that older snapshots have also been
garbage collected [1]." was moved to a common section outside of the v1-v3/v4
tabs, because it applies to both.
##########
format/spec.md:
##########
@@ -693,27 +716,14 @@ The schema of a manifest file is defined by the
`manifest_entry` struct, which c
| | _optional_ | **`4 file_sequence_number`** | `long`
| File sequence number indicating
when the file was added. Inherited when null and status is 1 (added). |
| _required_ | _required_ | **`2 data_file`** | `data_file`
`struct` (see below) | File path, partition tuple,
metrics, ... |
-The manifest entry fields are used to keep track of the snapshot in which
files were added or logically deleted. The `data_file` struct, defined below,
is nested inside the manifest entry so that it can be easily passed to job
planning without the manifest entry fields.
+ The manifest entry fields are used to keep track of the snapshot in which
files were added or logically deleted. The `data_file` struct, defined below,
is nested inside the manifest entry so that it can be easily passed to job
planning without the manifest entry fields.
Review Comment:
Indentation had to change to get this nested within the v1-v3 tabs
##########
format/spec.md:
##########
@@ -144,8 +147,11 @@ Version 4 of the Iceberg spec adds support for relative
locations in metadata, e
* **Schema** -- Names and types of fields in a table.
* **Partition spec** -- A definition of how partition values are derived from
data fields.
* **Snapshot** -- The state of a table at some point in time, including the
set of all data files.
-* **Manifest list** -- A file that lists manifest files; one per snapshot.
-* **Manifest** -- A file that lists data or delete files; a subset of a
snapshot.
+* **Snapshot root** -- The per-snapshot file that tracks a snapshot's
manifests; a manifest list (v1-v3) or a root manifest (v4).
Review Comment:
After discussion with @rdblue last week, introduced this concept so that way
we don't have to branch off of "If it's a manifest list..." and "if it's a root
manifest".
##########
format/spec.md:
##########
@@ -820,14 +946,18 @@ Each stats struct holds statistics for one table field.
It may contain the follo
| Requirement | Offset | Name | Type
| Included for | Description |
|-------------|--------|---------------------------|---------------------------|-----------------------------------------------|-------------|
-| _optional_ | 1 | `lower_bound` | Field type or `geo_lower`
| all primitives or `variant` | Lower bound stored as the
field's type, or `geo_lower` for geo types |
-| _optional_ | 2 | `upper_bound` | Field type or `geo_upper`
| all primitives or `variant` | Upper bound stored as the
field's type, or `geo_upper` for geo types |
+| _optional_ | 1 | `lower_bound` | Field type or `geo_lower`
| all primitives or `variant` | Lower bound stored as the
field's type, or `geo_lower` for geo types [1] |
+| _optional_ | 2 | `upper_bound` | Field type or `geo_upper`
| all primitives or `variant` | Upper bound stored as the
field's type, or `geo_upper` for geo types [1] |
| _optional_ | 3 | `tight_bounds` | `boolean`
| all primitives except for `geometry` and `geography` | When true,
`lower_bound` and `upper_bound` must be equal to the min and max values |
| _optional_ | 4 | `value_count` | `long`
| all | Number of values in the
column (including null and NaN values) |
| _optional_ | 5 | `null_value_count` | `long`
| optional fields | Number of null values in the
column |
| _optional_ | 6 | `nan_value_count` | `long`
| `float`, `double` | Number of NaN values in the
column |
| _optional_ | 7 | `avg_value_size_in_bytes` | `int`
| `string`, `binary`, `variant`, `geometry`, `geography` | Avg value size in
memory (uncompressed) in bytes over non-null values to estimate memory
consumption |
+Notes:
+
+1. For `float` and `double`, the value `-0.0` must precede `+0.0`, as in the
IEEE 754 `totalOrder` predicate. NaNs are not permitted as lower or upper
bounds.
Review Comment:
@nastra @danielcweeks @rdblue @RussellSpitzer @stevenzwu This note may look
redundant but it's not. in V1-V3 we have the note as is untouched. For v4
implementors this is moved to the content stats portion of the spec
##########
format/spec.md:
##########
@@ -742,18 +760,126 @@ The `data_file` struct consists of the following fields:
| | | _optional_ | **`144 content_offset`**
| `long` |
The offset in the file where the content starts [5] |
| | | _optional_ | **`145 content_size_in_bytes`**
| `long` |
The length of a referenced content stored in the file; required if
`content_offset` is present [5] |
-The `partition` struct stores the tuple of partition values for each file. Its
type is derived from the partition fields of the partition spec used to write
the manifest file. In v2, the partition struct's field ids must match the ids
from the partition spec.
+ The `partition` struct stores the tuple of partition values for each file.
Its type is derived from the partition fields of the partition spec used to
write the manifest file. In v2, the partition struct's field ids must match the
ids from the partition spec.
+
+ Notes:
-The v4 `content_stats` container struct stores field-level metrics. Unlike the
metrics maps, the type of `content_stats` is based on table metadata, like
schema. Similar to the `partition` struct, the same type is used for all files
tracked in a manifest.
+ 1. Single-value serialization for lower and upper bounds is detailed in
Appendix D.
+ 2. For `float` and `double`, the value `-0.0` must precede `+0.0`, as in
the IEEE 754 `totalOrder` predicate. NaNs are not permitted as lower or upper
bounds.
+ 3. If sort order ID is missing or unknown, then the order is assumed to be
unsorted. Only data files and equality delete files should be written with a
non-null order id. [Position deletes](#position-delete-files) are required to
be sorted by file and position, not a table order, and should set sort order id
to null. Readers must ignore sort order id for position delete files.
+ 4. Position delete metadata can use `referenced_data_file` when all
deletes tracked by the entry are in a single data file. Setting the referenced
file is required for deletion vectors.
+ 5. The `content_offset` and `content_size_in_bytes` fields are used to
reference a specific blob for direct access to a deletion vector. For deletion
vectors, these values are required and must exactly match the `offset` and
`length` stored in the Puffin footer for the deletion vector blob.
+ 6. The following field ids are reserved on `data_file`: 141.
+
+=== "v4"
+ **Tracked Files**
+
+ | Field id | Name | Type | Required | Description |
+ |----------|------|------|----------|-------------|
+ | 134 | **`content_type`** | `int` (0: DATA, 3: DATA_MANIFEST, 4:
DELETE_MANIFEST) | *required* | Type of content stored in the entry. |
+ | 157 | **`format_version`** | `int` (0: PRE-V4, 4: V4) | *required* |
Writer format version. |
+ | 100 | **`location`** | `string` | *required* | Location of the file or
manifest. |
+ | 101 | **`file_format`** | `string` | *required* | String file format
name: `avro`, `orc`, `parquet`, or `puffin` |
+ | 147 | **`tracking`** | `tracking` struct | *required* | Groups status,
snapshot, and sequence number. See tracking struct below. |
+ | 502 | **`spec_id`** | `int` | *optional* | ID of the partition spec used
to write this manifest or data file. |
+ | 140 | **`sort_order_id`** | `int` | *optional* | ID representing sort
order for this file. If missing or unknown, the order is assumed to be
unsorted. |
+ | 103 | **`record_count`** | `long` | *required* | Number of records in
this file. |
+ | 104 | **`file_size_in_bytes`** | `long` | *required* | Total file size
in bytes. |
+ | 146 | **`content_stats`** | `content_stats` struct | *optional* | Column
stats. See [Content Stats](#content-stats). |
+ | 150 | **`manifest_info`** | `manifest_info` struct | *optional* | See
manifest_info struct below. |
+ | 131 | **`key_metadata`** | `binary` | *optional* |
Implementation-specific key metadata for encryption. |
+ | 132 | **`split_offsets`** | `list<133: long>` | *optional* | Split
offsets for the data file. Must be sorted ascending. |
+ | 148 | **`deletion_vector`** | `deletion_vector` struct | *optional* |
Row-level deletion vector for a data file. |
+ | 158 | **`column_files`** | `list<159: column_file>` | *optional* |
Column update files associated with this entry. |
+
+ **`tracking` struct (field 147)**
+
+ | Field id | Name | Type | Required | Description |
+ |----------|------|------|----------|-------------|
+ | 0 | **`status`** | `int` (0: EXISTING, 1: ADDED, 2: DELETED, 3:
REPLACED, 4: MODIFIED) | *required* | Used to track additions, deletions,
replacements, and modifications. Deletes are not used in scans. |
+ | 1 | **`snapshot_id`** | `long` | *optional* | Snapshot ID where the file
was added or deleted. Inherited when null. |
+ | 5 | **`dv_snapshot_id`** | `long` | *optional* | Snapshot ID where the
deletion vector was added. |
+ | 160 | **`latest_column_file_snapshot_id`** | `long` | *optional* |
Snapshot ID where the latest column file was added. |
+ | 3 | **`sequence_number`** | `long` | *optional* | Data sequence number
of the file. Inherited when null and status is 1 (ADDED). |
+ | 4 | **`file_sequence_number`** | `long` | *optional* | File sequence
number indicating when the file was added. Inherited when null and status is
ADDED. |
+ | 142 | **`first_row_id`** | `long` | *optional* | For a data file, the
`_row_id` for its first row. For a data manifest, the starting `_row_id` to
assign to rows added by ADDED data files. See [First Row ID
Inheritance](#first-row-id-inheritance). |
+ | 6 | **`deleted_positions`** | `binary` | *optional* | Positions deleted
in the referenced leaf manifest this snapshot. See [Manifest Deletion
Vectors](#manifest-deletion-vectors). |
+ | 7 | **`replaced_positions`** | `binary` | *optional* | Positions
replaced in the referenced leaf manifest this snapshot. See [Manifest Deletion
Vectors](#manifest-deletion-vectors). |
+
+ **`deletion_vector` struct (field 148)**
+
+ | Field id | Name | Type | Required | Description |
+ |----------|------|------|----------|-------------|
+ | 155 | **`location`** | `string` | *required* | Location of the Puffin
file. |
+ | 144 | **`offset`** | `long` | *required* | Offset in the file where the
content starts. |
+ | 145 | **`size_in_bytes`** | `long` | *required* | Length of the
referenced content stored in the file. |
+ | 156 | **`cardinality`** | `long` | *required* | Cardinality of the
deletion vector. |
+ | 149 | **`key_metadata`** | `binary` | *optional* |
Implementation-specific key metadata for encryption. |
+
+ **`manifest_info` struct (field 150)**
+
+ | Field id | Name | Type | Required | Description |
+ |----------|------|------|----------|-------------|
+ | 504 | **`added_files_count`** | `long` | *required* | Count of entries
with status ADDED in the manifest. |
+ | 505 | **`existing_files_count`** | `long` | *required* | Count of
entries with status EXISTING in the manifest. |
+ | 506 | **`deleted_files_count`** | `long` | *required* | Count of entries
with status DELETED in the manifest. |
+ | 520 | **`replaced_files_count`** | `long` | *required* | Count of
entries with status REPLACED in the manifest. |
+ | 524 | **`modified_files_count`** | `long` | *required* | Count of
entries with status MODIFIED in the manifest. |
+ | 512 | **`added_rows_count`** | `long` | *required* | Total number of
rows in ADDED entries. |
+ | 513 | **`existing_rows_count`** | `long` | *required* | Total number of
rows in EXISTING entries. |
+ | 514 | **`deleted_rows_count`** | `long` | *required* | Total number of
rows in DELETED entries. |
+ | 521 | **`replaced_rows_count`** | `long` | *required* | Total number of
rows in REPLACED entries. |
+ | 525 | **`modified_rows_count`** | `long` | *required* | Total number of
rows in MODIFIED entries. |
+ | 516 | **`min_sequence_number`** | `long` | *required* | Minimum data
sequence number of all live entries in the manifest. |
+ | 522 | **`dv`** | `binary` | *optional* | Positions in the referenced
leaf manifest that are not live. See [Manifest Deletion
Vectors](#manifest-deletion-vectors). |
+ | 523 | **`dv_cardinality`** | `long` | *optional* | Cardinality of the
manifest deletion vector. |
+
+ **`column_file` struct (element 159 of `column_files`, field 158)**
+
+ | Field id | Name | Type | Required | Description |
+ |----------|------|------|----------|-------------|
+ | 161 | **`format_version`** | `int` | *required* | Format version of this
column file. |
+ | 162 | **`field_ids`** | `list<163: int>` | *required* | Live field IDs
stored in this column file. |
+ | 164 | **`location`** | `string` | *required* | Location of the column
file. |
+ | 165 | **`file_format`** | `string` | *required* | String file format
name: `avro`, `orc`, or `parquet`. |
+ | 166 | **`file_size_in_bytes`** | `long` | *required* | Total column file
size in bytes. |
+ | 167 | **`key_metadata`** | `binary` | *optional* |
Implementation-specific key metadata for encryption. |
+ | 168 | **`split_offsets`** | `list<169: long>` | *optional* | Split
offsets for the column file. Must be sorted ascending. |
+
+ **Tracked File Requirements**
+
+ - `content_type` must not be 1 (POSITION_DELETES) or 2 (EQUALITY DELETES).
+ - `deletion_vector.offset` and `deletion_vector.size_in_bytes` must
exactly match the `offset` and `length` stored in the Puffin footer for the
deletion vector blob.
+ - A leaf manifest may only contain data files.
+ - A root manifest may reference v1-v3 manifests; a referenced v1-v3 leaf
manifest must have `format_version` PRE-V4.
+ - Other v4 tracked files must have `format_version` V4.
+ - `manifest_info` must be set if and only if the tracked file is a
manifest.
+ - `deletion_vector` may only be set if the tracked file is a data file.
+ - `column_files` may only be set if the tracked file is a data file or a
data manifest.
+ - `tracking.deleted_positions` and `tracking.replaced_positions` may only
be set if the tracked file is a manifest.
+ - `tracking.snapshot_id` and `tracking.sequence_number` are required for
the tracked file in the root manifest.
+ - For manifests, `tracking.sequence_number` must equal
`tracking.file_sequence_number`.
+ - `tracking.dv_snapshot_id` may only be set if `deletion_vector` or
`manifest_info.dv` is set.
+ - `tracking.latest_column_file_snapshot_id` may only be set if
`column_files` is set.
+ - `manifest_info.dv_cardinality` must be set if and only if
`manifest_info.dv` is non-null.
+
+ When a file is added to the dataset, its tracked file must set status to
ADDED and store the snapshot ID in which the file was added.
+
+ When a data file's deletion vector or column files are updated, the writer
records a MODIFIED entry for the live version and marks the prior version as
replaced, either with a REPLACED entry or in a [manifest deletion
vector](#manifest-deletion-vectors). The resulting entries' `dv_snapshot_id` or
`latest_column_file_snapshot_id` must record the snapshot in which the deletion
vector or column files, respectively, last changed. For leaf manifest entries,
MODIFIED marks a live manifest whose `dv` changed.
+
+ When a file is deleted from the dataset, its tracked file must set status
to DELETED and store the snapshot ID in which the file was deleted. Writers
must include DELETED entries in the manifest for the snapshot that deletes the
file. The next manifest written for those entries must omit the DELETED entries.
+
+The file may be deleted from the file system when the snapshot in which it was
deleted is garbage collected, assuming that older snapshots have also been
garbage collected [1].
+
+Iceberg v2 adds data and file sequence numbers to the entry and makes the
snapshot ID optional. Values for these fields are inherited from manifest
metadata when `null`. That is, if the field is `null` for an entry, then the
entry must inherit its value from the manifest file's metadata, stored in the
snapshot root.
+The `sequence_number` field represents the data sequence number and must never
change after a file is added to the dataset, except during the addition of a
column file. The data sequence number represents a relative age of the file
content and should be used for planning which delete files apply to a data file.
+The `file_sequence_number` field represents the sequence number of the
snapshot that added the file and must also remain unchanged upon assigning at
commit. The file sequence number can't be used for pruning delete files as the
data within the file may have an older data sequence number.
+The data and file sequence numbers are inherited only if the entry status is 1
(added). If the entry status is 0 (existing) or 2 (deleted), the entry must
include both sequence numbers explicitly.
Notes:
-1. Single-value serialization for lower and upper bounds is detailed in
Appendix D.
Review Comment:
Only applies to v1-v3, moved to that tab.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]