Skip to content

Deleted users that awarded achievements cause invalid UserAchievements

Problem

We observed that the awardedByUser field on the UserAchievement type can error out because of null values on this non-nullable field.

  "errors": [
    {
      "message": "Cannot return null for non-nullable field UserAchievement.awardedByUser"
    }
  ]

The relation on the UserAchievement model is defined as optional: false (which effectively makes it required)

    belongs_to :awarded_by_user,
      class_name: 'User',
      inverse_of: :awarded_user_achievements,
      optional: false

(https://gitlab.com/gitlab-org/gitlab/-/blob/a31062c8a87c6299b3a14a3d8e2174935c85b162/app/models/achievements/user_achievement.rb#L8-11)

The problem is that the foreign key is defined with on_delete: :nullify (https://gitlab.com/gitlab-org/gitlab/-/blob/a31062c8a87c6299b3a14a3d8e2174935c85b162/db/migrate/20221214204247_user_achievements_foreign_keys.rb#L8)
This causes the field to be set to null when the user that awarded the achievement is deleted.

But a awarded_by_user of null is invalid and causes errors when queried with GraphQL. The record probably can't be updated as well because of the model validations.

(Initial investigation happened in the community discord: https://canary.discord.com/channels/778180511088640070/1151947235081334835/1151957658509779004)

Proposed solution

Migrate this field to the Ghost user (@ghost1 on gitlab.com) by adding it to Users::MigrateRecordsToGhostUserService (https://gitlab.com/gitlab-org/gitlab/-/blob/a31062c8a87c6299b3a14a3d8e2174935c85b162/app/services/users/migrate_records_to_ghost_user_service.rb)

We probably also need a migration that will back-fill all null values in awarded_by_user with the Ghost user to fix already broken records.

Edited by Niklas van Schrick