Skip to content

Commit 8b4b361

Browse files
committed
[IMPROVED] Reduce lock contention in saveClosedClient
Protect the closed connection ring buffer with a dedicated mutex instead of the server lock. This allows saveClosedClient to record closed connections concurrently with closeConnection for the server lock. Signed-off-by: Daniele Sciascia <daniele@nats.io>
1 parent 374bd08 commit 8b4b361

4 files changed

Lines changed: 21 additions & 18 deletions

File tree

locksordering.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,9 @@ while holding the Server lock, but the Server lock must not be acquired while
6565
holding eventIdsMu.
6666

6767
Server -> eventIdsMu
68+
69+
The "closedMu" lock protects the server's closed connection ring. It may be
70+
acquired while holding the Server lock, but the Server lock must not be acquired
71+
while holding closedMu.
72+
73+
Server -> closedMu

server/monitor.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,11 +316,11 @@ func (s *Server) Connz(opts *ConnzOptions) (*Connz, error) {
316316
case ConnOpen:
317317
c.Total = len(clist)
318318
case ConnClosed:
319-
closedClients = s.closed.closedClients()
319+
closedClients = s.closedClients()
320320
c.Total = len(closedClients)
321321
case ConnAll:
322322
c.Total = len(clist)
323-
closedClients = s.closed.closedClients()
323+
closedClients = s.closedClients()
324324
c.Total += len(closedClients)
325325
}
326326

server/monitor_test.go

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,14 +1205,12 @@ func TestMonitorConnzSortedByStopTimeClosedConn(t *testing.T) {
12051205
checkClosedConns(t, s, 4, time.Second)
12061206

12071207
// Now adjust the Stop times for these with some random values.
1208-
s.mu.Lock()
12091208
now := time.Now().UTC()
1210-
ccs := s.closed.closedClients()
1209+
ccs := s.closedClients()
12111210
for _, cc := range ccs {
12121211
newStop := now.Add(time.Duration(rand.Int()%120) * -time.Minute)
12131212
cc.Stop = &newStop
12141213
}
1215-
s.mu.Unlock()
12161214

12171215
url = fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port)
12181216
for mode := 0; mode < 2; mode++ {
@@ -1250,13 +1248,11 @@ func TestMonitorConnzSortedByReason(t *testing.T) {
12501248
checkClosedConns(t, s, 20, time.Second)
12511249

12521250
// Now adjust the Reasons for these with some random values.
1253-
s.mu.Lock()
1254-
ccs := s.closed.closedClients()
1251+
ccs := s.closedClients()
12551252
max := int(ServerShutdown)
12561253
for _, cc := range ccs {
12571254
cc.Reason = ClosedState(rand.Int() % max).String()
12581255
}
1259-
s.mu.Unlock()
12601256

12611257
url = fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port)
12621258
for mode := 0; mode < 2; mode++ {
@@ -7008,9 +7004,9 @@ func TestConnzClosedSubsDetailNoSharedMutation(t *testing.T) {
70087004
cc.Cid = 1
70097005
cc.subs = []SubDetail{{Subject: "foo.bar"}}
70107006
cc.NumSubs = 1
7011-
s.mu.Lock()
7007+
s.closedMu.Lock()
70127008
s.closed.append(cc)
7013-
s.mu.Unlock()
7009+
s.closedMu.Unlock()
70147010

70157011
// Concurrently request closed connections with subscription detail.
70167012
var wg sync.WaitGroup

server/server.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ type Server struct {
211211
users map[string]*User
212212
nkeys map[string]*NkeyUser
213213
totalClients uint64
214+
closedMu sync.Mutex // Protects the closedRingBuffer
214215
closed *closedRingBuffer
215216
done chan bool
216217
start time.Time
@@ -3608,11 +3609,11 @@ func (s *Server) saveClosedClient(c *client, nc net.Conn, subs map[string]*subsc
36083609
c.mu.Unlock()
36093610

36103611
// Place in the ring buffer
3611-
s.mu.Lock()
3612+
s.closedMu.Lock()
36123613
if s.closed != nil {
36133614
s.closed.append(cc)
36143615
}
3615-
s.mu.Unlock()
3616+
s.closedMu.Unlock()
36163617
}
36173618

36183619
// Adds to the list of client and websocket clients connect URLs.
@@ -4102,20 +4103,20 @@ func (s *Server) startGoRoutine(f func(), tags ...pprofLabels) bool {
41024103
}
41034104

41044105
func (s *Server) numClosedConns() int {
4105-
s.mu.RLock()
4106-
defer s.mu.RUnlock()
4106+
s.closedMu.Lock()
4107+
defer s.closedMu.Unlock()
41074108
return s.closed.len()
41084109
}
41094110

41104111
func (s *Server) totalClosedConns() uint64 {
4111-
s.mu.RLock()
4112-
defer s.mu.RUnlock()
4112+
s.closedMu.Lock()
4113+
defer s.closedMu.Unlock()
41134114
return s.closed.totalConns()
41144115
}
41154116

41164117
func (s *Server) closedClients() []*closedClient {
4117-
s.mu.RLock()
4118-
defer s.mu.RUnlock()
4118+
s.closedMu.Lock()
4119+
defer s.closedMu.Unlock()
41194120
return s.closed.closedClients()
41204121
}
41214122

0 commit comments

Comments
 (0)