nandi/gleanpublic Fork 0
f254c53
Commits
Clone
git clone https://git.rickub.com/nandi/glean.git
git clone ssh://git@rickub.com/nandi/glean.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Handle errors better in sync and cleanup stream handlerUnverified

Julien Robert committed 2026-04-23T12:06:40+02:00 Browse files
f254c53 parent: 926777b
modified go.mod +1 -1
@@ -11,6 +11,7 @@ require (
1111 github.com/prometheus/client_golang v1.19.1
1212 go.uber.org/atomic v1.11.0
1313 golang.org/x/net v0.53.0
14+ golang.org/x/sync v0.20.0
1415 gotest.tools/v3 v3.5.2
1516 )
1617
@@ -42,7 +43,6 @@ require (
4243 gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b // indirect
4344 gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 // indirect
4445 golang.org/x/crypto v0.50.0 // indirect
45- golang.org/x/sync v0.20.0 // indirect
4646 golang.org/x/sys v0.43.0 // indirect
4747 golang.org/x/text v0.36.0 // indirect
4848 golang.org/x/time v0.5.0 // indirect
@@ -11,6 +11,7 @@ require (
11 github.com/prometheus/client_golang v1.19.111 github.com/prometheus/client_golang v1.19.1
12 go.uber.org/atomic v1.11.012 go.uber.org/atomic v1.11.0
13 golang.org/x/net v0.53.013 golang.org/x/net v0.53.0
14+ golang.org/x/sync v0.20.0
14 gotest.tools/v3 v3.5.215 gotest.tools/v3 v3.5.2
15 )16 )
16 17
@@ -42,7 +43,6 @@ require (
42 gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b // indirect43 gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b // indirect
43 gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 // indirect44 gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 // indirect
44 golang.org/x/crypto v0.50.0 // indirect45 golang.org/x/crypto v0.50.0 // indirect
45- golang.org/x/sync v0.20.0 // indirect
46 golang.org/x/sys v0.43.0 // indirect46 golang.org/x/sys v0.43.0 // indirect
47 golang.org/x/text v0.36.0 // indirect47 golang.org/x/text v0.36.0 // indirect
48 golang.org/x/time v0.5.0 // indirect48 golang.org/x/time v0.5.0 // indirect
modified internal/atproto/stream_handler.go +19 -35
@@ -11,6 +11,17 @@ import (
1111 "pkg.rbrt.fr/glean/internal/db"
1212 )
1313
14+var sentinelErrors = []error{db.ErrDuplicateSubscription, db.ErrDuplicateLike}
15+
16+func isSentinel(err error) bool {
17+ for _, s := range sentinelErrors {
18+ if errors.Is(err, s) {
19+ return true
20+ }
21+ }
22+ return false
23+}
24+
1425 type StreamDBHandler struct {
1526 articles *db.DB
1627 users *db.DB
@@ -50,18 +61,9 @@ func (h *StreamDBHandler) handleSubscription(ctx context.Context, event *Event)
5061 return nil
5162 }
5263
53- existing, err := h.articles.GetSubscription(ctx, event.DID, rec.FeedURL)
54- if err == nil && existing != nil {
55- if !existing.URI.Valid || existing.URI.String == "" {
56- return h.articles.UpdateSubscriptionURI(ctx, event.DID, rec.FeedURL, event.URI, event.CID)
57- }
58- return nil
59- }
60-
61- f := &db.Feed{FeedURL: rec.FeedURL, Title: db.NullStr(rec.Title)}
62- _ = h.articles.UpsertFeed(ctx, f)
63- err = h.articles.CreateSubscription(ctx, event.DID, rec.FeedURL, rec.Title, rec.Category, event.URI, event.CID)
64- if errors.Is(err, db.ErrDuplicateSubscription) {
64+ _ = h.articles.UpsertFeed(ctx, &db.Feed{FeedURL: rec.FeedURL, Title: db.NullStr(rec.Title)})
65+ err := h.articles.CreateSubscription(ctx, event.DID, rec.FeedURL, rec.Title, rec.Category, event.URI, event.CID)
66+ if isSentinel(err) {
6567 return nil
6668 }
6769 return err
@@ -91,13 +93,8 @@ func (h *StreamDBHandler) handleLike(ctx context.Context, event *Event) error {
9193 return nil
9294 }
9395
94- exists, err := h.articles.HasLiked(ctx, event.DID, rec.FeedURL, rec.ArticleURL)
95- if err != nil || exists {
96- return nil
97- }
98-
9996 t, _ := time.Parse(time.RFC3339, rec.CreatedAt)
100- err = h.articles.CreateLike(ctx, &db.Like{
97+ err := h.articles.CreateLike(ctx, &db.Like{
10198 URI: event.URI,
10299 AuthorDID: event.DID,
103100 FeedURL: rec.FeedURL,
@@ -105,7 +102,7 @@ func (h *StreamDBHandler) handleLike(ctx context.Context, event *Event) error {
105102 CreatedAt: sql.NullTime{Time: t, Valid: true},
106103 CID: sql.NullString{String: event.CID, Valid: event.CID != ""},
107104 })
108- if errors.Is(err, db.ErrDuplicateLike) {
105+ if isSentinel(err) {
109106 return nil
110107 }
111108 return err
@@ -213,22 +210,9 @@ func (h *StreamDBHandler) handleSkyreaderSubscription(ctx context.Context, event
213210 return nil
214211 }
215212
216- existing, err := h.articles.GetSubscription(ctx, event.DID, rec.FeedURL)
217- if err == nil && existing != nil {
218- if !existing.URI.Valid || existing.URI.String == "" {
219- return h.articles.UpdateSubscriptionURI(ctx, event.DID, rec.FeedURL, event.URI, event.CID)
220- }
221- return nil
222- }
223-
224- f := &db.Feed{
225- FeedURL: rec.FeedURL,
226- Title: db.NullStr(rec.Title),
227- SiteURL: db.NullStr(rec.SiteURL),
228- }
229- _ = h.articles.UpsertFeed(ctx, f)
230- err = h.articles.CreateSubscription(ctx, event.DID, rec.FeedURL, rec.Title, rec.Category, event.URI, event.CID)
231- if errors.Is(err, db.ErrDuplicateSubscription) {
213+ _ = h.articles.UpsertFeed(ctx, &db.Feed{FeedURL: rec.FeedURL, Title: db.NullStr(rec.Title), SiteURL: db.NullStr(rec.SiteURL)})
214+ err := h.articles.CreateSubscription(ctx, event.DID, rec.FeedURL, rec.Title, "", event.URI, event.CID)
215+ if isSentinel(err) {
232216 return nil
233217 }
234218 return err
@@ -11,6 +11,17 @@ import (
11 "pkg.rbrt.fr/glean/internal/db"11 "pkg.rbrt.fr/glean/internal/db"
12 )12 )
13 13
14+var sentinelErrors = []error{db.ErrDuplicateSubscription, db.ErrDuplicateLike}
15+
16+func isSentinel(err error) bool {
17+ for _, s := range sentinelErrors {
18+ if errors.Is(err, s) {
19+ return true
20+ }
21+ }
22+ return false
23+}
24+
14 type StreamDBHandler struct {25 type StreamDBHandler struct {
15 articles *db.DB26 articles *db.DB
16 users *db.DB27 users *db.DB
@@ -50,18 +61,9 @@ func (h *StreamDBHandler) handleSubscription(ctx context.Context, event *Event)
50 return nil61 return nil
51 }62 }
52 63
53- existing, err := h.articles.GetSubscription(ctx, event.DID, rec.FeedURL)64+ _ = h.articles.UpsertFeed(ctx, &db.Feed{FeedURL: rec.FeedURL, Title: db.NullStr(rec.Title)})
54- if err == nil && existing != nil {65+ err := h.articles.CreateSubscription(ctx, event.DID, rec.FeedURL, rec.Title, rec.Category, event.URI, event.CID)
55- if !existing.URI.Valid || existing.URI.String == "" {66+ if isSentinel(err) {
56- return h.articles.UpdateSubscriptionURI(ctx, event.DID, rec.FeedURL, event.URI, event.CID)
57- }
58- return nil
59- }
60-
61- f := &db.Feed{FeedURL: rec.FeedURL, Title: db.NullStr(rec.Title)}
62- _ = h.articles.UpsertFeed(ctx, f)
63- err = h.articles.CreateSubscription(ctx, event.DID, rec.FeedURL, rec.Title, rec.Category, event.URI, event.CID)
64- if errors.Is(err, db.ErrDuplicateSubscription) {
65 return nil67 return nil
66 }68 }
67 return err69 return err
@@ -91,13 +93,8 @@ func (h *StreamDBHandler) handleLike(ctx context.Context, event *Event) error {
91 return nil93 return nil
92 }94 }
93 95
94- exists, err := h.articles.HasLiked(ctx, event.DID, rec.FeedURL, rec.ArticleURL)
95- if err != nil || exists {
96- return nil
97- }
98-
99 t, _ := time.Parse(time.RFC3339, rec.CreatedAt)96 t, _ := time.Parse(time.RFC3339, rec.CreatedAt)
100- err = h.articles.CreateLike(ctx, &db.Like{97+ err := h.articles.CreateLike(ctx, &db.Like{
101 URI: event.URI,98 URI: event.URI,
102 AuthorDID: event.DID,99 AuthorDID: event.DID,
103 FeedURL: rec.FeedURL,100 FeedURL: rec.FeedURL,
@@ -105,7 +102,7 @@ func (h *StreamDBHandler) handleLike(ctx context.Context, event *Event) error {
105 CreatedAt: sql.NullTime{Time: t, Valid: true},102 CreatedAt: sql.NullTime{Time: t, Valid: true},
106 CID: sql.NullString{String: event.CID, Valid: event.CID != ""},103 CID: sql.NullString{String: event.CID, Valid: event.CID != ""},
107 })104 })
108- if errors.Is(err, db.ErrDuplicateLike) {105+ if isSentinel(err) {
109 return nil106 return nil
110 }107 }
111 return err108 return err
@@ -213,22 +210,9 @@ func (h *StreamDBHandler) handleSkyreaderSubscription(ctx context.Context, event
213 return nil210 return nil
214 }211 }
215 212
216- existing, err := h.articles.GetSubscription(ctx, event.DID, rec.FeedURL)213+ _ = h.articles.UpsertFeed(ctx, &db.Feed{FeedURL: rec.FeedURL, Title: db.NullStr(rec.Title), SiteURL: db.NullStr(rec.SiteURL)})
217- if err == nil && existing != nil {214+ err := h.articles.CreateSubscription(ctx, event.DID, rec.FeedURL, rec.Title, "", event.URI, event.CID)
218- if !existing.URI.Valid || existing.URI.String == "" {215+ if isSentinel(err) {
219- return h.articles.UpdateSubscriptionURI(ctx, event.DID, rec.FeedURL, event.URI, event.CID)
220- }
221- return nil
222- }
223-
224- f := &db.Feed{
225- FeedURL: rec.FeedURL,
226- Title: db.NullStr(rec.Title),
227- SiteURL: db.NullStr(rec.SiteURL),
228- }
229- _ = h.articles.UpsertFeed(ctx, f)
230- err = h.articles.CreateSubscription(ctx, event.DID, rec.FeedURL, rec.Title, rec.Category, event.URI, event.CID)
231- if errors.Is(err, db.ErrDuplicateSubscription) {
232 return nil216 return nil
233 }217 }
234 return err218 return err
modified internal/atproto/sync.go +30 -35
@@ -10,10 +10,12 @@ package atproto
1010 import (
1111 "context"
1212 "encoding/json"
13+ "fmt"
1314 "log/slog"
14- "sync"
1515 "time"
1616
17+ "golang.org/x/sync/errgroup"
18+
1719 "pkg.rbrt.fr/glean/internal/db"
1820 )
1921
@@ -96,7 +98,9 @@ func (s *Sync) batchReconcileSubscriptions(ctx context.Context, userDID string,
9698 }
9799
98100 if len(feeds) > 0 {
99- _ = s.articles.BatchUpsertFeeds(ctx, feeds)
101+ if err := s.articles.BatchUpsertFeeds(ctx, feeds); err != nil {
102+ return fmt.Errorf("upsert feeds: %w", err)
103+ }
100104 }
101105 return s.articles.BatchReconcileSubscriptions(ctx, userDID, subs)
102106 }
@@ -123,8 +127,11 @@ func (s *Sync) batchReconcileSkyreaderSubscriptions(ctx context.Context, userDID
123127 }
124128
125129 if len(feeds) > 0 {
126- _ = s.articles.BatchUpsertFeeds(ctx, feeds)
130+ if err := s.articles.BatchUpsertFeeds(ctx, feeds); err != nil {
131+ return fmt.Errorf("failed to upsert feeds: %w", err)
132+ }
127133 }
134+
128135 return s.articles.BatchReconcileSubscriptions(ctx, userDID, subs)
129136 }
130137
@@ -259,42 +266,30 @@ func (s *Sync) syncFollows(ctx context.Context, userDID string) error {
259266 return nil
260267 }
261268
262- type profileResult struct {
263- did string
264- handle string
265- displayName string
266- avatarURL string
269+ g, gCtx := errgroup.WithContext(ctx)
270+ g.SetLimit(10)
271+
272+ dids := make([]string, 0, len(activeFollows))
273+ profiles := make([]db.UserData, len(activeFollows))
274+ for did := range activeFollows {
275+ dids = append(dids, did)
267276 }
268277
269- var wg sync.WaitGroup
270- sem := make(chan struct{}, 10)
271- results := make([]profileResult, 0, len(activeFollows))
272- var mu sync.Mutex
273-
274- for targetDID := range activeFollows {
275- sem <- struct{}{}
276- wg.Add(1)
277- go func(did string) {
278- defer func() { <-sem }()
279- defer wg.Done()
280- var handle, displayName, avatarURL string
281- if h, dn, avatar, err := FetchProfile(ctx, did); err == nil {
282- handle = h
283- displayName = dn
284- avatarURL = avatar
278+ for i, did := range dids {
279+ g.Go(func() error {
280+ if h, dn, avatar, err := FetchProfile(gCtx, did); err == nil {
281+ profiles[i] = db.UserData{DID: did, Handle: h, DisplayName: dn, AvatarURL: avatar}
282+ } else {
283+ profiles[i] = db.UserData{DID: did}
285284 }
286- mu.Lock()
287- results = append(results, profileResult{did, handle, displayName, avatarURL})
288- mu.Unlock()
289- }(targetDID)
285+ return nil
286+ })
290287 }
291- wg.Wait()
292-
293- var users []db.UserData
294- for _, r := range results {
295- users = append(users, db.UserData{DID: r.did, Handle: r.handle, DisplayName: r.displayName, AvatarURL: r.avatarURL})
288+ if err := g.Wait(); err != nil {
289+ return fmt.Errorf("fetch profiles: %w", err)
290+ }
291+ if err := s.users.BatchCreateUsers(ctx, profiles); err != nil {
292+ return fmt.Errorf("batch create users: %w", err)
296293 }
297- _ = s.users.BatchCreateUsers(ctx, users)
298-
299294 return s.users.SyncFollows(ctx, userDID, activeFollows)
300295 }
@@ -10,10 +10,12 @@ package atproto
10 import (10 import (
11 "context"11 "context"
12 "encoding/json"12 "encoding/json"
13+ "fmt"
13 "log/slog"14 "log/slog"
14- "sync"
15 "time"15 "time"
16 16
17+ "golang.org/x/sync/errgroup"
18+
17 "pkg.rbrt.fr/glean/internal/db"19 "pkg.rbrt.fr/glean/internal/db"
18 )20 )
19 21
@@ -96,7 +98,9 @@ func (s *Sync) batchReconcileSubscriptions(ctx context.Context, userDID string,
96 }98 }
97 99
98 if len(feeds) > 0 {100 if len(feeds) > 0 {
99- _ = s.articles.BatchUpsertFeeds(ctx, feeds)101+ if err := s.articles.BatchUpsertFeeds(ctx, feeds); err != nil {
102+ return fmt.Errorf("upsert feeds: %w", err)
103+ }
100 }104 }
101 return s.articles.BatchReconcileSubscriptions(ctx, userDID, subs)105 return s.articles.BatchReconcileSubscriptions(ctx, userDID, subs)
102 }106 }
@@ -123,8 +127,11 @@ func (s *Sync) batchReconcileSkyreaderSubscriptions(ctx context.Context, userDID
123 }127 }
124 128
125 if len(feeds) > 0 {129 if len(feeds) > 0 {
126- _ = s.articles.BatchUpsertFeeds(ctx, feeds)130+ if err := s.articles.BatchUpsertFeeds(ctx, feeds); err != nil {
131+ return fmt.Errorf("failed to upsert feeds: %w", err)
132+ }
127 }133 }
134+
128 return s.articles.BatchReconcileSubscriptions(ctx, userDID, subs)135 return s.articles.BatchReconcileSubscriptions(ctx, userDID, subs)
129 }136 }
130 137
@@ -259,42 +266,30 @@ func (s *Sync) syncFollows(ctx context.Context, userDID string) error {
259 return nil266 return nil
260 }267 }
261 268
262- type profileResult struct {269+ g, gCtx := errgroup.WithContext(ctx)
263- did string270+ g.SetLimit(10)
264- handle string271+
265- displayName string272+ dids := make([]string, 0, len(activeFollows))
266- avatarURL string273+ profiles := make([]db.UserData, len(activeFollows))
274+ for did := range activeFollows {
275+ dids = append(dids, did)
267 }276 }
268 277
269- var wg sync.WaitGroup278+ for i, did := range dids {
270- sem := make(chan struct{}, 10)279+ g.Go(func() error {
271- results := make([]profileResult, 0, len(activeFollows))280+ if h, dn, avatar, err := FetchProfile(gCtx, did); err == nil {
272- var mu sync.Mutex281+ profiles[i] = db.UserData{DID: did, Handle: h, DisplayName: dn, AvatarURL: avatar}
273-282+ } else {
274- for targetDID := range activeFollows {283+ profiles[i] = db.UserData{DID: did}
275- sem <- struct{}{}
276- wg.Add(1)
277- go func(did string) {
278- defer func() { <-sem }()
279- defer wg.Done()
280- var handle, displayName, avatarURL string
281- if h, dn, avatar, err := FetchProfile(ctx, did); err == nil {
282- handle = h
283- displayName = dn
284- avatarURL = avatar
285 }284 }
286- mu.Lock()285+ return nil
287- results = append(results, profileResult{did, handle, displayName, avatarURL})286+ })
288- mu.Unlock()
289- }(targetDID)
290 }287 }
291- wg.Wait()288+ if err := g.Wait(); err != nil {
292-289+ return fmt.Errorf("fetch profiles: %w", err)
293- var users []db.UserData290+ }
294- for _, r := range results {291+ if err := s.users.BatchCreateUsers(ctx, profiles); err != nil {
295- users = append(users, db.UserData{DID: r.did, Handle: r.handle, DisplayName: r.displayName, AvatarURL: r.avatarURL})292+ return fmt.Errorf("batch create users: %w", err)
296 }293 }
297- _ = s.users.BatchCreateUsers(ctx, users)
298-
299 return s.users.SyncFollows(ctx, userDID, activeFollows)294 return s.users.SyncFollows(ctx, userDID, activeFollows)
300 }295 }
modified internal/db/feed.go +6 -13
@@ -114,14 +114,7 @@ func (db *DB) MarkFeedFetchError(ctx context.Context, feedURL, lastError string)
114114 return err
115115 }
116116
117-func (db *DB) IncrementSubscriberCount(ctx context.Context, feedURL string) error {
118- _, err := db.ExecContext(ctx, `
119- UPDATE feeds SET subscriber_count = subscriber_count + 1 WHERE feed_url = ?
120- `, feedURL)
121- return err
122-}
123-
124-func (db *DB) DecrementSubscriberCount(ctx context.Context, feedURL string) error {
117+func (db *DB) decrementSubscriberCount(ctx context.Context, feedURL string) error {
125118 _, err := db.ExecContext(ctx, `
126119 UPDATE feeds SET subscriber_count = MAX(subscriber_count - 1, 0) WHERE feed_url = ?
127120 `, feedURL)
@@ -132,21 +125,21 @@ func (db *DB) CreateSubscription(ctx context.Context, userDID, feedURL, title, c
132125 existing, err := db.GetSubscription(ctx, userDID, feedURL)
133126 if err == nil && existing != nil {
134127 if !existing.URI.Valid || existing.URI.String == "" {
135- return db.UpdateSubscriptionURI(ctx, userDID, feedURL, uri, cid)
128+ return db.updateSubscriptionURI(ctx, userDID, feedURL, uri, cid)
136129 }
137130 return ErrDuplicateSubscription
138131 }
139132 return db.BatchReconcileSubscriptions(ctx, userDID, []SubData{{FeedURL: feedURL, Title: title, Category: category, URI: uri, CID: cid}})
140133 }
141134
142-func (db *DB) UpdateSubscriptionURI(ctx context.Context, userDID, feedURL, uri, cid string) error {
135+func (db *DB) updateSubscriptionURI(ctx context.Context, userDID, feedURL, uri, cid string) error {
143136 _, err := db.ExecContext(ctx, `
144137 UPDATE subscriptions SET uri = ?, cid = ? WHERE user_did = ? AND feed_url = ?
145138 `, uri, cid, userDID, feedURL)
146139 return err
147140 }
148141
149-func uriOrNil(category, v string) any {
142+func uriOrNil(v string) any {
150143 if v == "" {
151144 return nil
152145 }
@@ -167,7 +160,7 @@ func (db *DB) DeleteSubscription(ctx context.Context, userDID, feedURL string) e
167160 if err != nil {
168161 return err
169162 }
170- return db.DecrementSubscriberCount(ctx, feedURL)
163+ return db.decrementSubscriberCount(ctx, feedURL)
171164 }
172165
173166 func (db *DB) DeleteAllSubscriptions(ctx context.Context, userDID string) error {
@@ -466,7 +459,7 @@ func (db *DB) BatchReconcileSubscriptions(ctx context.Context, userDID string, s
466459 }
467460 continue
468461 }
469- result, err := insertStmt.ExecContext(ctx, userDID, sub.FeedURL, nilIfEmpty(sub.Title), sub.Category, uriOrNil(sub.Category, sub.URI), uriOrNil(sub.Category, sub.CID))
462+ result, err := insertStmt.ExecContext(ctx, userDID, sub.FeedURL, nilIfEmpty(sub.Title), sub.Category, uriOrNil(sub.URI), uriOrNil(sub.CID))
470463 if err != nil {
471464 return err
472465 }
@@ -114,14 +114,7 @@ func (db *DB) MarkFeedFetchError(ctx context.Context, feedURL, lastError string)
114 return err114 return err
115 }115 }
116 116
117-func (db *DB) IncrementSubscriberCount(ctx context.Context, feedURL string) error {117+func (db *DB) decrementSubscriberCount(ctx context.Context, feedURL string) error {
118- _, err := db.ExecContext(ctx, `
119- UPDATE feeds SET subscriber_count = subscriber_count + 1 WHERE feed_url = ?
120- `, feedURL)
121- return err
122-}
123-
124-func (db *DB) DecrementSubscriberCount(ctx context.Context, feedURL string) error {
125 _, err := db.ExecContext(ctx, `118 _, err := db.ExecContext(ctx, `
126 UPDATE feeds SET subscriber_count = MAX(subscriber_count - 1, 0) WHERE feed_url = ?119 UPDATE feeds SET subscriber_count = MAX(subscriber_count - 1, 0) WHERE feed_url = ?
127 `, feedURL)120 `, feedURL)
@@ -132,21 +125,21 @@ func (db *DB) CreateSubscription(ctx context.Context, userDID, feedURL, title, c
132 existing, err := db.GetSubscription(ctx, userDID, feedURL)125 existing, err := db.GetSubscription(ctx, userDID, feedURL)
133 if err == nil && existing != nil {126 if err == nil && existing != nil {
134 if !existing.URI.Valid || existing.URI.String == "" {127 if !existing.URI.Valid || existing.URI.String == "" {
135- return db.UpdateSubscriptionURI(ctx, userDID, feedURL, uri, cid)128+ return db.updateSubscriptionURI(ctx, userDID, feedURL, uri, cid)
136 }129 }
137 return ErrDuplicateSubscription130 return ErrDuplicateSubscription
138 }131 }
139 return db.BatchReconcileSubscriptions(ctx, userDID, []SubData{{FeedURL: feedURL, Title: title, Category: category, URI: uri, CID: cid}})132 return db.BatchReconcileSubscriptions(ctx, userDID, []SubData{{FeedURL: feedURL, Title: title, Category: category, URI: uri, CID: cid}})
140 }133 }
141 134
142-func (db *DB) UpdateSubscriptionURI(ctx context.Context, userDID, feedURL, uri, cid string) error {135+func (db *DB) updateSubscriptionURI(ctx context.Context, userDID, feedURL, uri, cid string) error {
143 _, err := db.ExecContext(ctx, `136 _, err := db.ExecContext(ctx, `
144 UPDATE subscriptions SET uri = ?, cid = ? WHERE user_did = ? AND feed_url = ?137 UPDATE subscriptions SET uri = ?, cid = ? WHERE user_did = ? AND feed_url = ?
145 `, uri, cid, userDID, feedURL)138 `, uri, cid, userDID, feedURL)
146 return err139 return err
147 }140 }
148 141
149-func uriOrNil(category, v string) any {142+func uriOrNil(v string) any {
150 if v == "" {143 if v == "" {
151 return nil144 return nil
152 }145 }
@@ -167,7 +160,7 @@ func (db *DB) DeleteSubscription(ctx context.Context, userDID, feedURL string) e
167 if err != nil {160 if err != nil {
168 return err161 return err
169 }162 }
170- return db.DecrementSubscriberCount(ctx, feedURL)163+ return db.decrementSubscriberCount(ctx, feedURL)
171 }164 }
172 165
173 func (db *DB) DeleteAllSubscriptions(ctx context.Context, userDID string) error {166 func (db *DB) DeleteAllSubscriptions(ctx context.Context, userDID string) error {
@@ -466,7 +459,7 @@ func (db *DB) BatchReconcileSubscriptions(ctx context.Context, userDID string, s
466 }459 }
467 continue460 continue
468 }461 }
469- result, err := insertStmt.ExecContext(ctx, userDID, sub.FeedURL, nilIfEmpty(sub.Title), sub.Category, uriOrNil(sub.Category, sub.URI), uriOrNil(sub.Category, sub.CID))462+ result, err := insertStmt.ExecContext(ctx, userDID, sub.FeedURL, nilIfEmpty(sub.Title), sub.Category, uriOrNil(sub.URI), uriOrNil(sub.CID))
470 if err != nil {463 if err != nil {
471 return err464 return err
472 }465 }
modified internal/db/follow.go +1 -1
@@ -21,7 +21,7 @@ func (db *DB) UpsertFollow(ctx context.Context, userDID, targetDID, uri, cid str
2121 ON CONFLICT(user_did, target_did) DO UPDATE SET
2222 uri = excluded.uri,
2323 cid = excluded.cid
24- `, userDID, targetDID, uriOrNil("", uri), uriOrNil("", cid))
24+ `, userDID, targetDID, uriOrNil(uri), uriOrNil(cid))
2525 return err
2626 }
2727
@@ -21,7 +21,7 @@ func (db *DB) UpsertFollow(ctx context.Context, userDID, targetDID, uri, cid str
21 ON CONFLICT(user_did, target_did) DO UPDATE SET21 ON CONFLICT(user_did, target_did) DO UPDATE SET
22 uri = excluded.uri,22 uri = excluded.uri,
23 cid = excluded.cid23 cid = excluded.cid
24- `, userDID, targetDID, uriOrNil("", uri), uriOrNil("", cid))24+ `, userDID, targetDID, uriOrNil(uri), uriOrNil(cid))
25 return err25 return err
26 }26 }
27 27