Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions locksordering.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,15 @@ mirrors/sources, info requests, etc.) are sequentially consistent and may observ
an inflight batch. It must be acquired before the stream lock, never while holding it.

isolateMu -> stream

The "eventIdsMu" lock protects the server's event ID generator. It may be acquired
while holding the Server lock, but the Server lock must not be acquired while
holding eventIdsMu.

Server -> eventIdsMu

The "closedMu" lock protects the server's closed connection ring. It may be
acquired while holding the Server lock, but the Server lock must not be acquired
while holding closedMu.

Server -> closedMu
6 changes: 4 additions & 2 deletions server/accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -1068,6 +1068,7 @@ func (a *Account) addClient(c *client) int {
} else if c.kind == LEAF {
a.nleafs++
}
isGlobal := a.Name == globalAccountName
a.mu.Unlock()

// If we added a new leaf use the list lock and add it to the list.
Expand All @@ -1077,7 +1078,7 @@ func (a *Account) addClient(c *client) int {
a.lmu.Unlock()
}

if c != nil && c.srv != nil {
if !isGlobal && c != nil && c.srv != nil {
c.srv.accConnsUpdate(a)
}

Expand Down Expand Up @@ -1162,13 +1163,14 @@ func (a *Account) removeClient(c *client) int {
}
}
}
isGlobal := a.Name == globalAccountName
a.mu.Unlock()

if c.kind == LEAF {
a.removeLeafNode(c)
}

if c != nil && c.srv != nil {
if !isGlobal && c != nil && c.srv != nil {
c.srv.accConnsUpdate(a)
}

Expand Down
7 changes: 5 additions & 2 deletions server/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -876,8 +876,11 @@ func (c *client) registerWithAccount(acc *Account) error {
return ErrBadAccount
}
// If we were previously registered, usually to $G, do accounting here to remove.
if c.acc != nil {
if prev := c.acc.removeClient(c); prev == 1 && c.srv != nil {
c.mu.Lock()
prevAcc := c.acc
c.mu.Unlock()
if prevAcc != nil {
if prev := prevAcc.removeClient(c); prev == 1 && c.srv != nil {
c.srv.decActiveAccounts()
}
}
Expand Down
62 changes: 34 additions & 28 deletions server/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -2561,21 +2561,23 @@ func (s *Server) accConnsUpdate(a *Account) {
s.sendAccConnsUpdate(a, fmt.Sprintf(accConnsEventSubjOld, a.Name), fmt.Sprintf(accConnsEventSubjNew, a.Name))
}

// server lock should be held
func (s *Server) nextEventID() string {
return s.eventIds.Next()
s.eventIdsMu.Lock()
id := s.eventIds.Next()
s.eventIdsMu.Unlock()
return id
}

// accountConnectEvent will send an account client connect event if there is interest.
// This is a billing event.
func (s *Server) accountConnectEvent(c *client) {
s.mu.Lock()
if !s.eventsEnabled() {
s.mu.Unlock()
s.mu.RLock()
eventsEnabled := s.eventsEnabled()
s.mu.RUnlock()
if !eventsEnabled {
return
}
eid := s.nextEventID()
s.mu.Unlock()

c.mu.Lock()
if c.acc == nil {
Expand Down Expand Up @@ -2616,13 +2618,13 @@ func (s *Server) accountConnectEvent(c *client) {
// accountDisconnectEvent will send an account client disconnect event if there is interest.
// This is a billing event.
func (s *Server) accountDisconnectEvent(c *client, now time.Time, reason string) {
s.mu.Lock()
if !s.eventsEnabled() {
s.mu.Unlock()
s.mu.RLock()
eventsEnabled := s.eventsEnabled()
s.mu.RUnlock()
if !eventsEnabled {
return
}
eid := s.nextEventID()
s.mu.Unlock()

c.mu.Lock()

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

// This is the system level event sent to the system account for operators.
func (s *Server) sendAuthErrorEvent(c *client, reason string) {
s.mu.Lock()
if !s.eventsEnabled() {
s.mu.Unlock()
s.mu.RLock()
eventsEnabled := s.eventsEnabled()
s.mu.RUnlock()
if !eventsEnabled {
return
}
eid := s.nextEventID()
s.mu.Unlock()

now := time.Now().UTC()
c.mu.Lock()
Expand Down Expand Up @@ -2740,13 +2742,13 @@ func (s *Server) sendAccountAuthErrorEvent(c *client, acc *Account, reason strin
if acc == nil {
return
}
s.mu.Lock()
if !s.eventsEnabled() {
s.mu.Unlock()
s.mu.RLock()
eventsEnabled := s.eventsEnabled()
s.mu.RUnlock()
if !eventsEnabled {
return
}
eid := s.nextEventID()
s.mu.Unlock()

now := time.Now().UTC()
c.mu.Lock()
Expand Down Expand Up @@ -3314,9 +3316,11 @@ func (s *Server) wrapChk(f func()) func() {
// sendOCSPPeerRejectEvent sends a system level event to system account when a peer connection is
// rejected due to OCSP invalid status of its trust chain(s).
func (s *Server) sendOCSPPeerRejectEvent(kind string, peer *x509.Certificate, reason string) {
s.mu.Lock()
defer s.mu.Unlock()
if !s.eventsEnabled() {
s.mu.RLock()
eventsEnabled := s.eventsEnabled()
serverID := s.info.ID
s.mu.RUnlock()
if !eventsEnabled {
return
}
if peer == nil {
Expand All @@ -3340,16 +3344,18 @@ func (s *Server) sendOCSPPeerRejectEvent(kind string, peer *x509.Certificate, re
},
Reason: reason,
}
subj := fmt.Sprintf(ocspPeerRejectEventSubj, s.info.ID)
s.sendInternalMsg(subj, _EMPTY_, &m.Server, &m)
subj := fmt.Sprintf(ocspPeerRejectEventSubj, serverID)
s.sendInternalMsgLocked(subj, _EMPTY_, &m.Server, &m)
}

// sendOCSPPeerChainlinkInvalidEvent sends a system level event to system account when a link in a peer's trust chain
// is OCSP invalid.
func (s *Server) sendOCSPPeerChainlinkInvalidEvent(peer *x509.Certificate, link *x509.Certificate, reason string) {
s.mu.Lock()
defer s.mu.Unlock()
if !s.eventsEnabled() {
s.mu.RLock()
eventsEnabled := s.eventsEnabled()
serverID := s.info.ID
s.mu.RUnlock()
if !eventsEnabled {
return
}
if peer == nil || link == nil {
Expand Down Expand Up @@ -3378,6 +3384,6 @@ func (s *Server) sendOCSPPeerChainlinkInvalidEvent(peer *x509.Certificate, link
},
Reason: reason,
}
subj := fmt.Sprintf(ocspPeerChainlinkInvalidEventSubj, s.info.ID)
s.sendInternalMsg(subj, _EMPTY_, &m.Server, &m)
subj := fmt.Sprintf(ocspPeerChainlinkInvalidEventSubj, serverID)
s.sendInternalMsgLocked(subj, _EMPTY_, &m.Server, &m)
}
18 changes: 12 additions & 6 deletions server/monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,8 @@ func (s *Server) Connz(opts *ConnzOptions) (*Connz, error) {

// Open clients
var openClients []*client
// Selected client
var cidClient *client
// Hold for closed clients if requested.
var closedClients []*closedClient

Expand All @@ -298,7 +300,7 @@ func (s *Server) Connz(opts *ConnzOptions) (*Connz, error) {
a.mu.RUnlock()
}

// Walk the open client list with server lock held.
// Snapshot server connection state.
s.mu.RLock()
// Default to all client unless filled in above.
if clist == nil {
Expand All @@ -309,18 +311,23 @@ func (s *Server) Connz(opts *ConnzOptions) (*Connz, error) {

// copy the server id for monitoring
c.ID = s.info.ID
// select client by CID
if cid > 0 {
cidClient = s.clients[cid]
}
s.mu.RUnlock()

// Number of total clients. The resulting ConnInfo array
// may be smaller if pagination is used.
switch state {
case ConnOpen:
c.Total = len(clist)
case ConnClosed:
closedClients = s.closed.closedClients()
closedClients = s.closedClients()
c.Total = len(closedClients)
case ConnAll:
c.Total = len(clist)
closedClients = s.closed.closedClients()
closedClients = s.closedClients()
c.Total += len(closedClients)
}

Expand Down Expand Up @@ -365,8 +372,8 @@ func (s *Server) Connz(opts *ConnzOptions) (*Connz, error) {
// Let's first check if user also selects on ConnOpen or ConnAll
// and look for opened connections.
if state == ConnOpen || state == ConnAll {
if client := s.clients[cid]; client != nil {
openClients = append(openClients, client)
if cidClient != nil {
openClients = append(openClients, cidClient)
closedClients = nil
}
}
Expand Down Expand Up @@ -412,7 +419,6 @@ func (s *Server) Connz(opts *ConnzOptions) (*Connz, error) {
}
}
}
s.mu.RUnlock()

// Filter by subject now if needed. We do this outside of server lock.
if filter != _EMPTY_ {
Expand Down
12 changes: 4 additions & 8 deletions server/monitor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1205,14 +1205,12 @@ func TestMonitorConnzSortedByStopTimeClosedConn(t *testing.T) {
checkClosedConns(t, s, 4, time.Second)

// Now adjust the Stop times for these with some random values.
s.mu.Lock()
now := time.Now().UTC()
ccs := s.closed.closedClients()
ccs := s.closedClients()
for _, cc := range ccs {
newStop := now.Add(time.Duration(rand.Int()%120) * -time.Minute)
cc.Stop = &newStop
}
s.mu.Unlock()

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

// Now adjust the Reasons for these with some random values.
s.mu.Lock()
ccs := s.closed.closedClients()
ccs := s.closedClients()
max := int(ServerShutdown)
for _, cc := range ccs {
cc.Reason = ClosedState(rand.Int() % max).String()
}
s.mu.Unlock()

url = fmt.Sprintf("http://127.0.0.1:%d/", s.MonitorAddr().Port)
for mode := 0; mode < 2; mode++ {
Expand Down Expand Up @@ -7008,9 +7004,9 @@ func TestConnzClosedSubsDetailNoSharedMutation(t *testing.T) {
cc.Cid = 1
cc.subs = []SubDetail{{Subject: "foo.bar"}}
cc.NumSubs = 1
s.mu.Lock()
s.closedMu.Lock()
s.closed.append(cc)
s.mu.Unlock()
s.closedMu.Unlock()

// Concurrently request closed connections with subscription detail.
var wg sync.WaitGroup
Expand Down
20 changes: 11 additions & 9 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ type Server struct {
users map[string]*User
nkeys map[string]*NkeyUser
totalClients uint64
closedMu sync.Mutex // Protects the closedRingBuffer
closed *closedRingBuffer
done chan bool
start time.Time
Expand Down Expand Up @@ -309,7 +310,8 @@ type Server struct {
}

// For eventIDs
eventIds *nuid.NUID
eventIdsMu sync.Mutex
eventIds *nuid.NUID

// Websocket structure
websocket srvWebsocket
Expand Down Expand Up @@ -3607,11 +3609,11 @@ func (s *Server) saveClosedClient(c *client, nc net.Conn, subs map[string]*subsc
c.mu.Unlock()

// Place in the ring buffer
s.mu.Lock()
s.closedMu.Lock()
if s.closed != nil {
s.closed.append(cc)
}
s.mu.Unlock()
s.closedMu.Unlock()
}

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

func (s *Server) numClosedConns() int {
s.mu.RLock()
defer s.mu.RUnlock()
s.closedMu.Lock()
defer s.closedMu.Unlock()
return s.closed.len()
}

func (s *Server) totalClosedConns() uint64 {
s.mu.RLock()
defer s.mu.RUnlock()
s.closedMu.Lock()
defer s.closedMu.Unlock()
return s.closed.totalConns()
}

func (s *Server) closedClients() []*closedClient {
s.mu.RLock()
defer s.mu.RUnlock()
s.closedMu.Lock()
defer s.closedMu.Unlock()
return s.closed.closedClients()
}

Expand Down
Loading