def on_POSITION(self, conn: IReplicationConnection, cmd: PositionCommand) -> None: if cmd.instance_name == self._instance_name: # Ignore POSITION that are just our own echoes return logger.debug("Handling '%s %s'", cmd.NAME, cmd.to_line()) self._add_command_to_stream_queue(conn, cmd)
async def on_POSITION(self, conn: AbstractConnection, cmd: PositionCommand): if cmd.instance_name == self._instance_name: # Ignore POSITION that are just our own echoes return logger.info("Handling '%s %s'", cmd.NAME, cmd.to_line()) stream_name = cmd.stream_name stream = self._streams.get(stream_name) if not stream: logger.error("Got POSITION for unknown stream: %s", stream_name) return # We protect catching up with a linearizer in case the replication # connection reconnects under us. with await self._position_linearizer.queue(stream_name): # We're about to go and catch up with the stream, so remove from set # of connected streams. for streams in self._streams_by_connection.values(): streams.discard(stream_name) # We clear the pending batches for the stream as the fetching of the # missing updates below will fetch all rows in the batch. self._pending_batches.pop(stream_name, []) # Find where we previously streamed up to. current_token = stream.current_token(cmd.instance_name) # If the position token matches our current token then we're up to # date and there's nothing to do. Otherwise, fetch all updates # between then and now. missing_updates = cmd.token != current_token while missing_updates: logger.info( "Fetching replication rows for '%s' between %i and %i", stream_name, current_token, cmd.token, ) ( updates, current_token, missing_updates, ) = await stream.get_updates_since( cmd.instance_name, current_token, cmd.token ) # TODO: add some tests for this # Some streams return multiple rows with the same stream IDs, # which need to be processed in batches. for token, rows in _batch_updates(updates): await self.on_rdata( stream_name, cmd.instance_name, token, [stream.parse_row(row) for row in rows], ) logger.info("Caught up with stream '%s' to %i", stream_name, cmd.token) # We've now caught up to position sent to us, notify handler. await self._replication_data_handler.on_position( cmd.stream_name, cmd.instance_name, cmd.token ) self._streams_by_connection.setdefault(conn, set()).add(stream_name)