-
Notifications
You must be signed in to change notification settings - Fork 24.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use separate BitSet cache in Doc Level Security #43669
Conversation
Document level security was depending on the shared "BitsetFilterCache" which (by design) never expires its entries. However, when using DLS queries - particularly templated ones - the number (and memory usage) of generated bitsets can be significant. This change introduces a new cache specifically for BitSets used in DLS queries, that has memory usage constraints and access time expiry. The whole cache is automatically cleared if the role cache is cleared. Individual bitsets are cleared when the corresponding lucene index reader is closed.
Pinging @elastic/es-security |
@elasticmachine update branch |
I haven't written docs for these new cache config properties ( I'm curious about other people's opinions on the default values for these.
I could easily be persuaded to a different set of values, but since the main purpose of this change it to reduce the amount of memory used by DLS bitsets, we don't want to make those numbers too big. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should try to find a better way to evict on closed segments. We used to have other caches that required linear scans for eviction and this caused issues, eg. when closing a node because then all its indices would close one after the other, and since close listeners are called synchonously from IndexReader#close, this would run in O(n^2) as a function of the number of segments on the node.
|
||
@Override | ||
public void onClose(IndexReader.CacheKey ownerCoreCacheKey) { | ||
bitsetCacheHelper.removeKeysIf(key -> key.matchesIndex(ownerCoreCacheKey)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have had performance issues with using linear scans over cache keys in close listeners in the past. I'm not sure how to address it, some other caches have two levels of keys, ie. they look like a Map<IndexReader.CacheKey, Map<Query, Value>>
. But maybe we could also separately maintain a mapping between indexreader cache keys and queries that have a cache entry on that particular segment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll look at maintaining a secondary mapping. The problem with the two-level cache is that we don't have good ways to manage expiry.
The lower level caches could have access-time expiry, but it's hard to do memory-size expiry.
The upper level caches can do memory-size expiry, but will expire everything under a IndexReader.CacheKey
rather than expiring the LRU queries.
this.bitsetCache = CacheBuilder.<BitsetCacheKey, BitSet>builder() | ||
.setExpireAfterAccess(ttl) | ||
.setMaximumWeight(size.getBytes()) | ||
.weigher((key, bitSet) -> bitSet.ramBytesUsed()).build(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe return 0 if bitSet == NULL_MARKER
?
# Conflicts: # x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/accesscontrol/SecurityIndexSearcherWrapper.java # x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/accesscontrol/SecurityIndexReaderWrapperIntegrationTests.java # x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/accesscontrol/SecurityIndexSearcherWrapperUnitTests.java # x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The changes / logic looks good to me, I just left some wording nits and a suggestion about the naming of the related settings. I don't feel particularly confident with this part but thankfully @jpountz makes up for it
...ava/org/elasticsearch/xpack/core/security/authz/accesscontrol/DocumentSubsetBitsetCache.java
Outdated
Show resolved
Hide resolved
...ava/org/elasticsearch/xpack/core/security/authz/accesscontrol/DocumentSubsetBitsetCache.java
Outdated
Show resolved
Hide resolved
...in/core/src/main/java/org/elasticsearch/xpack/core/security/support/CacheIteratorHelper.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left some minor comments but otherwise it looks good to me, including the mapping from queries to cache keys.
...ava/org/elasticsearch/xpack/core/security/authz/accesscontrol/DocumentSubsetBitsetCache.java
Outdated
Show resolved
Hide resolved
...ava/org/elasticsearch/xpack/core/security/authz/accesscontrol/DocumentSubsetBitsetCache.java
Outdated
Show resolved
Hide resolved
...ava/org/elasticsearch/xpack/core/security/authz/accesscontrol/DocumentSubsetBitsetCache.java
Outdated
Show resolved
Hide resolved
- Rename "max_bytes" to "size" - Rename "dls_fls" to "dls" - Fix typos in Javadoc
@elasticmachine update branch |
Document level security was depending on the shared "BitsetFilterCache" which (by design) never expires its entries. However, when using DLS queries - particularly templated ones - the number (and memory usage) of generated bitsets can be significant. This change introduces a new cache specifically for BitSets used in DLS queries, that has memory usage constraints and access time expiry. The whole cache is automatically cleared if the role cache is cleared. Individual bitsets are cleared when the corresponding lucene index reader is closed. The cache defaults to 50MB, and entries expire if unused for 7 days. Backport of: elastic#43669
Document level security was depending on the shared "BitsetFilterCache" which (by design) never expires its entries. However, when using DLS queries - particularly templated ones - the number (and memory usage) of generated bitsets can be significant. This change introduces a new cache specifically for BitSets used in DLS queries, that has memory usage constraints and access time expiry. The whole cache is automatically cleared if the role cache is cleared. Individual bitsets are cleared when the corresponding lucene index reader is closed. The cache defaults to 50MB, and entries expire if unused for 7 days. Backport of: #43669
Two new settings were introduced in elastic#43669 (bb130f5) to control the behaviour of the Document Level Security BitSet cache. This change adds documentation for these 2 settings.
Two new settings were introduced in elastic#43669 (bb130f5) to control the behaviour of the Document Level Security BitSet cache. This change adds documentation for these 2 settings. Backport of: elastic#44100
Two new settings were introduced in elastic#43669 (bb130f5) to control the behaviour of the Document Level Security BitSet cache. This change adds documentation for these 2 settings. Backport of: elastic#44100
The Document Level Security BitSet Cache (see elastic#43669) had a default configuration of "small size, long lifetime". However, this is not a very useful default as the cache is most valuable for BitSets that take a long time to construct, which is (generally speaking) the same ones that operate over a large number of documents and contain many bytes. This commit changes the cache to be "large size, short lifetime" so that it can hold bitsets representing billions of documents, but releases memory quickly. The new defaults are 10% of heap, and 2 hours. This also adds some logging when a single BitSet exceeds the size of the cache and when the cache is full.
The Document Level Security BitSet Cache (see #43669) had a default configuration of "small size, long lifetime". However, this is not a very useful default as the cache is most valuable for BitSets that take a long time to construct, which is (generally speaking) the same ones that operate over a large number of documents and contain many bytes. This commit changes the cache to be "large size, short lifetime" so that it can hold bitsets representing billions of documents, but releases memory quickly. The new defaults are 10% of heap, and 2 hours. This also adds some logging when a single BitSet exceeds the size of the cache and when the cache is full. Resolves: #49260
The Document Level Security BitSet Cache (see elastic#43669) had a default configuration of "small size, long lifetime". However, this is not a very useful default as the cache is most valuable for BitSets that take a long time to construct, which is (generally speaking) the same ones that operate over a large number of documents and contain many bytes. This commit changes the cache to be "large size, short lifetime" so that it can hold bitsets representing billions of documents, but releases memory quickly. The new defaults are 10% of heap, and 2 hours. This also adds some logging when a single BitSet exceeds the size of the cache and when the cache is full. Resolves: elastic#49260 Backport of: elastic#50535
The Document Level Security BitSet Cache (see #43669) had a default configuration of "small size, long lifetime". However, this is not a very useful default as the cache is most valuable for BitSets that take a long time to construct, which is (generally speaking) the same ones that operate over a large number of documents and contain many bytes. This commit changes the cache to be "large size, short lifetime" so that it can hold bitsets representing billions of documents, but releases memory quickly. The new defaults are 10% of heap, and 2 hours. This also adds some logging when a single BitSet exceeds the size of the cache and when the cache is full. Backport of: #50535
The Document Level Security BitSet Cache (see elastic#43669) had a default configuration of "small size, long lifetime". However, this is not a very useful default as the cache is most valuable for BitSets that take a long time to construct, which is (generally speaking) the same ones that operate over a large number of documents and contain many bytes. This commit changes the cache to be "large size, short lifetime" so that it can hold bitsets representing billions of documents, but releases memory quickly. The new defaults are 10% of heap, and 2 hours. This also adds some logging when a single BitSet exceeds the size of the cache and when the cache is full. Resolves: elastic#49260
Document level security was depending on the shared
"BitsetFilterCache" which (by design) never expires its entries.
However, when using DLS queries - particularly templated ones - the
number (and memory usage) of generated bitsets can be significant.
This change introduces a new cache specifically for BitSets used in
DLS queries, that has memory usage constraints and access time expiry.
The whole cache is automatically cleared if the role cache is cleared.
Individual bitsets are cleared when the corresponding lucene index
reader is closed.
Closes: #30974