[arvados] created: 2.6.0-98-g505c7b69a

git repository hosting git at public.arvados.org
Thu May 4 14:43:52 UTC 2023


        at  505c7b69ad3c29f2ec0413af0d8974e084a7669d (commit)


commit 505c7b69ad3c29f2ec0413af0d8974e084a7669d
Author: Peter Amstutz <peter.amstutz at curii.com>
Date:   Wed May 3 23:11:14 2023 -0400

    20472: Add a few comments and add container_tree function
    
    Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <peter.amstutz at curii.com>

diff --git a/services/api/db/migrate/20230503224107_priority_update_functions.rb b/services/api/db/migrate/20230503224107_priority_update_functions.rb
index dc9717f67..df6f9d0bb 100644
--- a/services/api/db/migrate/20230503224107_priority_update_functions.rb
+++ b/services/api/db/migrate/20230503224107_priority_update_functions.rb
@@ -8,6 +8,10 @@ class PriorityUpdateFunctions < ActiveRecord::Migration[5.2]
 CREATE OR REPLACE FUNCTION container_priority(for_container_uuid character varying, inherited bigint, inherited_from character varying) returns bigint
     LANGUAGE sql
     AS $$
+/* Determine the priority of an individual container.
+   The "inherited" priority comes from the path we followed from the root, the parent container
+   priority hasn't been updated in the table yet but we need to behave it like it has been.
+*/
 select coalesce(max(case when container_requests.priority = 0 then 0
                          when containers.uuid = inherited_from then inherited
                          when containers.priority is not NULL then containers.priority
@@ -22,6 +26,10 @@ $$;
 CREATE OR REPLACE FUNCTION update_priorities(for_container_uuid character varying) returns table (pri_container_uuid character varying, upd_priority bigint)
     LANGUAGE sql
     AS $$
+/* Calculate the priorities of all containers starting from for_container_uuid.
+   This traverses the process tree downward and calls container_priority for each container
+   and returns a table of container uuids and their new priorities.
+*/
 with recursive tab(upd_container_uuid, upd_priority) as (
   select for_container_uuid, container_priority(for_container_uuid, 0, '')
 union
@@ -32,11 +40,31 @@ union
 )
 select upd_container_uuid, upd_priority from tab;
 $$;
+}
+
+    ActiveRecord::Base.connection.execute %{
+CREATE OR REPLACE FUNCTION container_tree(for_container_uuid character varying) returns table (pri_container_uuid character varying)
+    LANGUAGE sql
+    AS $$
+/* A lighter weight version of the update_priorities query that only returns the containers in a tree,
+   used by SELECT FOR UPDATE.
+*/
+with recursive tab(upd_container_uuid) as (
+  select for_container_uuid
+union
+  select containers.uuid
+  from (tab join container_requests on tab.upd_container_uuid = container_requests.requesting_container_uuid) as child_requests
+  join containers on child_requests.container_uuid = containers.uuid
+  where containers.state in ('Queued', 'Locked', 'Running')
+)
+select upd_container_uuid from tab;
+$$;
 }
   end
 
   def down
     ActiveRecord::Base.connection.execute "DROP FUNCTION container_priority"
     ActiveRecord::Base.connection.execute "DROP FUNCTION update_priorities"
+    ActiveRecord::Base.connection.execute "DROP FUNCTION container_tree"
   end
 end
diff --git a/services/api/db/structure.sql b/services/api/db/structure.sql
index 1b0eab5f6..ffb581baf 100644
--- a/services/api/db/structure.sql
+++ b/services/api/db/structure.sql
@@ -197,6 +197,10 @@ $$;
 CREATE FUNCTION public.container_priority(for_container_uuid character varying, inherited bigint, inherited_from character varying) RETURNS bigint
     LANGUAGE sql
     AS $$
+/* Determine the priority of an individual container.
+   The "inherited" priority comes from the path we followed from the root, the parent container
+   priority hasn't been updated in the table yet but we need to behave it like it has been.
+*/
 select coalesce(max(case when container_requests.priority = 0 then 0
                          when containers.uuid = inherited_from then inherited
                          when containers.priority is not NULL then containers.priority
@@ -207,6 +211,28 @@ select coalesce(max(case when container_requests.priority = 0 then 0
 $$;
 
 
+--
+-- Name: container_tree(character varying); Type: FUNCTION; Schema: public; Owner: -
+--
+
+CREATE FUNCTION public.container_tree(for_container_uuid character varying) RETURNS TABLE(pri_container_uuid character varying)
+    LANGUAGE sql
+    AS $$
+/* A lighter weight version of the update_priorities query that only returns the containers in a tree,
+   used by SELECT FOR UPDATE.
+*/
+with recursive tab(upd_container_uuid) as (
+  select for_container_uuid
+union
+  select containers.uuid
+  from (tab join container_requests on tab.upd_container_uuid = container_requests.requesting_container_uuid) as child_requests
+  join containers on child_requests.container_uuid = containers.uuid
+  where containers.state in ('Queued', 'Locked', 'Running')
+)
+select upd_container_uuid from tab;
+$$;
+
+
 --
 -- Name: project_subtree_with_is_frozen(character varying, boolean); Type: FUNCTION; Schema: public; Owner: -
 --
@@ -276,6 +302,10 @@ $$;
 CREATE FUNCTION public.update_priorities(for_container_uuid character varying) RETURNS TABLE(pri_container_uuid character varying, upd_priority bigint)
     LANGUAGE sql
     AS $$
+/* Calculate the priorities of all containers starting from for_container_uuid.
+   This traverses the process tree downward and calls container_priority for each container
+   and returns a table of container uuids and their new priorities.
+*/
 with recursive tab(upd_container_uuid, upd_priority) as (
   select for_container_uuid, container_priority(for_container_uuid, 0, '')
 union
diff --git a/services/api/lib/update_priorities.rb b/services/api/lib/update_priorities.rb
index bd5b71b90..b7799cb4b 100644
--- a/services/api/lib/update_priorities.rb
+++ b/services/api/lib/update_priorities.rb
@@ -5,12 +5,12 @@
 def update_priorities starting_container_uuid
   ActiveRecord::Base.connection.exec_query %{
 update containers set priority=computed.upd_priority from (select pri_container_uuid, upd_priority from update_priorities($1) order by pri_container_uuid) as computed
- where containers.uuid = computed.pri_container_uuid
+ where containers.uuid = computed.pri_container_uuid and priority != computed.upd_priority
 }, 'update_priorities', [[nil, starting_container_uuid]]
 end
 
 def row_lock_for_priority_update container_uuid
   ActiveRecord::Base.connection.exec_query %{
-        select 1 from containers where containers.uuid in (select pri_container_uuid from update_priorities($1)) order by containers.uuid for update
+        select 1 from containers where containers.uuid in (select pri_container_uuid from container_tree($1)) order by containers.uuid for update
   }, 'select_for_update_priorities', [[nil, container_uuid]]
 end

commit 9429a5c85024430b96e2349f3ec36cc2161058b2
Author: Peter Amstutz <peter.amstutz at curii.com>
Date:   Wed May 3 22:58:15 2023 -0400

    20472: Inherit priority being propagated down
    
    Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <peter.amstutz at curii.com>

diff --git a/services/api/app/controllers/arvados/v1/container_requests_controller.rb b/services/api/app/controllers/arvados/v1/container_requests_controller.rb
index afddd5686..69729162f 100644
--- a/services/api/app/controllers/arvados/v1/container_requests_controller.rb
+++ b/services/api/app/controllers/arvados/v1/container_requests_controller.rb
@@ -2,6 +2,8 @@
 #
 # SPDX-License-Identifier: AGPL-3.0
 
+require 'update_priorities'
+
 class Arvados::V1::ContainerRequestsController < ApplicationController
   accept_attribute_as_json :environment, Hash
   accept_attribute_as_json :mounts, Hash
@@ -29,4 +31,18 @@ class Arvados::V1::ContainerRequestsController < ApplicationController
       })
   end
 
+  def update
+    if (resource_attrs.keys - [:owner_uuid, :name, :description, :properties]).empty? or @object.container_uuid.nil?
+      # If no attributes are being updated besides these, there are no
+      # cascading changes to other rows/tables, so we should just use
+      # row locking.
+      super
+    else
+      # Lock containers table to avoid deadlock in cascading priority update (see #20240)
+      Container.transaction do
+        row_lock_for_priority_update @object.container_uuid
+        super
+      end
+    end
+  end
 end
diff --git a/services/api/app/controllers/arvados/v1/containers_controller.rb b/services/api/app/controllers/arvados/v1/containers_controller.rb
index a66271f8c..8e36cdc0d 100644
--- a/services/api/app/controllers/arvados/v1/containers_controller.rb
+++ b/services/api/app/controllers/arvados/v1/containers_controller.rb
@@ -2,6 +2,8 @@
 #
 # SPDX-License-Identifier: AGPL-3.0
 
+require 'update_priorities'
+
 class Arvados::V1::ContainersController < ApplicationController
   accept_attribute_as_json :environment, Hash
   accept_attribute_as_json :mounts, Hash
@@ -28,6 +30,22 @@ class Arvados::V1::ContainersController < ApplicationController
     show
   end
 
+  def update
+    if (resource_attrs.keys - [:cost, :gateway_address, :output_properties, :progress, :runtime_status]).empty?
+      # If no attributes are being updated besides these, there are no
+      # cascading changes to other rows/tables, so we should just use
+      # row locking.
+      super
+    else
+      Container.transaction do
+        # Get locks ahead of time to avoid deadlock in cascading priority
+        # update
+        row_lock_for_priority_update @object.uuid
+        super
+      end
+    end
+  end
+
   def find_objects_for_index
     super
     if action_name == 'lock' || action_name == 'unlock'
diff --git a/services/api/app/models/container.rb b/services/api/app/models/container.rb
index 6301fe808..d897ff7af 100644
--- a/services/api/app/models/container.rb
+++ b/services/api/app/models/container.rb
@@ -130,6 +130,7 @@ class Container < ArvadosModel
   # user-assigned priority and request creation time.
   def update_priority!
     update_priorities uuid
+    reload
   end
 
   # Create a new container (or find an existing one) to satisfy the
diff --git a/services/api/db/migrate/20230503224107_priority_update_functions.rb b/services/api/db/migrate/20230503224107_priority_update_functions.rb
index fbb3e27bf..dc9717f67 100644
--- a/services/api/db/migrate/20230503224107_priority_update_functions.rb
+++ b/services/api/db/migrate/20230503224107_priority_update_functions.rb
@@ -5,11 +5,12 @@
 class PriorityUpdateFunctions < ActiveRecord::Migration[5.2]
   def up
     ActiveRecord::Base.connection.execute %{
-CREATE OR REPLACE FUNCTION container_priority(for_container_uuid character varying, inherited bigint) returns bigint
+CREATE OR REPLACE FUNCTION container_priority(for_container_uuid character varying, inherited bigint, inherited_from character varying) returns bigint
     LANGUAGE sql
     AS $$
 select coalesce(max(case when container_requests.priority = 0 then 0
-                         when containers.priority is not NULL then greatest(containers.priority, inherited)
+                         when containers.uuid = inherited_from then inherited
+                         when containers.priority is not NULL then containers.priority
                          else container_requests.priority * 1125899906842624::bigint - (extract(epoch from container_requests.created_at)*1000)::bigint
                     end), 0) from
     container_requests left outer join containers on container_requests.requesting_container_uuid = containers.uuid
@@ -18,13 +19,13 @@ $$;
 }
 
     ActiveRecord::Base.connection.execute %{
-CREATE OR REPLACE FUNCTION update_priorities(for_container_uuid character varying) returns table (pri_container_uuid character varying, priority bigint)
+CREATE OR REPLACE FUNCTION update_priorities(for_container_uuid character varying) returns table (pri_container_uuid character varying, upd_priority bigint)
     LANGUAGE sql
     AS $$
 with recursive tab(upd_container_uuid, upd_priority) as (
-  select for_container_uuid, container_priority(for_container_uuid, 0)
+  select for_container_uuid, container_priority(for_container_uuid, 0, '')
 union
-  select containers.uuid, container_priority(containers.uuid, child_requests.upd_priority)
+  select containers.uuid, container_priority(containers.uuid, child_requests.upd_priority, child_requests.upd_container_uuid)
   from (tab join container_requests on tab.upd_container_uuid = container_requests.requesting_container_uuid) as child_requests
   join containers on child_requests.container_uuid = containers.uuid
   where containers.state in ('Queued', 'Locked', 'Running')
diff --git a/services/api/db/structure.sql b/services/api/db/structure.sql
index 3dd705a80..1b0eab5f6 100644
--- a/services/api/db/structure.sql
+++ b/services/api/db/structure.sql
@@ -190,6 +190,23 @@ case (edges.edge_id = perm_edge_id)
 $$;
 
 
+--
+-- Name: container_priority(character varying, bigint, character varying); Type: FUNCTION; Schema: public; Owner: -
+--
+
+CREATE FUNCTION public.container_priority(for_container_uuid character varying, inherited bigint, inherited_from character varying) RETURNS bigint
+    LANGUAGE sql
+    AS $$
+select coalesce(max(case when container_requests.priority = 0 then 0
+                         when containers.uuid = inherited_from then inherited
+                         when containers.priority is not NULL then containers.priority
+                         else container_requests.priority * 1125899906842624::bigint - (extract(epoch from container_requests.created_at)*1000)::bigint
+                    end), 0) from
+    container_requests left outer join containers on container_requests.requesting_container_uuid = containers.uuid
+    where container_requests.container_uuid = for_container_uuid and container_requests.state = 'Committed' and container_requests.priority > 0;
+$$;
+
+
 --
 -- Name: project_subtree_with_is_frozen(character varying, boolean); Type: FUNCTION; Schema: public; Owner: -
 --
@@ -252,8 +269,29 @@ select starting_uuid like '_____-j7d0g-_______________' or
 $$;
 
 
+--
+-- Name: update_priorities(character varying); Type: FUNCTION; Schema: public; Owner: -
+--
+
+CREATE FUNCTION public.update_priorities(for_container_uuid character varying) RETURNS TABLE(pri_container_uuid character varying, upd_priority bigint)
+    LANGUAGE sql
+    AS $$
+with recursive tab(upd_container_uuid, upd_priority) as (
+  select for_container_uuid, container_priority(for_container_uuid, 0, '')
+union
+  select containers.uuid, container_priority(containers.uuid, child_requests.upd_priority, child_requests.upd_container_uuid)
+  from (tab join container_requests on tab.upd_container_uuid = container_requests.requesting_container_uuid) as child_requests
+  join containers on child_requests.container_uuid = containers.uuid
+  where containers.state in ('Queued', 'Locked', 'Running')
+)
+select upd_container_uuid, upd_priority from tab;
+$$;
+
+
 SET default_tablespace = '';
 
+SET default_with_oids = false;
+
 --
 -- Name: api_client_authorizations; Type: TABLE; Schema: public; Owner: -
 --
@@ -3202,6 +3240,7 @@ INSERT INTO "schema_migrations" (version) VALUES
 ('20220804133317'),
 ('20221219165512'),
 ('20221230155924'),
-('20230421142716');
+('20230421142716'),
+('20230503224107');
 
 
diff --git a/services/api/lib/update_priorities.rb b/services/api/lib/update_priorities.rb
index 66d307436..bd5b71b90 100644
--- a/services/api/lib/update_priorities.rb
+++ b/services/api/lib/update_priorities.rb
@@ -4,7 +4,13 @@
 
 def update_priorities starting_container_uuid
   ActiveRecord::Base.connection.exec_query %{
-update containers set priority=computed.priority from (select pri_container_uuid, priority from update_priorities($1) order by pri_container_uuid) as computed
+update containers set priority=computed.upd_priority from (select pri_container_uuid, upd_priority from update_priorities($1) order by pri_container_uuid) as computed
  where containers.uuid = computed.pri_container_uuid
 }, 'update_priorities', [[nil, starting_container_uuid]]
 end
+
+def row_lock_for_priority_update container_uuid
+  ActiveRecord::Base.connection.exec_query %{
+        select 1 from containers where containers.uuid in (select pri_container_uuid from update_priorities($1)) order by containers.uuid for update
+  }, 'select_for_update_priorities', [[nil, container_uuid]]
+end
diff --git a/services/api/test/unit/container_request_test.rb b/services/api/test/unit/container_request_test.rb
index 006bb7941..931176b8b 100644
--- a/services/api/test/unit/container_request_test.rb
+++ b/services/api/test/unit/container_request_test.rb
@@ -466,6 +466,16 @@ class ContainerRequestTest < ActiveSupport::TestCase
     assert_operator shared_grandchild.priority, :<=, grandchildren[2].priority
     assert_operator shared_grandchild.priority, :<=, children[2].priority
     assert_operator shared_grandchild.priority, :<=, parents[2].priority
+
+    # cancelling the most recent toplevel container should
+    # reprioritize all of its descendants (except the shared
+    # grandchild) to zero
+    toplevel_crs[2].update_attributes!(priority: 0)
+    (parents + children + grandchildren + [shared_grandchild]).map(&:reload)
+    assert_operator 0, :==, parents[2].priority
+    assert_operator 0, :==, children[2].priority
+    assert_operator 0, :==, grandchildren[2].priority
+    assert_operator shared_grandchild.priority, :==, grandchildren[0].priority
   end
 
   [

commit 4ab2fa0e6033a7c638a80f3ba73f220d8181fa3c
Author: Peter Amstutz <peter.amstutz at curii.com>
Date:   Wed May 3 21:31:06 2023 -0400

    20472: Remove special handling of update_priority
    
    Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <peter.amstutz at curii.com>

diff --git a/services/api/app/controllers/arvados/v1/containers_controller.rb b/services/api/app/controllers/arvados/v1/containers_controller.rb
index b7dc03022..a66271f8c 100644
--- a/services/api/app/controllers/arvados/v1/containers_controller.rb
+++ b/services/api/app/controllers/arvados/v1/containers_controller.rb
@@ -34,11 +34,6 @@ class Arvados::V1::ContainersController < ApplicationController
       # Avoid loading more fields than we need
       @objects = @objects.select(:id, :uuid, :state, :priority, :auth_uuid, :locked_by_uuid, :lock_count)
       @select = %w(uuid state priority auth_uuid locked_by_uuid)
-    elsif action_name == 'update_priority'
-      # We're going to reload(lock: true) in the handler, which will
-      # select all attributes, but will fail if we don't select :id
-      # now.
-      @objects = @objects.select(:id, :uuid)
     end
   end
 

commit bc882be0f75601397fedb7ad16d3540df015256f
Author: Peter Amstutz <peter.amstutz at curii.com>
Date:   Wed May 3 21:17:27 2023 -0400

    20470: Remove locks on containers table
    
    Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <peter.amstutz at curii.com>

diff --git a/services/api/app/controllers/arvados/v1/container_requests_controller.rb b/services/api/app/controllers/arvados/v1/container_requests_controller.rb
index 586567cb2..afddd5686 100644
--- a/services/api/app/controllers/arvados/v1/container_requests_controller.rb
+++ b/services/api/app/controllers/arvados/v1/container_requests_controller.rb
@@ -29,27 +29,4 @@ class Arvados::V1::ContainerRequestsController < ApplicationController
       })
   end
 
-  def create
-    # Lock containers table to avoid deadlock in cascading priority update (see #20240)
-    Container.transaction do
-      ActiveRecord::Base.connection.execute "LOCK TABLE containers IN EXCLUSIVE MODE"
-      super
-    end
-  end
-
-  def update
-    if (resource_attrs.keys - [:owner_uuid, :name, :description, :properties]).empty?
-      # If no attributes are being updated besides these, there are no
-      # cascading changes to other rows/tables, so we should just use
-      # row locking.
-      @object.reload(lock: true)
-      super
-    else
-      # Lock containers table to avoid deadlock in cascading priority update (see #20240)
-      Container.transaction do
-        ActiveRecord::Base.connection.execute "LOCK TABLE containers IN EXCLUSIVE MODE"
-        super
-      end
-    end
-  end
 end
diff --git a/services/api/app/controllers/arvados/v1/containers_controller.rb b/services/api/app/controllers/arvados/v1/containers_controller.rb
index 83f99bf92..b7dc03022 100644
--- a/services/api/app/controllers/arvados/v1/containers_controller.rb
+++ b/services/api/app/controllers/arvados/v1/containers_controller.rb
@@ -28,23 +28,6 @@ class Arvados::V1::ContainersController < ApplicationController
     show
   end
 
-  def update
-    if (resource_attrs.keys - [:cost, :gateway_address, :output_properties, :progress, :runtime_status]).empty?
-      # If no attributes are being updated besides these, there are no
-      # cascading changes to other rows/tables, so we should just use
-      # row locking.
-      @object.reload(lock: true)
-      super
-    else
-      # Lock containers table to avoid deadlock in cascading priority
-      # update (see #20240)
-      Container.transaction do
-        ActiveRecord::Base.connection.execute "LOCK TABLE containers IN EXCLUSIVE MODE"
-        super
-      end
-    end
-  end
-
   def find_objects_for_index
     super
     if action_name == 'lock' || action_name == 'unlock'
@@ -70,13 +53,8 @@ class Arvados::V1::ContainersController < ApplicationController
   end
 
   def update_priority
-    # Lock containers table to avoid deadlock in cascading priority update (see #20240)
-    Container.transaction do
-      ActiveRecord::Base.connection.execute "LOCK TABLE containers IN EXCLUSIVE MODE"
-      @object.reload(lock: true)
-      @object.update_priority!
-      show
-    end
+    @object.update_priority!
+    show
   end
 
   def current

commit 5be45d68cc47f06314339cc8aac9e39ae267ef85
Author: Peter Amstutz <peter.amstutz at curii.com>
Date:   Wed May 3 21:13:06 2023 -0400

    20470: Update priorities with a single stored query
    
    Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <peter.amstutz at curii.com>

diff --git a/services/api/app/models/container.rb b/services/api/app/models/container.rb
index 61557eacb..6301fe808 100644
--- a/services/api/app/models/container.rb
+++ b/services/api/app/models/container.rb
@@ -5,6 +5,7 @@
 require 'log_reuse_info'
 require 'whitelist_update'
 require 'safe_json'
+require 'update_priorities'
 
 class Container < ArvadosModel
   include ArvadosModelUpdates
@@ -49,7 +50,6 @@ class Container < ArvadosModel
   before_save :clear_runtime_status_when_queued
   after_save :update_cr_logs
   after_save :handle_completed
-  after_save :propagate_priority
 
   has_many :container_requests, :foreign_key => :container_uuid, :class_name => 'ContainerRequest', :primary_key => :uuid
   belongs_to :auth, :class_name => 'ApiClientAuthorization', :foreign_key => :auth_uuid, :primary_key => :uuid
@@ -129,34 +129,7 @@ class Container < ArvadosModel
   # priority of a user-submitted request is a function of
   # user-assigned priority and request creation time.
   def update_priority!
-    return if ![Queued, Locked, Running].include?(state)
-    p = ContainerRequest.
-          where('container_uuid=? and priority>0 and state=?', uuid, ContainerRequest::Committed).
-          select("priority, requesting_container_uuid, created_at").
-          lock(true).
-          map do |cr|
-      if cr.requesting_container_uuid
-        Container.where(uuid: cr.requesting_container_uuid).pluck(:priority).first
-      else
-        (cr.priority << 50) - (cr.created_at.to_time.to_f * 1000).to_i
-      end
-    end.max || 0
-    update_attributes!(priority: p)
-  end
-
-  def propagate_priority
-    return true unless saved_change_to_priority?
-    act_as_system_user do
-      # Update the priority of child container requests to match new
-      # priority of the parent container (ignoring requests with no
-      # container assigned, because their priority doesn't matter).
-      ContainerRequest.
-        where('requesting_container_uuid = ? and state = ? and container_uuid is not null',
-              self.uuid, ContainerRequest::Committed).
-        pluck(:container_uuid).each do |container_uuid|
-        Container.find_by_uuid(container_uuid).update_priority!
-      end
-    end
+    update_priorities uuid
   end
 
   # Create a new container (or find an existing one) to satisfy the
diff --git a/services/api/app/models/container_request.rb b/services/api/app/models/container_request.rb
index 09da141ea..3c3896771 100644
--- a/services/api/app/models/container_request.rb
+++ b/services/api/app/models/container_request.rb
@@ -562,11 +562,8 @@ class ContainerRequest < ArvadosModel
 
   def update_priority
     return unless saved_change_to_state? || saved_change_to_priority? || saved_change_to_container_uuid?
-    act_as_system_user do
-      Container.
-        where('uuid in (?)', [container_uuid_before_last_save, self.container_uuid].compact).
-        map(&:update_priority!)
-    end
+    update_priorities container_uuid_before_last_save if !container_uuid_before_last_save.nil? and container_uuid_before_last_save != self.container_uuid
+    update_priorities self.container_uuid if self.container_uuid
   end
 
   def set_requesting_container_uuid
diff --git a/services/api/db/migrate/20230503224107_priority_update_functions.rb b/services/api/db/migrate/20230503224107_priority_update_functions.rb
new file mode 100644
index 000000000..fbb3e27bf
--- /dev/null
+++ b/services/api/db/migrate/20230503224107_priority_update_functions.rb
@@ -0,0 +1,41 @@
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: AGPL-3.0
+
+class PriorityUpdateFunctions < ActiveRecord::Migration[5.2]
+  def up
+    ActiveRecord::Base.connection.execute %{
+CREATE OR REPLACE FUNCTION container_priority(for_container_uuid character varying, inherited bigint) returns bigint
+    LANGUAGE sql
+    AS $$
+select coalesce(max(case when container_requests.priority = 0 then 0
+                         when containers.priority is not NULL then greatest(containers.priority, inherited)
+                         else container_requests.priority * 1125899906842624::bigint - (extract(epoch from container_requests.created_at)*1000)::bigint
+                    end), 0) from
+    container_requests left outer join containers on container_requests.requesting_container_uuid = containers.uuid
+    where container_requests.container_uuid = for_container_uuid and container_requests.state = 'Committed' and container_requests.priority > 0;
+$$;
+}
+
+    ActiveRecord::Base.connection.execute %{
+CREATE OR REPLACE FUNCTION update_priorities(for_container_uuid character varying) returns table (pri_container_uuid character varying, priority bigint)
+    LANGUAGE sql
+    AS $$
+with recursive tab(upd_container_uuid, upd_priority) as (
+  select for_container_uuid, container_priority(for_container_uuid, 0)
+union
+  select containers.uuid, container_priority(containers.uuid, child_requests.upd_priority)
+  from (tab join container_requests on tab.upd_container_uuid = container_requests.requesting_container_uuid) as child_requests
+  join containers on child_requests.container_uuid = containers.uuid
+  where containers.state in ('Queued', 'Locked', 'Running')
+)
+select upd_container_uuid, upd_priority from tab;
+$$;
+}
+  end
+
+  def down
+    ActiveRecord::Base.connection.execute "DROP FUNCTION container_priority"
+    ActiveRecord::Base.connection.execute "DROP FUNCTION update_priorities"
+  end
+end
diff --git a/services/api/lib/update_priorities.rb b/services/api/lib/update_priorities.rb
new file mode 100644
index 000000000..66d307436
--- /dev/null
+++ b/services/api/lib/update_priorities.rb
@@ -0,0 +1,10 @@
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: AGPL-3.0
+
+def update_priorities starting_container_uuid
+  ActiveRecord::Base.connection.exec_query %{
+update containers set priority=computed.priority from (select pri_container_uuid, priority from update_priorities($1) order by pri_container_uuid) as computed
+ where containers.uuid = computed.pri_container_uuid
+}, 'update_priorities', [[nil, starting_container_uuid]]
+end

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list