Skip to content

Commit 7d792e6

Browse files
Fix xdelta3 output buffer issue (#7174)
* Fix xdelta3 output buffer issue * Fix buckets * Update commit hash to `main` * Tag TODO(hdiff) * Update cargo lock
1 parent 0875326 commit 7d792e6

File tree

4 files changed

+45
-9
lines changed

4 files changed

+45
-9
lines changed

Cargo.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ validator_metrics = { path = "validator_client/validator_metrics" }
289289
validator_store = { path = "validator_client/validator_store" }
290290
validator_test_rig = { path = "testing/validator_test_rig" }
291291
warp_utils = { path = "common/warp_utils" }
292-
xdelta3 = { git = "http://github.com/sigp/xdelta3-rs", rev = "50d63cdf1878e5cf3538e9aae5eed34a22c64e4a" }
292+
xdelta3 = { git = "http://github.com/sigp/xdelta3-rs", rev = "4db64086bb02e9febb584ba93b9d16bb2ae3825a" }
293293
zstd = "0.13"
294294

295295
[profile.maxperf]

beacon_node/store/src/hdiff.rs

+36-7
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ static EMPTY_PUBKEY: LazyLock<PublicKeyBytes> = LazyLock::new(PublicKeyBytes::em
2121
pub enum Error {
2222
InvalidHierarchy,
2323
DiffDeletionsNotSupported,
24-
UnableToComputeDiff,
25-
UnableToApplyDiff,
24+
UnableToComputeDiff(xdelta3::Error),
25+
UnableToApplyDiff(xdelta3::Error),
2626
BalancesIncompleteChunk,
2727
Compression(std::io::Error),
2828
InvalidSszState(ssz::DecodeError),
@@ -323,9 +323,15 @@ impl BytesDiff {
323323
}
324324

325325
pub fn compute_xdelta(source_bytes: &[u8], target_bytes: &[u8]) -> Result<Self, Error> {
326-
let bytes = xdelta3::encode(target_bytes, source_bytes)
327-
.ok_or(Error::UnableToComputeDiff)
328-
.unwrap();
326+
// TODO(hdiff): Use a smaller estimate for the output diff buffer size, currently the
327+
// xdelta3 lib will use 2x the size of the source plus the target length, which is 4x the
328+
// size of the hdiff buffer. In practice, diffs are almost always smaller than buffers (by a
329+
// signficiant factor), so this is 4-16x larger than necessary in a temporary allocation.
330+
//
331+
// We should use an estimated size that *should* be enough, and then dynamically increase it
332+
// if we hit an insufficient space error.
333+
let bytes =
334+
xdelta3::encode(target_bytes, source_bytes).map_err(Error::UnableToComputeDiff)?;
329335
Ok(Self { bytes })
330336
}
331337

@@ -334,8 +340,31 @@ impl BytesDiff {
334340
}
335341

336342
pub fn apply_xdelta(&self, source: &[u8], target: &mut Vec<u8>) -> Result<(), Error> {
337-
*target = xdelta3::decode(&self.bytes, source).ok_or(Error::UnableToApplyDiff)?;
338-
Ok(())
343+
// TODO(hdiff): Dynamic buffer allocation. This is a stopgap until we implement a schema
344+
// change to store the output buffer size inside the `BytesDiff`.
345+
let mut output_length = ((source.len() + self.bytes.len()) * 3) / 2;
346+
let mut num_resizes = 0;
347+
loop {
348+
match xdelta3::decode_with_output_len(&self.bytes, source, output_length as u32) {
349+
Ok(result_buffer) => {
350+
*target = result_buffer;
351+
352+
metrics::observe(
353+
&metrics::BEACON_HDIFF_BUFFER_APPLY_RESIZES,
354+
num_resizes as f64,
355+
);
356+
return Ok(());
357+
}
358+
Err(xdelta3::Error::InsufficientOutputLength) => {
359+
// Double the output buffer length and try again.
360+
output_length *= 2;
361+
num_resizes += 1;
362+
}
363+
Err(err) => {
364+
return Err(Error::UnableToApplyDiff(err));
365+
}
366+
}
367+
}
339368
}
340369

341370
/// Byte size of this instance

beacon_node/store/src/metrics.rs

+7
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,13 @@ pub static BEACON_HDIFF_BUFFER_CLONE_TIMES: LazyLock<Result<Histogram>> = LazyLo
202202
"Time required to clone hierarchical diff buffer bytes",
203203
)
204204
});
205+
pub static BEACON_HDIFF_BUFFER_APPLY_RESIZES: LazyLock<Result<Histogram>> = LazyLock::new(|| {
206+
try_create_histogram_with_buckets(
207+
"store_hdiff_buffer_apply_resizes",
208+
"Number of times during diff application that the output buffer had to be resized before decoding succeeded",
209+
Ok(vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0])
210+
)
211+
});
205212
/*
206213
* Beacon Block
207214
*/

0 commit comments

Comments
 (0)