Merge branch '15964-fix-docs' refs #15964
[arvados.git] / services / api / lib / sweep_trashed_objects.rb
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: AGPL-3.0
4
5 require 'current_api_client'
6
7 module SweepTrashedObjects
8   extend CurrentApiClient
9
10   def self.delete_project_and_contents(p_uuid)
11     p = Group.find_by_uuid(p_uuid)
12     if !p || p.group_class != 'project'
13       raise "can't sweep group '#{p_uuid}', it may not exist or not be a project"
14     end
15     # First delete sub projects
16     Group.where({group_class: 'project', owner_uuid: p_uuid}).each do |sub_project|
17       delete_project_and_contents(sub_project.uuid)
18     end
19     # Next, iterate over all tables which have owner_uuid fields, with some
20     # exceptions, and delete records owned by this project
21     skipped_classes = ['Group', 'User']
22     ActiveRecord::Base.descendants.reject(&:abstract_class?).each do |klass|
23       if !skipped_classes.include?(klass.name) && klass.columns.collect(&:name).include?('owner_uuid')
24         klass.where({owner_uuid: p_uuid}).destroy_all
25       end
26     end
27     # Finally delete the project itself
28     p.destroy
29   end
30
31   def self.sweep_now
32     act_as_system_user do
33       # Sweep trashed collections
34       Collection.
35         where('delete_at is not null and delete_at < statement_timestamp()').
36         destroy_all
37       Collection.
38         where('is_trashed = false and trash_at < statement_timestamp()').
39         update_all('is_trashed = true')
40
41       # Sweep trashed projects and their contents
42       Group.
43         where({group_class: 'project'}).
44         where('delete_at is not null and delete_at < statement_timestamp()').each do |project|
45           delete_project_and_contents(project.uuid)
46       end
47       Group.
48         where({group_class: 'project'}).
49         where('is_trashed = false and trash_at < statement_timestamp()').
50         update_all('is_trashed = true')
51
52       # Sweep expired tokens
53       ActiveRecord::Base.connection.execute("DELETE from api_client_authorizations where expires_at <= statement_timestamp()")
54     end
55   end
56
57   def self.sweep_if_stale
58     return if Rails.configuration.Collections.TrashSweepInterval <= 0
59     exp = Rails.configuration.Collections.TrashSweepInterval.seconds
60     need = false
61     Rails.cache.fetch('SweepTrashedObjects', expires_in: exp) do
62       need = true
63     end
64     if need
65       Thread.new do
66         Thread.current.abort_on_exception = false
67         begin
68           sweep_now
69         rescue => e
70           Rails.logger.error "#{e.class}: #{e}\n#{e.backtrace.join("\n\t")}"
71         ensure
72           # Rails 5.1+ makes test threads share a database connection, so we can't
73           # close a connection shared with other threads.
74           # https://github.com/rails/rails/commit/deba47799ff905f778e0c98a015789a1327d5087
75           if Rails.env != "test"
76             ActiveRecord::Base.connection.close
77           end
78         end
79       end
80     end
81   end
82 end