mirror of
https://github.com/binwiederhier/ntfy.git
synced 2026-05-15 07:35:49 -06:00
Fixes
This commit is contained in:
parent
e55d1cee6b
commit
071543efda
15 changed files with 102 additions and 141 deletions
|
|
@ -24,7 +24,6 @@ var errNoRows = errors.New("no rows found")
|
|||
// queries holds the database-specific SQL queries
|
||||
type queries struct {
|
||||
insertMessage string
|
||||
deleteMessage string
|
||||
selectScheduledMessageIDsBySeqID string
|
||||
deleteScheduledBySequenceID string
|
||||
updateMessagesForTopicExpiry string
|
||||
|
|
@ -35,12 +34,11 @@ type queries struct {
|
|||
selectMessagesSinceIDScheduled string
|
||||
selectMessagesLatest string
|
||||
selectMessagesDue string
|
||||
selectMessagesExpired string
|
||||
deleteExpiredMessages string
|
||||
updateMessagePublished string
|
||||
selectMessagesCount string
|
||||
selectTopics string
|
||||
updateAttachmentDeleted string
|
||||
selectAttachmentsExpired string
|
||||
markExpiredAttachmentsDeleted string
|
||||
selectAttachmentsSizeBySender string
|
||||
selectAttachmentsSizeByUserID string
|
||||
selectAttachmentsWithSizes string
|
||||
|
|
@ -246,14 +244,16 @@ func (c *Cache) MessagesDue() ([]*model.Message, error) {
|
|||
return readMessages(rows)
|
||||
}
|
||||
|
||||
// MessagesExpired returns a list of message IDs that have expired and should be deleted
|
||||
func (c *Cache) MessagesExpired() ([]string, error) {
|
||||
rows, err := c.db.Query(c.queries.selectMessagesExpired, time.Now().Unix())
|
||||
// DeleteExpiredMessages deletes up to `limit` expired messages in a single query
|
||||
// and returns the number of deleted rows.
|
||||
func (c *Cache) DeleteExpiredMessages(limit int) (int64, error) {
|
||||
c.maybeLock()
|
||||
defer c.maybeUnlock()
|
||||
result, err := c.db.Exec(c.queries.deleteExpiredMessages, time.Now().Unix(), limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return 0, err
|
||||
}
|
||||
defer rows.Close()
|
||||
return readStrings(rows)
|
||||
return result.RowsAffected()
|
||||
}
|
||||
|
||||
// Message returns the message with the given ID, or ErrMessageNotFound if not found
|
||||
|
|
@ -312,20 +312,6 @@ func (c *Cache) Topics() ([]string, error) {
|
|||
return readStrings(rows)
|
||||
}
|
||||
|
||||
// DeleteMessages deletes the messages with the given IDs
|
||||
func (c *Cache) DeleteMessages(ids ...string) error {
|
||||
c.maybeLock()
|
||||
defer c.maybeUnlock()
|
||||
return db.ExecTx(c.db, func(tx *sql.Tx) error {
|
||||
for _, id := range ids {
|
||||
if _, err := tx.Exec(c.queries.deleteMessage, id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteScheduledBySequenceID deletes unpublished (scheduled) messages with the given topic and sequence ID.
|
||||
// It returns the message IDs of the deleted messages, which can be used to clean up attachment files.
|
||||
func (c *Cache) DeleteScheduledBySequenceID(topic, sequenceID string) ([]string, error) {
|
||||
|
|
@ -363,28 +349,16 @@ func (c *Cache) ExpireMessages(topics ...string) error {
|
|||
})
|
||||
}
|
||||
|
||||
// AttachmentsExpired returns message IDs with expired attachments that have not been deleted
|
||||
func (c *Cache) AttachmentsExpired() ([]string, error) {
|
||||
rows, err := c.db.Query(c.queries.selectAttachmentsExpired, time.Now().Unix())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
return readStrings(rows)
|
||||
}
|
||||
|
||||
// MarkAttachmentsDeleted marks the attachments for the given message IDs as deleted
|
||||
func (c *Cache) MarkAttachmentsDeleted(ids ...string) error {
|
||||
// MarkExpiredAttachmentsDeleted marks up to `limit` expired attachments as deleted in a single
|
||||
// query and returns the number of updated rows.
|
||||
func (c *Cache) MarkExpiredAttachmentsDeleted(limit int) (int64, error) {
|
||||
c.maybeLock()
|
||||
defer c.maybeUnlock()
|
||||
return db.ExecTx(c.db, func(tx *sql.Tx) error {
|
||||
for _, id := range ids {
|
||||
if _, err := tx.Exec(c.queries.updateAttachmentDeleted, id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
result, err := c.db.Exec(c.queries.markExpiredAttachmentsDeleted, time.Now().Unix(), limit)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return result.RowsAffected()
|
||||
}
|
||||
|
||||
// AttachmentBytesUsedBySender returns the total size of active attachments sent by the given sender
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ const (
|
|||
INSERT INTO message (mid, sequence_id, time, event, expires, topic, message, title, priority, tags, click, icon, actions, attachment_name, attachment_type, attachment_size, attachment_expires, attachment_url, attachment_deleted, sender, user_id, content_type, encoding, published)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24)
|
||||
`
|
||||
postgresDeleteMessageQuery = `DELETE FROM message WHERE mid = $1`
|
||||
postgresSelectScheduledMessageIDsBySeqIDQuery = `SELECT mid FROM message WHERE topic = $1 AND sequence_id = $2 AND published = FALSE`
|
||||
postgresDeleteScheduledBySequenceIDQuery = `DELETE FROM message WHERE topic = $1 AND sequence_id = $2 AND published = FALSE`
|
||||
postgresUpdateMessagesForTopicExpiryQuery = `UPDATE message SET expires = $1 WHERE topic = $2`
|
||||
|
|
@ -61,13 +60,12 @@ const (
|
|||
WHERE time <= $1 AND published = FALSE
|
||||
ORDER BY time, id
|
||||
`
|
||||
postgresSelectMessagesExpiredQuery = `SELECT mid FROM message WHERE expires <= $1 AND published = TRUE`
|
||||
postgresUpdateMessagePublishedQuery = `UPDATE message SET published = TRUE WHERE mid = $1`
|
||||
postgresSelectMessagesCountQuery = `SELECT COUNT(*) FROM message`
|
||||
postgresSelectTopicsQuery = `SELECT topic FROM message GROUP BY topic`
|
||||
|
||||
postgresUpdateAttachmentDeletedQuery = `UPDATE message SET attachment_deleted = TRUE WHERE mid = $1`
|
||||
postgresSelectAttachmentsExpiredQuery = `SELECT mid FROM message WHERE attachment_expires > 0 AND attachment_expires <= $1 AND attachment_deleted = FALSE`
|
||||
postgresDeleteExpiredMessagesQuery = `DELETE FROM message WHERE mid IN (SELECT mid FROM message WHERE expires <= $1 AND published = TRUE LIMIT $2)`
|
||||
postgresMarkExpiredAttachmentsDeletedQuery = `UPDATE message SET attachment_deleted = TRUE WHERE mid IN (SELECT mid FROM message WHERE attachment_expires > 0 AND attachment_expires <= $1 AND attachment_deleted = FALSE LIMIT $2)`
|
||||
postgresSelectAttachmentsSizeBySenderQuery = `SELECT COALESCE(SUM(attachment_size), 0) FROM message WHERE user_id = '' AND sender = $1 AND attachment_expires >= $2`
|
||||
postgresSelectAttachmentsSizeByUserIDQuery = `SELECT COALESCE(SUM(attachment_size), 0) FROM message WHERE user_id = $1 AND attachment_expires >= $2`
|
||||
postgresSelectAttachmentsWithSizesQuery = `SELECT mid, attachment_size FROM message WHERE attachment_expires > $1 AND attachment_deleted = FALSE`
|
||||
|
|
@ -79,7 +77,6 @@ const (
|
|||
|
||||
var postgresQueries = queries{
|
||||
insertMessage: postgresInsertMessageQuery,
|
||||
deleteMessage: postgresDeleteMessageQuery,
|
||||
selectScheduledMessageIDsBySeqID: postgresSelectScheduledMessageIDsBySeqIDQuery,
|
||||
deleteScheduledBySequenceID: postgresDeleteScheduledBySequenceIDQuery,
|
||||
updateMessagesForTopicExpiry: postgresUpdateMessagesForTopicExpiryQuery,
|
||||
|
|
@ -90,12 +87,11 @@ var postgresQueries = queries{
|
|||
selectMessagesSinceIDScheduled: postgresSelectMessagesSinceIDIncludeScheduledQuery,
|
||||
selectMessagesLatest: postgresSelectMessagesLatestQuery,
|
||||
selectMessagesDue: postgresSelectMessagesDueQuery,
|
||||
selectMessagesExpired: postgresSelectMessagesExpiredQuery,
|
||||
deleteExpiredMessages: postgresDeleteExpiredMessagesQuery,
|
||||
updateMessagePublished: postgresUpdateMessagePublishedQuery,
|
||||
selectMessagesCount: postgresSelectMessagesCountQuery,
|
||||
selectTopics: postgresSelectTopicsQuery,
|
||||
updateAttachmentDeleted: postgresUpdateAttachmentDeletedQuery,
|
||||
selectAttachmentsExpired: postgresSelectAttachmentsExpiredQuery,
|
||||
markExpiredAttachmentsDeleted: postgresMarkExpiredAttachmentsDeletedQuery,
|
||||
selectAttachmentsSizeBySender: postgresSelectAttachmentsSizeBySenderQuery,
|
||||
selectAttachmentsSizeByUserID: postgresSelectAttachmentsSizeByUserIDQuery,
|
||||
selectAttachmentsWithSizes: postgresSelectAttachmentsWithSizesQuery,
|
||||
|
|
|
|||
|
|
@ -73,14 +73,14 @@ const (
|
|||
`
|
||||
)
|
||||
|
||||
var postgresMigrations = map[int]func(db *sql.DB) error{
|
||||
var postgresMigrations = map[int]func(d *sql.DB) error{
|
||||
14: postgresMigrateFrom14,
|
||||
}
|
||||
|
||||
func setupPostgres(sqlDB *sql.DB) error {
|
||||
func setupPostgres(d *sql.DB) error {
|
||||
var schemaVersion int
|
||||
if err := sqlDB.QueryRow(postgresSelectSchemaVersionQuery).Scan(&schemaVersion); err != nil {
|
||||
return setupNewPostgresDB(sqlDB)
|
||||
if err := d.QueryRow(postgresSelectSchemaVersionQuery).Scan(&schemaVersion); err != nil {
|
||||
return setupNewPostgresDB(d)
|
||||
} else if schemaVersion == postgresCurrentSchemaVersion {
|
||||
return nil
|
||||
} else if schemaVersion > postgresCurrentSchemaVersion {
|
||||
|
|
@ -90,16 +90,16 @@ func setupPostgres(sqlDB *sql.DB) error {
|
|||
fn, ok := postgresMigrations[i]
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot find migration step from schema version %d to %d", i, i+1)
|
||||
} else if err := fn(sqlDB); err != nil {
|
||||
} else if err := fn(d); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func postgresMigrateFrom14(sqlDB *sql.DB) error {
|
||||
func postgresMigrateFrom14(d *sql.DB) error {
|
||||
log.Tag(tagMessageCache).Info("Migrating message cache database schema: from 14 to 15")
|
||||
return db.ExecTx(sqlDB, func(tx *sql.Tx) error {
|
||||
return db.ExecTx(d, func(tx *sql.Tx) error {
|
||||
if _, err := tx.Exec(postgresMigrate14To15CreateIndexQuery); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ const (
|
|||
INSERT INTO messages (mid, sequence_id, time, event, expires, topic, message, title, priority, tags, click, icon, actions, attachment_name, attachment_type, attachment_size, attachment_expires, attachment_url, attachment_deleted, sender, user, content_type, encoding, published)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`
|
||||
sqliteDeleteMessageQuery = `DELETE FROM messages WHERE mid = ?`
|
||||
sqliteSelectScheduledMessageIDsBySeqIDQuery = `SELECT mid FROM messages WHERE topic = ? AND sequence_id = ? AND published = 0`
|
||||
sqliteDeleteScheduledBySequenceIDQuery = `DELETE FROM messages WHERE topic = ? AND sequence_id = ? AND published = 0`
|
||||
sqliteUpdateMessagesForTopicExpiryQuery = `UPDATE messages SET expires = ? WHERE topic = ?`
|
||||
|
|
@ -64,13 +63,12 @@ const (
|
|||
WHERE time <= ? AND published = 0
|
||||
ORDER BY time, id
|
||||
`
|
||||
sqliteSelectMessagesExpiredQuery = `SELECT mid FROM messages WHERE expires <= ? AND published = 1`
|
||||
sqliteUpdateMessagePublishedQuery = `UPDATE messages SET published = 1 WHERE mid = ?`
|
||||
sqliteSelectMessagesCountQuery = `SELECT COUNT(*) FROM messages`
|
||||
sqliteSelectTopicsQuery = `SELECT topic FROM messages GROUP BY topic`
|
||||
|
||||
sqliteUpdateAttachmentDeletedQuery = `UPDATE messages SET attachment_deleted = 1 WHERE mid = ?`
|
||||
sqliteSelectAttachmentsExpiredQuery = `SELECT mid FROM messages WHERE attachment_expires > 0 AND attachment_expires <= ? AND attachment_deleted = 0`
|
||||
sqliteDeleteExpiredMessagesQuery = `DELETE FROM messages WHERE mid IN (SELECT mid FROM messages WHERE expires <= ? AND published = 1 LIMIT ?)`
|
||||
sqliteMarkExpiredAttachmentsDeletedQuery = `UPDATE messages SET attachment_deleted = 1 WHERE mid IN (SELECT mid FROM messages WHERE attachment_expires > 0 AND attachment_expires <= ? AND attachment_deleted = 0 LIMIT ?)`
|
||||
sqliteSelectAttachmentsSizeBySenderQuery = `SELECT IFNULL(SUM(attachment_size), 0) FROM messages WHERE user = '' AND sender = ? AND attachment_expires >= ?`
|
||||
sqliteSelectAttachmentsSizeByUserIDQuery = `SELECT IFNULL(SUM(attachment_size), 0) FROM messages WHERE user = ? AND attachment_expires >= ?`
|
||||
sqliteSelectAttachmentsWithSizesQuery = `SELECT mid, attachment_size FROM messages WHERE attachment_expires > ? AND attachment_deleted = 0`
|
||||
|
|
@ -82,7 +80,6 @@ const (
|
|||
|
||||
var sqliteQueries = queries{
|
||||
insertMessage: sqliteInsertMessageQuery,
|
||||
deleteMessage: sqliteDeleteMessageQuery,
|
||||
selectScheduledMessageIDsBySeqID: sqliteSelectScheduledMessageIDsBySeqIDQuery,
|
||||
deleteScheduledBySequenceID: sqliteDeleteScheduledBySequenceIDQuery,
|
||||
updateMessagesForTopicExpiry: sqliteUpdateMessagesForTopicExpiryQuery,
|
||||
|
|
@ -93,12 +90,11 @@ var sqliteQueries = queries{
|
|||
selectMessagesSinceIDScheduled: sqliteSelectMessagesSinceIDIncludeScheduledQuery,
|
||||
selectMessagesLatest: sqliteSelectMessagesLatestQuery,
|
||||
selectMessagesDue: sqliteSelectMessagesDueQuery,
|
||||
selectMessagesExpired: sqliteSelectMessagesExpiredQuery,
|
||||
deleteExpiredMessages: sqliteDeleteExpiredMessagesQuery,
|
||||
updateMessagePublished: sqliteUpdateMessagePublishedQuery,
|
||||
selectMessagesCount: sqliteSelectMessagesCountQuery,
|
||||
selectTopics: sqliteSelectTopicsQuery,
|
||||
updateAttachmentDeleted: sqliteUpdateAttachmentDeletedQuery,
|
||||
selectAttachmentsExpired: sqliteSelectAttachmentsExpiredQuery,
|
||||
markExpiredAttachmentsDeleted: sqliteMarkExpiredAttachmentsDeletedQuery,
|
||||
selectAttachmentsSizeBySender: sqliteSelectAttachmentsSizeBySenderQuery,
|
||||
selectAttachmentsSizeByUserID: sqliteSelectAttachmentsSizeByUserIDQuery,
|
||||
selectAttachmentsWithSizes: sqliteSelectAttachmentsWithSizesQuery,
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ func TestSqliteStore_Migration_From9(t *testing.T) {
|
|||
require.True(t, rows.Next())
|
||||
var version int
|
||||
require.Nil(t, rows.Scan(&version))
|
||||
require.Equal(t, 14, version)
|
||||
require.Equal(t, 15, version)
|
||||
require.Nil(t, rows.Close())
|
||||
|
||||
messages, err := s.Messages("mytopic", model.SinceAllMessages, false)
|
||||
|
|
@ -287,6 +287,6 @@ func checkSqliteSchemaVersion(t *testing.T, filename string) {
|
|||
require.True(t, rows.Next())
|
||||
var schemaVersion int
|
||||
require.Nil(t, rows.Scan(&schemaVersion))
|
||||
require.Equal(t, 14, schemaVersion)
|
||||
require.Equal(t, 15, schemaVersion)
|
||||
require.Nil(t, rows.Close())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package message_test
|
|||
import (
|
||||
"net/netip"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
|
@ -274,9 +273,9 @@ func TestStore_Prune(t *testing.T) {
|
|||
require.Nil(t, err)
|
||||
require.Equal(t, 3, count)
|
||||
|
||||
expiredMessageIDs, err := s.MessagesExpired()
|
||||
deleted, err := s.DeleteExpiredMessages(10)
|
||||
require.Nil(t, err)
|
||||
require.Nil(t, s.DeleteMessages(expiredMessageIDs...))
|
||||
require.Equal(t, int64(2), deleted)
|
||||
|
||||
count, err = s.MessagesCount()
|
||||
require.Nil(t, err)
|
||||
|
|
@ -414,10 +413,9 @@ func TestStore_AttachmentsExpired(t *testing.T) {
|
|||
}
|
||||
require.Nil(t, s.AddMessage(m))
|
||||
|
||||
ids, err := s.AttachmentsExpired()
|
||||
count, err := s.MarkExpiredAttachmentsDeleted(10)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 1, len(ids))
|
||||
require.Equal(t, "m4", ids[0])
|
||||
require.Equal(t, int64(1), count)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -583,13 +581,9 @@ func TestStore_ExpireMessages(t *testing.T) {
|
|||
require.Nil(t, s.ExpireMessages("topic1"))
|
||||
|
||||
// topic1 messages should now be expired (expires set to past)
|
||||
expiredIDs, err := s.MessagesExpired()
|
||||
deleted, err := s.DeleteExpiredMessages(100)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 2, len(expiredIDs))
|
||||
sort.Strings(expiredIDs)
|
||||
expectedIDs := []string{m1.ID, m2.ID}
|
||||
sort.Strings(expectedIDs)
|
||||
require.Equal(t, expectedIDs, expiredIDs)
|
||||
require.Equal(t, int64(2), deleted)
|
||||
|
||||
// topic2 should be unaffected
|
||||
messages, err = s.Messages("topic2", model.SinceAllMessages, false)
|
||||
|
|
@ -629,27 +623,15 @@ func TestStore_MarkAttachmentsDeleted(t *testing.T) {
|
|||
}
|
||||
require.Nil(t, s.AddMessage(m2))
|
||||
|
||||
// Both should show as expired attachments needing cleanup
|
||||
ids, err := s.AttachmentsExpired()
|
||||
// Both should be marked as deleted in one batch
|
||||
count, err := s.MarkExpiredAttachmentsDeleted(10)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 2, len(ids))
|
||||
|
||||
// Mark msg1's attachment as deleted (file cleaned up)
|
||||
require.Nil(t, s.MarkAttachmentsDeleted("msg1"))
|
||||
|
||||
// Now only msg2 should show as needing cleanup
|
||||
ids, err = s.AttachmentsExpired()
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 1, len(ids))
|
||||
require.Equal(t, "msg2", ids[0])
|
||||
|
||||
// Mark msg2 too
|
||||
require.Nil(t, s.MarkAttachmentsDeleted("msg2"))
|
||||
require.Equal(t, int64(2), count)
|
||||
|
||||
// No more expired attachments to clean up
|
||||
ids, err = s.AttachmentsExpired()
|
||||
count, err = s.MarkExpiredAttachmentsDeleted(10)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 0, len(ids))
|
||||
require.Equal(t, int64(0), count)
|
||||
|
||||
// Messages themselves still exist
|
||||
messages, err := s.Messages("mytopic", model.SinceAllMessages, false)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue