Class PartitionDenylist


  • public class PartitionDenylist
    extends java.lang.Object
    PartitionDenylist uses the system_distributed.partition_denylist table to maintain a list of denylisted partition keys for each keyspace/table. Keys can be entered manually into the partition_denylist table or via the JMX operation StorageProxyMBean.denylistKey The denylist is stored as one CQL partition per table, and the denylisted keys are column names in that partition. The denylisted keys for each table are cached in memory, and reloaded from the partition_denylist table every 10 minutes (default) or when the StorageProxyMBean.loadPartitionDenylist is called via JMX. Concurrency of the cache is provided by the concurrency semantics of the guava LoadingCache. All values (DenylistEntry) are immutable collections of keys/tokens which are replaced in whole when the cache refreshes from disk. The CL for the denylist is used on initial node load as well as on timer instigated cache refreshes. A JMX call by the operator to load the denylist cache will warn on CL unavailability but go through with the denylist load. This is to allow operators flexibility in the face of degraded cluster state and still grant them the ability to mutate the denylist cache and bring it up if there are things they need to block on startup. Notably, in the current design it's possible for a table *cache expiration instigated* reload to end up violating the contract on total denylisted keys allowed in the case where it initially loads with a value less than the DBD allowable max per table limit due to global constraint enforcement on initial load. Our load and reload function simply enforce the *per table* limit without consideration to what that entails at the global key level. While we could track the constrained state and count in DenylistEntry, for now the complexity doesn't seem to justify the protection against that edge case. The enforcement should take place on a user-instigated full reload as well as error messaging about count violations, so this only applies to situations in which someone adds a key and doesn't actively tell the cache to fully reload to take that key into consideration, which one could reasonably expect to be an antipattern.
    • Constructor Detail

      • PartitionDenylist

        public PartitionDenylist()
    • Method Detail

      • getLoadAttempts

        public int getLoadAttempts()
      • getLoadSuccesses

        public int getLoadSuccesses()
      • initialLoad

        public void initialLoad()
        Performs initial load of the partition denylist. Should be called at startup and only loads if the operation is expected to succeed. If it is not possible to load at call time, a timer is set to retry.
      • load

        public void load()
        We need to fully rebuild a new cache to accommodate deleting items from the denylist and potentially shrinking the max allowable size in the list. We do not serve queries out of this denylist until it is populated so as not to introduce a window of having a partially filled cache allow denylisted entries.
      • addKeyToDenylist

        public boolean addKeyToDenylist​(java.lang.String keyspace,
                                        java.lang.String table,
                                        java.nio.ByteBuffer key)
        We expect the caller to confirm that we are working with a valid keyspace and table. Further, we expect the usage pattern of this to be one-off key by key, not in a bulk process, so we reload the entire table's deny list entry on an addition or removal.
      • removeKeyFromDenylist

        public boolean removeKeyFromDenylist​(java.lang.String keyspace,
                                             java.lang.String table,
                                             java.nio.ByteBuffer key)
        We expect the caller to confirm that we are working with a valid keyspace and table.
      • isKeyPermitted

        public boolean isKeyPermitted​(java.lang.String keyspace,
                                      java.lang.String table,
                                      java.nio.ByteBuffer key)
      • isKeyPermitted

        public boolean isKeyPermitted​(TableId tid,
                                      java.nio.ByteBuffer key)
      • getDeniedKeysInRangeCount

        public int getDeniedKeysInRangeCount​(java.lang.String keyspace,
                                             java.lang.String table,
                                             AbstractBounds<PartitionPosition> range)
        Returns:
        number of denylisted keys in range