From e711e48313021fb79e6ef65595af48c92a0caafc Mon Sep 17 00:00:00 2001 From: Peter Amstutz Date: Mon, 9 May 2016 16:40:58 -0400 Subject: [PATCH] 8886: Add timestamp checking to permission updates. --- services/api/app/models/group.rb | 4 +-- services/api/app/models/link.rb | 2 +- services/api/app/models/user.rb | 11 ++++--- services/api/script/permission-updater.rb | 35 ++++++++++++----------- 4 files changed, 29 insertions(+), 23 deletions(-) diff --git a/services/api/app/models/group.rb b/services/api/app/models/group.rb index 0e857ad15c..ac3309bb8c 100644 --- a/services/api/app/models/group.rb +++ b/services/api/app/models/group.rb @@ -20,14 +20,14 @@ class Group < ArvadosModel if uuid_changed? or owner_uuid_changed? # This can change users' permissions on other groups as well as # this one. - invalidate_permissions_cache + invalidate_permissions_cache db_current_time.to_i end end def invalidate_permissions_cache # Ensure a new group can be accessed by the appropriate users # immediately after being created. - User.invalidate_permissions_cache + User.invalidate_permissions_cache db_current_time.to_i end def assign_name diff --git a/services/api/app/models/link.rb b/services/api/app/models/link.rb index d9b8f6f09b..24872b21ec 100644 --- a/services/api/app/models/link.rb +++ b/services/api/app/models/link.rb @@ -66,7 +66,7 @@ class Link < ArvadosModel # permissions for head_uuid and tail_uuid, and invalidate the # cache for only those users. (This would require a browseable # cache.) - User.invalidate_permissions_cache + User.invalidate_permissions_cache db_current_time.to_i end end diff --git a/services/api/app/models/user.rb b/services/api/app/models/user.rb index 041557ce3f..3cb595b499 100644 --- a/services/api/app/models/user.rb +++ b/services/api/app/models/user.rb @@ -123,9 +123,9 @@ class User < ArvadosModel true end - def self.invalidate_permissions_cache + def self.invalidate_permissions_cache timestamp if Rails.configuration.async_permissions_update - connection.execute "NOTIFY invalidate_permissions_cache" + connection.execute "NOTIFY invalidate_permissions_cache, '#{timestamp}'" else Rails.cache.delete_matched(/^groups_for_user_/) end @@ -191,9 +191,12 @@ class User < ArvadosModel # 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 + r = Rails.cache.read "groups_for_user_#{self.uuid}" + while r.nil? + sleep(0.1) + r = Rails.cache.read "groups_for_user_#{self.uuid}" end + r end def self.setup(user, openid_prefix, repo_name=nil, vm_uuid=nil) diff --git a/services/api/script/permission-updater.rb b/services/api/script/permission-updater.rb index 5ecab0f94d..f7d672de3a 100755 --- a/services/api/script/permission-updater.rb +++ b/services/api/script/permission-updater.rb @@ -1,35 +1,38 @@ #!/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' +include DbCurrentTime -User.all.each do |u| - u.calculate_group_permissions +def update_permissions + timestamp = DbCurrentTime::db_current_time.to_i + Rails.logger.info "Begin updating permission cache" + User.all.each do |u| + u.calculate_group_permissions + end + Rails.cache.write "last_updated_permissions", timestamp + Rails.logger.info "Permission cache updated" end ActiveRecord::Base.connection_pool.with_connection do |connection| conn = connection.instance_variable_get(:@connection) begin conn.async_exec "LISTEN invalidate_permissions_cache" + + # Initial refresh of permissions graph + update_permissions + 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. + # notification from Postgres about the permission cache, + # and then rebuild the permission cache. conn.wait_for_notify do |channel, pid, payload| - Rails.logger.info "Begin updating permission cache" - User.all.each do |u| - u.calculate_group_permissions + last_updated = Rails.cache.read("last_updated_permissions") + Rails.logger.info "Got notify #{payload} last update #{last_updated}" + if last_updated.nil? || last_updated.to_i <= payload.to_i + update_permissions end - Rails.logger.info "Permission cache updated" end end ensure -- 2.30.2