Sfoglia il codice sorgente

perf(storage): replace correlated subqueries in CategoriesWithFeedCount

The old query ran two correlated subqueries per category row: one to
count feeds and one to count unread entries. For N categories this meant
2xN subquery executions.

This commit replaces them with two pre-aggregated subqueries joined once to
categories.

The change was validated against my live database with 5 categories, ~500 feeds
and 12k unreads:

|                     | Old (correlated)    | New (pre-aggregated)   |
|---------------------|---------------------|------------------------|
| Execution time      | 3.216 ms            | 3.123 ms               |
| Planning time       | 0.732 ms            | 0.778 ms               |
| Buffer hits         | 3,141               | 540                    |
| Index searches      | 1,237               | 1                      |
| Seq scans on feeds  | 10 (2 per category) | 2 (once each subquery) |
| Subquery executions | 10 (2x5 categories) | 2 (fixed)              |
jvoisin 1 mese fa
parent
commit
5e766332be
1 ha cambiato i file con 17 aggiunte e 7 eliminazioni
  1. 17 7
      internal/storage/category.go

+ 17 - 7
internal/storage/category.go

@@ -117,14 +117,24 @@ func (s *Storage) CategoriesWithFeedCount(userID int64, sortOrder string) (model
 			c.user_id,
 			c.title,
 			c.hide_globally,
-			(SELECT count(*) FROM feeds WHERE feeds.category_id=c.id) AS count,
-			(SELECT count(*)
-			   FROM feeds
-			     JOIN entries ON (feeds.id = entries.feed_id)
-			   WHERE feeds.category_id = c.id AND entries.status = $1) AS count_unread
+			coalesce(fc.feed_count, 0),
+			coalesce(uc.unread_count, 0)
 		FROM categories c
+		LEFT JOIN (
+			SELECT category_id, count(*) AS feed_count
+			FROM feeds
+			WHERE user_id = $2
+			GROUP BY category_id
+		) fc ON fc.category_id = c.id
+		LEFT JOIN (
+			SELECT f.category_id, count(*) AS unread_count
+			FROM entries e
+			INNER JOIN feeds f ON f.id = e.feed_id
+			WHERE e.user_id = $2 AND e.status = $1
+			GROUP BY f.category_id
+		) uc ON uc.category_id = c.id
 		WHERE
-			user_id=$2
+			c.user_id=$2
 	`
 
 	if sortOrder == "alphabetical" {
@@ -135,7 +145,7 @@ func (s *Storage) CategoriesWithFeedCount(userID int64, sortOrder string) (model
 	} else {
 		query += `
 			ORDER BY
-				count_unread DESC,
+				coalesce(uc.unread_count, 0) DESC,
 				c.title ASC
 		`
 	}