14878: Add comment.
[arvados.git] / services / api / lib / update_priority.rb
index 724d2b20a85dd58e79897b563e84ae86b44f83a8..c688ac008b44b21944e86b36cdb3abbb15273e12 100644 (file)
@@ -3,17 +3,41 @@
 # SPDX-License-Identifier: AGPL-3.0
 
 module UpdatePriority
-  # Clean up after races: if container priority>0 but there are no
-  # committed container requests for it, reset priority to 0.
-  def self.update_priority
+  extend CurrentApiClient
+
+  # Clean up after races.
+  #
+  # If container priority>0 but there are no committed container
+  # requests for it, reset priority to 0.
+  #
+  # If container priority=0 but there are committed container requests
+  # for it with priority>0, update priority.
+  #
+  # Normally, update_priority is a no-op if another thread/process is
+  # already updating. Test cases that need to check priorities after
+  # updating can force a (possibly overlapping) update in the current
+  # thread/transaction by setting the "nolock" flag. See #14878.
+  def self.update_priority(nolock: false)
     if !File.owned?(Rails.root.join('tmp'))
       Rails.logger.warn("UpdatePriority: not owner of #{Rails.root}/tmp, skipping")
       return
     end
     lockfile = Rails.root.join('tmp', 'update_priority.lock')
     File.open(lockfile, File::RDWR|File::CREAT, 0600) do |f|
-      return unless f.flock(File::LOCK_NB|File::LOCK_EX)
-      ActiveRecord::Base.connection.execute("UPDATE containers AS c SET priority=0 WHERE state='Queued' AND priority>0 AND uuid NOT IN (SELECT container_uuid FROM container_requests WHERE priority>0);")
+      return unless nolock || f.flock(File::LOCK_NB|File::LOCK_EX)
+
+      # priority>0 but should be 0:
+      ActiveRecord::Base.connection.
+        exec_query("UPDATE containers AS c SET priority=0 WHERE state IN ('Queued', 'Locked', 'Running') AND priority>0 AND uuid NOT IN (SELECT container_uuid FROM container_requests WHERE priority>0 AND state='Committed');", 'UpdatePriority')
+
+      # priority==0 but should be >0:
+      act_as_system_user do
+        Container.
+          joins("JOIN container_requests ON container_requests.container_uuid=containers.uuid AND container_requests.state=#{Container.sanitize(ContainerRequest::Committed)} AND container_requests.priority>0").
+          where('containers.state IN (?) AND containers.priority=0 AND container_requests.uuid IS NOT NULL',
+                [Container::Queued, Container::Locked, Container::Running]).
+          map(&:update_priority!)
+      end
     end
   end