Monorepo for Tangled tangled.org

appview/{db,state}: delete all duplicate records on delete #997

open opened by boltless.me targeting master from sl/sqkrqopzkvoo
Labels

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:plc:xasnlahkri4ewmbuzly2rlc5/sh.tangled.repo.pull/3mcsfwwhttb22
+150 -95
Diff #0
+20 -23
appview/db/follow.go
··· 6 6 "strings" 7 7 "time" 8 8 9 + "github.com/bluesky-social/indigo/atproto/syntax" 9 10 "tangled.org/core/appview/models" 10 11 "tangled.org/core/orm" 11 12 ) ··· 16 17 return err 17 18 } 18 19 19 - // Get a follow record 20 - func GetFollow(e Execer, userDid, subjectDid string) (*models.Follow, error) { 21 - query := `select did, subject_did, created, rkey from follows where did = ? and subject_did = ?` 22 - row := e.QueryRow(query, userDid, subjectDid) 23 - 24 - var follow models.Follow 25 - var followedAt string 26 - err := row.Scan(&follow.UserDid, &follow.SubjectDid, &followedAt, &follow.Rkey) 20 + // Remove a follow 21 + func DeleteFollow(e Execer, did, subjectDid syntax.DID) ([]syntax.ATURI, error) { 22 + var deleted []syntax.ATURI 23 + rows, err := e.Query( 24 + `delete from follows 25 + where did = ? and subject_did = ? 26 + returning at_uri`, 27 + did, 28 + subjectDid, 29 + ) 27 30 if err != nil { 28 - return nil, err 31 + return nil, fmt.Errorf("deleting stars: %w", err) 29 32 } 33 + defer rows.Close() 30 34 31 - followedAtTime, err := time.Parse(time.RFC3339, followedAt) 32 - if err != nil { 33 - log.Println("unable to determine followed at time") 34 - follow.FollowedAt = time.Now() 35 - } else { 36 - follow.FollowedAt = followedAtTime 35 + for rows.Next() { 36 + var aturi syntax.ATURI 37 + if err := rows.Scan(&aturi); err != nil { 38 + return nil, fmt.Errorf("scanning at_uri: %w", err) 39 + } 40 + deleted = append(deleted, aturi) 37 41 } 38 - 39 - return &follow, nil 40 - } 41 - 42 - // Remove a follow 43 - func DeleteFollow(e Execer, userDid, subjectDid string) error { 44 - _, err := e.Exec(`delete from follows where did = ? and subject_did = ?`, userDid, subjectDid) 45 - return err 42 + return deleted, nil 46 43 } 47 44 48 45 // Remove a follow
+24 -3
appview/db/reaction.go
··· 1 1 package db 2 2 3 3 import ( 4 + "fmt" 4 5 "log" 5 6 "time" 6 7 ··· 41 42 } 42 43 43 44 // Remove a reaction 44 - func DeleteReaction(e Execer, did string, subjectAt syntax.ATURI, kind models.ReactionKind) error { 45 - _, err := e.Exec(`delete from reactions where did = ? and subject_at = ? and kind = ?`, did, subjectAt, kind) 46 - return err 45 + func DeleteReaction(e Execer, did syntax.DID, subjectAt syntax.ATURI, kind models.ReactionKind) ([]syntax.ATURI, error) { 46 + var deleted []syntax.ATURI 47 + rows, err := e.Query( 48 + `delete from reactions 49 + where did = ? and subject_at = ? and kind = ? 50 + returning at_uri`, 51 + did, 52 + subjectAt, 53 + kind, 54 + ) 55 + if err != nil { 56 + return nil, fmt.Errorf("deleting stars: %w", err) 57 + } 58 + defer rows.Close() 59 + 60 + for rows.Next() { 61 + var aturi syntax.ATURI 62 + if err := rows.Scan(&aturi); err != nil { 63 + return nil, fmt.Errorf("scanning at_uri: %w", err) 64 + } 65 + deleted = append(deleted, aturi) 66 + } 67 + return deleted, nil 47 68 } 48 69 49 70 // Remove a reaction
+19 -27
appview/db/star.go
··· 4 4 "database/sql" 5 5 "errors" 6 6 "fmt" 7 - "log" 8 7 "slices" 9 8 "strings" 10 9 "time" ··· 25 24 return err 26 25 } 27 26 28 - // Get a star record 29 - func GetStar(e Execer, did string, subjectAt syntax.ATURI) (*models.Star, error) { 30 - query := ` 31 - select did, subject_at, created, rkey 32 - from stars 33 - where did = ? and subject_at = ?` 34 - row := e.QueryRow(query, did, subjectAt) 35 - 36 - var star models.Star 37 - var created string 38 - err := row.Scan(&star.Did, &star.RepoAt, &created, &star.Rkey) 27 + // Remove a star 28 + func DeleteStar(tx *sql.Tx, did syntax.DID, subjectAt syntax.ATURI) ([]syntax.ATURI, error) { 29 + var deleted []syntax.ATURI 30 + rows, err := tx.Query( 31 + `delete from stars 32 + where did = ? and subject_at = ? 33 + returning at_uri`, 34 + did, 35 + subjectAt, 36 + ) 39 37 if err != nil { 40 - return nil, err 38 + return nil, fmt.Errorf("deleting stars: %w", err) 41 39 } 40 + defer rows.Close() 42 41 43 - createdAtTime, err := time.Parse(time.RFC3339, created) 44 - if err != nil { 45 - log.Println("unable to determine followed at time") 46 - star.Created = time.Now() 47 - } else { 48 - star.Created = createdAtTime 42 + for rows.Next() { 43 + var aturi syntax.ATURI 44 + if err := rows.Scan(&aturi); err != nil { 45 + return nil, fmt.Errorf("scanning at_uri: %w", err) 46 + } 47 + deleted = append(deleted, aturi) 49 48 } 50 - 51 - return &star, nil 52 - } 53 - 54 - // Remove a star 55 - func DeleteStar(e Execer, did string, subjectAt syntax.ATURI) error { 56 - _, err := e.Exec(`delete from stars where did = ? and subject_at = ?`, did, subjectAt) 57 - return err 49 + return deleted, nil 58 50 } 59 51 60 52 // Remove a star
+32 -15
appview/state/follow.go
··· 6 6 "time" 7 7 8 8 comatproto "github.com/bluesky-social/indigo/api/atproto" 9 + "github.com/bluesky-social/indigo/atproto/syntax" 9 10 lexutil "github.com/bluesky-social/indigo/lex/util" 10 11 "tangled.org/core/api/tangled" 11 12 "tangled.org/core/appview/db" ··· 88 89 89 90 return 90 91 case http.MethodDelete: 91 - // find the record in the db 92 - follow, err := db.GetFollow(s.db, currentUser.Active.Did, subjectIdent.DID.String()) 92 + tx, err := s.db.BeginTx(r.Context(), nil) 93 93 if err != nil { 94 - log.Println("failed to get follow relationship") 94 + s.logger.Error("failed to start transaction", "err", err) 95 + } 96 + defer tx.Rollback() 97 + 98 + follows, err := db.DeleteFollow(tx, syntax.DID(currentUser.Active.Did), subjectIdent.DID) 99 + if err != nil { 100 + s.logger.Error("failed to delete follows from db", "err", err) 95 101 return 96 102 } 97 103 98 - _, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{ 99 - Collection: tangled.GraphFollowNSID, 100 - Repo: currentUser.Active.Did, 101 - Rkey: follow.Rkey, 104 + var writes []*comatproto.RepoApplyWrites_Input_Writes_Elem 105 + for _, followAt := range follows { 106 + writes = append(writes, &comatproto.RepoApplyWrites_Input_Writes_Elem{ 107 + RepoApplyWrites_Delete: &comatproto.RepoApplyWrites_Delete{ 108 + Collection: tangled.GraphFollowNSID, 109 + Rkey: followAt.RecordKey().String(), 110 + }, 111 + }) 112 + } 113 + _, err = comatproto.RepoApplyWrites(r.Context(), client, &comatproto.RepoApplyWrites_Input{ 114 + Repo: currentUser.Active.Did, 115 + Writes: writes, 102 116 }) 103 - 104 117 if err != nil { 105 - log.Println("failed to unfollow") 118 + s.logger.Error("failed to delete follows from PDS", "err", err) 106 119 return 107 120 } 108 121 109 - err = db.DeleteFollowByRkey(s.db, currentUser.Active.Did, follow.Rkey) 110 - if err != nil { 111 - log.Println("failed to delete follow from DB") 112 - // this is not an issue, the firehose event might have already done this 122 + if err := tx.Commit(); err != nil { 123 + s.logger.Error("failed to commit transaction", "err", err) 124 + // DB op failed but record is created in PDS. Ingester will backfill the missed operation 113 125 } 114 126 127 + s.notifier.DeleteFollow(r.Context(), &models.Follow{ 128 + UserDid: currentUser.Active.Did, 129 + SubjectDid: subjectIdent.DID.String(), 130 + // Rkey 131 + // FollowedAt 132 + }) 133 + 115 134 followStats, err := db.GetFollowerFollowingCount(s.db, subjectIdent.DID.String()) 116 135 if err != nil { 117 136 log.Println("failed to get follow stats", err) ··· 123 142 FollowersCount: followStats.Followers, 124 143 }) 125 144 126 - s.notifier.DeleteFollow(r.Context(), follow) 127 - 128 145 return 129 146 } 130 147
+24 -12
appview/state/reaction.go
··· 87 87 88 88 return 89 89 case http.MethodDelete: 90 - reaction, err := db.GetReaction(s.db, currentUser.Active.Did, subjectUri, reactionKind) 90 + tx, err := s.db.BeginTx(r.Context(), nil) 91 91 if err != nil { 92 - log.Println("failed to get reaction relationship for", currentUser.Active.Did, subjectUri) 92 + s.logger.Error("failed to start transaction", "err", err) 93 + } 94 + defer tx.Rollback() 95 + 96 + reactions, err := db.DeleteReaction(tx, syntax.DID(currentUser.Active.Did), subjectUri, reactionKind) 97 + if err != nil { 98 + s.logger.Error("failed to delete reactions from db", "err", err) 93 99 return 94 100 } 95 101 96 - _, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{ 97 - Collection: tangled.FeedReactionNSID, 98 - Repo: currentUser.Active.Did, 99 - Rkey: reaction.Rkey, 102 + var writes []*comatproto.RepoApplyWrites_Input_Writes_Elem 103 + for _, reactionAt := range reactions { 104 + writes = append(writes, &comatproto.RepoApplyWrites_Input_Writes_Elem{ 105 + RepoApplyWrites_Delete: &comatproto.RepoApplyWrites_Delete{ 106 + Collection: tangled.FeedReactionNSID, 107 + Rkey: reactionAt.RecordKey().String(), 108 + }, 109 + }) 110 + } 111 + _, err = comatproto.RepoApplyWrites(r.Context(), client, &comatproto.RepoApplyWrites_Input{ 112 + Repo: currentUser.Active.Did, 113 + Writes: writes, 100 114 }) 101 - 102 115 if err != nil { 103 - log.Println("failed to remove reaction") 116 + s.logger.Error("failed to delete reactions from PDS", "err", err) 104 117 return 105 118 } 106 119 107 - err = db.DeleteReactionByRkey(s.db, currentUser.Active.Did, reaction.Rkey) 108 - if err != nil { 109 - log.Println("failed to delete reaction from DB") 110 - // this is not an issue, the firehose event might have already done this 120 + if err := tx.Commit(); err != nil { 121 + s.logger.Error("failed to commit transaction", "err", err) 122 + // DB op failed but record is created in PDS. Ingester will backfill the missed operation 111 123 } 112 124 113 125 reactionMap, err := db.GetReactionMap(s.db, 20, subjectUri)
+31 -15
appview/state/star.go
··· 83 83 84 84 return 85 85 case http.MethodDelete: 86 - // find the record in the db 87 - star, err := db.GetStar(s.db, currentUser.Active.Did, subjectUri) 86 + tx, err := s.db.BeginTx(r.Context(), nil) 88 87 if err != nil { 89 - log.Println("failed to get star relationship") 88 + s.logger.Error("failed to start transaction", "err", err) 89 + } 90 + defer tx.Rollback() 91 + 92 + stars, err := db.DeleteStar(tx, syntax.DID(currentUser.Active.Did), subjectUri) 93 + if err != nil { 94 + s.logger.Error("failed to delete stars from db", "err", err) 90 95 return 91 96 } 92 97 93 - _, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{ 94 - Collection: tangled.FeedStarNSID, 95 - Repo: currentUser.Active.Did, 96 - Rkey: star.Rkey, 98 + var writes []*comatproto.RepoApplyWrites_Input_Writes_Elem 99 + for _, starAt := range stars { 100 + writes = append(writes, &comatproto.RepoApplyWrites_Input_Writes_Elem{ 101 + RepoApplyWrites_Delete: &comatproto.RepoApplyWrites_Delete{ 102 + Collection: tangled.FeedStarNSID, 103 + Rkey: starAt.RecordKey().String(), 104 + }, 105 + }) 106 + } 107 + _, err = comatproto.RepoApplyWrites(r.Context(), client, &comatproto.RepoApplyWrites_Input{ 108 + Repo: currentUser.Active.Did, 109 + Writes: writes, 97 110 }) 98 - 99 111 if err != nil { 100 - log.Println("failed to unstar") 112 + s.logger.Error("failed to delete stars from PDS", "err", err) 101 113 return 102 114 } 103 115 104 - err = db.DeleteStarByRkey(s.db, currentUser.Active.Did, star.Rkey) 105 - if err != nil { 106 - log.Println("failed to delete star from DB") 107 - // this is not an issue, the firehose event might have already done this 116 + if err := tx.Commit(); err != nil { 117 + s.logger.Error("failed to commit transaction", "err", err) 118 + // DB op failed but record is created in PDS. Ingester will backfill the missed operation 108 119 } 109 120 121 + s.notifier.DeleteStar(r.Context(), &models.Star{ 122 + Did: currentUser.Active.Did, 123 + RepoAt: subjectUri, 124 + // Rkey 125 + // Created 126 + }) 127 + 110 128 starCount, err := db.GetStarCount(s.db, subjectUri) 111 129 if err != nil { 112 130 log.Println("failed to get star count for ", subjectUri) 113 131 return 114 132 } 115 133 116 - s.notifier.DeleteStar(r.Context(), star) 117 - 118 134 s.pages.StarBtnFragment(w, pages.StarBtnFragmentParams{ 119 135 IsStarred: false, 120 136 SubjectAt: subjectUri,

Submissions

sign up or login to add to the discussion
boltless.me submitted #0
1 commit
expand
appview/{db,state}: delete all duplicate records on delete
3/3 success
expand
no conflicts, ready to merge