async def test(): get_rpc_mock.return_value = self.channel # Ensure setup is initialized properly self.nid_client.clear() self.idlist_client.clear() self.log_client.clear() self.foo_client.clear() key = 'id1' foo = Foo("boo", 4) self.nid_client[key] = NetworkID(id='foo') self.idlist_client[key] = IDList(ids=['bar', 'blah']) self.foo_client[key] = foo # Increment version self.idlist_client[key] = IDList(ids=['bar', 'blah']) req = await self.state_replicator._collect_states_to_replicate() self.assertEqual(3, len(req.states)) # Ensure in-memory map updates properly await self.state_replicator._send_to_state_service(req) self.assertEqual(3, len(self.state_replicator._state_versions)) mem_key1 = make_mem_key('id1', NID_TYPE) mem_key2 = make_mem_key('aaa-bbb:id1', IDList_TYPE) mem_key3 = make_mem_key('id1', FOO_TYPE) self.assertEqual(1, self.state_replicator._state_versions[mem_key1]) self.assertEqual(2, self.state_replicator._state_versions[mem_key2]) self.assertEqual(1, self.state_replicator._state_versions[mem_key3]) # Now add new state and update some existing state key2 = 'id2' self.nid_client[key2] = NetworkID(id='bar') self.idlist_client[key] = IDList(ids=['bar', 'foo']) req = await self.state_replicator._collect_states_to_replicate() self.assertEqual(2, len(req.states)) # Ensure in-memory map updates properly await self.state_replicator._send_to_state_service(req) self.assertEqual(4, len(self.state_replicator._state_versions)) mem_key4 = make_mem_key('id2', NID_TYPE) self.assertEqual(1, self.state_replicator._state_versions[mem_key1]) self.assertEqual(3, self.state_replicator._state_versions[mem_key2]) self.assertEqual(1, self.state_replicator._state_versions[mem_key3]) self.assertEqual(1, self.state_replicator._state_versions[mem_key4])
async def test(): get_grpc_mock.return_value = self.channel self.nid_client.clear() self.idlist_client.clear() self.log_client.clear() self.foo_client.clear() key = 'id1' self.nid_client[key] = NetworkID(id='foo') req = await self.state_replicator._collect_states_to_replicate() self.assertEqual(1, len(req.states)) # Ensure in-memory map updates properly await self.state_replicator._send_to_state_service(req) self.assertEqual(1, len(self.state_replicator._state_versions)) mem_key1 = make_mem_key('id1', NID_TYPE) self.assertEqual(1, self.state_replicator._state_versions[mem_key1]) # Now delete state and ensure in-memory map gets updated properly del self.nid_client[key] req = await self.state_replicator._collect_states_to_replicate() self.assertIsNone(req) await self.state_replicator._cleanup_deleted_keys() self.assertFalse(key in self.state_replicator._state_versions)
async def _resync(self): states_to_sync = [] for redis_dict in self._redis_dicts: for key in redis_dict: version = redis_dict.get_version(key) device_id = make_scoped_device_id(key, redis_dict.state_scope) state_id = StateID(type=redis_dict.redis_type, deviceID=device_id) id_and_version = IDAndVersion(id=state_id, version=version) states_to_sync.append(id_and_version) if len(states_to_sync) == 0: logging.debug("Not re-syncing state. No local state found.") return state_client = self._grpc_client_manager.get_client() request = SyncStatesRequest(states=states_to_sync) response = await grpc_async_wrapper( state_client.SyncStates.future( request, DEFAULT_GRPC_TIMEOUT, ), self._loop) unsynced_states = set() for id_and_version in response.unsyncedStates: unsynced_states.add( (id_and_version.id.type, id_and_version.id.deviceID)) # Update in-memory map to add already synced states for state in request.states: in_mem_key = make_mem_key(state.id.deviceID, state.id.type) if (state.id.type, state.id.deviceID) not in unsynced_states: self._state_versions[in_mem_key] = state.version self._has_resync_completed = True logging.info("Successfully resynced state with Orchestrator!")
async def _send_to_state_service(self, request: ReportStatesRequest): state_client = self._grpc_client_manager.get_client() try: response = await grpc_async_wrapper( state_client.ReportStates.future( request, DEFAULT_GRPC_TIMEOUT, ), self._loop) except grpc.RpcError as err: logging.error("GRPC call failed for state replication: %s", err) else: unreplicated_states = set() for idAndError in response.unreportedStates: logging.warning("Failed to replicate state for (%s,%s): %s", idAndError.type, idAndError.deviceID, idAndError.error) unreplicated_states.add((idAndError.type, idAndError.deviceID)) # Update in-memory map for successfully reported states for state in request.states: if (state.type, state.deviceID) in unreplicated_states: continue in_mem_key = make_mem_key(state.deviceID, state.type) self._state_versions[in_mem_key] = state.version logging.debug( "Successfully replicated state for: " "deviceID: %s," "type: %s, " "version: %d", state.deviceID, state.type, state.version) finally: # reset timeout to config-specified + some buffer self.set_timeout(self._interval * 2)
async def _collect_states_to_replicate(self): states_to_report = [] for redis_dict in self._redis_dicts: for key in redis_dict: device_id = make_scoped_device_id(key, redis_dict.state_scope) in_mem_key = make_mem_key(device_id, redis_dict.redis_type) redis_version = redis_dict.get_version(key) self._state_keys_from_current_iteration.add(in_mem_key) if in_mem_key in self._state_versions and \ self._state_versions[in_mem_key] == redis_version: continue redis_state = redis_dict.get(key) if redis_dict.state_format == PROTO_FORMAT: state_to_serialize = MessageToDict(redis_state) serialized_json_state = json.dumps(state_to_serialize) else: serialized_json_state = jsonpickle.encode(redis_state) state_proto = State(type=redis_dict.redis_type, deviceID=device_id, value=serialized_json_state.encode("utf-8"), version=redis_version) states_to_report.append(state_proto) if len(states_to_report) == 0: logging.debug("Not replicating state. No state has changed!") return None return ReportStatesRequest(states=states_to_report)
async def _collect_states_to_replicate(self): states_to_report = [] for redis_dict in self._redis_dicts: for key in redis_dict: redis_state = redis_dict.get(key) device_id = make_scoped_device_id(key, redis_dict.state_scope) in_mem_key = make_mem_key(device_id, redis_dict.redis_type) if redis_state is None: logging.debug( "Content of key %s is empty, skipping", in_mem_key, ) continue redis_version = redis_dict.get_version(key) self._state_keys_from_current_iteration.add(in_mem_key) if in_mem_key in self._state_versions and \ self._state_versions[in_mem_key] == redis_version: logging.debug( "key %s already read on this iteration, skipping", in_mem_key, ) continue try: if redis_dict.state_format == PROTO_FORMAT: state_to_serialize = MessageToDict(redis_state) serialized_json_state = json.dumps(state_to_serialize) else: serialized_json_state = jsonpickle.encode(redis_state) except Exception as e: # pylint: disable=broad-except logging.error( "Found bad state for %s for %s, not " "replicating this state: %s", key, device_id, e, ) continue state_proto = State( type=redis_dict.redis_type, deviceID=device_id, value=serialized_json_state.encode("utf-8", ), version=redis_version, ) logging.debug( "key with version, %s contains: %s", in_mem_key, serialized_json_state, ) states_to_report.append(state_proto) if len(states_to_report) == 0: logging.debug("Not replicating state. No state has changed!") return None return ReportStatesRequest(states=states_to_report)
async def test(): get_grpc_mock.return_value = self.channel # Add initial state to be replicated self.nid_client.clear() self.idlist_client.clear() self.log_client.clear() self.foo_client.clear() key = 'id1' key2 = 'id2' self.nid_client[key] = NetworkID(id='foo') self.idlist_client[key] = IDList(ids=['bar', 'blah']) # Increment version self.idlist_client[key] = IDList(ids=['bar', 'blah']) # Set state that will be 'unreplicated' self.log_client[key2] = LogVerbosity(verbosity=5) req = await self.state_replicator._collect_states_to_replicate() self.assertEqual(3, len(req.states)) # Ensure in-memory map updates properly for successful replications await self.state_replicator._send_to_state_service(req) self.assertEqual(2, len(self.state_replicator._state_versions)) mem_key1 = make_mem_key('id1', NID_TYPE) mem_key2 = make_mem_key( 'aaa-bbb:id1', IDList_TYPE, ) self.assertEqual( 1, self.state_replicator._state_versions[mem_key1], ) self.assertEqual( 2, self.state_replicator._state_versions[mem_key2], ) # Now run again, ensuring only the state the wasn't replicated # will be sent again req = await self.state_replicator._collect_states_to_replicate() self.assertEqual(1, len(req.states)) self.assertEqual('aaa-bbb:id2', req.states[0].deviceID) self.assertEqual(LOG_TYPE, req.states[0].type)
async def test(): get_grpc_mock.return_value = self.channel self.nid_client.clear() self.idlist_client.clear() self.log_client.clear() self.foo_client.clear() key = 'id1' # Set state that will be 'unsynced' self.nid_client[key] = NetworkID(id='foo') self.idlist_client[key] = IDList(ids=['bar', 'blah']) # Increment state's version self.idlist_client[key] = IDList(ids=['bar', 'blah']) await self.state_replicator._resync() self.assertEqual(True, self.state_replicator._has_resync_completed) self.assertEqual(1, len(self.state_replicator._state_versions)) mem_key = make_mem_key('aaa-bbb:id1', IDList_TYPE) self.assertEqual(2, self.state_replicator._state_versions[mem_key])