File: remove_expired_members_worker.rb

package info (click to toggle)
gitlab 17.6.5-19
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 629,368 kB
  • sloc: ruby: 1,915,304; javascript: 557,307; sql: 60,639; xml: 6,509; sh: 4,567; makefile: 1,239; python: 406
file content (71 lines) | stat: -rw-r--r-- 1,961 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# frozen_string_literal: true

class RemoveExpiredMembersWorker # rubocop:disable Scalability/IdempotentWorker
  include ApplicationWorker

  data_consistency :always

  include CronjobQueue

  feature_category :system_access
  worker_resource_boundary :cpu

  BATCH_SIZE = 1000
  BATCH_DELAY = 10.seconds

  # rubocop: disable CodeReuse/ActiveRecord
  def perform(cursor = nil)
    @updated_count = 0
    paginator = paginate(cursor)

    paginator.each { |member| process_member(member) }

    status = paginator.has_next_page? ? :limit_reached : :completed
    log_extra_metadata_on_done(:result,
      status: status,
      updated_rows: @updated_count
    )

    return unless paginator.has_next_page?

    self.class.perform_in(BATCH_DELAY, paginator.cursor_for_next_page)
  end

  private

  def paginate(cursor)
    Member.expired
      .includes(:user, :source)
      .order(expires_at: :desc, id: :desc)
      .keyset_paginate(cursor: cursor, per_page: BATCH_SIZE)
  end

  def process_member(member)
    context = {
      user: member.user,
      # The ApplicationContext will reject type-mismatches. So a GroupMemeber will only populate `namespace`.
      # while a `ProjectMember` will populate `project
      project: member.source,
      namespace: member.source
    }
    with_context(context) do
      Members::DestroyService.new.execute(member, skip_authorization: true, skip_subresources: true)

      expired_user = member.user

      if expired_user.project_bot?
        Users::DestroyService.new(nil).execute(expired_user, {
          skip_authorization: true,
          project_bot_resource: member.source,
          reason_for_deletion: "Membership expired"
        })
      end

      @updated_count += 1
    end
  rescue StandardError => ex
    logger.error("Expired Member ID=#{member.id} cannot be removed - #{ex}")
    Gitlab::ErrorTracking.track_and_raise_for_dev_exception(ex)
  end
  # rubocop: enable CodeReuse/ActiveRecord
end