From cdca30f34603ad5e7e0444519bc68620094e3e44 Mon Sep 17 00:00:00 2001 From: Evan Jarrett Date: Wed, 18 Mar 2026 10:44:17 -0500 Subject: [PATCH] clear old handles from db if migrated to new did --- pkg/appview/db/queries.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pkg/appview/db/queries.go b/pkg/appview/db/queries.go index a5e759c..9d358ef 100644 --- a/pkg/appview/db/queries.go +++ b/pkg/appview/db/queries.go @@ -373,6 +373,12 @@ func GetUserByHandle(db DBTX, handle string) (*User, error) { // InsertUserIfNotExists inserts a user record only if it doesn't already exist. // Used by non-profile collections to avoid unnecessary writes during backfill. func InsertUserIfNotExists(db DBTX, user *User) error { + // Clear handle from any other DID that currently holds it. + // In ATProto, a handle belongs to exactly one DID at a time — + // if a new DID claims this handle, the old association is stale. + _, _ = db.Exec(`UPDATE users SET handle = did WHERE handle = ? AND did != ?`, + user.Handle, user.DID) + _, err := db.Exec(` INSERT INTO users (did, handle, pds_endpoint, avatar, last_seen) VALUES (?, ?, ?, ?, ?) @@ -383,6 +389,12 @@ func InsertUserIfNotExists(db DBTX, user *User) error { // UpsertUser inserts or updates a user record func UpsertUser(db DBTX, user *User) error { + // Clear handle from any other DID that currently holds it. + // In ATProto, a handle belongs to exactly one DID at a time — + // if a new DID claims this handle, the old association is stale. + _, _ = db.Exec(`UPDATE users SET handle = did WHERE handle = ? AND did != ?`, + user.Handle, user.DID) + _, err := db.Exec(` INSERT INTO users (did, handle, pds_endpoint, avatar, last_seen) VALUES (?, ?, ?, ?, ?) @@ -398,6 +410,12 @@ func UpsertUser(db DBTX, user *User) error { // UpsertUserIgnoreAvatar inserts or updates a user record, but preserves existing avatar on update // This is useful when avatar fetch fails, and we don't want to overwrite an existing avatar with empty string func UpsertUserIgnoreAvatar(db DBTX, user *User) error { + // Clear handle from any other DID that currently holds it. + // In ATProto, a handle belongs to exactly one DID at a time — + // if a new DID claims this handle, the old association is stale. + _, _ = db.Exec(`UPDATE users SET handle = did WHERE handle = ? AND did != ?`, + user.Handle, user.DID) + _, err := db.Exec(` INSERT INTO users (did, handle, pds_endpoint, avatar, last_seen) VALUES (?, ?, ?, ?, ?)