digest.rs
Overview
This file defines data structures and serialization logic for maintaining and exchanging a digest that summarizes the staleness and versioning state of nodes in a distributed system. The digest essentially provides a snapshot of each peer node's heartbeat and version metadata, which is used to track freshness and garbage collection progress in the system.
The main components of this file are:
NodeDigest: Represents the heartbeat and version information for a single node.Digest: Represents a collection ofNodeDigestentries keyed by node identifiers (ChitchatId).Serialization and deserialization implementations for efficient transmission and storage of these structures, including optional compression using Zstandard.
This functionality is critical for the system's peer-to-peer synchronization and consistency protocols, as it allows nodes to exchange summarized state to detect outdated or missing data.
Data Structures and Their Details
NodeDigest
pub(crate) struct NodeDigest {
pub(crate) heartbeat: Heartbeat,
pub(crate) last_gc_version: Version,
pub(crate) max_version: Version,
}
Purpose: Stores metadata about a single node's state relevant to staleness.
Fields:
heartbeat: AHeartbeatvalue indicating the latest heartbeat timestamp or count from the node.last_gc_version: AVersionrepresenting the last version number at which garbage collection was performed.max_version: The highest known version number the node has observed or processed.
Traits Implemented
Serializable: Allows serializing theNodeDigestinto a byte buffer.Deserializable: Allows constructing aNodeDigestfrom a byte buffer.
Serialization Details
Serialization writes the
heartbeat,last_gc_version, andmax_versionsequentially.Deserialization reads these values in the same order.
Example Usage
let node_digest = NodeDigest {
heartbeat: Heartbeat(123),
last_gc_version: 10,
max_version: 20,
};
let mut buffer = Vec::new();
node_digest.serialize(&mut buffer);
Digest
pub struct Digest {
pub(crate) node_digests: BTreeMap<ChitchatId, NodeDigest>,
}
Purpose: Aggregates multiple
NodeDigestentries keyed by node IDs to represent the overall state digest of the cluster or peer set.Fields:
node_digests: ABTreeMapfromChitchatId(a unique node identifier) to the correspondingNodeDigest.
Key Methods
add_node: (Test only) Adds a node and its digest data into the map.serialize_uncompressed: Serializes the digest data without compression.deserialize_uncompressed: Deserializes an uncompressed digest.serialize_compressed: Serializes the digest data with Zstandard compression.deserialize_compressed: Deserializes a compressed digest.
Serialization and Compression Workflow
The primary serialization interface uses compressed serialization by default.
Compression is done using the
zstdlibrary at the default compression level.The compressed data size is serialized first (as a 32-bit integer), followed by the compressed byte stream.
Deserialization reverses the process by reading the length, extracting the compressed slice, decompressing it, and then deserializing the uncompressed data.
Example Usage
let mut digest = Digest::default();
digest.add_node(
ChitchatId::for_local_test(1001),
Heartbeat(50),
5,
10,
);
let mut buffer = Vec::new();
digest.serialize(&mut buffer);
Important Implementation Details and Algorithms
Serialization Strategy:
Serialization is layered. TheDigestserializes all containedNodeDigestentries sequentially, prefixing the count as a 16-bit unsigned integer (u16).Compression Usage:
To optimize network transmission and storage, the serialized digest bytes are compressed with Zstandard (zstd). This reduces payload size, especially when the digest contains many nodes.Use of
BTreeMap:
Thenode_digestsmap is aBTreeMap, ensuring keys are sorted. This deterministic ordering is important for consistent serialization and easier debugging.Error Handling:
Deserialization functions returnanyhow::Result<Self>, propagating errors encountered during parsing or decompression.Test Utilities:
The file includes test-only helper methods and unit tests for verifying serialization correctness.
Interaction with Other System Components
ChitchatId:
Serves as the unique identifier for nodes in the system. It is used as the key in theDigestmap to associate nodes with theirNodeDigest.HeartbeatandVersion:
Fundamental types representing heartbeat counters and version numbers of data. These types also implement serialization traits and are used byNodeDigest.Serialization Utilities (
serializemodule):
The file uses traitsSerializableandDeserializablefrom theserializemodule to provide consistent encoding and decoding of data structures.Compression (
zstdcrate):
Used internally for compressing and decompressing serialized digests.
This file primarily serves as a building block for the synchronization or gossip protocol layer, where nodes exchange their state digests to detect discrepancies or updates needed.
Mermaid Diagram: Structure of digest.rs
classDiagram
class NodeDigest {
+heartbeat: Heartbeat
+last_gc_version: Version
+max_version: Version
+serialize()
+serialized_len()
+deserialize()
}
class Digest {
-node_digests: BTreeMap<ChitchatId, NodeDigest>
+add_node()
-serialize_uncompressed()
-deserialize_uncompressed()
-serialize_compressed()
-deserialize_compressed()
+serialize()
+serialized_len()
+deserialize()
}
NodeDigest <|-- Digest : contains
This diagram illustrates the two primary structs: NodeDigest encapsulates the state of a single node, and Digest manages a collection of these, along with serialization methods supporting both compressed and uncompressed formats.