21703: Delete unused entries in uuid_locks during trash sweep.
[arvados.git] / services / api / app / controllers / sys_controller.rb
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: AGPL-3.0
4
5 class SysController < ApplicationController
6   skip_before_action :find_object_by_uuid
7   skip_before_action :render_404_if_no_object
8   before_action :admin_required
9
10   def trash_sweep
11     act_as_system_user do
12       # Sweep trashed collections
13       Collection.
14         where('delete_at is not null and delete_at < statement_timestamp()').
15         in_batches(of: 15).
16         destroy_all
17       Collection.
18         where('is_trashed = false and trash_at < statement_timestamp()').
19         in_batches(of: 15).
20         update_all('is_trashed = true')
21
22       # Want to make sure the #update_trash hook on the Group class
23       # runs.  It does a couple of important things:
24       #
25       # - For projects, puts all the subprojects in the trashed_groups table.
26       #
27       # - For role groups, outbound permissions are deleted.
28       Group.
29         where("is_trashed = false and trash_at < statement_timestamp()").each do |grp|
30         grp.is_trashed = true
31         grp.save
32       end
33
34       # Sweep groups and their contents that are ready to be deleted
35       Group.
36         where('delete_at is not null and delete_at < statement_timestamp()').each do |group|
37           delete_project_and_contents(group.uuid)
38       end
39
40       # Sweep expired tokens
41       ActiveRecord::Base.connection.execute("DELETE from api_client_authorizations where expires_at <= statement_timestamp()")
42
43       # Sweep unused uuid_locks entries
44       ActiveRecord::Base.connection.execute("DELETE FROM uuid_locks WHERE uuid IN (SELECT uuid FROM uuid_locks FOR UPDATE SKIP LOCKED)")
45     end
46     head :no_content
47   end
48
49   protected
50
51   def delete_project_and_contents(p_uuid)
52     p = Group.find_by_uuid(p_uuid)
53     if !p
54       raise "can't sweep group '#{p_uuid}', it may not exist"
55     end
56     if p.group_class == 'project'
57       # First delete sub projects and owned filter groups
58       Group.where({owner_uuid: p_uuid}).each do |sub_project|
59         delete_project_and_contents(sub_project.uuid)
60       end
61       # Next, iterate over all tables which have owner_uuid fields, with some
62       # exceptions, and delete records owned by this project
63       skipped_classes = ['Group', 'User']
64       ActiveRecord::Base.descendants.reject(&:abstract_class?).each do |klass|
65         if !skipped_classes.include?(klass.name) && klass.columns.collect(&:name).include?('owner_uuid')
66           klass.where({owner_uuid: p_uuid}).in_batches(of: 15).destroy_all
67         end
68       end
69     end
70     # Finally delete the group itself
71     p.destroy
72   end
73 end