Skip to content

Commit

Permalink
doc: p2p resource manager/metric channel scoring explanation
Browse files Browse the repository at this point in the history
  • Loading branch information
darkfi committed Jan 30, 2025
1 parent d9a819b commit bc74f89
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 1 deletion.
2 changes: 1 addition & 1 deletion bin/app/release/win/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ args=(
# We forward 22 to 10022 for SSH
#-net nic -net user,hostname=windowsvm
-net nic -net user,hostname=windowsvm,hostfwd=tcp::10146-:22
-net nic -net user,hostname=windowsvm,hostfwd=tcp:127.0.0.1:10146-:22
# This fixes the fucked up mouse
#-device qemu-xhci -device usb-mouse -device usb-tablet
Expand Down
79 changes: 79 additions & 0 deletions doc/src/arch/p2p-network.md
Original file line number Diff line number Diff line change
Expand Up @@ -304,3 +304,82 @@ user-defined types.
TODO:
* implement `ScoringSubsystem`, `AbstractComputer`, and `Controller`.

#### Channel Scoring

We start with a score that keeps track of the resources used. Concretely the
resources are: CPU, bandwidth, memory, disk space - any computing source.
But to keep things simple, lets condense these measures into just two:
work (representing CPU) and bytes (bandwidth/memory). These are primarily
the main cause of resource starvation anyway (disk space is cheap).

```
struct ScoreTable {
/// CPU
work: u32,
/// Memory and bandwidth
bytes: u32
}
```

There is an existing trait `Message`. We add a new trait method that takes
a message and returns its score:

```
impl Message for FooMessage {
fn score(&self) -> ScoreTable {
// calculate the score of this message depending on its data
}
// ...
}
```

We store the scores like this:

```
struct ScoringMetric {
scores: VecDeque<(Timestamp, ScoreTable)>,
}
impl ScoringMetric {
fn add(&mut self, score: ScoreTable) {
self.scores.push((Timestamp::now(), score));
}
}
```

The channels then store a `scores: ScoringMetric`. When it receives a
message then:

1. Call `scores.update()` which does:
1. Apply an exponential decay to all the scores.
2. Call `scores.pop_front()` for any scores who are below the threshold (they
have expired).
2. Get the score of the received message.
3. Store the score and timestamp in the vec.

This logic is implemented in a `ScoringMetric` class.

Now summing the vec will give you the cumulative score. Depending on how high
this score is, we will:

* Throttle the channel's incoming messages before processing them.
* `channel.ban()`.

#### Protocol Scoring

The same mechanism can also be used by protocols, however mostly we can probably
just use the channel scoring for most usecases as a good enough approximation.
However there might be tight edge situations like the current dag sync which
require more fine grained control.

In which case the protocol will instantiate its own `ScoringMetric` and keep
track itself including calling ban on the channel.

#### Tracking the Remote

The remote will ban us if we cross the limit. So our channel must also have a
`scores_remote` which keeps track of *our* score (how the remote sees us).
If we start getting too high then ease up on the messages to avoid getting
blacklisted `channel.ban()` called on us.

0 comments on commit bc74f89

Please sign in to comment.