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