[ARVADOS] created: 1.1.3-221-g7cf1861
Git user
git at public.curoverse.com
Fri Mar 16 14:17:55 EDT 2018
at 7cf18615c7104bb59f1be54ca35e99f7ee7c5f27 (commit)
commit 7cf18615c7104bb59f1be54ca35e99f7ee7c5f27
Author: Peter Amstutz <pamstutz at veritasgenetics.com>
Date: Fri Mar 16 14:13:37 2018 -0400
13208: Refactor permission checking to improve query performance
Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <pamstutz at veritasgenetics.com>
diff --git a/services/api/app/models/arvados_model.rb b/services/api/app/models/arvados_model.rb
index 962ca4a..6a59d3b 100644
--- a/services/api/app/models/arvados_model.rb
+++ b/services/api/app/models/arvados_model.rb
@@ -257,56 +257,66 @@ class ArvadosModel < ActiveRecord::Base
sql_table = kwargs.fetch(:table_name, table_name)
include_trash = kwargs.fetch(:include_trash, false)
- sql_conds = []
+ sql_conds = nil
user_uuids = users_list.map { |u| u.uuid }
- exclude_trashed_records = if !include_trash and (sql_table == "groups" or sql_table == "collections") then
- # Only include records that are not explicitly trashed
- "AND #{sql_table}.is_trashed = false"
- else
- ""
- end
+ exclude_trashed_records = ""
+ if !include_trash and (sql_table == "groups" or sql_table == "collections") then
+ # Only include records that are not explicitly trashed
+ exclude_trashed_records = "AND #{sql_table}.is_trashed = false"
+ end
if users_list.select { |u| u.is_admin }.any?
+ # Admin skips most permission checks, but still want to filter on trashed items.
if !include_trash
if sql_table != "api_client_authorizations"
- # Exclude rows where the owner is trashed
- sql_conds.push "NOT EXISTS(SELECT 1 "+
- "FROM #{PERMISSION_VIEW} "+
- "WHERE trashed = 1 AND "+
- "(#{sql_table}.owner_uuid = target_uuid)) "+
- exclude_trashed_records
+ # Only include records where the owner is not trashed
+ sql_conds = "NOT EXISTS(SELECT 1 FROM #{PERMISSION_VIEW} "+
+ "WHERE trashed = 1 AND "+
+ "(#{sql_table}.owner_uuid = target_uuid)) #{exclude_trashed_records}"
end
end
else
- trashed_check = if !include_trash then
- "AND trashed = 0"
- else
- ""
- end
-
- owner_check = if sql_table != "api_client_authorizations" and sql_table != "groups" then
- "OR (target_uuid = #{sql_table}.owner_uuid AND target_owner_uuid IS NOT NULL)"
- else
- ""
- end
-
- sql_conds.push "EXISTS(SELECT 1 FROM #{PERMISSION_VIEW} "+
- "WHERE user_uuid IN (:user_uuids) AND perm_level >= 1 #{trashed_check} AND (target_uuid = #{sql_table}.uuid #{owner_check})) "+
- exclude_trashed_records
+ trashed_check = ""
+ if !include_trash then
+ trashed_check = "AND trashed = 0"
+ end
+
+ # Note: it is possible to combine the direct_check and
+ # owner_check into a single EXISTS() clause, however it turns
+ # out query optimizer doesn't like it and forces a sequential
+ # table scan. Constructing the query with separate EXISTS()
+ # clauses enables it to use the index.
+ #
+ # see issue 13208 for details.
+
+ # Match a direct read permission link from the user to the record uuid
+ direct_check = "EXISTS(SELECT 1 FROM #{PERMISSION_VIEW} "+
+ "WHERE user_uuid IN (:user_uuids) AND perm_level >= 1 #{trashed_check} AND target_uuid = #{sql_table}.uuid)"
+
+ # Match a read permission link from the user to the record's owner_uuid
+ owner_check = ""
+ if sql_table != "api_client_authorizations" and sql_table != "groups" then
+ owner_check = "OR EXISTS(SELECT 1 FROM #{PERMISSION_VIEW} "+
+ "WHERE user_uuid IN (:user_uuids) AND perm_level >= 1 #{trashed_check} AND target_uuid = #{sql_table}.owner_uuid AND target_owner_uuid IS NOT NULL) "
+ end
+ links_cond = ""
if sql_table == "links"
# Match any permission link that gives one of the authorized
# users some permission _or_ gives anyone else permission to
# view one of the authorized users.
- sql_conds.push "(#{sql_table}.link_class IN (:permission_link_classes) AND "+
+ links_cond = "OR (#{sql_table}.link_class IN (:permission_link_classes) AND "+
"(#{sql_table}.head_uuid IN (:user_uuids) OR #{sql_table}.tail_uuid IN (:user_uuids)))"
end
+
+ sql_conds = "(#{direct_check} #{owner_check} #{links_cond}) #{exclude_trashed_records}"
+
end
- self.where(sql_conds.join(' OR '),
- user_uuids: user_uuids,
- permission_link_classes: ['permission', 'resources'])
+ self.where(sql_conds,
+ user_uuids: user_uuids,
+ permission_link_classes: ['permission', 'resources'])
end
def save_with_unique_name!
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list