Ao usar o plugin Gamification, pontos são recompensados por convites resgatados.
Isso é facilmente abusado enviando convites e resgatando-os com uma conta duplicada (falsa).
Quando uma conta falsa é removida pelo administrador, o comportamento do plugin gamification (e do sistema de convites) depende de como o convite foi enviado.
Se o convite foi vinculado a um endereço de e-mail específico e o usuário for removido, tanto o registro invite quanto os registros user_invite são excluídos (!) permanentemente. Na próxima vez que o trabalho Sidekiq UpdateScoresFor* for executado, o plugin gamification deduzirá os pontos concedidos da pontuação novamente.
Se o convite não foi vinculado a um endereço de e-mail específico e o usuário for removido, o registro invited_users será removido, mas o registro invite permanecerá. O valor redemption_count não será diminuído. Isso faz sentido de um ponto de vista porque o resgate do convite ocorreu, mesmo que o usuário tenha sido removido posteriormente. Mas o plugin Gamification usa o redemption_count para calcular a pontuação, então os pontos não serão deduzidos.
Enquanto depurava isso, também descobri que o trabalho de pontuação considerará apenas convites com menos de 10 dias. Portanto, se um convite for resgatado após 11 dias, nenhum ponto será concedido. Eu ia dizer 'Acho que precisa olhar para updated_at em vez de created_at, mas quando a contagem de resgates para o convite é aumentada, o carimbo de data/hora updated_at não está sendo tocado.
Algo assim funcionaria melhor (ajustado para o formato de consulta pontuável - este é um teste para o explorador de dados ):
-- [params]
-- date :start_date
-- date :end_date
SELECT
invited_by_id AS user_id,
COUNT(*) AS user_invites,
COUNT(*) * 10 AS invite_score
FROM invited_users iu
JOIN invites i ON i.id = iu.invite_id
JOIN users u ON u.id = iu.user_id
WHERE iu.redeemed_at::date BETWEEN :start_date AND :end_date
AND iu.user_id <> i.invited_by_id
AND u.created_at > iu.redeemed_at
GROUP BY invited_by_id
ORDER BY user_invites DESC
Quando um usuário é excluído, ele é removido da tabela invited_users, portanto, não estaria mais na contagem. Se a exclusão ocorresse em até 10 dias, seria auto-corrigida; se demorasse mais, seria necessária uma atualização manual da pontuação.
Usar a data redeemed_at contabilizaria os convites criados há mais de 10 dias.
AND iu.user_id <> i.invited_by_id também excluiria convites próprios.
A junção da tabela users e a adição de AND u.created_at > iu.redeemed_at também excluiriam o convite de usuários existentes.
Isso seria uma boa abordagem, exceto por uma coisa:
Isso não funciona bem. Às vezes, o usuário é criado um pouco antes de a troca ocorrer. Não sei por quê. Na maioria das vezes, são décimos de segundo, mas também encontrei alguns de dezenas de segundos.
Testado em um banco de dados real.
select
iu.redeemed_at iu_AS redeemed_at,
u.created_at AS u_created_at,
u.created_at > iu.redeemed_at AS u_created_gt_iu_redeemed
from invited_users iu
left join users u on u.id = iu.user_id
where iu.redeemed_at is not null
order by iu.id desc;
iu_redeemed_at | u_created_at | u_created_gt_iu_redeemed
----------------------------+----------------------------+--------------------------
2023-09-08 00:00:47.557057 | 2023-09-08 00:00:48.376446 | t
2023-08-25 20:09:03.486362 | 2023-08-25 20:09:03.201357 | f
2023-08-15 23:38:32.271709 | 2023-08-15 23:38:33.570299 | t
2023-08-14 10:44:34.19912 | 2023-08-14 10:44:35.429371 | t
2023-08-12 13:41:10.428013 | 2023-08-12 13:41:11.733973 | t
2023-07-31 17:58:13.511289 | 2023-07-31 17:57:50.427111 | f
2023-07-23 00:56:33.455185 | 2023-07-23 00:55:47.999263 | f
2023-07-19 08:42:44.908096 | 2023-07-19 08:42:46.040201 | t
2023-06-30 09:11:38.829692 | 2023-06-30 09:11:39.618586 | t
2023-06-30 08:37:02.322192 | 2023-06-30 08:37:03.133769 | t
2023-06-29 16:24:01.705616 | 2023-06-29 16:24:02.55067 | t
2023-06-29 12:53:33.245688 | 2023-06-29 12:53:34.067159 | t
Acho isso um pouco “mágica negra” demais, já que não sabemos o que está causando o atraso.
Suspeito que o atraso seja causado por uma grande fila do sidekiq ou algo semelhante e, embora a maioria esteja abaixo de um segundo, cerca de 3% está na faixa de 0:00:01 - 0:15:00 (um segundo a 15 minutos).
E meio por cento está na ordem de dias, o que parece o tipo de abuso que estamos tentando evitar. Portanto, embora isso seja eficaz, estaria fazendo mais mal do que bem por causa da quantidade de falsos positivos.