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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//! Telling somebody what happened to a key when no command reported it.
//!
//! Almost everything a database does happens because a client asked, and the
//! client's own reply says it happened. A few things do not. A key whose
//! deadline passed goes on the way past, either the next time anything looks at
//! it or when the cycle in [`super::expiry`] gets to it, and a key a write
//! needed the memory of goes because the policy picked it. A key coming into
//! being is the other way round: a command did ask, but what it asked for was
//! `SET` or `RPUSH` or `XADD`, and whether the name was already taken is not
//! part of the answer it gets back.
//!
//! All three are things a client watching a key wants to hear about and nothing
//! on the way out would otherwise mention.
//!
//! # Why a hook and not a return value
//!
//! Because these happen a long way below the caller that would care. A `SADD`
//! that finds a dead key under the name it wants reaps it inside the lookup, and
//! the lookup answers "not there", which is all `SADD` needs and is exactly what
//! it would have been told about a key that never existed. Threading "and by the
//! way one went" back up through every lookup would put a return value nobody
//! reads on the funnel every command comes through, and it would still be wrong
//! for the expiry cycle, which has no caller in the command path at all.
//!
//! A key arriving is the same shape of problem from the other end. Every group
//! writes records and every group can create one, so a return value would have
//! to be added to a few dozen entry points and then carried back through all of
//! them, when what actually knows is the one function underneath that puts a
//! record in the map.
//!
//! # Why a thread local
//!
//! This crate does not know what a subscriber is and should not learn. What it
//! knows is which key and what happened to it. Somewhere above there is a layer
//! that knows who is listening, and the same reason
//! [`super::keyspace::Keyspace`] does not carry a pub/sub registry is the reason
//! it does not carry a notifier either.
//!
//! So the layer above installs a plain function pointer on the thread it is
//! about to run work on, and this crate calls it if it is there. On a thread
//! with nobody installed each of these costs a thread local read and a test
//! against null, on paths that are already doing a good deal more than that.
//!
//! Ordering falls out of this for free. The hook fires at the moment the thing
//! happens, which for a key that was just created is before the command that
//! created it has said what it did, so a listener that queues what it hears
//! keeps the order a real server publishes in without having to be told what
//! that order is.
use Cell;
/// What happened to a key that the command's own reply does not cover.
/// What gets called when one of them happens.
///
/// A bare function pointer and not a boxed closure, so that installing one is a
/// word written to a thread local and calling one is an indirect call, with
/// nothing allocated and nothing dropped. What a listener needs to know beyond
/// the key and what happened, the database number in practice, it keeps on the
/// side, since it is the one arranging for this to be installed at all.
pub type Told = fn;
thread_local!
/// Ask to be told what happens to the keys on this thread, answering who was
/// being told before.
///
/// The answer goes back so that a caller which installs one around a piece of
/// work can put back whatever was there, which is what makes it safe to do this
/// inside something that has already done it.
/// Say what happened to a key, if anybody asked to hear about it.
pub