Skip to main content

Mountain/Vine/Client/
ConnectToSideCar.rs

1
2//! Establish a gRPC connection to a Cocoon sidecar with exponential
3//! back-off retry. On success initialises the per-connection metadata
4//! tracked by `Shared::CONNECTION_METADATA`.
5
6use std::time::{Duration, Instant};
7
8use crate::{
9	Vine::{
10		Client::{
11			Shared::{
12				CONNECTION_METADATA,
13				ConnectionMetadata,
14				FireConnectionNotify,
15				MAX_RETRY_ATTEMPTS,
16				RETRY_BASE_DELAY_MS,
17			},
18			TryConnectSingle,
19		},
20		Error::VineError,
21	},
22	dev_log,
23};
24
25pub async fn Fn(SideCarIdentifier:String, Address:String) -> Result<(), VineError> {
26	dev_log!(
27		"grpc",
28		"[VineClient] Connecting to sidecar '{}' at '{}'...",
29		SideCarIdentifier,
30		Address
31	);
32
33	let Endpoint = format!("http://{}", Address);
34
35	if Endpoint.len() > 256 {
36		return Err(VineError::RPCError(
37			"Invalid endpoint address: exceeds maximum length".to_string(),
38		));
39	}
40
41	let mut LastError = None;
42
43	for Attempt in 1..=MAX_RETRY_ATTEMPTS {
44		let Result = TryConnectSingle::Fn(&SideCarIdentifier, &Endpoint).await;
45
46		if Result.is_ok() {
47			CONNECTION_METADATA.lock().insert(
48				SideCarIdentifier.clone(),
49				ConnectionMetadata { LastActivity:Instant::now(), FailureCount:0, IsHealthy:true },
50			);
51
52			dev_log!("grpc", "[VineClient] Successfully connected to sidecar '{}'", SideCarIdentifier);
53
54			// Unblock any `WaitForClientConnection` callers immediately.
55			FireConnectionNotify(&SideCarIdentifier);
56
57			return Result;
58		}
59
60		LastError = Some(Result.unwrap_err());
61
62		if Attempt < MAX_RETRY_ATTEMPTS {
63			let DelayMilliseconds = RETRY_BASE_DELAY_MS * 2_u64.pow(Attempt as u32);
64
65			tokio::time::sleep(Duration::from_millis(DelayMilliseconds)).await;
66		}
67	}
68
69	Err(LastError.unwrap_or_else(|| VineError::RPCError("Connection failed".to_string())))
70}