因数据库损坏导致大量用户账户异常

我遇到了一个关于许多用户的真实问题。在管理面板中,用户显示如下:

但当我尝试查看他们的电子邮件地址时,没有任何内容显示。此外,他们的公开个人资料页面返回 404 错误。

据我观察,所有这些用户几乎都处于不活跃状态(从激活状态来看他们是激活的,但在论坛活动方面是不活跃的)。因此,我认为这可能是由于很久以前自动移除不活跃用户的机制出现错误所导致的。

我还注意到一件事:

如果我停用它们,然后再启用,它们就会恢复正常!

如果用户在一定时间(默认为 730 天)内未活动,系统将自动将其停用。该设置位于您的仪表板中的“设置/用户”部分,向下滚动至接近底部即可找到。不过,除非这些用户重新出现,否则没有必要为了更改而更改。如果这些用户已经两年未登录,除非他们再次活跃,否则将其重新激活并无意义。:wink:

不,我的问题不是这个。仪表板显示用户已激活。

这似乎与我遇到的问题类似 the issue I’m having。您能否检查一下受影响的名称在您的数据库中是否存在重复或“非常接近的匹配项”?例如 user.useruseruser

是的,受影响的正是那些非常流行的用户名。因此,Discourse 会建议一个相近的用户名。我通过 API 注册用户,从用户处获取用户名后,会调用 Discourse API 进行校验。如果该用户名已被占用,系统会自动采用 Discourse 建议的替代名称。

顺便提一下,在 @RGJ 的帮助下,我们已将问题锁定为以下两个条件:

补充一点,@hosna 遇到的问题显然是数据库层面的问题。看起来 users 表中存在某些损坏。将内容复制到新表可以解决这些问题。

不过,我在 @hosna 的数据库中发现了两处与 @bartv 相同的问题(即 9 月 22 日之前的那两个重复项,它们的用户名中都包含一个点),但我不确定这两个问题是否相关。它们只是症状相同。

听起来像是数据库索引损坏了。执行 REINDEX TABLE users 应该能解决问题。

那重复的用户名怎么办?我发现有很多相同的用户名被两个不同的用户使用。

该问题已在此处得到确认:

这很可能是索引损坏的副作用。在重新索引生效之前,您可能需要手动清理它。

您能解释一下索引损坏是如何发生的吗?以及如何防止未来再次发生?

硬件故障、Postgres 中的 bug……很难说。这种情况时有发生。

但这是不可能的,因为索引已损坏。
以下方法可以解决问题:

# 创建一个无约束的临时表,并将内容复制到其中
create table users_test (like users);
insert into users_test select * from users;

# 删除区分大小写的重复用户名,重复项出现在 9 月 22 日之后
delete from users_test where username in (
  select username 
  from users_test 
  group by username 
  having count(username) > 1
) and created_at > '2019-09-22' ;

# 删除不区分大小写的重复用户名,重复项出现在 9 月 22 日之后
delete from users_test where lower(username) in (
  select lower(username) 
  from users_test 
  group by lower(username) 
  having count(lower(username)) > 1
) and created_at > '2019-09-22' ;

# 还剩两个问题,分别删除它们
delete from users_test where id in (184534, 130826);

# 创建一个带约束的新表,并复制用户数据
create table users_clean (like users including indexes);
insert into users_clean select * from users_test;

然后将 users 重命名为 users_old,将 users_clean 重命名为 users

我想在此插一句:这可能会比损坏的用户数据更严重地破坏你的数据库

现在我们卡在升级的中间阶段,因为许多约束条件仍然依赖 users_old(因为我们重命名了表)。这个问题是在应用这个看似不完整的修复几天后才暴露出来的。此外,like users including indexes 并不足够(例如,它会忽略 id 序列)。

您完全正确,我确实记得在重命名表后需要重新创建约束。对于这一重要遗漏,我深表歉意。

根据我的笔记:

alter table poll_votes drop constraint fk_rails_b64de9b025;
alter table poll_votes add constraint fk_rails_b64de9b025 FOREIGN KEY (user_id) REFERENCES users(id);

alter table user_security_keys drop constraint fk_rails_90999b0454;
alter table user_security_keys add  constraint fk_rails_90999b0454 FOREIGN KEY (user_id) REFERENCES users(id);

如今还需要执行:

alter table bookmarks drop constraint fk_rails_c1ff6fa4ac;
alter table bookmarks add  constraint fk_rails_c1ff6fa4ac FOREIGN KEY (user_id) REFERENCES users(id);

重要免责声明:仅在您完全清楚自己在做什么的情况下才使用此方法

这确实与我们在查询 pg_catalog 中影响 users_old 的约束后发现的情况一致。

此外,我记得“包含默认值”是至少必须的操作,否则会导致注册功能失效。

感谢您的修正!