gringron_util/
rate_counter.rs

1// Copyright 2021 The GrinGron Developers
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::convert::TryInto;
16/// Utility to track the rate of data transfers
17use std::time::SystemTime;
18
19struct Entry {
20	bytes: u64,
21	timestamp: u64,
22}
23
24impl Entry {
25	fn new(bytes: u64) -> Entry {
26		Entry {
27			bytes,
28			timestamp: millis_since_epoch(),
29		}
30	}
31
32	// Create new "quiet" entry with zero timestamp.
33	// This will count toward total bytes but will not affect the "msg rate".
34	fn new_quiet(bytes: u64) -> Entry {
35		Entry {
36			bytes,
37			timestamp: 0,
38		}
39	}
40
41	// We want to filter out "quiet" entries when calculating the "msg rate".
42	fn is_quiet(&self) -> bool {
43		self.timestamp == 0
44	}
45}
46
47/// A rate counter tracks the number of transfers, the amount of data
48/// exchanged and the rate of transfer (via a few timers) over the last
49/// minute. The counter does not try to be accurate and update times
50/// proactively, instead it only does so lazily. As a result, produced
51/// rates are worst-case estimates.
52pub struct RateCounter {
53	last_min_entries: Vec<Entry>,
54}
55
56impl RateCounter {
57	/// Instantiate a new rate counter
58	pub fn new() -> RateCounter {
59		RateCounter {
60			last_min_entries: vec![],
61		}
62	}
63
64	/// Increments number of bytes transferred, updating counts and rates.
65	pub fn inc(&mut self, bytes: u64) {
66		self.last_min_entries.push(Entry::new(bytes));
67		self.truncate();
68	}
69
70	/// Increments number of bytes without updating the count or rate.
71	/// We filter out 0 last_min_times when calculating rate.
72	/// Used during txhashset.zip download to track bytes downloaded
73	/// without treating a peer as abusive (too high a rate of download).
74	pub fn inc_quiet(&mut self, bytes: u64) {
75		self.last_min_entries.push(Entry::new_quiet(bytes));
76		self.truncate();
77	}
78
79	fn truncate(&mut self) {
80		let now_millis = millis_since_epoch();
81		while !self.last_min_entries.is_empty()
82			&& self.last_min_entries[0].timestamp + 60000 < now_millis
83		{
84			self.last_min_entries.remove(0);
85		}
86	}
87
88	/// Number of bytes counted in the last minute.
89	/// Includes "quiet" byte increments.
90	pub fn bytes_per_min(&self) -> u64 {
91		self.last_min_entries.iter().map(|x| x.bytes).sum()
92	}
93
94	/// Count of increases in the last minute.
95	/// Excludes "quiet" byte increments.
96	pub fn count_per_min(&self) -> u64 {
97		self.last_min_entries
98			.iter()
99			.filter(|x| !x.is_quiet())
100			.count() as u64
101	}
102
103	/// Elapsed time in ms since the last entry.
104	/// We use this to rate limit when sending.
105	pub fn elapsed_since_last_msg(&self) -> Option<u64> {
106		self.last_min_entries
107			.last()
108			.map(|x| millis_since_epoch().saturating_sub(x.timestamp))
109	}
110}
111
112// turns out getting the millisecs since epoch in Rust isn't as easy as it
113// could be
114fn millis_since_epoch() -> u64 {
115	SystemTime::now()
116		.duration_since(SystemTime::UNIX_EPOCH)
117		.map(|since_epoch| since_epoch.as_millis().try_into().unwrap_or(0))
118		.unwrap_or(0)
119}