From d1503bfa320e1c9ed0aba72e6a2a15a11ba56a7a Mon Sep 17 00:00:00 2001 From: Petar Maymounkov Date: Sat, 7 Mar 2020 20:56:23 -0800 Subject: [PATCH] Shared backoffs interface proposal. --- backoff/backoff.go | 113 ++++++++++++++++++++++----------------------- 1 file changed, 56 insertions(+), 57 deletions(-) diff --git a/backoff/backoff.go b/backoff/backoff.go index 1ecc303a..c74f5428 100644 --- a/backoff/backoff.go +++ b/backoff/backoff.go @@ -1,80 +1,79 @@ -// Package backoff provides facilities for creating interdependent backoff timers. +// Package backoff provides facilities for working with interdependent backoff timers. // -// The design of "backoff trees" proposed in this package is motivated by -// https://github.com/libp2p/go-libp2p-swarm/issues/37. +// This package is motivated by https://github.com/libp2p/go-libp2p-swarm/issues/37. // -// The design proposed here can be dubbed "backoff trees", and is motivated by the following use case in libp2p: -// -// (1) Each peer has an associated backoff timer, whose significance is that no interaction -// with this peer can commence unless the timer is clear. -// (2) Each peer has a subordinate "find peer" timer, such that find peer operations should not -// be attempted unless the "find peer" timer is clear and the "peer" timer is clear, as well. -// (3) Each peer has any number of subordinate "transport" timers, such that dial attempts on any -// given transport should not happen unless the "transport" timer is clear and the "peer" timer is clear, as well. -// (4) Each TCP transport may have subordinate "IP" timers, such that TCP dial attempts to the given IP -// should not commence unless the "IP", "transport" and "peer" timers are all clear. +// We would like to associate backoff logic to each component of a multiaddr. +// For instance, consider a typical multiaddr of the form IP/transport/protocol. +// The backoff associated with IP dictates whether any multiaddr +// using that IP can be dialed (regardless of transport and protocol). +// The backoff associated with IP and transport dictates whether +// any multiaddr using that IP and transport can be dialed (regardless of protocol). // And so on. // -// This example highlights a hierarchical relationship that one might express, for instance as, as: -// peer operations -// find operations -// transport-related operations -// TCP-related operations -// IP-related operations -// Port-related operations -// QUIC-related operations -// -// All nodes in this hierarchy may wish to use a different backoff policy. -// However, in all cases, a timer node is considered "clear" if its own backoff timer is clear, -// as well as the timers of all of its ancestors (all the way to the root). -// -// REMARKS +// Furthermore, we stipulate that software components that utilize backoffs on multiaddrs +// should share the backoff timers in order to benefit from each other's learnings. // -// Note that this design can be extended to "backoff DAGS", wherein a timer can have multiple parents. -// This can be useful, for instance, in the case when an IP address is viewed as a subordinate timer to multiple protocols, -// like TCP and QUIC. -// -// While supporting timers with multiple parents is straightforward, it is not clear that it can be used conveniently -// by independent code paths. In particular, a runtime instance of the QUIC transport may not (and probably should not) -// be aware that a TCP transport instance is running in parallel. +// To this end, this package provides the definition of a backoff timer (as the interface BackoffTimer), +// as well as an interface for a shared set of backoff timers, called SharedBackoffs. package backoff import ( "time" + + ma "github.com/multiformats/go-multiaddr" ) -// NewBackoffTimer creates a new backoff timer. -// The timer name is for display purposes only. -func NewBackoffTimer(name string, policy BackoffPolicy, parent BackoffTimer) BackoffTimer { - panic("not implemented") -} +// SharedBackoffs maintains backoff timers that are shared by multiple IPFS components. +// Some of the timers are correlated. If this is the case, the relationships are documented in the access methods below. +type SharedBackoffs interface { -// BackoffPolicy represents a specific backoff logic. -// -// Implementations of BackoffPolicy are purely concerned with the "arithmetic" -// of computing when the respective timer should be cleared (e.g. for making new connection retries). -// -// This interface allows for the implementation of flexible backoff policies. -// For instance, a policy could treat a burst of backoffs as a single one. -type BackoffPolicy interface { + // Timers for dialing multiaddresses. - // Clear informs the policy of the current time and sets its state to cleared. - Clear(now time.Time) + // IP returns the timer associated with a given IP address. + // If the argument multiaddr does not start with an IP component, a panic is thrown. + // The IP timer should be backed off, if it is determined that the IP itself is unreachable + // (e.g. in response to a "no route to IP" error). + // The IP timer should be cleared whenever any successful connection to this IP is established. + IP(ma.Multiaddr) BackoffTimer - // Backoff informs the policy of the current time and sets its state to backing off. - Backoff(now time.Time) + // IPTransport returns the timer associated with a given IP address and a transport (TCP, UDP, etc.). + // If the argument multiaddr does not start with an IP and transport components, a panic is thrown. + // The IP/Transport timer should be backed off, if it is determined that the IP is reachable but the transport is not. + // The IP/Transport timer should be cleared whenever any successful connection to this IP/transport pair is established. + // The IP/Transport timer will report a clear state whenever the IP and IP/Transport timers are both clear. + IPTransport(ma.Multiaddr) BackoffTimer - // TimeToClear informs the policy of the current time and returns the duration - // remaining until the back off state is cleared. Zero or negative durations indicate - // that the state is already cleared. - TimeToClear(now time.Time) time.Duration + // IPTransportSwarm returns the timer associated with a given IP address, a transport (TCP, UDP, etc.) and the swarm service. + // If the argument multiaddr does not start with an IP and transport components, a panic is thrown. + // The IP/Transport/Swarm timer should be backed off, if it is determined that the IP is reachable + // using the given transport, but the swarm service is not supported. + // The IP/Transport/Swarm timer should be cleared whenever any successful connection to the IP/transport pair is established + // and the swarm service is available. + // The IP/Transport/Swarm timer will report a clear state whenever the respective IP, IP/Transport and IP/Transport/Swarm + // timers are all clear. + IPTransportSwarm(ma.Multiaddr) BackoffTimer + + // IPTransportSwarmProtocol returns the timer associated with a given IP address, transport (TCP, UDP, etc.) and + // protocol, supported through the swarm service. + // If the argument multiaddr does not start with an IP, transport and protocol components, a panic is thrown. + // The IP/Transport/Swarm/Protocol timer should be backed off, if it is determined that the IP is reachable + // using the given transport, the swarm service is supported, but the protocol is not. + // The IP/Transport/Swarm/Protocol timer should be cleared whenever any successful connection to the protocol + // is established through the swarm service on the given IP/transport. + // The IP/Transport/Swarm/Protocol timer will report a clear state whenever the respective + // IP, IP/Transport and IP/Transport/Swarm, and IP/Transport/Swarm/Protocol timers are all clear. + IPTransportSwarmProtocol(ma.Multiaddr) BackoffTimer + + // Other shared backoff timers should be added here. } -// BackoffTimer is a synchronous user-facing interface to a backoff timer. -// Timers are created using NewBackoffTimer. +// BackoffTimer is a user-facing interface to a backoff timer. type BackoffTimer interface { // Wait blocks until the timer and all of its ancestors (parent, grandparent, etc.), if any, have been cleared. Wait() + // TimeToClear returns the duration remaining until the back off state is cleared. + // Zero or negative durations indicate that the state is already cleared. + TimeToClear(now time.Time) time.Duration // Clear clears this timer and returns instantaneously. // For example, a user might call this function after a successul connection attempt. Clear()