[ARVADOS] created: 1.3.0-490-g7a90bba15

Git user git at public.curoverse.com
Tue Mar 12 08:36:03 EDT 2019


        at  7a90bba153cdc9732beeef7cfba41f2f700dfd23 (commit)


commit 7a90bba153cdc9732beeef7cfba41f2f700dfd23
Author: Lucas Di Pentima <ldipentima at veritasgenetics.com>
Date:   Tue Mar 12 09:33:30 2019 -0300

    13593: Adds 'async' parameter to groups create & update calls for perms updates.
    
    When async=true, the permission graph update will be deferred by the configured
    amount of seconds at 'async_permissions_update_interval' (default: 60)
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <ldipentima at veritasgenetics.com>

diff --git a/services/api/app/controllers/application_controller.rb b/services/api/app/controllers/application_controller.rb
index 3cfe5b54f..6dbba1a24 100644
--- a/services/api/app/controllers/application_controller.rb
+++ b/services/api/app/controllers/application_controller.rb
@@ -164,6 +164,10 @@ class ApplicationController < ActionController::Base
     send_error("Path not found", status: 404)
   end
 
+  def render_accepted
+    send_json ({accepted: true}), status: 202
+  end
+
   protected
 
   def send_error(*args)
diff --git a/services/api/app/controllers/arvados/v1/groups_controller.rb b/services/api/app/controllers/arvados/v1/groups_controller.rb
index 98989db07..6163f893c 100644
--- a/services/api/app/controllers/arvados/v1/groups_controller.rb
+++ b/services/api/app/controllers/arvados/v1/groups_controller.rb
@@ -33,6 +33,57 @@ class Arvados::V1::GroupsController < ApplicationController
     params
   end
 
+  def self._create_requires_parameters
+    super.merge(
+      {
+        async: {
+          required: false,
+          type: 'boolean',
+          location: 'query',
+          default: false,
+          description: 'defer permissions update'
+        }
+      }
+    )
+  end
+
+  def self._update_requires_parameters
+    super.merge(
+      {
+        async: {
+          required: false,
+          type: 'boolean',
+          location: 'query',
+          default: false,
+          description: 'defer permissions update'
+        }
+      }
+    )
+  end
+
+  def create
+    if params[:async]
+      @object = model_class.new(resource_attrs.merge({async_permissions_update: true}))
+      @object.save!
+      render_accepted
+    else
+      super
+    end
+  end
+
+  def update
+    if params[:async]
+      attrs_to_update = resource_attrs.reject { |k, v|
+        [:kind, :etag, :href].index k
+      }.merge({async_permissions_update: true})
+      @object.update_attributes!(attrs_to_update)
+      @object.save!
+      render_accepted
+    else
+      super
+    end
+  end
+
   def render_404_if_no_object
     if params[:action] == 'contents'
       if !params[:uuid]
diff --git a/services/api/app/models/arvados_model.rb b/services/api/app/models/arvados_model.rb
index eea95e2be..2002e90ac 100644
--- a/services/api/app/models/arvados_model.rb
+++ b/services/api/app/models/arvados_model.rb
@@ -41,6 +41,11 @@ class ArvadosModel < ActiveRecord::Base
            class_name: 'Link',
            primary_key: :uuid)
 
+  # If async is true at create or update, permission graph
+  # update is deferred allowing making multiple calls without the performance
+  # penalty.
+  attr_accessor :async_permissions_update
+
   class PermissionDeniedError < RequestError
     def http_status
       403
diff --git a/services/api/app/models/group.rb b/services/api/app/models/group.rb
index 7a7f0a3a6..f4d5ec589 100644
--- a/services/api/app/models/group.rb
+++ b/services/api/app/models/group.rb
@@ -40,7 +40,7 @@ class Group < ArvadosModel
   def invalidate_permissions_cache
     # Ensure a new group can be accessed by the appropriate users
     # immediately after being created.
-    User.invalidate_permissions_cache db_current_time.to_i
+    User.invalidate_permissions_cache db_current_time.to_i, self.async_permissions_update
   end
 
   def assign_name
diff --git a/services/api/app/models/user.rb b/services/api/app/models/user.rb
index e62150541..2f3c6c89c 100644
--- a/services/api/app/models/user.rb
+++ b/services/api/app/models/user.rb
@@ -141,12 +141,12 @@ class User < ArvadosModel
     true
   end
 
-  def self.invalidate_permissions_cache(timestamp=nil)
+  def self.invalidate_permissions_cache(timestamp=nil, async=false)
     if Rails.configuration.async_permissions_update
       timestamp = DbCurrentTime::db_current_time.to_i if timestamp.nil?
       connection.execute "NOTIFY invalidate_permissions_cache, '#{timestamp}'"
     else
-      refresh_permission_view
+      refresh_permission_view(async)
     end
   end
 
diff --git a/services/api/config/application.default.yml b/services/api/config/application.default.yml
index d0f3a4cae..0d44ef447 100644
--- a/services/api/config/application.default.yml
+++ b/services/api/config/application.default.yml
@@ -189,6 +189,11 @@ common:
   # arrived, and deleted if their delete_at time has arrived.
   trash_sweep_interval: 60
 
+  # Interval (seconds) between asynchronous permission view updates. Any
+  # permission-updating API called with the 'async' parameter schedules a an
+  # update on the permission view in the future, if not already scheduled.
+  async_permissions_update_interval: 60
+
   # Maximum characters of (JSON-encoded) query parameters to include
   # in each request log entry. When params exceed this size, they will
   # be JSON-encoded, truncated to this size, and logged as
diff --git a/services/api/lib/refresh_permission_view.rb b/services/api/lib/refresh_permission_view.rb
index 4ee45ab08..cfd7b6ae5 100644
--- a/services/api/lib/refresh_permission_view.rb
+++ b/services/api/lib/refresh_permission_view.rb
@@ -4,9 +4,32 @@
 
 PERMISSION_VIEW = "materialized_permission_view"
 
-def refresh_permission_view
+def do_refresh_permission_view
   ActiveRecord::Base.transaction do
     ActiveRecord::Base.connection.execute("LOCK TABLE permission_refresh_lock")
     ActiveRecord::Base.connection.execute("REFRESH MATERIALIZED VIEW #{PERMISSION_VIEW}")
   end
 end
+
+def refresh_permission_view(async=false)
+  if async and Rails.configuration.async_permissions_update_interval > 0
+    exp = Rails.configuration.async_permissions_update_interval.seconds
+    Rails.cache.fetch('AsyncRefreshPermissionView', expires_in: exp) do
+      # Schedule a new permission update and return immediately
+      Thread.new do
+        Thread.current.abort_on_exception = false
+        begin
+          sleep(exp)
+          do_refresh_permission_view
+        rescue => e
+          Rails.logger.error "Updating permission view: #{e}\n#{e.backtrace.join("\n\t")}"
+        ensure
+          ActiveRecord::Base.connection.close
+        end
+      end
+      true
+    end
+  else
+    do_refresh_permission_view
+  end
+end
diff --git a/services/api/test/functional/arvados/v1/groups_controller_test.rb b/services/api/test/functional/arvados/v1/groups_controller_test.rb
index 55493046e..564a94d51 100644
--- a/services/api/test/functional/arvados/v1/groups_controller_test.rb
+++ b/services/api/test/functional/arvados/v1/groups_controller_test.rb
@@ -403,6 +403,20 @@ class Arvados::V1::GroupsControllerTest < ActionController::TestCase
                  new_project['name'])
   end
 
+  test "create request with async=true returns 202 Accepted" do
+    name = "Random group #{rand(1000)}"
+    assert_equal nil, Group.find_by_name(name)
+    authorize_with :active
+    post :create, {
+      group: {
+        name: name
+      },
+      async: true
+    }
+    assert_response :accepted
+    assert_not_equal nil, Group.find_by_name(name)
+  end
+
   test "unsharing a project results in hiding it from previously shared user" do
     # remove sharing link for project
     @controller = Arvados::V1::LinksController.new

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


hooks/post-receive
-- 




More information about the arvados-commits mailing list