Monorepo for Tangled tangled.org
4

Configure Feed

Select the types of activity you want to include in your feed.

appview: remove `Rkey` from `models.Follow`

From model layer, we expect follows to be unique per (did, subject). But
from PDS, there is no such constraint. So hide the rkey from model while
keeping duplicated records in DB.

We keep duplicated records in DB to delete them all at once on unfollow

Signed-off-by: Seongmin Lee <git@boltless.me>

author
Seongmin Lee
committer
Tangled
date (Jul 22, 2026, 5:50 PM +0300) commit 67a7aa29 parent 61ce90e5 change-id yoloxkrv
+8 -10
+4 -4
appview/db/follow.go
··· 11 11 "tangled.org/core/orm" 12 12 ) 13 13 14 - func UpsertFollow(e Execer, follow models.Follow) error { 14 + func UpsertFollow(e Execer, rkey string, follow models.Follow) error { 15 15 _, err := e.Exec( 16 16 `insert into follows (did, rkey, subject_did, created) 17 17 values (?, ?, ?, ?) ··· 19 19 subject_did = excluded.subject_did, 20 20 created = excluded.created`, 21 21 follow.UserDid, 22 - follow.Rkey, 22 + rkey, 23 23 follow.SubjectDid, 24 24 follow.FollowedAt.Format(time.RFC3339), 25 25 ) ··· 189 189 } 190 190 191 191 query := fmt.Sprintf( 192 - `select did, subject_did, created, rkey 192 + `select did, subject_did, max(created) 193 193 from follows 194 194 %s 195 + group by did, subject_did 195 196 order by created desc 196 197 %s 197 198 `, whereClause, limitClause) ··· 209 210 &follow.UserDid, 210 211 &follow.SubjectDid, 211 212 &followedAt, 212 - &follow.Rkey, 213 213 ) 214 214 if err != nil { 215 215 return nil, err
+1 -2
appview/ingester.go
··· 287 287 return err 288 288 } 289 289 290 - err = db.UpsertFollow(i.Db, models.Follow{ 290 + err = db.UpsertFollow(i.Db, e.Commit.RKey, models.Follow{ 291 291 UserDid: did, 292 292 SubjectDid: record.Subject, 293 - Rkey: e.Commit.RKey, 294 293 FollowedAt: followedAt, 295 294 }) 296 295 case jmodels.CommitOperationDelete:
-1
appview/models/follow.go
··· 10 10 UserDid string 11 11 SubjectDid string 12 12 FollowedAt time.Time 13 - Rkey string 14 13 } 15 14 16 15 func (f *Follow) AsRecord() tangled.GraphFollow {
+3 -3
appview/state/follow.go
··· 46 46 follow := models.Follow{ 47 47 UserDid: currentUser.Did, 48 48 SubjectDid: subjectIdent.DID.String(), 49 - Rkey: tid.TID(), 50 49 FollowedAt: time.Now(), 51 50 } 51 + rkey := tid.TID() 52 52 53 53 tx, err := s.db.BeginTx(r.Context(), nil) 54 54 if err != nil { ··· 57 57 } 58 58 defer tx.Rollback() 59 59 60 - if err := db.UpsertFollow(tx, follow); err != nil { 60 + if err := db.UpsertFollow(tx, rkey, follow); err != nil { 61 61 s.logger.Error("failed to follow", "err", err) 62 62 return 63 63 } ··· 66 66 resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 67 67 Collection: tangled.GraphFollowNSID, 68 68 Repo: currentUser.Did, 69 - Rkey: follow.Rkey, 69 + Rkey: rkey, 70 70 Record: &lexutil.LexiconTypeDecoder{ 71 71 Val: &record, 72 72 },