|
1 | 1 | # Flow Table |
2 | 2 |
|
3 | | -The current implementation of flow table uses `dash_map` and per-thread priority queue's (for timeouts) along with `Arc` and `Weak` to get a reasonable flow table with timeouts. |
4 | | -However, it leaves a lot of room for optimizations. |
| 3 | +The flow table uses `DashMap` for concurrent key→value storage and per-flow |
| 4 | +tokio timers for expiration. |
5 | 5 |
|
6 | 6 | ## Flow Table Implementation |
7 | 7 |
|
8 | | -The main `DashMap` holds `Weak` references to all the flow entries so that the memory gets automatically deallocated when the entry times out. |
9 | | - |
10 | | -The priority queue's hold `Arc` references to the flow entries to keep them alive when they are not in any packet meta data. |
11 | | -When the entry times-out and is removed from the priority queue and the last packet referencing that flow is dropped, the memory for the entry is freed. |
12 | | - |
13 | | -Note that in the current implementation, a flow is not removed from the flow table until the last Arc to the flow_info is dropped or the flow entry is replaced. This can be changed if needed, or even have it be an option on the flow as to whether timeout removes the flow or not. |
14 | | - |
15 | | -## Optimizations |
16 | | - |
17 | | -In the current implementation, there has to be periodic or on-timeout reaping the Weak reference in the hash table. |
18 | | -This is better done by having a version of `DashMap` that can reap the dead `Weak` reference as it walks the table on lookups, instead of waiting for key collisions. |
19 | | -The hope, for now, is that the entries in the hash table array will contain a small pointer and not take up too much extra memory. |
20 | | -Those dead `Weak` pointers will prevent shrinking of the hash table though, if the implementation supports that. |
21 | | - |
22 | | -Second, the `priority_queue` crate uses a `HashMap` inside the queue in order to allow fast removal and re-insertion. |
23 | | -However, this wastes space and requires extra hashes. |
24 | | -The better way to do this is to have a custom priority queue integrated with the custom weak-reaping hash map so that the same hash table can be used for both operations. |
25 | | -This improves cache locality, reduces memory utlization, and avoids multiple hash table lookups in many cases. |
26 | | - |
27 | | -However, in the interest of time to completion for the code, this module currently uses existing data structures instead of full custom implementations of everything. |
28 | | -However, the interface should be able to hide a change from the current to the optimized implementation. |
| 8 | +The `DashMap` stores `Arc<FlowInfo>` values directly, so the table is the |
| 9 | +primary strong-reference keeper for each flow entry. Callers that need to |
| 10 | +retain a flow (e.g. pipeline stages that tag packets) clone the `Arc` out of |
| 11 | +`lookup()`. |
| 12 | + |
| 13 | +When a flow is inserted, a `tokio::task` is spawned (if a tokio runtime is |
| 14 | +present) that sleeps until the flow's deadline and then calls |
| 15 | +`update_status(FlowStatus::Expired)`. The DashMap entry is not removed by the |
| 16 | +timer; instead, `lookup()` performs lazy cleanup: if a looked-up entry is |
| 17 | +`Expired` or `Cancelled` it is removed from the map and `None` is returned. |
| 18 | +The same lazy path covers the case where a deadline passes without a timer |
| 19 | +firing (e.g. in non-tokio test contexts). |
| 20 | + |
| 21 | +`FlowInfo::related` holds a `Weak<FlowInfo>` pointing to the reverse-direction |
| 22 | +flow of a bidirectional pair. This avoids reference cycles: the two entries |
| 23 | +are independent `Arc`s in the table, and the `Weak` merely lets one side |
| 24 | +observe the other without keeping it alive. |
| 25 | + |
| 26 | +## Non-tokio contexts (shuttle / sync tests) |
| 27 | + |
| 28 | +When no tokio runtime is present `Handle::try_current()` returns `Err` and no |
| 29 | +timer is spawned. Tests simulate expiration by calling |
| 30 | +`flow_info.update_status(FlowStatus::Expired)` directly, after which the next |
| 31 | +`lookup()` call performs the lazy removal. |
0 commit comments