[ARVADOS] created: 3716a6870c40f82e99a61b6fe67550d8cbb69614
Git user
git at public.curoverse.com
Wed May 4 14:55:08 EDT 2016
at 3716a6870c40f82e99a61b6fe67550d8cbb69614 (commit)
commit 3716a6870c40f82e99a61b6fe67550d8cbb69614
Author: Peter Amstutz <peter.amstutz at curoverse.com>
Date: Wed May 4 14:55:03 2016 -0400
8886: Experimental asynchronous permissions update.
Add configuration parameter 'async_permissions_update' (default false). If
true, do not delete permission cache in #invalidate_permissions_cache, but
instead trigger "NOTIFY invalidate_permissions_cache" on the database.
Add script/permission-updater.rb which runs as an independent process. It
blocks on "LISTEN invalidate_permissions_cache" and updates the permission
cache whenever notified.
This is not ready for use; in particular it creates a race condition
recomputing permissions with effects such as not being able to read back API
records that were just created.
diff --git a/services/api/app/models/user.rb b/services/api/app/models/user.rb
index 2200d05..de3d4ce 100644
--- a/services/api/app/models/user.rb
+++ b/services/api/app/models/user.rb
@@ -124,18 +124,14 @@ class User < ArvadosModel
end
def self.invalidate_permissions_cache
- Rails.cache.delete_matched(/^groups_for_user_/)
+ if Rails.configuration.async_permissions_update
+ connection.execute "NOTIFY invalidate_permissions_cache"
+ else
+ Rails.cache.delete_matched(/^groups_for_user_/) unless
+ end
end
- # Return a hash of {group_uuid: perm_hash} where perm_hash[:read]
- # and perm_hash[:write] are true if this user can read and write
- # objects owned by group_uuid.
- #
- # The permission graph is built by repeatedly enumerating all
- # permission links reachable from self.uuid, and then calling
- # search_permissions
- def group_permissions
- Rails.cache.fetch "groups_for_user_#{self.uuid}" do
+ def calculate_group_permissions
permissions_from = {}
todo = {self.uuid => true}
done = {}
@@ -182,7 +178,21 @@ class User < ArvadosModel
end
end
end
- search_permissions(self.uuid, permissions_from)
+ perms = search_permissions(self.uuid, permissions_from)
+ Rails.cache.write "groups_for_user_#{self.uuid}", perms
+ perms
+ end
+
+ # Return a hash of {group_uuid: perm_hash} where perm_hash[:read]
+ # and perm_hash[:write] are true if this user can read and write
+ # objects owned by group_uuid.
+ #
+ # The permission graph is built by repeatedly enumerating all
+ # permission links reachable from self.uuid, and then calling
+ # search_permissions
+ def group_permissions
+ Rails.cache.fetch "groups_for_user_#{self.uuid}" do
+ calculate_group_permissions
end
end
diff --git a/services/api/config/application.default.yml b/services/api/config/application.default.yml
index c709633..f1c4dd0 100644
--- a/services/api/config/application.default.yml
+++ b/services/api/config/application.default.yml
@@ -345,6 +345,12 @@ common:
crunch_log_partial_line_throttle_period: 5
+ # Enable asynchronous permission graph rebuild. Must run
+ # script/permission-updater.rb as a separate process. When the permission
+ # cache is invalidated, the background process will update the permission
+ # graph cache. This feature is experimental!
+ async_permissions_update: false
+
development:
force_ssl: false
cache_classes: false
diff --git a/services/api/script/permission-updater.rb b/services/api/script/permission-updater.rb
new file mode 100755
index 0000000..5ecab0f
--- /dev/null
+++ b/services/api/script/permission-updater.rb
@@ -0,0 +1,41 @@
+#!/usr/bin/env ruby
+
+dispatch_argv = []
+ARGV.reject! do |arg|
+ dispatch_argv.push(arg) if /^--/ =~ arg
+end
+
+ENV["RAILS_ENV"] = ARGV[0] || ENV["RAILS_ENV"] || "development"
+require File.dirname(__FILE__) + '/../config/boot'
+require File.dirname(__FILE__) + '/../config/environment'
+
+User.all.each do |u|
+ u.calculate_group_permissions
+end
+
+ActiveRecord::Base.connection_pool.with_connection do |connection|
+ conn = connection.instance_variable_get(:@connection)
+ begin
+ conn.async_exec "LISTEN invalidate_permissions_cache"
+ while true
+ # wait_for_notify will block until there is a change
+ # notification from Postgres about the logs table, then push
+ # the notification into the EventMachine channel. Each
+ # websocket connection subscribes to the other end of the
+ # channel and calls #push_events to actually dispatch the
+ # events to the client.
+ conn.wait_for_notify do |channel, pid, payload|
+ Rails.logger.info "Begin updating permission cache"
+ User.all.each do |u|
+ u.calculate_group_permissions
+ end
+ Rails.logger.info "Permission cache updated"
+ end
+ end
+ ensure
+ # Don't want the connection to still be listening once we return
+ # it to the pool - could result in weird behavior for the next
+ # thread to check it out.
+ conn.async_exec "UNLISTEN *"
+ end
+end
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list