def _get_state_for_group_using_cache( self, cache: DictionaryCache[int, StateKey, str], group: int, state_filter: StateFilter, ) -> Tuple[MutableStateMap[str], bool]: """Checks if group is in cache. See `_get_state_for_groups` Args: cache: the state group cache to use group: The state group to lookup state_filter: The state filter used to fetch state from the database. Returns: 2-tuple (`state_dict`, `got_all`). `got_all` is a bool indicating if we successfully retrieved all requests state from the cache, if False we need to query the DB for the missing state. """ cache_entry = cache.get(group) state_dict_ids = cache_entry.value if cache_entry.full or state_filter.is_full(): # Either we have everything or want everything, either way # `is_all` tells us whether we've gotten everything. return state_filter.filter_state(state_dict_ids), cache_entry.full # tracks whether any of our requested types are missing from the cache missing_types = False if state_filter.has_wildcards(): # We don't know if we fetched all the state keys for the types in # the filter that are wildcards, so we have to assume that we may # have missed some. missing_types = True else: # There aren't any wild cards, so `concrete_types()` returns the # complete list of event types we're wanting. for key in state_filter.concrete_types(): if key not in state_dict_ids and key not in cache_entry.known_absent: missing_types = True break return state_filter.filter_state(state_dict_ids), not missing_types
def _get_state_for_groups(self, groups: Iterable[int], state_filter: StateFilter = StateFilter.all()): """Gets the state at each of a list of state groups, optionally filtering by type/state_key Args: groups: list of state groups for which we want to get the state. state_filter: The state filter used to fetch state from the database. Returns: Deferred[Dict[int, StateMap[str]]]: Dict of state group to state map. """ member_filter, non_member_filter = state_filter.get_member_split() # Now we look them up in the member and non-member caches ( non_member_state, incomplete_groups_nm, ) = yield self._get_state_for_groups_using_cache( groups, self._state_group_cache, state_filter=non_member_filter) ( member_state, incomplete_groups_m, ) = yield self._get_state_for_groups_using_cache( groups, self._state_group_members_cache, state_filter=member_filter) state = dict(non_member_state) for group in groups: state[group].update(member_state[group]) # Now fetch any missing groups from the database incomplete_groups = incomplete_groups_m | incomplete_groups_nm if not incomplete_groups: return state cache_sequence_nm = self._state_group_cache.sequence cache_sequence_m = self._state_group_members_cache.sequence # Help the cache hit ratio by expanding the filter a bit db_state_filter = state_filter.return_expanded() group_to_state_dict = yield self._get_state_groups_from_groups( list(incomplete_groups), state_filter=db_state_filter) # Now lets update the caches self._insert_into_cache( group_to_state_dict, db_state_filter, cache_seq_num_members=cache_sequence_m, cache_seq_num_non_members=cache_sequence_nm, ) # And finally update the result dict, by filtering out any extra # stuff we pulled out of the database. for group, group_state_dict in iteritems(group_to_state_dict): # We just replace any existing entries, as we will have loaded # everything we need from the database anyway. state[group] = state_filter.filter_state(group_state_dict) return state