18004: Fixes a couple of race condition bugs related to caching remote users.
[arvados.git] / services / api / app / controllers / database_controller.rb
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: AGPL-3.0
4
5 class DatabaseController < ApplicationController
6   skip_before_action :find_object_by_uuid
7   skip_before_action :render_404_if_no_object
8   before_action :admin_required
9   def reset
10     raise ArvadosModel::PermissionDeniedError unless Rails.env == 'test'
11
12     # Sanity check: If someone has actually logged in here, this might
13     # not really be a throwaway database. Client test suites should
14     # use @example.com email addresses when creating user records, so
15     # we can tell they're not valuable.
16     user_uuids = User.
17       where('email is null or (email not like ? and email not like ?)', '%@example.com', '%.example.com').
18       collect(&:uuid)
19     fixture_uuids =
20       YAML::load_file(File.expand_path('../../../test/fixtures/users.yml',
21                                        __FILE__)).
22       values.collect { |u| u['uuid'] }
23     unexpected_uuids = user_uuids - fixture_uuids
24     if unexpected_uuids.any?
25       logger.error("Running in test environment, but non-fixture users exist: " +
26                    "#{unexpected_uuids}")
27       raise ArvadosModel::PermissionDeniedError
28     end
29
30     require 'active_record/fixtures'
31
32     # What kinds of fixtures do we have?
33     fixturesets = Dir.glob(Rails.root.join('test', 'fixtures', '*.yml')).
34       collect { |yml| yml.match(/([^\/]*)\.yml$/)[1] }
35
36     # Don't reset keep_services: clients need to discover our
37     # integration-testing keepstores, not test fixtures.
38     fixturesets -= %w[keep_services]
39
40     table_names = '"' + ActiveRecord::Base.connection.tables.join('","') + '"'
41
42     attempts_left = 20
43     begin
44       ActiveRecord::Base.transaction do
45         # Avoid deadlock by locking all tables before doing anything
46         # drastic.
47         ActiveRecord::Base.connection.execute \
48         "LOCK TABLE #{table_names} IN ACCESS EXCLUSIVE MODE"
49
50         # Delete existing fixtures (and everything else) from fixture
51         # tables
52         fixturesets.each do |x|
53           x.classify.constantize.unscoped.delete_all
54         end
55
56         # create_fixtures() is a no-op for cached fixture sets, so
57         # uncache them all.
58         ActiveRecord::FixtureSet.reset_cache
59         ActiveRecord::FixtureSet.
60           create_fixtures(Rails.root.join('test', 'fixtures'), fixturesets)
61
62         # Dump cache of permissions etc.
63         Rails.cache.clear
64         ActiveRecord::Base.connection.clear_query_cache
65
66         # Reload database seeds
67         DatabaseSeeds.install
68       end
69     rescue ActiveRecord::StatementInvalid => e
70       if "#{e.inspect}" =~ /deadlock detected/i and (attempts_left -= 1) > 0
71         logger.info "Waiting for lock -- #{e.inspect}"
72         sleep 0.5
73         retry
74       end
75       raise
76     end
77
78     require 'update_permissions'
79
80     refresh_permissions
81     refresh_trashed
82
83     # Done.
84     send_json success: true
85   end
86 end