Skip to content

Commit 374bd08

Browse files
committed
[IMPROVED] Reduce lock contention in accountDisconnectEvent
Protect event ID generation with a dedicated mutex. This allows accountDisconnectEvent to check whether events are enabled under the server read lock, then generate the event ID without holding the server lock. This avoids serializing concurrent disconnects on the server write lock. Apply the same pattern to all methods that generate event IDs. Signed-off-by: Daniele Sciascia <daniele@nats.io>
1 parent 6f3d85d commit 374bd08

3 files changed

Lines changed: 42 additions & 29 deletions

File tree

locksordering.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,9 @@ mirrors/sources, info requests, etc.) are sequentially consistent and may observ
5959
an inflight batch. It must be acquired before the stream lock, never while holding it.
6060

6161
isolateMu -> stream
62+
63+
The "eventIdsMu" lock protects the server's event ID generator. It may be acquired
64+
while holding the Server lock, but the Server lock must not be acquired while
65+
holding eventIdsMu.
66+
67+
Server -> eventIdsMu

server/events.go

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2561,21 +2561,23 @@ func (s *Server) accConnsUpdate(a *Account) {
25612561
s.sendAccConnsUpdate(a, fmt.Sprintf(accConnsEventSubjOld, a.Name), fmt.Sprintf(accConnsEventSubjNew, a.Name))
25622562
}
25632563

2564-
// server lock should be held
25652564
func (s *Server) nextEventID() string {
2566-
return s.eventIds.Next()
2565+
s.eventIdsMu.Lock()
2566+
id := s.eventIds.Next()
2567+
s.eventIdsMu.Unlock()
2568+
return id
25672569
}
25682570

25692571
// accountConnectEvent will send an account client connect event if there is interest.
25702572
// This is a billing event.
25712573
func (s *Server) accountConnectEvent(c *client) {
2572-
s.mu.Lock()
2573-
if !s.eventsEnabled() {
2574-
s.mu.Unlock()
2574+
s.mu.RLock()
2575+
eventsEnabled := s.eventsEnabled()
2576+
s.mu.RUnlock()
2577+
if !eventsEnabled {
25752578
return
25762579
}
25772580
eid := s.nextEventID()
2578-
s.mu.Unlock()
25792581

25802582
c.mu.Lock()
25812583
if c.acc == nil {
@@ -2616,13 +2618,13 @@ func (s *Server) accountConnectEvent(c *client) {
26162618
// accountDisconnectEvent will send an account client disconnect event if there is interest.
26172619
// This is a billing event.
26182620
func (s *Server) accountDisconnectEvent(c *client, now time.Time, reason string) {
2619-
s.mu.Lock()
2620-
if !s.eventsEnabled() {
2621-
s.mu.Unlock()
2621+
s.mu.RLock()
2622+
eventsEnabled := s.eventsEnabled()
2623+
s.mu.RUnlock()
2624+
if !eventsEnabled {
26222625
return
26232626
}
26242627
eid := s.nextEventID()
2625-
s.mu.Unlock()
26262628

26272629
c.mu.Lock()
26282630

@@ -2678,13 +2680,13 @@ func (s *Server) accountDisconnectEvent(c *client, now time.Time, reason string)
26782680

26792681
// This is the system level event sent to the system account for operators.
26802682
func (s *Server) sendAuthErrorEvent(c *client, reason string) {
2681-
s.mu.Lock()
2682-
if !s.eventsEnabled() {
2683-
s.mu.Unlock()
2683+
s.mu.RLock()
2684+
eventsEnabled := s.eventsEnabled()
2685+
s.mu.RUnlock()
2686+
if !eventsEnabled {
26842687
return
26852688
}
26862689
eid := s.nextEventID()
2687-
s.mu.Unlock()
26882690

26892691
now := time.Now().UTC()
26902692
c.mu.Lock()
@@ -2740,13 +2742,13 @@ func (s *Server) sendAccountAuthErrorEvent(c *client, acc *Account, reason strin
27402742
if acc == nil {
27412743
return
27422744
}
2743-
s.mu.Lock()
2744-
if !s.eventsEnabled() {
2745-
s.mu.Unlock()
2745+
s.mu.RLock()
2746+
eventsEnabled := s.eventsEnabled()
2747+
s.mu.RUnlock()
2748+
if !eventsEnabled {
27462749
return
27472750
}
27482751
eid := s.nextEventID()
2749-
s.mu.Unlock()
27502752

27512753
now := time.Now().UTC()
27522754
c.mu.Lock()
@@ -3314,9 +3316,11 @@ func (s *Server) wrapChk(f func()) func() {
33143316
// sendOCSPPeerRejectEvent sends a system level event to system account when a peer connection is
33153317
// rejected due to OCSP invalid status of its trust chain(s).
33163318
func (s *Server) sendOCSPPeerRejectEvent(kind string, peer *x509.Certificate, reason string) {
3317-
s.mu.Lock()
3318-
defer s.mu.Unlock()
3319-
if !s.eventsEnabled() {
3319+
s.mu.RLock()
3320+
eventsEnabled := s.eventsEnabled()
3321+
serverID := s.info.ID
3322+
s.mu.RUnlock()
3323+
if !eventsEnabled {
33203324
return
33213325
}
33223326
if peer == nil {
@@ -3340,16 +3344,18 @@ func (s *Server) sendOCSPPeerRejectEvent(kind string, peer *x509.Certificate, re
33403344
},
33413345
Reason: reason,
33423346
}
3343-
subj := fmt.Sprintf(ocspPeerRejectEventSubj, s.info.ID)
3344-
s.sendInternalMsg(subj, _EMPTY_, &m.Server, &m)
3347+
subj := fmt.Sprintf(ocspPeerRejectEventSubj, serverID)
3348+
s.sendInternalMsgLocked(subj, _EMPTY_, &m.Server, &m)
33453349
}
33463350

33473351
// sendOCSPPeerChainlinkInvalidEvent sends a system level event to system account when a link in a peer's trust chain
33483352
// is OCSP invalid.
33493353
func (s *Server) sendOCSPPeerChainlinkInvalidEvent(peer *x509.Certificate, link *x509.Certificate, reason string) {
3350-
s.mu.Lock()
3351-
defer s.mu.Unlock()
3352-
if !s.eventsEnabled() {
3354+
s.mu.RLock()
3355+
eventsEnabled := s.eventsEnabled()
3356+
serverID := s.info.ID
3357+
s.mu.RUnlock()
3358+
if !eventsEnabled {
33533359
return
33543360
}
33553361
if peer == nil || link == nil {
@@ -3378,6 +3384,6 @@ func (s *Server) sendOCSPPeerChainlinkInvalidEvent(peer *x509.Certificate, link
33783384
},
33793385
Reason: reason,
33803386
}
3381-
subj := fmt.Sprintf(ocspPeerChainlinkInvalidEventSubj, s.info.ID)
3382-
s.sendInternalMsg(subj, _EMPTY_, &m.Server, &m)
3387+
subj := fmt.Sprintf(ocspPeerChainlinkInvalidEventSubj, serverID)
3388+
s.sendInternalMsgLocked(subj, _EMPTY_, &m.Server, &m)
33833389
}

server/server.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,8 @@ type Server struct {
309309
}
310310

311311
// For eventIDs
312-
eventIds *nuid.NUID
312+
eventIdsMu sync.Mutex
313+
eventIds *nuid.NUID
313314

314315
// Websocket structure
315316
websocket srvWebsocket

0 commit comments

Comments
 (0)