Coverage Report

Created: 2026-11-13 23:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/build/source/nativelink-worker/src/worker_api_client_wrapper.rs
Line
Count
Source
1
// Copyright 2024 Trace Machina, Inc. 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
use core::future::Future;
16
17
use futures::stream::unfold;
18
use nativelink_error::{make_err, Error, ResultExt};
19
use nativelink_proto::com::github::trace_machina::nativelink::remote_execution::update_for_scheduler::Update;
20
use nativelink_proto::com::github::trace_machina::nativelink::remote_execution::worker_api_client::WorkerApiClient;
21
use nativelink_proto::com::github::trace_machina::nativelink::remote_execution::{
22
    ConnectWorkerRequest, ExecuteAccepted, ExecuteComplete, ExecuteDeclined, ExecuteResult, GoingAwayRequest, KeepAliveRequest, UpdateForScheduler, UpdateForWorker
23
};
24
use tokio::sync::mpsc::Sender;
25
use tonic::codec::Streaming;
26
use tonic::transport::Channel;
27
use tonic::{Code, Response, Status};
28
29
/// This is used in order to allow unit tests to intercept these calls. This should always match
30
/// the API of `WorkerApiClient` defined in the `worker_api.proto` file.
31
pub trait WorkerApiClientTrait: Clone + Sync + Send + Sized + Unpin {
32
    fn connect_worker(
33
        &mut self,
34
        request: ConnectWorkerRequest,
35
    ) -> impl Future<Output = Result<Response<Streaming<UpdateForWorker>>, Status>> + Send;
36
37
    fn keep_alive(
38
        &mut self,
39
        request: KeepAliveRequest,
40
    ) -> impl Future<Output = Result<(), Error>> + Send;
41
42
    fn going_away(
43
        &mut self,
44
        request: GoingAwayRequest,
45
    ) -> impl Future<Output = Result<(), Error>> + Send;
46
47
    fn execution_response(
48
        &mut self,
49
        request: ExecuteResult,
50
    ) -> impl Future<Output = Result<(), Error>> + Send;
51
52
    fn execute_accepted(
53
        &mut self,
54
        request: ExecuteAccepted,
55
    ) -> impl Future<Output = Result<(), Error>> + Send;
56
57
    fn execute_declined(
58
        &mut self,
59
        request: ExecuteDeclined,
60
    ) -> impl Future<Output = Result<(), Error>> + Send;
61
62
    fn execution_complete(
63
        &mut self,
64
        request: ExecuteComplete,
65
    ) -> impl Future<Output = Result<(), Error>> + Send;
66
}
67
68
#[derive(Debug, Clone)]
69
pub struct WorkerApiClientWrapper {
70
    inner: WorkerApiClient<Channel>,
71
    channel: Option<Sender<Update>>,
72
}
73
74
impl WorkerApiClientWrapper {
75
0
    async fn send_update(&mut self, update: Update) -> Result<(), Error> {
76
0
        let tx = self
77
0
            .channel
78
0
            .as_ref()
79
0
            .err_tip(|| "worker update without connect_worker")?;
80
0
        match tx.send(update).await {
81
0
            Ok(()) => Ok(()),
82
0
            Err(_err) => {
83
                // Remove the sender if it's not going anywhere.
84
0
                self.channel.take();
85
0
                Err(make_err!(
86
0
                    Code::Unavailable,
87
0
                    "worker update with disconnected channel"
88
0
                ))
89
            }
90
        }
91
0
    }
92
}
93
94
impl From<WorkerApiClient<Channel>> for WorkerApiClientWrapper {
95
0
    fn from(other: WorkerApiClient<Channel>) -> Self {
96
0
        Self {
97
0
            inner: other,
98
0
            channel: None,
99
0
        }
100
0
    }
101
}
102
103
impl WorkerApiClientTrait for WorkerApiClientWrapper {
104
0
    async fn connect_worker(
105
0
        &mut self,
106
0
        request: ConnectWorkerRequest,
107
0
    ) -> Result<Response<Streaming<UpdateForWorker>>, Status> {
108
0
        drop(self.channel.take());
109
0
        let (tx, rx) = tokio::sync::mpsc::channel(1);
110
0
        if tx
111
0
            .send(Update::ConnectWorkerRequest(request))
112
0
            .await
113
0
            .is_err()
114
        {
115
0
            return Err(Status::data_loss("Unable to push to newly created channel"));
116
0
        }
117
0
        self.channel = Some(tx);
118
0
        self.inner
119
0
            .connect_worker(unfold(rx, |mut rx| async move {
120
0
                let update = rx.recv().await?;
121
0
                Some((
122
0
                    UpdateForScheduler {
123
0
                        update: Some(update),
124
0
                    },
125
0
                    rx,
126
0
                ))
127
0
            }))
128
0
            .await
129
0
    }
130
131
0
    async fn keep_alive(&mut self, request: KeepAliveRequest) -> Result<(), Error> {
132
0
        self.send_update(Update::KeepAliveRequest(request)).await
133
0
    }
134
135
0
    async fn going_away(&mut self, request: GoingAwayRequest) -> Result<(), Error> {
136
0
        self.send_update(Update::GoingAwayRequest(request)).await
137
0
    }
138
139
0
    async fn execution_response(&mut self, request: ExecuteResult) -> Result<(), Error> {
140
0
        self.send_update(Update::ExecuteResult(request)).await
141
0
    }
142
143
0
    async fn execution_complete(&mut self, request: ExecuteComplete) -> Result<(), Error> {
144
0
        self.send_update(Update::ExecuteComplete(request)).await
145
0
    }
146
147
0
    async fn execute_accepted(&mut self, request: ExecuteAccepted) -> Result<(), Error> {
148
0
        self.send_update(Update::ExecuteAccepted(request)).await
149
0
    }
150
151
0
    async fn execute_declined(&mut self, request: ExecuteDeclined) -> Result<(), Error> {
152
0
        self.send_update(Update::ExecuteDeclined(request)).await
153
0
    }
154
}