My thoughts on design practices for adaptive path request and link establishment windows
Started by Zenith ·
This is just a quick post of my thoughts going over some findings from applications with regards to cold path request and link establishment timeouts for slow interfaces
One issue is that many applications (MeshChat, NomadNet, and MeshChatX, inherited from MeshChat) set their own timers when it comes to waiting for a path response and link to establish for the first hop.
The first thing here is: Don't use timers unless they are absolutely necessary. The Reticulum API is built to handle all of this happening asynchronously. But when you have to use timers, like say for Nomad page downloading, this is my recommendation:
In MeshChat, the link and path establishment timeout are both pinned to 15 seconds for accessing Nomad pages for example
Fixed timers like these will always fail over slow interfaces. Consider a packet radio link where the net throughput is 125 bits per second or super low LoRa CF/SF settings. These are all valid Reticulum interfaces.
At a 15 second path request timeout, a full round-trip for one path request will take 234 bytes and a little more with an IFAC.
At 125 bits per second, this is not possible to achieve in a 15 second window. At 250 bps, it just barely fits in the fixed timer with no margin at all.
So what is a better way of doing things?
Get the lowest advertised bitrate of an online interface, clamped to 50 bps, and compute how long it would take using an MTU to get the first cold path request timeout.
The drawback is that a instance with mixed interfaces (Some slow, some fast) will always wait that ceiling for a first cold path request. This is unavoidable because we don't know what itnerface it's sitting behind. But the alternative is that path requests fail always every time.
I put an example of this as a PR into MeshChatX which you can find the code for here, in path_utils
https://github.com/RFnexus/MeshChatX/commit/f785e6a896205c85ecbfa0d054a04c9fc2b81d10
The simplest form of this looks like:
def path_response_window(destination_hash, reticulum=None):
if reticulum is None:
reticulum = RNS.Reticulum.get_instance()
window = reticulum.get_first_hop_timeout(destination_hash)
bitrate = slowest_online_bitrate(reticulum)
if bitrate:
window = max(
window,
2 * (PATH_EXCHANGE_BYTES * 8 / max(bitrate, MIN_WINDOW_BITRATE)) + 10,
)
return max(window, RNS.Transport.PATH_REQUEST_TIMEOUT)
We compute the path request timeout based on the actual MTU it takes over the air.
The next is link establishment timeouts. Every application should use the timeout reported by Reticulum versus it's own timer. Reticulum already computes a timeout for every link it creates with link.establishment_timeout but I don't think the API has this in the documentation anywhere
No application should use a flat timer for this. It should always use what is reported by RNS, and my suggestion is to add a little bit of margin to it, maybe +3 or +6 seconds.
timeout = getattr(link, "establishment_timeout", None)
You should also always use RNS.Reticulum.get_instance().get_first_hop_timeout() instead of RNS.Transport.first_hop_timeout() because if your app is a client of a shared RNSD instance it will just use its local socket timeout and not the interfaces.
Those are all my thoughts for now, there may even be a better way of doing this and I would like to hear some feedback or suggestions on what the best patterns are when considering very slow interfaces which a lot of stock RNS/LXMF applications struggle with.
Good write-up, Zenith. One thing: why clamp the bitrate of communication channels to a minimum of 50 bits/s instead of 5 bits/s?
According to the manual and API reference, 5 bits/s communication channels are also valid (albeit slow):
Very low bandwidth requirements
- Reticulum should be able to function reliably over links with a transmission capacity as low as 5 bits per second.
...
It is important to note that Reticulum is designed to be usable on more or less any computing device, and over more or less any medium that allows you to send and receive data, which satisfies some very low minimum requirements.
The communication channel must support at least half-duplex operation, and provide an average throughput of 5 bits per second or greater, and supports a physical layer MTU of 500 bytes.
...
MINIMUM_BITRATE = 5
Minimum bitrate required across a medium for Reticulum to be able to successfully establish links. Currently 5 bits per second.
steveplays wrote:
Good write-up, Zenith. One thing: why clamp the bitrate of communication channels to a minimum of 50 bits/s instead of 5 bits/s?
According to the manual and API reference, 5 bits/s communication channels are also valid (albeit slow):
> Very low bandwidth requirements
>
> - Reticulum should be able to function reliably over links with a transmission capacity as low as 5 bits per second.
>
> ...
>
> It is important to note that Reticulum is designed to be usable on more or less any computing device, and over more or less any medium that allows you to send and receive data, which satisfies some very low minimum requirements.
>
> The communication channel must support at least half-duplex operation, and provide an average throughput of 5 bits per second or greater, and supports a physical layer MTU of 500 bytes.
>
> ...
>
> MINIMUM_BITRATE = 5
> Minimum bitrate required across a medium for Reticulum to be able to successfully establish links. Currently 5 bits per second.
I know that the API and manual define the usable bitrate as 5 bps, but the idea behind the 50 bps clamp was for interfaces that were advertising broken bitrates. I think if you are anticipating bitrates that low it is much better to use callbacks versus fixed timeouts. This mostly refers to stuff that is synchronous like Nomad page downloading or rncp
In the example I gave above the timeout for a 5 bps path request would be 778 seconds. If say a interface like an RNode was reporting something weird like 8 bps or someones CustomInterface might have done that (I may or may not have had a bug for something I wrote lol) your cold path request timeout is now 12 minutes. At 50 bps the max ceiling is 86.8 seconds
Wouldn't a 12 minute timeout be acceptable if the user is aware of the timeout length?
I understand that 12 minutes for a cold path request timeout is long, but 5 bits/s is still a valid communication channel. If interfaces are reporting broken/incorrect low bitrates, shouldn't that be fixed on the interface's side, ideally?
If a node consistently incorrectly reports its own interface bitrates, that node could simply be blackholed