I strongly believe the current growth based on the portion of full nodes is unsustainable (see farming pools and infinite gb nodes). Instead, we could use the portion of nodes above a target node size. I feel this will lead to a more sustainable network growth rate (see safe network growth spreadsheet).
Current network growth technique:
- elders check how many nodes are full
- if it’s more than a certain threshold (50%) more nodes join the section
Proposed network growth technique:
- elders check how many nodes are storing more than a certain amount of data (eg 50 GB)
- if it’s more than a certain threshold (50%) more nodes join the section
There’s a bunch of reasons for why this might be the case. Using a target node size to govern network growth is superior because:
-
Participation becomes easier over time rather than harder.
-
Allows devs / network engineers to optimize around a predictable set of parameters and behaviors.
-
Problem of large nodes gets worse exponentially, problem of hops gets worse logarithmically.
-
Improved scaling characteristics, ie overhead for nodes (cpu/ram) scales with network size.
-
Simple overall network size calculation, good for marketing.
-
Parallelism always beats sheer size (disk/cpu/ram/network) when it comes to distributed networking.
-
Improves the unfairness and difficulties of asymmetric consumer upload/download speeds.
-
Improves granularity of disk usage, eg can only run a single 2 TB node on a 3 TB drive so 1 TB must be wasted.
I won’t elaborate on each point because it becomes very long-winded but I’m sure there will be doubts about some of these points (I have them myself) so feel free to debate and discuss, I will be happy explore any of these ideas.
The change is in essence very simple (although in practice there’s a bunch of signalling that would be affected).
sn_node/src/capacity/rate_limit.rs#L51-60:
pub async fn check_network_storage(&self) -> bool {
info!("Checking network storage");
let all_nodes = self.elder_state.adults().await.len() as f64;
- let full_nodes = self.capacity.full_nodes() as f64;
+ let full_nodes = self.capacity.nodes_over_size(50_gb) as f64;
let usage_ratio = full_nodes / all_nodes;
info!("Total number of adult nodes: {:?}", all_nodes);
info!("Number of Full adult nodes: {:?}", full_nodes);
info!("Section storage usage ratio: {:?}", usage_ratio);
usage_ratio > MAX_NETWORK_STORAGE_RATIO
}
In reality I would prefer this to be not a fixed X GB target node size. A fixed size an easy analogy to get a deeper point across. I would prefer to use a velocity measurement. eg if a section would take longer than X minutes to replace Y nodes then the velocity has dropped too far and we need to get more nodes to join. Velocity is the same as a target node size that varies with current bandwidth availability. This is a little more complex to calculate and reach consensus than a fixed target node size but would be much more flexible in the face of technological improvement over time.