Coverage Report

Created: 2026-09-18 20:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/build/source/nativelink-store/src/ac_utils.rs
Line
Count
Source
1
// Copyright 2024 The NativeLink Authors. All rights reserved.
2
//
3
// Licensed under the Functional Source License, Version 1.1, Apache 2.0 Future License (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//    See LICENSE file for details
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
16
// TODO(palfrey): IMPORTANT TODO: IMPORTING THIS SOMETIMES BREAKS
17
//                    THREADSAFETY. FIGURE OUT WHY AND MOVE IT TO UTILS.
18
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
19
20
use core::pin::Pin;
21
22
use bytes::BytesMut;
23
use futures::TryFutureExt;
24
use nativelink_error::{Code, Error, ResultExt};
25
use nativelink_util::common::DigestInfo;
26
use nativelink_util::digest_hasher::DigestHasher;
27
use nativelink_util::store_trait::{StoreKey, StoreLike};
28
use prost::Message;
29
30
// NOTE(aaronmondal) From some local testing it looks like action cache items are rarely greater than
31
// 1.2k. Giving a bit more just in case to reduce allocs.
32
pub const ESTIMATED_DIGEST_SIZE: usize = 2048;
33
34
/// This is more of a safety check. We are going to collect this entire message
35
/// into memory. If we don't bound the max size of the object we enable users
36
/// to use up all the memory on this machine.
37
const MAX_ACTION_MSG_SIZE: usize = 10 << 20; // 10mb.
38
39
/// Attempts to fetch the digest contents from a store into the associated proto.
40
576
pub async fn get_and_decode_digest<T: Message + Default + 'static>(
41
576
    store: &impl StoreLike,
42
576
    key: StoreKey<'_>,
43
576
) -> Result<T, Error> {
44
576
    get_size_and_decode_digest(store, key)
45
576
        .map_ok(|(v, _)| v)
46
576
        .await
47
573
}
48
49
/// Attempts to fetch the digest contents from a store into the associated proto.
50
584
pub async fn get_size_and_decode_digest<T: Message + Default + 'static>(
51
584
    store: &impl StoreLike,
52
584
    key: impl Into<StoreKey<'_>>,
53
584
) -> Result<(T, u64), Error> {
54
584
    let key = key.into();
55
    // Note: For unknown reasons we appear to be hitting:
56
    // https://github.com/rust-lang/rust/issues/92096
57
    // or a smiliar issue if we try to use the non-store driver function, so we
58
    // are using the store driver function here.
59
584
    let 
mut store_data_resp581
= store
60
584
        .as_store_driver_pin()
61
584
        .get_part_unchunked(key.borrow(), 0, Some(MAX_ACTION_MSG_SIZE as u64))
62
584
        .await;
63
581
    if let Err(
err8
) = &mut store_data_resp
64
8
        && err.code == Code::NotFound
65
7
    {
66
7
        // Trim the error code. Not Found is quite common and we don't want to send a large
67
7
        // error (debug) message for something that is common. We resize to just the last
68
7
        // message as it will be the most relevant.
69
7
        err.messages.resize_with(1, String::new);
70
574
    }
71
581
    let 
store_data573
= store_data_resp
?8
;
72
573
    let store_data_len =
73
573
        u64::try_from(store_data.len()).err_tip(|| "Could not convert store_data.len() to u64")
?0
;
74
75
573
    T::decode(store_data)
76
573
        .err_tip_with_code(|_e| 
{0
77
0
            (
78
0
                Code::NotFound,
79
0
                format!("Stored value appears to be corrupt for {key:?}"),
80
0
            )
81
0
        })
82
573
        .map(|v| (v, store_data_len))
83
581
}
84
85
/// Computes the digest of a message.
86
141
pub fn message_to_digest(
87
141
    message: &impl Message,
88
141
    buf: &mut BytesMut,
89
141
    hasher: &mut impl DigestHasher,
90
141
) -> Result<DigestInfo, Error> {
91
141
    message
92
141
        .encode(buf)
93
141
        .err_tip(|| "Could not encode directory proto")
?0
;
94
141
    hasher.update(buf);
95
141
    Ok(hasher.finalize_digest())
96
141
}
97
98
/// Takes a proto message and will serialize it and upload it to the provided store.
99
141
pub async fn serialize_and_upload_message<'a, T: Message>(
100
141
    message: &'a T,
101
141
    cas_store: Pin<&'a impl StoreLike>,
102
141
    hasher: &mut impl DigestHasher,
103
141
) -> Result<DigestInfo, Error> {
104
141
    let mut buffer = BytesMut::with_capacity(message.encoded_len());
105
141
    let digest = message_to_digest(message, &mut buffer, hasher)
106
141
        .err_tip(|| "In serialize_and_upload_message")
?0
;
107
    // Note: For unknown reasons we appear to be hitting:
108
    // https://github.com/rust-lang/rust/issues/92096
109
    // or a smiliar issue if we try to use the non-store driver function, so we
110
    // are using the store driver function here.
111
141
    cas_store
112
141
        .as_store_driver_pin()
113
141
        .update_oneshot(digest.into(), buffer.freeze())
114
141
        .await
115
141
        .err_tip(|| "In serialize_and_upload_message")
?0
;
116
141
    Ok(digest)
117
141
}
118
119
/// Computes a digest of a given buffer.
120
37
pub fn compute_buf_digest(buf: &[u8], hasher: &mut impl DigestHasher) -> DigestInfo {
121
37
    hasher.update(buf);
122
37
    hasher.finalize_digest()
123
37
}