Depends on: Data Model
RFCs: RFC-002
Abstract
This spec defines how hash sums of data part files and checkpoints are computed, including the physical hashes used to identify files in content-addressable storage and the logical hashes of data records that remain stable regardless of how data is physically encoded.Physical vs. Logical Hashes
Because the on-disk Parquet data format is non-deterministic (serializing same logical data may result in different binary layouts depending on heuristics and implementation) two different hash sums are maintained by the coordinator:- Physical hash - a (non-reproducible) hash sum of an entire binary file as originally produced by the owner of the dataset
- Logical hash - a stable (reproducible) hash sum computed on data records, resistant to encoding variations
Validation and Verification
Hashes stored in are used in two scenarios:- Validation of integrity - when a data part file or a checkpoint is obtained from any (possibly untrusted) source, its physical hash is computed and compared to the
physicalHashstored in the corresponding or object. A mismatch means that the file was corrupted or tampered with, and it must be rejected. - Verification of reproduced results - when data is reproduced by repeating a transformation (see Processing Model: Verification via Reproducibility), the physical hash of the result is compared first. If it doesn’t match due to non-determinism of the on-disk format, the logical hash is compared instead, and a match confirms that the resulting records are logically the same.
Logical Hashing Algorithm
The logical hashing algorithm computes a stable hash of structured data based on its Apache Arrow in-memory representation. The algorithm is parametrized by the underlying digest function, which in ODF is SHA3-256. The current version of the algorithm is0, as reflected in the arrow0-sha3-256 hash code. Future versions may change the resulting hashes, in which case they will be assigned new hash codes.
The reference implementation is provided by the arrow-digest Rust crate.
Properties
- The hash is the same no matter how many record batches the data was split into
- The hash is the same no matter which Arrow encoding variant of a type is used (e.g.
Utf8,LargeUtf8, andUtf8View) - the difference between them is purely an encoding concern - The hash is the same for arrays without a validity bitmap and arrays where a validity bitmap marks all values as valid
- The hash is order-dependent - reordering records will produce a different hash
Value Encoding
Values are fed into the digest function according to the following rules:- Endianness - all multi-byte values are encoded as little-endian
- Fixed size types (
Int,FloatingPoint,Decimal,Date,Time,Timestamp) - hashed using their in-memory binary representation - Booleans - hashed as byte-sized values:
1forfalseand2fortrue - Binaries and strings (
Binary,LargeBinary,FixedSizeBinary,BinaryView,Utf8,LargeUtf8,Utf8View) - length of the value in bytes asu64, followed by the bytes of the value - Lists (
List,LargeList,FixedSizeList) - length of the list asu64, followed by the items of the list hashed according to their data type - Nulls - every null value is represented by a single
0byte
Data Type Encoding
When hashing an array, its data type is hashed once at the start of the hashing session, followed by all items of the array hashed sequentially. Data type is encoded as a type identifier (u16) followed by type parameters:
Type IDs
10 (Interval), 12 (Struct), 13 (Union), 16 (Map), and 17 (Duration) are reserved.
Units are encoded as u16:
Record Batch Hashing
Data consisting of multiple columns is hashed as follows:- (once per hashing session) The schema is hashed into the combined digest by traversing all fields recursively in depth-first order and hashing for every field:
- Field name as length in bytes (
u64) followed by its UTF-8 bytes - Nesting level of the field (zero-based) as
u64
- Field name as length in bytes (
- Every leaf column (a column that is not a struct) is hashed by a separate column digest using the rules above:
- Nested columns use a combined nullability bitmap produced from the nullability of the column and all of its parent structs
- Subsequent record batches update the same column digests
- (final step) Digests of all leaf columns are fed into the combined digest in the depth-first order to produce the final hash.
Checkpoint Hashing
are stored as opaque files and referenced by using their physical hash. The process of computing a hash sum is identical to computing a physical hash for a data part file (see Physical vs. Logical Hashes).Hash Codes
Hashes are represented using multihash and multibase as described in Hash Representation. Themulticodec table is extended with the following codes in the “private use area”:
See also: