]> git.arvados.org - arvados.git/blob - services/api/lib/update_priorities.rb
22424: Write dedicated tests for packageVersion schema
[arvados.git] / services / api / lib / update_priorities.rb
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: AGPL-3.0
4
5 def row_lock_for_priority_update container_uuid
6   # Locks all the containers under this container, and also any
7   # immediate parent containers.  This ensures we have locked
8   # everything that gets touched by either a priority update or state
9   # update.
10   # This method assumes we are already in a transaction.
11   max_retries = 6
12   conn = ActiveRecord::Base.connection
13   conn.exec_query 'SAVEPOINT row_lock_for_priority_update'
14   begin
15     conn.exec_query %{
16         select containers.uuid from containers where containers.uuid in (
17   select pri_container_uuid from container_tree($1)
18 UNION
19   select container_requests.requesting_container_uuid from container_requests
20     where container_requests.container_uuid = $1
21           and container_requests.state = 'Committed'
22           and container_requests.requesting_container_uuid is not NULL
23 )
24         order by containers.uuid for update of containers
25   }, 'select_for_update_priorities', [container_uuid]
26   rescue ActiveRecord::Deadlocked => rn
27     # bug #21540
28     #
29     # Despite deliberately taking the locks in uuid order, reportedly
30     # this method still occasionally deadlocks with another request
31     # handler that is also doing updates on the same container tree.
32     # This happens infrequently so we don't know how to reproduce it
33     # or precisely what circumstances cause it.
34     #
35     # However, in this situation it is safe to retry because this
36     # query has no effect on the database content, its only job is to
37     # acquire a set of row locks so we can safely update the container
38     # records later.
39
40     raise if max_retries == 0
41     max_retries -= 1
42
43     # Wait random 0-10 seconds then rollback and retry
44     sleep(rand(10))
45
46     conn.exec_query 'ROLLBACK TO SAVEPOINT row_lock_for_priority_update'
47     retry
48   end
49 end
50
51 def update_priorities starting_container_uuid
52   Container.transaction do
53     # Ensure the row locks were taken in order
54     row_lock_for_priority_update starting_container_uuid
55
56     ActiveRecord::Base.connection.exec_query %{
57 update containers set priority=computed.upd_priority from container_tree_priorities($1) as computed
58  where containers.uuid = computed.pri_container_uuid and priority != computed.upd_priority
59 }, 'update_priorities', [starting_container_uuid]
60   end
61 end