Compare commits

..
Author SHA1 Message Date
binwiederhier 4f52663dda Fix exact count that caused the RO replicate to fall over... 2026-09-23 23:13:22 +00:00
5 changed files with 38 additions and 7 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
module heckel.io/ntfy/v2
go 1.26.0
go 1.25.8
require (
cloud.google.com/go/firestore v1.25.0 // indirect
@@ -16,7 +16,7 @@ require (
github.com/urfave/cli/v2 v2.27.7
golang.org/x/crypto v0.55.0
golang.org/x/oauth2 v0.36.0 // indirect
golang.org/x/sync v0.23.0
golang.org/x/sync v0.22.0
golang.org/x/term v0.45.0
golang.org/x/time v0.15.0
google.golang.org/api v0.294.0
+2 -2
View File
@@ -221,8 +221,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.23.0 h1:KameEIfc1IkluZyXWLn39Wd4tURc6GbCiISGiZm2bQk=
golang.org/x/sync v0.23.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+2 -1
View File
@@ -300,7 +300,8 @@ func (c *Cache) MarkPublished(m *model.Message) error {
return err
}
// MessagesCount returns the total number of messages in the cache
// MessagesCount returns the total number of messages in the cache. On Postgres, this is the
// planner's estimate once the table has been analyzed, not an exact count.
func (c *Cache) MessagesCount() (int, error) {
rows, err := c.db.ReadOnly().Query(c.queries.selectMessagesCount)
if err != nil {
+7 -2
View File
@@ -62,8 +62,13 @@ const (
ORDER BY time, id
`
postgresUpdateMessagePublishedQuery = `UPDATE message SET published = TRUE WHERE mid = $1`
postgresSelectMessagesCountQuery = `SELECT COUNT(*) FROM message`
postgresSelectTopicsQuery = `SELECT topic FROM message GROUP BY topic`
// Planner estimate, since a COUNT(*) scans the whole table; reltuples is -1 if never analyzed
postgresSelectMessagesCountQuery = `
SELECT CASE WHEN reltuples < 0 THEN (SELECT COUNT(*) FROM message) ELSE reltuples::BIGINT END
FROM pg_class
WHERE oid = 'message'::regclass
`
postgresSelectTopicsQuery = `SELECT topic FROM message GROUP BY topic`
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)`
+25
View File
@@ -74,3 +74,28 @@ func TestPostgresStore_Migration_From14(t *testing.T) {
require.Nil(t, err)
require.Equal(t, dbtest.PostgresSchema(t, freshDB), dbtest.PostgresSchema(t, testDB))
}
func TestPostgresStore_MessagesCount_UsesPlannerEstimate(t *testing.T) {
// The manager calls MessagesCount every minute for a metric; a COUNT(*) scans the whole
// table on every call, so once the table has been analyzed, the planner's estimate is used
testDB := dbtest.CreateTestPostgres(t)
store, err := message.NewPostgresStore(testDB, 0, 0)
require.Nil(t, err)
for i := 0; i < 10; i++ {
require.Nil(t, store.AddMessage(model.NewDefaultMessage("mytopic", "some message")))
}
// Never analyzed: falls back to an exact count
count, err := store.MessagesCount()
require.Nil(t, err)
require.Equal(t, 10, count)
// Analyzed, then rows deleted: the estimate lags until the next (auto)analyze
_, err = testDB.Exec(`ANALYZE message`)
require.Nil(t, err)
_, err = testDB.Exec(`DELETE FROM message WHERE id IN (SELECT id FROM message LIMIT 4)`)
require.Nil(t, err)
count, err = store.MessagesCount()
require.Nil(t, err)
require.Equal(t, 10, count)
}