fix p2p topic canonicalization and stale subscriptions
This commit is contained in:
@@ -1111,6 +1111,9 @@ func (a *adapter) UpgradeDb() error {
|
||||
if err := a.ensureMessageTopicSeqIndex(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := a.ensureTopicNameUniqueIndex(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := a.ensureTopicSeqMatchesMessages(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1139,6 +1142,97 @@ func (a *adapter) ensureMessageTopicSeqIndex() error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (a *adapter) ensureTopicNameUniqueIndex() error {
|
||||
ctx := context.Background()
|
||||
conn, err := a.db.Connx(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
tenantScoped, err := identityColumnExists(ctx, conn, "im_topics", "tenant_id")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var count int
|
||||
if tenantScoped {
|
||||
if err = conn.QueryRowContext(ctx,
|
||||
`SELECT COUNT(*) FROM information_schema.statistics
|
||||
WHERE table_schema=DATABASE() AND table_name='im_topics' AND index_name='uk_topics_tenant_name' AND non_unique=0`,
|
||||
).Scan(&count); err != nil {
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
return nil
|
||||
}
|
||||
if _, err = conn.ExecContext(ctx, `UPDATE im_topics AS keep_t
|
||||
JOIN (
|
||||
SELECT tenant_id,name,MIN(id) AS keep_id,
|
||||
MAX(seqid) AS seqid,MAX(delid) AS delid,
|
||||
MAX(COALESCE(touchedat,'1000-01-01')) AS touchedat,
|
||||
MAX(updatedat) AS updatedat
|
||||
FROM im_topics GROUP BY tenant_id,name HAVING COUNT(*)>1
|
||||
) AS dup ON dup.keep_id=keep_t.id
|
||||
SET keep_t.seqid=GREATEST(keep_t.seqid,dup.seqid),
|
||||
keep_t.delid=GREATEST(keep_t.delid,dup.delid),
|
||||
keep_t.touchedat=NULLIF(GREATEST(COALESCE(keep_t.touchedat,'1000-01-01'),dup.touchedat),'1000-01-01'),
|
||||
keep_t.updatedat=GREATEST(keep_t.updatedat,dup.updatedat)`); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = conn.ExecContext(ctx, `DELETE t FROM im_topics AS t
|
||||
JOIN (
|
||||
SELECT tenant_id,name,MIN(id) AS keep_id
|
||||
FROM im_topics GROUP BY tenant_id,name HAVING COUNT(*)>1
|
||||
) AS dup ON dup.tenant_id=t.tenant_id AND dup.name=t.name AND t.id<>dup.keep_id`); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = conn.ExecContext(ctx, "ALTER TABLE im_topics ADD UNIQUE INDEX uk_topics_tenant_name(tenant_id, name)")
|
||||
if isDupe(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if err = conn.QueryRowContext(ctx,
|
||||
`SELECT COUNT(*) FROM information_schema.statistics
|
||||
WHERE table_schema=DATABASE() AND table_name='im_topics' AND index_name='topics_name' AND non_unique=0`,
|
||||
).Scan(&count); err != nil {
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
return nil
|
||||
}
|
||||
if _, err = conn.ExecContext(ctx, `UPDATE im_topics AS keep_t
|
||||
JOIN (
|
||||
SELECT name,MIN(id) AS keep_id,
|
||||
MAX(seqid) AS seqid,MAX(delid) AS delid,
|
||||
MAX(COALESCE(touchedat,'1000-01-01')) AS touchedat,
|
||||
MAX(updatedat) AS updatedat
|
||||
FROM im_topics GROUP BY name HAVING COUNT(*)>1
|
||||
) AS dup ON dup.keep_id=keep_t.id
|
||||
SET keep_t.seqid=GREATEST(keep_t.seqid,dup.seqid),
|
||||
keep_t.delid=GREATEST(keep_t.delid,dup.delid),
|
||||
keep_t.touchedat=NULLIF(GREATEST(COALESCE(keep_t.touchedat,'1000-01-01'),dup.touchedat),'1000-01-01'),
|
||||
keep_t.updatedat=GREATEST(keep_t.updatedat,dup.updatedat)`); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = conn.ExecContext(ctx, `DELETE t FROM im_topics AS t
|
||||
JOIN (
|
||||
SELECT name,MIN(id) AS keep_id
|
||||
FROM im_topics GROUP BY name HAVING COUNT(*)>1
|
||||
) AS dup ON dup.name=t.name AND t.id<>dup.keep_id`); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = conn.ExecContext(ctx, "ALTER TABLE im_topics ADD UNIQUE INDEX topics_name(name)"); err != nil {
|
||||
if isDupe(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *adapter) ensureTopicSeqMatchesMessages() error {
|
||||
ctx := context.Background()
|
||||
conn, err := a.db.Connx(ctx)
|
||||
@@ -3224,6 +3318,7 @@ func (a *adapter) TopicsForUser(uid t.Uid, keepDeleted bool, opts *t.QueryOpt) (
|
||||
// Fetch subscriptions. Two queries are needed: users table (p2p) and topics table (grp).
|
||||
// Prepare a list of separate subscriptions to users vs topics
|
||||
join := make(map[string]t.Subscription) // Keeping these to make a join with table for .private and .access
|
||||
p2pResolved := make(map[string]bool)
|
||||
topq := make([]any, 0, 16)
|
||||
usrq := make([]any, 0, 16)
|
||||
for rows.Next() {
|
||||
@@ -3242,6 +3337,7 @@ func (a *adapter) TopicsForUser(uid t.Uid, keepDeleted bool, opts *t.QueryOpt) (
|
||||
} else if tcat == t.TopicCatP2P {
|
||||
// P2P subscription, find the other user to get user.Public and user.Trusted.
|
||||
uid1, uid2, _ := t.ParseP2P(tname)
|
||||
p2pResolved[tname] = false
|
||||
if uid1 == uid {
|
||||
usrq = append(usrq, store.DecodeUid(uid2))
|
||||
sub.SetWith(uid2.UserId())
|
||||
@@ -3374,6 +3470,7 @@ func (a *adapter) TopicsForUser(uid t.Uid, keepDeleted bool, opts *t.QueryOpt) (
|
||||
sub.SetDefaultAccess(usr2.Access.Auth, usr2.Access.Anon)
|
||||
sub.SetLastSeenAndUA(usr2.lastSeen(), usr2.UserAgent)
|
||||
join[joinOn] = sub
|
||||
p2pResolved[joinOn] = true
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
@@ -3388,6 +3485,9 @@ func (a *adapter) TopicsForUser(uid t.Uid, keepDeleted bool, opts *t.QueryOpt) (
|
||||
|
||||
subs = make([]t.Subscription, 0, len(join))
|
||||
for _, sub := range join {
|
||||
if t.GetTopicCat(sub.Topic) == t.TopicCatP2P && !p2pResolved[sub.Topic] {
|
||||
continue
|
||||
}
|
||||
subs = append(subs, sub)
|
||||
}
|
||||
|
||||
|
||||
@@ -942,6 +942,7 @@ func (a *adapter) TenantTopicsForUser(id t.TenantID, uid t.Uid, keepDeleted bool
|
||||
return nil, err
|
||||
}
|
||||
join := map[string]t.Subscription{}
|
||||
p2pResolved := map[string]bool{}
|
||||
var topq, usrq []any
|
||||
for rows.Next() {
|
||||
var sub t.Subscription
|
||||
@@ -956,6 +957,7 @@ func (a *adapter) TenantTopicsForUser(id t.TenantID, uid t.Uid, keepDeleted bool
|
||||
continue
|
||||
case t.TopicCatP2P:
|
||||
uid1, uid2, _ := t.ParseP2P(tname)
|
||||
p2pResolved[tname] = false
|
||||
if uid1 == uid {
|
||||
usrq = append(usrq, decodeUID(uid2))
|
||||
sub.SetWith(uid2.UserId())
|
||||
@@ -1047,6 +1049,7 @@ func (a *adapter) TenantTopicsForUser(id t.TenantID, uid t.Uid, keepDeleted bool
|
||||
sub.SetDefaultAccess(user.Access.Auth, user.Access.Anon)
|
||||
sub.SetLastSeenAndUA(user.lastSeen(), user.UserAgent)
|
||||
join[joinOn] = sub
|
||||
p2pResolved[joinOn] = true
|
||||
}
|
||||
}
|
||||
err = rows.Err()
|
||||
@@ -1057,6 +1060,9 @@ func (a *adapter) TenantTopicsForUser(id t.TenantID, uid t.Uid, keepDeleted bool
|
||||
}
|
||||
subs := make([]t.Subscription, 0, len(join))
|
||||
for _, sub := range join {
|
||||
if t.GetTopicCat(sub.Topic) == t.TopicCatP2P && !p2pResolved[sub.Topic] {
|
||||
continue
|
||||
}
|
||||
subs = append(subs, sub)
|
||||
}
|
||||
return common.SelectEarliestUpdatedSubs(subs, opts, a.maxResults), nil
|
||||
@@ -9637,10 +9643,16 @@ func (a *adapter) TenantFriendshipDeletePair(id t.TenantID, uid1, uid2 t.Uid, no
|
||||
if cancel != nil {
|
||||
defer cancel()
|
||||
}
|
||||
_, err := a.db.ExecContext(ctx,
|
||||
res, err := a.db.ExecContext(ctx,
|
||||
"UPDATE im_friendships SET status=?,updated_at=? WHERE (user_id=? AND peer_id=?) OR (user_id=? AND peer_id=?)",
|
||||
t.RelationshipNone, now, decodeUID(uid1), decodeUID(uid2), decodeUID(uid2), decodeUID(uid1))
|
||||
return err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if affected, err := res.RowsAffected(); err == nil && affected == 0 {
|
||||
return t.ErrNotFound
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *adapter) TenantUserBlockGet(id t.TenantID, blockerID, blockedID t.Uid) (*t.UserBlock, error) {
|
||||
|
||||
Reference in New Issue
Block a user