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
//! The session liveness this process holds in memory, and what a reader is handed.
//!
//! A session beats far more often than its projection changes: the client
//! republishes an unchanged tuple every ten seconds. v1 turned each of those
//! beats into a session-row write, a durable `session_state` event, and a
//! broadcast to every subscriber, all serialized through one SQLite writer, and
//! that path was the cluster's concurrency ceiling (plan §"网络与并发", v1
//! finding 6). v2 keeps the beat here instead: the row moves when its content
//! changes, when the session ends, and once per presence window.
//!
//! Two facts about one session are in play, and the precedence between them is
//! the whole point of this module:
//!
//! - the row's `last_seen`, the freshness a reader judges the mirror by;
//! - the freshest beat this process took, which the row has not been told.
//!
//! A reader must never be handed the older of the two while the server holds
//! the newer — that is v1's frozen mirror in its second form — so every read of
//! the table passes through [`LiveSessions::freshest`], and `last_seen` never
//! regresses: an entry is dropped only when the row's own value catches up with
//! it.
use HashMap;
use Mutex;
/// The freshest beat of each session this process took without writing it down.
///
/// An entry is spent as soon as the row's own `last_seen` reaches it, which a
/// content write or an interval flush does. The map therefore holds the
/// sessions that are currently ahead of their row rather than every session
/// ever seen: a session that ends writes its ending, and the write settles it.
pub