ant_protocol/storage.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
// Copyright 2024 MaidSafe.net limited.
//
// This SAFE Network Software is licensed to you under The General Public License (GPL), version 3.
// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed
// under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. Please review the Licences for the specific language governing
// permissions and limitations relating to use of the SAFE Network Software.
mod address;
mod chunks;
mod header;
mod scratchpad;
mod transaction;
use core::fmt;
use exponential_backoff::Backoff;
use std::{num::NonZeroUsize, time::Duration};
pub use self::{
address::{ChunkAddress, RegisterAddress, ScratchpadAddress, TransactionAddress},
chunks::Chunk,
header::{try_deserialize_record, try_serialize_record, RecordHeader, RecordKind, RecordType},
scratchpad::Scratchpad,
transaction::Transaction,
};
/// A strategy that translates into a configuration for exponential backoff.
/// The first retry is done after 2 seconds, after which the backoff is roughly doubled each time.
/// The interval does not go beyond 32 seconds. So the intervals increase from 2 to 4, to 8, to 16, to 32 seconds and
/// all attempts are made at most 32 seconds apart.
///
/// The exact timings depend on jitter, which is set to 0.2, meaning the intervals can deviate quite a bit
/// from the ones listed in the docs.
#[derive(Clone, Debug, Copy, Default)]
pub enum RetryStrategy {
/// Attempt once (no retries)
None,
/// Retry 3 times (waits 2s, 4s and lastly 8s; max total time ~14s)
Quick,
/// Retry 5 times (waits 2s, 4s, 8s, 16s and lastly 32s; max total time ~62s)
#[default]
Balanced,
/// Retry 9 times (waits 2s, 4s, 8s, 16s, 32s, 32s, 32s, 32s and lastly 32s; max total time ~190s)
Persistent,
/// Attempt a specific number of times
N(NonZeroUsize),
}
impl RetryStrategy {
pub fn attempts(&self) -> usize {
match self {
RetryStrategy::None => 1,
RetryStrategy::Quick => 4,
RetryStrategy::Balanced => 6,
RetryStrategy::Persistent => 10,
RetryStrategy::N(x) => x.get(),
}
}
pub fn backoff(&self) -> Backoff {
let mut backoff = Backoff::new(
self.attempts() as u32,
Duration::from_secs(1), // First interval is double of this (see https://github.com/yoshuawuyts/exponential-backoff/issues/23)
Some(Duration::from_secs(32)),
);
backoff.set_factor(2); // Default.
backoff.set_jitter(0.2); // Default is 0.3.
backoff
}
}
impl fmt::Display for RetryStrategy {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{self:?}")
}
}
#[test]
fn verify_retry_strategy_intervals() {
let intervals = |strategy: RetryStrategy| -> Vec<u32> {
let mut backoff = strategy.backoff();
backoff.set_jitter(0.01); // Make intervals deterministic.
backoff
.into_iter()
.flatten()
.map(|duration| duration.as_secs_f64().round() as u32)
.collect()
};
assert_eq!(intervals(RetryStrategy::None), Vec::<u32>::new());
assert_eq!(intervals(RetryStrategy::Quick), vec![2, 4, 8]);
assert_eq!(intervals(RetryStrategy::Balanced), vec![2, 4, 8, 16, 32]);
assert_eq!(
intervals(RetryStrategy::Persistent),
vec![2, 4, 8, 16, 32, 32, 32, 32, 32]
);
assert_eq!(
intervals(RetryStrategy::N(NonZeroUsize::new(12).unwrap())),
vec![2, 4, 8, 16, 32, 32, 32, 32, 32, 32, 32]
);
}