-
-
Notifications
You must be signed in to change notification settings - Fork 121
Expand file tree
/
Copy pathkeyed.go
More file actions
116 lines (104 loc) · 2.94 KB
/
Copy pathkeyed.go
File metadata and controls
116 lines (104 loc) · 2.94 KB
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
package centrifuge
import "sync"
// keyedChannelOptions are generic options for any keyed channel.
type keyedChannelOptions struct {
// MaxTrackedPerConnection limits how many keys a single connection
// can track in this channel. Zero value means 5000.
MaxTrackedPerConnection int
}
// keyedManager manages keyed channel state: per-channel reverse index
// (key→subscribers) and keyed hub for per-key fan-out.
type keyedManager struct {
node *Node
mu sync.RWMutex
channels map[string]*keyedChannelState
}
type keyedChannelState struct {
hub *keyedHub
opts keyedChannelOptions
}
func newKeyedManager(node *Node) *keyedManager {
return &keyedManager{
node: node,
channels: make(map[string]*keyedChannelState),
}
}
func (m *keyedManager) getOrCreateChannel(channel string, opts keyedChannelOptions) *keyedChannelState {
m.mu.Lock()
s, ok := m.channels[channel]
if !ok {
s = &keyedChannelState{
hub: newKeyedHub(),
opts: opts,
}
m.channels[channel] = s
}
m.mu.Unlock()
return s
}
func (m *keyedManager) getHub(channel string) *keyedHub {
m.mu.RLock()
s, ok := m.channels[channel]
m.mu.RUnlock()
if !ok {
return nil
}
return s.hub
}
func (m *keyedManager) removeChannel(channel string) {
m.mu.Lock()
delete(m.channels, channel)
m.mu.Unlock()
}
// addSubscribers ensures a keyedChannelState exists for the channel and
// atomically adds the given client as a subscriber to each key. The
// "create state + add subscriber" sequence runs under m.mu so a concurrent
// removeChannelIfEmpty cannot delete the state between creation and the
// first addSubscriber — that race would otherwise leave the client in an
// orphaned hub that future broadcasts (via getHub) no longer reach.
func (m *keyedManager) addSubscribers(channel string, keys []string, c *Client, opts keyedChannelOptions) {
m.mu.Lock()
defer m.mu.Unlock()
s, ok := m.channels[channel]
if !ok {
s = &keyedChannelState{
hub: newKeyedHub(),
opts: opts,
}
m.channels[channel] = s
}
for _, key := range keys {
s.hub.addSubscriber(key, c)
}
}
// removeChannelIfEmpty deletes the channel from the manager only when its
// hub has no subscribers. Used by sharedPollChannelState.finalizeShutdown
// instead of the unconditional removeChannel, so a new client whose track
// has just added itself to the hub via addSubscribers is not orphaned by
// an in-flight shutdown from an older sharedPollChannelState.
func (m *keyedManager) removeChannelIfEmpty(channel string) {
m.mu.Lock()
defer m.mu.Unlock()
s, ok := m.channels[channel]
if !ok {
return
}
if s.hub.numKeys() > 0 {
return
}
delete(m.channels, channel)
}
const defaultMaxTrackedPerConnection = 5000
func (m *keyedManager) maxTrackedPerConnection(channel string) int {
m.mu.RLock()
s, ok := m.channels[channel]
m.mu.RUnlock()
if !ok {
return defaultMaxTrackedPerConnection
}
limit := s.opts.MaxTrackedPerConnection
if limit <= 0 {
return defaultMaxTrackedPerConnection
}
return limit
}