Compare commits

..
Author SHA1 Message Date
multipleof4 e2f4fc7469 Docs: Markdown is no longer web app only
Android renders Markdown since 1.17.8 (#310).
2026-09-25 09:34:04 -07:00
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 36 additions and 5 deletions
+1 -1
View File
@@ -37,7 +37,7 @@ jobs:
cache: 'npm' cache: 'npm'
cache-dependency-path: './web/package-lock.json' cache-dependency-path: './web/package-lock.json'
- name: Docker login - name: Docker login
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 uses: docker/login-action@371161bbe7024a29a25c5e19bfcbc0804fe9ad2c # v4.5.2
with: with:
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
password: ${{ secrets.DOCKER_HUB_TOKEN }} password: ${{ secrets.DOCKER_HUB_TOKEN }}
+1 -1
View File
@@ -666,7 +666,7 @@ them with a comma, e.g. `tag1,tag2,tag3`.
_Supported on:_ :material-android: :material-firefox: _Supported on:_ :material-android: :material-firefox:
You can format messages using [Markdown](https://www.markdownguide.org/basic-syntax/) 🤩. That means you can use You can format messages using [Markdown](https://www.markdownguide.org/basic-syntax/) 🤩. That means you can use
**bold text**, *italicized text*, links, images, and more. Supported Markdown features (web app only for now): **bold text**, *italicized text*, links, images, and more. Supported Markdown features:
- [Emphasis](https://www.markdownguide.org/basic-syntax/#emphasis) such as **bold** (`**bold**`), *italics* (`*italics*`) - [Emphasis](https://www.markdownguide.org/basic-syntax/#emphasis) such as **bold** (`**bold**`), *italics* (`*italics*`)
- [Links](https://www.markdownguide.org/basic-syntax/#links) (`[some tool](https://ntfy.sh)`) - [Links](https://www.markdownguide.org/basic-syntax/#links) (`[some tool](https://ntfy.sh)`)
+2 -1
View File
@@ -300,7 +300,8 @@ func (c *Cache) MarkPublished(m *model.Message) error {
return err 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) { func (c *Cache) MessagesCount() (int, error) {
rows, err := c.db.ReadOnly().Query(c.queries.selectMessagesCount) rows, err := c.db.ReadOnly().Query(c.queries.selectMessagesCount)
if err != nil { if err != nil {
+7 -2
View File
@@ -62,8 +62,13 @@ const (
ORDER BY time, id ORDER BY time, id
` `
postgresUpdateMessagePublishedQuery = `UPDATE message SET published = TRUE WHERE mid = $1` postgresUpdateMessagePublishedQuery = `UPDATE message SET published = TRUE WHERE mid = $1`
postgresSelectMessagesCountQuery = `SELECT COUNT(*) FROM message` // Planner estimate, since a COUNT(*) scans the whole table; reltuples is -1 if never analyzed
postgresSelectTopicsQuery = `SELECT topic FROM message GROUP BY topic` 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)` 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)` 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.Nil(t, err)
require.Equal(t, dbtest.PostgresSchema(t, freshDB), dbtest.PostgresSchema(t, testDB)) 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)
}