WIP: Shorter lock, for #338
This commit is contained in:
parent
4e29216b5f
commit
85f2252a77
3 changed files with 41 additions and 27 deletions
|
@ -82,7 +82,7 @@ const (
|
||||||
`
|
`
|
||||||
updateMessagePublishedQuery = `UPDATE messages SET published = 1 WHERE mid = ?`
|
updateMessagePublishedQuery = `UPDATE messages SET published = 1 WHERE mid = ?`
|
||||||
selectMessagesCountQuery = `SELECT COUNT(*) FROM messages`
|
selectMessagesCountQuery = `SELECT COUNT(*) FROM messages`
|
||||||
selectMessageCountForTopicQuery = `SELECT COUNT(*) FROM messages WHERE topic = ?`
|
selectMessageCountPerTopicQuery = `SELECT topic, COUNT(*) FROM messages GROUP BY topic`
|
||||||
selectTopicsQuery = `SELECT topic FROM messages GROUP BY topic`
|
selectTopicsQuery = `SELECT topic FROM messages GROUP BY topic`
|
||||||
selectAttachmentsSizeQuery = `SELECT IFNULL(SUM(attachment_size), 0) FROM messages WHERE sender = ? AND attachment_expires >= ?`
|
selectAttachmentsSizeQuery = `SELECT IFNULL(SUM(attachment_size), 0) FROM messages WHERE sender = ? AND attachment_expires >= ?`
|
||||||
selectAttachmentsExpiredQuery = `SELECT mid FROM messages WHERE attachment_expires > 0 AND attachment_expires < ?`
|
selectAttachmentsExpiredQuery = `SELECT mid FROM messages WHERE attachment_expires > 0 AND attachment_expires < ?`
|
||||||
|
@ -332,22 +332,24 @@ func (c *messageCache) MarkPublished(m *message) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *messageCache) MessageCount(topic string) (int, error) {
|
func (c *messageCache) MessageCounts() (map[string]int, error) {
|
||||||
rows, err := c.db.Query(selectMessageCountForTopicQuery, topic)
|
rows, err := c.db.Query(selectMessageCountPerTopicQuery)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
var topic string
|
||||||
var count int
|
var count int
|
||||||
if !rows.Next() {
|
counts := make(map[string]int)
|
||||||
return 0, errors.New("no rows found")
|
for rows.Next() {
|
||||||
}
|
if err := rows.Scan(&topic, &count); err != nil {
|
||||||
if err := rows.Scan(&count); err != nil {
|
return nil, err
|
||||||
return 0, err
|
|
||||||
} else if err := rows.Err(); err != nil {
|
} else if err := rows.Err(); err != nil {
|
||||||
return 0, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return count, nil
|
counts[topic] = count
|
||||||
|
}
|
||||||
|
return counts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *messageCache) Topics() (map[string]*topic, error) {
|
func (c *messageCache) Topics() (map[string]*topic, error) {
|
||||||
|
|
|
@ -34,7 +34,7 @@ func testCacheMessages(t *testing.T, c *messageCache) {
|
||||||
require.Equal(t, errUnexpectedMessageType, c.AddMessage(newOpenMessage("example"))) // These should not be added!
|
require.Equal(t, errUnexpectedMessageType, c.AddMessage(newOpenMessage("example"))) // These should not be added!
|
||||||
|
|
||||||
// mytopic: count
|
// mytopic: count
|
||||||
count, err := c.MessageCount("mytopic")
|
count, err := c.MessageCounts("mytopic")
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
require.Equal(t, 2, count)
|
require.Equal(t, 2, count)
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ func testCacheMessages(t *testing.T, c *messageCache) {
|
||||||
require.Equal(t, "my other message", messages[0].Message)
|
require.Equal(t, "my other message", messages[0].Message)
|
||||||
|
|
||||||
// example: count
|
// example: count
|
||||||
count, err = c.MessageCount("example")
|
count, err = c.MessageCounts("example")
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
require.Equal(t, 1, count)
|
require.Equal(t, 1, count)
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ func testCacheMessages(t *testing.T, c *messageCache) {
|
||||||
require.Equal(t, "my example message", messages[0].Message)
|
require.Equal(t, "my example message", messages[0].Message)
|
||||||
|
|
||||||
// non-existing: count
|
// non-existing: count
|
||||||
count, err = c.MessageCount("doesnotexist")
|
count, err = c.MessageCounts("doesnotexist")
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
require.Equal(t, 0, count)
|
require.Equal(t, 0, count)
|
||||||
|
|
||||||
|
@ -255,11 +255,11 @@ func testCachePrune(t *testing.T, c *messageCache) {
|
||||||
require.Nil(t, c.AddMessage(m3))
|
require.Nil(t, c.AddMessage(m3))
|
||||||
require.Nil(t, c.Prune(time.Unix(2, 0)))
|
require.Nil(t, c.Prune(time.Unix(2, 0)))
|
||||||
|
|
||||||
count, err := c.MessageCount("mytopic")
|
count, err := c.MessageCounts("mytopic")
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
require.Equal(t, 1, count)
|
require.Equal(t, 1, count)
|
||||||
|
|
||||||
count, err = c.MessageCount("another_topic")
|
count, err = c.MessageCounts("another_topic")
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
require.Equal(t, 0, count)
|
require.Equal(t, 0, count)
|
||||||
|
|
||||||
|
|
|
@ -1080,10 +1080,13 @@ func (s *Server) topicsFromIDs(ids ...string) ([]*topic, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) updateStatsAndPrune() {
|
func (s *Server) updateStatsAndPrune() {
|
||||||
s.mu.Lock()
|
log.Debug("Manager: Running cleanup")
|
||||||
defer s.mu.Unlock()
|
|
||||||
|
// WARNING: Make sure to only selectively lock with the mutex, and be aware that this
|
||||||
|
// there is no mutex for the entire function.
|
||||||
|
|
||||||
// Expire visitors from rate visitors map
|
// Expire visitors from rate visitors map
|
||||||
|
s.mu.Lock()
|
||||||
staleVisitors := 0
|
staleVisitors := 0
|
||||||
for ip, v := range s.visitors {
|
for ip, v := range s.visitors {
|
||||||
if v.Stale() {
|
if v.Stale() {
|
||||||
|
@ -1092,6 +1095,7 @@ func (s *Server) updateStatsAndPrune() {
|
||||||
staleVisitors++
|
staleVisitors++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
s.mu.Unlock()
|
||||||
log.Debug("Manager: Deleted %d stale visitor(s)", staleVisitors)
|
log.Debug("Manager: Deleted %d stale visitor(s)", staleVisitors)
|
||||||
|
|
||||||
// Delete expired attachments
|
// Delete expired attachments
|
||||||
|
@ -1116,22 +1120,30 @@ func (s *Server) updateStatsAndPrune() {
|
||||||
log.Warn("Manager: Error pruning cache: %s", err.Error())
|
log.Warn("Manager: Error pruning cache: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Message count per topic
|
||||||
|
var messages int
|
||||||
|
messageCounts, err := s.messageCache.MessageCounts()
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Manager: Cannot get message counts: %s", err.Error())
|
||||||
|
messageCounts = make(map[string]int) // Empty, so we can continue
|
||||||
|
}
|
||||||
|
for _, count := range messageCounts {
|
||||||
|
messages += count
|
||||||
|
}
|
||||||
|
|
||||||
// Prune old topics, remove subscriptions without subscribers
|
// Prune old topics, remove subscriptions without subscribers
|
||||||
var subscribers, messages int
|
s.mu.Lock()
|
||||||
|
var subscribers int
|
||||||
for _, t := range s.topics {
|
for _, t := range s.topics {
|
||||||
subs := t.Subscribers()
|
subs := t.Subscribers()
|
||||||
msgs, err := s.messageCache.MessageCount(t.ID)
|
msgs, exists := messageCounts[t.ID]
|
||||||
if err != nil {
|
if subs == 0 && (!exists || msgs == 0) {
|
||||||
log.Warn("Manager: Cannot get stats for topic %s: %s", t.ID, err.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if msgs == 0 && subs == 0 {
|
|
||||||
delete(s.topics, t.ID)
|
delete(s.topics, t.ID)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
subscribers += subs
|
subscribers += subs
|
||||||
messages += msgs
|
|
||||||
}
|
}
|
||||||
|
s.mu.Unlock()
|
||||||
|
|
||||||
// Mail stats
|
// Mail stats
|
||||||
var receivedMailTotal, receivedMailSuccess, receivedMailFailure int64
|
var receivedMailTotal, receivedMailSuccess, receivedMailFailure int64
|
||||||
|
|
Loading…
Reference in a new issue