pub struct SessionManager<S> { /* private fields */ }
Expand description
Manages lifecycles of Sessions.
Once the manager is started, the SessionManager::dispatch_message
should be called for each [ApplicationData
] received by the node.
This way, the SessionManager
takes care of proper Start sub-protocol message processing
and correct dispatch of Session-related packets to individual existing Sessions.
Secondly, the manager can initiate new outgoing sessions via SessionManager::new_session
,
probe sessions using SessionManager::ping_session
and list them via SessionManager::active_sessions.
Since the SessionManager
operates over the HOPR protocol,
the message transport S
is required.
Such transport must also be Clone
, since it will be cloned into all the created Session
objects.
§SURB balancing
The manager also can take care of automatic SURB balancing per Session.
With each packet sent from the session initiator over to the receiving party, zero to 2 SURBs might be delivered. When the receiving party wants to send reply packets back, it must consume 1 SURB per packet. This means that if the difference between the SURBs delivered and SURBs consumed is negative, the receiving party might soon run out of SURBs. If SURBs run out, the reply packets will be dropped, causing likely quality of service degradation.
In an attempt to counter this effect, there are two co-existing automated modes of SURB balancing: local SURB balancing and remote SURB balancing.
§Local SURB balancing
Local SURB balancing is performed on the sessions that were initiated by another party (and are therefore incoming to us). The local SURB balancing mechanism continuously evaluates the rate of SURB consumption and retrieval, and if SURBs are running out, the packet egress shaping takes effect. This by itself does not avoid the depletion of SURBs but slows it down in the hope that the initiating party can deliver more SURBs over time. This might happen either organically by sending effective payloads that allow non-zero number of SURBs in the packet, or non-organically by delivering KeepAlive messages via remote SURB balancing.
The egress shaping is done automatically, unless the Session initiator sets the Capability::NoRateControl
flag during Session initiation.
§Remote SURB balancing
Remote SURB balancing is performed by the Session initiator. The SURB balancer estimates the number of SURBs delivered to the other party, and also the number of SURBs consumed by seeing the amount of traffic received in replies. When enabled, a desired target level of SURBs at the Session counterparty is set. According to measured inflow and outflow of SURBs to/from the counterparty, the production of non-organic SURBs is started via keep-alive messages (sent to counterparty) and is controlled to maintain that target level.
In other words, the Session initiator tries to compensate for the usage of SURBs by the counterparty by sending new ones via the keep-alive messages.
This mechanism is configurable via the surb_management
field in SessionClientConfig
.
§Possible scenarios
There are 4 different scenarios of local vs. remote SURB balancing configuration, but an equilibrium (= matching the SURB production and consumption) is most likely to be reached only when both are configured (the ideal case below):
§1. Ideal local and remote SURB balancing
- The Session recipient (Exit) set the
initial_return_session_egress_rate
,max_surb_buffer_duration
andmaximum_surb_buffer_size
values in theSessionManagerConfig
. - The Session initiator (Entry) sets the
target_surb_buffer_size
which matches themaximum_surb_buffer_size
of the counterparty. - The Session initiator (Entry) does NOT set the
Capability::NoRateControl
capability flag when opening Session. - The Session initiator (Entry) sets
max_surbs_per_sec
slightly higher than themaximum_surb_buffer_size / max_surb_buffer_duration
value configured at the counterparty.
In this situation, the maximum Session egress from Exit to the Entry is given by the
maximum_surb_buffer_size / max_surb_buffer_duration
ratio. If there is enough bandwidth,
the (remote) SURB balancer sending SURBs to the Exit will stabilize roughly at this rate of SURBs/sec,
and the whole system will be in equilibrium during the Session’s lifetime (under ideal network conditions).
§2. Remote SURB balancing only
- The Session initiator (Entry) DOES set the
Capability::NoRateControl
capability flag when opening Session. - The Session initiator (Entry) sets
max_surbs_per_sec
andtarget_surb_buffer_size
values inSurbBalancerConfig
In this one-sided situation, the Entry node floods the Exit node with SURBs, only based on its estimated consumption of SURBs at the Exit. The Exit’s egress is not rate-limited at all. If the Exit runs out of SURBs at any point in time, it will simply drop egress packets.
This configuration could potentially only lead to an equilibrium
when the SurbBalancer
at the Entry can react fast enough to Exit’s demand.
§3. Local SURB balancing only
- The Session recipient (Exit) set the
initial_return_session_egress_rate
,max_surb_buffer_duration
andmaximum_surb_buffer_size
values in theSessionManagerConfig
. - The Session initiator (Entry) does NOT set the
Capability::NoRateControl
capability flag when opening Session. - The Session initiator (Entry) does NOT set the
SurbBalancerConfig
at all when opening Session.
In this one-sided situation, the Entry node does not provide any additional SURBs at all (except the ones which are naturally carried by the egress packets which have space to hold SURBs). It relies only on the Session egress limiting of the Exit node. The Exit will limit the egress roughly to the rate of natural SURB occurrence in the ingress.
This configuration could potentially only lead to an equilibrium when uploading non-full packets (ones that can carry at least a single SURB), and the Exit’s egress is limiting itself to such a rate. If Exit’s egress reaches low values due to SURB scarcity, the upper layer protocols over Session might break.
§4. No SURB balancing on each side
- The Session initiator (Entry) DOES set the
Capability::NoRateControl
capability flag when opening Session. - The Session initiator (Entry) does NOT set the
SurbBalancerConfig
at all when opening Session.
In this situation, no additional SURBs are being produced by the Entry and no Session egress rate-limiting takes place at the Exit.
This configuration can only lead to an equilibrium when Entry sends non-full packets (ones that carry at least a single SURB) and the Exit is consuming the SURBs (Session egress) at a slower or equal rate. Such configuration is very fragile, as any disturbances in the SURB flow might lead to a packet drop at the Exit’s egress.
§SURB decay
In a hypothetical scenario of a non-zero packet loss, the Session initiator (Entry) might send a certain number of SURBs to the Session recipient (Exit), but only a portion of it is actually delivered. The Entry has no way of knowing that and assumes that everything has been delivered. A similar problem happens when the Exit uses SURBs to construct return packets, but only a portion of those packets is actually delivered to the Entry. At this point, the Entry also subtracts fewer SURBs from its SURB estimate at the Exit.
In both situations, the Entry thinks there are more SURBs available at the Exit than there really are.
To compensate for a potential packet loss, the Entry’s estimation of Exit’s SURB buffer is regularly
diminished by a percentage of the target_surb_buffer_size
, even if no incoming traffic from the
Exit is detected.
This behavior can be controlled via the surb_decay
field of SurbBalancerConfig
.
§Automatic target_surb_buffer_size
increase
This mechanism only applies to the Session recipient (Exit) and on Sessions without the
Capability::NoRateControl
flag set.
In this case, the Exit throttles the Session egress based on the ratio between
of the estimated SURB balance and the hint of Entry’s target_surb_buffer_size
set during the Session initiation.
However, as the Entry might increase the target_surb_buffer_size
of the Session dynamically, the new value is never hinted again to the Exit (this only happens once during
the Session initiation).
For this reason, the Exit then might observe the ratio going higher than 1. When this happens consistently
over some given time period, the Exit can decide to increase the initial hint to the newly observed value.
See the growable_target_surb_buffer
field in the SessionManagerConfig
for details.
Implementations§
Source§impl<S> SessionManager<S>
impl<S> SessionManager<S>
Sourcepub fn new(cfg: SessionManagerConfig) -> Self
pub fn new(cfg: SessionManagerConfig) -> Self
Creates a new instance given the config
.
Sourcepub fn start(
&self,
msg_sender: S,
new_session_notifier: UnboundedSender<IncomingSession>,
) -> Result<Vec<AbortHandle>>
pub fn start( &self, msg_sender: S, new_session_notifier: UnboundedSender<IncomingSession>, ) -> Result<Vec<AbortHandle>>
Starts the instance with the given msg_sender
Sink
and a channel new_session_notifier
used to notify when a new incoming session is opened to us.
This method must be called prior to any calls to SessionManager::new_session
or
SessionManager::dispatch_message
.
Sourcepub fn is_started(&self) -> bool
pub fn is_started(&self) -> bool
Check if start
has been called and the instance is running.
Sourcepub async fn new_session(
&self,
destination: Address,
target: SessionTarget,
cfg: SessionClientConfig,
) -> Result<Session>
pub async fn new_session( &self, destination: Address, target: SessionTarget, cfg: SessionClientConfig, ) -> Result<Session>
Initiates a new outgoing Session to destination
with the given configuration.
If the Session’s counterparty does not respond within
the configured period,
this method returns TransportSessionError::Timeout
.
It will also fail if the instance has not been started.
Sourcepub async fn ping_session(&self, id: &SessionId) -> Result<()>
pub async fn ping_session(&self, id: &SessionId) -> Result<()>
Sends a keep-alive packet with the given SessionId
.
This currently “fires & forgets” and does not expect nor await any “pong” response.
Sourcepub async fn active_sessions(&self) -> Vec<SessionId>
pub async fn active_sessions(&self) -> Vec<SessionId>
Returns SessionIds
of all currently active sessions.
Sourcepub async fn update_surb_balancer_config(
&self,
id: &SessionId,
config: SurbBalancerConfig,
) -> Result<()>
pub async fn update_surb_balancer_config( &self, id: &SessionId, config: SurbBalancerConfig, ) -> Result<()>
Updates the configuration of the SURB balancer on the given SessionId
.
Returns an error if the Session with the given id
does not exist, or
if it does not use SURB balancing.
Sourcepub async fn get_surb_balancer_config(
&self,
id: &SessionId,
) -> Result<Option<SurbBalancerConfig>>
pub async fn get_surb_balancer_config( &self, id: &SessionId, ) -> Result<Option<SurbBalancerConfig>>
Retrieves the configuration of SURB balancing for the given Session.
Returns an error if the Session with the given id
does not exist.
Sourcepub async fn dispatch_message(
&self,
pseudonym: HoprPseudonym,
data: ApplicationData,
) -> Result<DispatchResult>
pub async fn dispatch_message( &self, pseudonym: HoprPseudonym, data: ApplicationData, ) -> Result<DispatchResult>
The main method to be called whenever data are received.
It tries to recognize the message and correctly dispatches either the Session protocol or Start protocol messages.
If the data are not recognized, they are returned as DispatchResult::Unrelated
.
Trait Implementations§
Auto Trait Implementations§
impl<S> Freeze for SessionManager<S>
impl<S> !RefUnwindSafe for SessionManager<S>
impl<S> Send for SessionManager<S>
impl<S> Sync for SessionManager<S>
impl<S> Unpin for SessionManager<S>
impl<S> !UnwindSafe for SessionManager<S>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<T> Conv for T
impl<T> Conv for T
§impl<T> FmtForward for T
impl<T> FmtForward for T
§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self
to use its Binary
implementation when Debug
-formatted.§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self
to use its Display
implementation when
Debug
-formatted.§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self
to use its LowerExp
implementation when
Debug
-formatted.§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self
to use its LowerHex
implementation when
Debug
-formatted.§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self
to use its Octal
implementation when Debug
-formatted.§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self
to use its Pointer
implementation when
Debug
-formatted.§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self
to use its UpperExp
implementation when
Debug
-formatted.§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self
to use its UpperHex
implementation when
Debug
-formatted.§fn fmt_list(self) -> FmtList<Self>where
&'a Self: for<'a> IntoIterator,
fn fmt_list(self) -> FmtList<Self>where
&'a Self: for<'a> IntoIterator,
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self
into a Left
variant of Either<Self, Self>
if into_left
is true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self
into a Left
variant of Either<Self, Self>
if into_left(&self)
returns true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read more§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self
and passes that borrow into the pipe function. Read more§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self
and passes that borrow into the pipe function. Read more§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self
, then passes self.as_ref()
into the pipe function.§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self
, then passes self.as_mut()
into the pipe
function.§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self
, then passes self.deref()
into the pipe function.§impl<T> Pointable for T
impl<T> Pointable for T
§impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> PolicyExt for Twhere
T: ?Sized,
§impl<T> Tap for T
impl<T> Tap for T
§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B>
of a value. Read more§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B>
of a value. Read more§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R>
view of a value. Read more§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R>
view of a value. Read more§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target
of a value. Read more§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target
of a value. Read more§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap()
only in debug builds, and is erased in release builds.§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut()
only in debug builds, and is erased in release
builds.§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow()
only in debug builds, and is erased in release
builds.§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut()
only in debug builds, and is erased in release
builds.§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref()
only in debug builds, and is erased in release
builds.§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut()
only in debug builds, and is erased in release
builds.§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref()
only in debug builds, and is erased in release
builds.