[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