-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
pegboard.go
115 lines (101 loc) · 3.21 KB
/
pegboard.go
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
package libkb
import (
"fmt"
"os"
"sync"
keybase1 "github.com/keybase/client/go/protocol/keybase1"
)
// Pegboard keeps track of automatic private follows.
// When the logged-in user interacts with another user, that other user
// gets pegged to their incarnation. After the target resets,
// the logged-in user will be alerted even if there's no explicit following.
// CORE-10522 For now, pegboard is disabled by default and when enabled
//
// only has in-memory storage.
type Pegboard struct {
mu sync.Mutex
enabled bool
store map[keybase1.UID]Peg
// CORE-10522 The eventual plan is to store pegs in user's private sigchain
// or just leveldb. With an lru in front.
}
// CORE-10522 When a peg breaks, the chat UI of "Let them in" doesn't make sense.
// CORE-10522 In chat only impteam convs are handled. Have to figure out story for KBFS convs.
// CORE-10522 In addition to chat, KBFS might should also use pegs.
// CORE-10522 Looking at a profile should call ObserveUV.
func NewPegboard() *Pegboard {
return &Pegboard{
enabled: os.Getenv("KEYBASE_EXPERIMENT_PEGBOARD") == "1",
store: make(map[keybase1.UID]Peg),
}
}
type Peg struct {
UID keybase1.UID
EldestSeqno keybase1.Seqno
// CORE-10522 Seqno may not be enough. Consider pegging LinkID.
}
// Update a peg.
// The logged-in user has purposefully interacted with this version of the user.
func (p *Pegboard) TrackUPAK(mctx MetaContext, upak keybase1.UserPlusKeysV2) (err error) {
if !p.enabled {
return nil
}
defer mctx.Trace("Pegboard.TrackUPAK", &err)()
p.mu.Lock()
defer p.mu.Unlock()
if peg, ok := p.store[upak.Uid]; ok && peg.EldestSeqno != 0 && upak.EldestSeqno != 0 && upak.EldestSeqno < peg.EldestSeqno {
// User is already tracked. But at a newer version than the argument.
// CORE-10522 check with explicit local and remote follows?
return fmt.Errorf("Cannot update to older version of user: %v %v < %v", upak.Uid, upak.EldestSeqno, peg.EldestSeqno)
}
p.store[upak.Uid] = Peg{
UID: upak.Uid,
EldestSeqno: upak.EldestSeqno,
}
return nil
}
// Returns an error if this user has reset since their peg was last udpated.
// Pegs users that haven't been seen before.
func (p *Pegboard) CheckUV(mctx MetaContext, uv keybase1.UserVersion) error {
return p.checkUV(mctx, uv)
}
// Pegs users that haven't been seen before.
func (p *Pegboard) ObserveUV(mctx MetaContext, uv keybase1.UserVersion) {
_ = p.checkUV(mctx, uv)
}
func (p *Pegboard) checkUV(mctx MetaContext, uv keybase1.UserVersion) error {
if !p.enabled {
return nil
}
p.mu.Lock()
defer p.mu.Unlock()
if peg, ok := p.store[uv.Uid]; ok {
// This uid has been seen before.
if peg.EldestSeqno == 0 {
// Track the new version.
p.store[uv.Uid] = Peg{
UID: uv.Uid,
EldestSeqno: uv.EldestSeqno,
}
} else {
if uv.EldestSeqno != peg.EldestSeqno {
return fmt.Errorf("User version does not match peg %v != %v", uv.EldestSeqno, peg.EldestSeqno)
}
}
} else {
// First time seeing this uid.
p.store[uv.Uid] = Peg{
UID: uv.Uid,
EldestSeqno: uv.EldestSeqno,
}
}
return nil
}
func (p *Pegboard) OnLogout(mctx MetaContext) {
if !p.enabled {
return
}
p.mu.Lock()
defer p.mu.Unlock()
p.store = make(map[keybase1.UID]Peg)
}