Skip to content

Commit 1219da9

Browse files
Simplify error handling after engines fallback removal (#3283)
## Issue Addressed Part of #3118, continuation of #3257 ## Proposed Changes - the [ `first_success_without_retry` ](https://github.com/sigp/lighthouse/blob/9c429d0764ed91cf56efb8a47a35a556b54a86a4/beacon_node/execution_layer/src/engines.rs#L348-L351) function returns a single error. - the [`first_success`](https://github.com/sigp/lighthouse/blob/9c429d0764ed91cf56efb8a47a35a556b54a86a4/beacon_node/execution_layer/src/engines.rs#L324) function returns a single error. - [ `EngineErrors` ](https://github.com/sigp/lighthouse/blob/9c429d0764ed91cf56efb8a47a35a556b54a86a4/beacon_node/execution_layer/src/lib.rs#L69) carries a single error. - [`EngineError`](https://github.com/sigp/lighthouse/blob/9c429d0764ed91cf56efb8a47a35a556b54a86a4/beacon_node/execution_layer/src/engines.rs#L173-L177) now does not need to carry an Id - [`process_multiple_payload_statuses`](https://github.com/sigp/lighthouse/blob/9c429d0764ed91cf56efb8a47a35a556b54a86a4/beacon_node/execution_layer/src/payload_status.rs#L46-L50) now doesn't need to receive an iterator of statuses and weight in different errors ## Additional Info This is built on top of #3294
1 parent 61ed5f0 commit 1219da9

File tree

3 files changed

+147
-241
lines changed

3 files changed

+147
-241
lines changed

beacon_node/execution_layer/src/engines.rs

Lines changed: 21 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -57,17 +57,15 @@ struct PayloadIdCacheKey {
5757

5858
/// An execution engine.
5959
pub struct Engine<T> {
60-
pub id: String,
6160
pub api: HttpJsonRpc<T>,
6261
payload_id_cache: Mutex<LruCache<PayloadIdCacheKey, PayloadId>>,
6362
state: RwLock<EngineState>,
6463
}
6564

6665
impl<T> Engine<T> {
6766
/// Creates a new, offline engine.
68-
pub fn new(id: String, api: HttpJsonRpc<T>) -> Self {
67+
pub fn new(api: HttpJsonRpc<T>) -> Self {
6968
Self {
70-
id,
7169
api,
7270
payload_id_cache: Mutex::new(LruCache::new(PAYLOAD_ID_LRU_CACHE_SIZE)),
7371
state: RwLock::new(EngineState::Offline),
@@ -135,10 +133,10 @@ pub struct Engines {
135133

136134
#[derive(Debug)]
137135
pub enum EngineError {
138-
Offline { id: String },
139-
Api { id: String, error: EngineApiError },
136+
Offline,
137+
Api { error: EngineApiError },
140138
BuilderApi { error: EngineApiError },
141-
Auth { id: String },
139+
Auth,
142140
}
143141

144142
impl Engines {
@@ -159,7 +157,6 @@ impl Engines {
159157
self.log,
160158
"No need to call forkchoiceUpdated";
161159
"msg" => "head does not have execution enabled",
162-
"id" => &self.engine.id,
163160
);
164161
return;
165162
}
@@ -168,7 +165,6 @@ impl Engines {
168165
self.log,
169166
"Issuing forkchoiceUpdated";
170167
"forkchoice_state" => ?forkchoice_state,
171-
"id" => &self.engine.id,
172168
);
173169

174170
// For simplicity, payload attributes are never included in this call. It may be
@@ -183,14 +179,12 @@ impl Engines {
183179
self.log,
184180
"Failed to issue latest head to engine";
185181
"error" => ?e,
186-
"id" => &self.engine.id,
187182
);
188183
}
189184
} else {
190185
debug!(
191186
self.log,
192187
"No head, not sending to engine";
193-
"id" => &self.engine.id,
194188
);
195189
}
196190
}
@@ -261,45 +255,36 @@ impl Engines {
261255
}
262256
}
263257

264-
/// Run `func` on all engines, in the order in which they are defined, returning the first
265-
/// successful result that is found.
258+
/// Run `func` on the node.
266259
///
267-
/// This function might try to run `func` twice. If all nodes return an error on the first time
268-
/// it runs, it will try to upcheck all offline nodes and then run the function again.
269-
pub async fn first_success<'a, F, G, H>(&'a self, func: F) -> Result<H, Vec<EngineError>>
260+
/// This function might try to run `func` twice. If the node returns an error it will try to
261+
/// upcheck it and then run the function again.
262+
pub async fn first_success<'a, F, G, H>(&'a self, func: F) -> Result<H, EngineError>
270263
where
271264
F: Fn(&'a Engine<EngineApi>) -> G + Copy,
272265
G: Future<Output = Result<H, EngineApiError>>,
273266
{
274267
match self.first_success_without_retry(func).await {
275268
Ok(result) => Ok(result),
276-
Err(mut first_errors) => {
277-
// Try to recover some nodes.
269+
Err(e) => {
270+
debug!(self.log, "First engine call failed. Retrying"; "err" => ?e);
271+
// Try to recover the node.
278272
self.upcheck_not_synced(Logging::Enabled).await;
279-
// Retry the call on all nodes.
280-
match self.first_success_without_retry(func).await {
281-
Ok(result) => Ok(result),
282-
Err(second_errors) => {
283-
first_errors.extend(second_errors);
284-
Err(first_errors)
285-
}
286-
}
273+
// Try again.
274+
self.first_success_without_retry(func).await
287275
}
288276
}
289277
}
290278

291-
/// Run `func` on all engines, in the order in which they are defined, returning the first
292-
/// successful result that is found.
279+
/// Run `func` on the node.
293280
pub async fn first_success_without_retry<'a, F, G, H>(
294281
&'a self,
295282
func: F,
296-
) -> Result<H, Vec<EngineError>>
283+
) -> Result<H, EngineError>
297284
where
298285
F: Fn(&'a Engine<EngineApi>) -> G,
299286
G: Future<Output = Result<H, EngineApiError>>,
300287
{
301-
let mut errors = vec![];
302-
303288
let (engine_synced, engine_auth_failed) = {
304289
let state = self.engine.state.read().await;
305290
(
@@ -309,32 +294,22 @@ impl Engines {
309294
};
310295
if engine_synced {
311296
match func(&self.engine).await {
312-
Ok(result) => return Ok(result),
297+
Ok(result) => Ok(result),
313298
Err(error) => {
314299
debug!(
315300
self.log,
316301
"Execution engine call failed";
317302
"error" => ?error,
318-
"id" => &&self.engine.id
319303
);
320304
*self.engine.state.write().await = EngineState::Offline;
321-
errors.push(EngineError::Api {
322-
id: self.engine.id.clone(),
323-
error,
324-
})
305+
Err(EngineError::Api { error })
325306
}
326307
}
327308
} else if engine_auth_failed {
328-
errors.push(EngineError::Auth {
329-
id: self.engine.id.clone(),
330-
})
309+
Err(EngineError::Auth)
331310
} else {
332-
errors.push(EngineError::Offline {
333-
id: self.engine.id.clone(),
334-
})
311+
Err(EngineError::Offline)
335312
}
336-
337-
Err(errors)
338313
}
339314

340315
/// Runs `func` on the node.
@@ -363,9 +338,7 @@ impl Engines {
363338
{
364339
let func = &func;
365340
if *self.engine.state.read().await == EngineState::Offline {
366-
Err(EngineError::Offline {
367-
id: self.engine.id.clone(),
368-
})
341+
Err(EngineError::Offline)
369342
} else {
370343
match func(&self.engine).await {
371344
Ok(res) => Ok(res),
@@ -376,10 +349,7 @@ impl Engines {
376349
"error" => ?error,
377350
);
378351
*self.engine.state.write().await = EngineState::Offline;
379-
Err(EngineError::Api {
380-
id: self.engine.id.clone(),
381-
error,
382-
})
352+
Err(EngineError::Api { error })
383353
}
384354
}
385355
}

beacon_node/execution_layer/src/lib.rs

Lines changed: 25 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ pub use engine_api::{http, http::deposit_methods, http::HttpJsonRpc};
1212
pub use engines::ForkChoiceState;
1313
use engines::{Engine, EngineError, Engines, Logging};
1414
use lru::LruCache;
15-
use payload_status::process_multiple_payload_statuses;
15+
use payload_status::process_payload_status;
1616
pub use payload_status::PayloadStatus;
1717
use sensitive_url::SensitiveUrl;
1818
use serde::{Deserialize, Serialize};
@@ -68,11 +68,10 @@ pub enum Error {
6868
NoPayloadBuilder,
6969
ApiError(ApiError),
7070
Builder(builder_client::Error),
71-
EngineErrors(Vec<EngineError>),
71+
EngineError(Box<EngineError>),
7272
NotSynced,
7373
ShuttingDown,
7474
FeeRecipientUnspecified,
75-
ConsensusFailure,
7675
MissingLatestValidHash,
7776
InvalidJWTSecret(String),
7877
}
@@ -200,12 +199,11 @@ impl<T: EthSpec> ExecutionLayer<T> {
200199
}?;
201200

202201
let engine: Engine<EngineApi> = {
203-
let id = execution_url.to_string();
204202
let auth = Auth::new(jwt_key, jwt_id, jwt_version);
205-
debug!(log, "Loaded execution endpoint"; "endpoint" => %id, "jwt_path" => ?secret_file.as_path());
203+
debug!(log, "Loaded execution endpoint"; "endpoint" => %execution_url, "jwt_path" => ?secret_file.as_path());
206204
let api = HttpJsonRpc::<EngineApi>::new_with_auth(execution_url, auth)
207205
.map_err(Error::ApiError)?;
208-
Engine::<EngineApi>::new(id, api)
206+
Engine::<EngineApi>::new(api)
209207
};
210208

211209
let builder = builder_url
@@ -709,7 +707,8 @@ impl<T: EthSpec> ExecutionLayer<T> {
709707
})
710708
})
711709
.await
712-
.map_err(Error::EngineErrors)
710+
.map_err(Box::new)
711+
.map_err(Error::EngineError)
713712
}
714713

715714
/// Maps to the `engine_newPayload` JSON-RPC call.
@@ -742,16 +741,14 @@ impl<T: EthSpec> ExecutionLayer<T> {
742741
"block_number" => execution_payload.block_number,
743742
);
744743

745-
let broadcast_results = self
744+
let broadcast_result = self
746745
.engines()
747746
.broadcast(|engine| engine.api.new_payload_v1(execution_payload.clone()))
748747
.await;
749748

750-
process_multiple_payload_statuses(
751-
execution_payload.block_hash,
752-
Some(broadcast_results).into_iter(),
753-
self.log(),
754-
)
749+
process_payload_status(execution_payload.block_hash, broadcast_result, self.log())
750+
.map_err(Box::new)
751+
.map_err(Error::EngineError)
755752
}
756753

757754
/// Register that the given `validator_index` is going to produce a block at `slot`.
@@ -879,7 +876,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
879876
.set_latest_forkchoice_state(forkchoice_state)
880877
.await;
881878

882-
let broadcast_results = self
879+
let broadcast_result = self
883880
.engines()
884881
.broadcast(|engine| async move {
885882
engine
@@ -888,13 +885,13 @@ impl<T: EthSpec> ExecutionLayer<T> {
888885
})
889886
.await;
890887

891-
process_multiple_payload_statuses(
888+
process_payload_status(
892889
head_block_hash,
893-
Some(broadcast_results)
894-
.into_iter()
895-
.map(|result| result.map(|response| response.payload_status)),
890+
broadcast_result.map(|response| response.payload_status),
896891
self.log(),
897892
)
893+
.map_err(Box::new)
894+
.map_err(Error::EngineError)
898895
}
899896

900897
pub async fn exchange_transition_configuration(&self, spec: &ChainSpec) -> Result<(), Error> {
@@ -909,9 +906,6 @@ impl<T: EthSpec> ExecutionLayer<T> {
909906
.broadcast(|engine| engine.api.exchange_transition_configuration_v1(local))
910907
.await;
911908

912-
let mut errors = vec![];
913-
// Having no fallbacks, the id of the used node is 0
914-
let i = 0usize;
915909
match broadcast_result {
916910
Ok(remote) => {
917911
if local.terminal_total_difficulty != remote.terminal_total_difficulty
@@ -922,38 +916,29 @@ impl<T: EthSpec> ExecutionLayer<T> {
922916
"Execution client config mismatch";
923917
"msg" => "ensure lighthouse and the execution client are up-to-date and \
924918
configured consistently",
925-
"execution_endpoint" => i,
926919
"remote" => ?remote,
927920
"local" => ?local,
928921
);
929-
errors.push(EngineError::Api {
930-
id: i.to_string(),
922+
Err(Error::EngineError(Box::new(EngineError::Api {
931923
error: ApiError::TransitionConfigurationMismatch,
932-
});
924+
})))
933925
} else {
934926
debug!(
935927
self.log(),
936928
"Execution client config is OK";
937-
"execution_endpoint" => i
938929
);
930+
Ok(())
939931
}
940932
}
941933
Err(e) => {
942934
error!(
943935
self.log(),
944936
"Unable to get transition config";
945937
"error" => ?e,
946-
"execution_endpoint" => i,
947938
);
948-
errors.push(e);
939+
Err(Error::EngineError(Box::new(e)))
949940
}
950941
}
951-
952-
if errors.is_empty() {
953-
Ok(())
954-
} else {
955-
Err(Error::EngineErrors(errors))
956-
}
957942
}
958943

959944
/// Used during block production to determine if the merge has been triggered.
@@ -992,7 +977,8 @@ impl<T: EthSpec> ExecutionLayer<T> {
992977
.await
993978
})
994979
.await
995-
.map_err(Error::EngineErrors)?;
980+
.map_err(Box::new)
981+
.map_err(Error::EngineError)?;
996982

997983
if let Some(hash) = &hash_opt {
998984
info!(
@@ -1102,7 +1088,8 @@ impl<T: EthSpec> ExecutionLayer<T> {
11021088
Ok(None)
11031089
})
11041090
.await
1105-
.map_err(|e| Error::EngineErrors(vec![e]))
1091+
.map_err(Box::new)
1092+
.map_err(Error::EngineError)
11061093
}
11071094

11081095
/// This function should remain internal.
@@ -1160,7 +1147,8 @@ impl<T: EthSpec> ExecutionLayer<T> {
11601147
.await
11611148
})
11621149
.await
1163-
.map_err(Error::EngineErrors)
1150+
.map_err(Box::new)
1151+
.map_err(Error::EngineError)
11641152
}
11651153

11661154
async fn get_payload_by_block_hash_from_engine(

0 commit comments

Comments
 (0)