mirror of
https://github.com/postgres/postgres.git
synced 2025-05-18 17:41:14 +03:00
Optimize roles_is_member_of() with a Bloom filter.
When the list of roles gathered by roles_is_member_of() grows very large, a Bloom filter is created to help avoid some linear searches through the list. The threshold for creating the Bloom filter is set arbitrarily high and may require future adjustment. Suggested-by: Tom Lane Reviewed-by: Tom Lane Discussion: https://postgr.es/m/CAGvXd3OSMbJQwOSc-Tq-Ro1CAz%3DvggErdSG7pv2s6vmmTOLJSg%40mail.gmail.com
This commit is contained in:
parent
fad3b5b5ac
commit
d365ae7054
@ -36,6 +36,7 @@
|
|||||||
#include "common/hashfn.h"
|
#include "common/hashfn.h"
|
||||||
#include "foreign/foreign.h"
|
#include "foreign/foreign.h"
|
||||||
#include "funcapi.h"
|
#include "funcapi.h"
|
||||||
|
#include "lib/bloomfilter.h"
|
||||||
#include "lib/qunique.h"
|
#include "lib/qunique.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "utils/acl.h"
|
#include "utils/acl.h"
|
||||||
@ -78,6 +79,13 @@ static Oid cached_role[] = {InvalidOid, InvalidOid, InvalidOid};
|
|||||||
static List *cached_roles[] = {NIL, NIL, NIL};
|
static List *cached_roles[] = {NIL, NIL, NIL};
|
||||||
static uint32 cached_db_hash;
|
static uint32 cached_db_hash;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the list of roles gathered by roles_is_member_of() grows larger than the
|
||||||
|
* below threshold, a Bloom filter is created to speed up list membership
|
||||||
|
* checks. This threshold is set arbitrarily high to avoid the overhead of
|
||||||
|
* creating the Bloom filter until it seems likely to provide a net benefit.
|
||||||
|
*/
|
||||||
|
#define ROLES_LIST_BLOOM_THRESHOLD 1024
|
||||||
|
|
||||||
static const char *getid(const char *s, char *n, Node *escontext);
|
static const char *getid(const char *s, char *n, Node *escontext);
|
||||||
static void putid(char *p, const char *s);
|
static void putid(char *p, const char *s);
|
||||||
@ -4918,6 +4926,53 @@ RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
|
|||||||
cached_role[ROLERECURSE_SETROLE] = InvalidOid;
|
cached_role[ROLERECURSE_SETROLE] = InvalidOid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A helper function for roles_is_member_of() that provides an optimized
|
||||||
|
* implementation of list_append_unique_oid() via a Bloom filter. The caller
|
||||||
|
* (i.e., roles_is_member_of()) is responsible for freeing bf once it is done
|
||||||
|
* using this function.
|
||||||
|
*/
|
||||||
|
static inline List *
|
||||||
|
roles_list_append(List *roles_list, bloom_filter **bf, Oid role)
|
||||||
|
{
|
||||||
|
unsigned char *roleptr = (unsigned char *) &role;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If there is a previously-created Bloom filter, use it to try to
|
||||||
|
* determine whether the role is missing from the list. If it says yes,
|
||||||
|
* that's a hard fact and we can go ahead and add the role. If it says
|
||||||
|
* no, that's only probabilistic and we'd better search the list. Without
|
||||||
|
* a filter, we must always do an ordinary linear search through the
|
||||||
|
* existing list.
|
||||||
|
*/
|
||||||
|
if ((*bf && bloom_lacks_element(*bf, roleptr, sizeof(Oid))) ||
|
||||||
|
!list_member_oid(roles_list, role))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* If the list is large, we take on the overhead of creating and
|
||||||
|
* populating a Bloom filter to speed up future calls to this
|
||||||
|
* function.
|
||||||
|
*/
|
||||||
|
if (*bf == NULL &&
|
||||||
|
list_length(roles_list) > ROLES_LIST_BLOOM_THRESHOLD)
|
||||||
|
{
|
||||||
|
*bf = bloom_create(ROLES_LIST_BLOOM_THRESHOLD * 10, work_mem, 0);
|
||||||
|
foreach_oid(roleid, roles_list)
|
||||||
|
bloom_add_element(*bf, (unsigned char *) &roleid, sizeof(Oid));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Finally, add the role to the list and the Bloom filter, if it
|
||||||
|
* exists.
|
||||||
|
*/
|
||||||
|
roles_list = lappend_oid(roles_list, role);
|
||||||
|
if (*bf)
|
||||||
|
bloom_add_element(*bf, roleptr, sizeof(Oid));
|
||||||
|
}
|
||||||
|
|
||||||
|
return roles_list;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get a list of roles that the specified roleid is a member of
|
* Get a list of roles that the specified roleid is a member of
|
||||||
*
|
*
|
||||||
@ -4946,6 +5001,7 @@ roles_is_member_of(Oid roleid, enum RoleRecurseType type,
|
|||||||
ListCell *l;
|
ListCell *l;
|
||||||
List *new_cached_roles;
|
List *new_cached_roles;
|
||||||
MemoryContext oldctx;
|
MemoryContext oldctx;
|
||||||
|
bloom_filter *bf = NULL;
|
||||||
|
|
||||||
Assert(OidIsValid(admin_of) == PointerIsValid(admin_role));
|
Assert(OidIsValid(admin_of) == PointerIsValid(admin_role));
|
||||||
if (admin_role != NULL)
|
if (admin_role != NULL)
|
||||||
@ -5023,16 +5079,22 @@ roles_is_member_of(Oid roleid, enum RoleRecurseType type,
|
|||||||
* graph, we must test for having already seen this role. It is
|
* graph, we must test for having already seen this role. It is
|
||||||
* legal for instance to have both A->B and A->C->B.
|
* legal for instance to have both A->B and A->C->B.
|
||||||
*/
|
*/
|
||||||
roles_list = list_append_unique_oid(roles_list, otherid);
|
roles_list = roles_list_append(roles_list, &bf, otherid);
|
||||||
}
|
}
|
||||||
ReleaseSysCacheList(memlist);
|
ReleaseSysCacheList(memlist);
|
||||||
|
|
||||||
/* implement pg_database_owner implicit membership */
|
/* implement pg_database_owner implicit membership */
|
||||||
if (memberid == dba && OidIsValid(dba))
|
if (memberid == dba && OidIsValid(dba))
|
||||||
roles_list = list_append_unique_oid(roles_list,
|
roles_list = roles_list_append(roles_list, &bf,
|
||||||
ROLE_PG_DATABASE_OWNER);
|
ROLE_PG_DATABASE_OWNER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free the Bloom filter created by roles_list_append(), if there is one.
|
||||||
|
*/
|
||||||
|
if (bf)
|
||||||
|
bloom_free(bf);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copy the completed list into TopMemoryContext so it will persist.
|
* Copy the completed list into TopMemoryContext so it will persist.
|
||||||
*/
|
*/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user