Beispiel #1
0
class IPCHandler(logging.Handler):
    logger = get_logger('trinity._utils.logging.IPCHandler')

    def __init__(self, sock: socket.socket):
        self._socket = BufferedSocket(sock)
        super().__init__()

    def __enter__(self) -> None:
        pass

    def __exit__(self,
                 exc_type: Type[BaseException],
                 exc_value: BaseException,
                 exc_tb: TracebackType) -> None:
        self._socket.close()

    @classmethod
    def connect(cls: Type[THandler], path: Path) -> THandler:
        wait_for_ipc(path)
        s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        cls.logger.debug("Opened connection to %s: %s", path, s)
        s.connect(str(path))
        return cls(s)

    def prepare(self, record: logging.LogRecord) -> logging.LogRecord:
        msg = self.format(record)
        new_record = copy.copy(record)
        new_record.message = msg
        new_record.msg = msg
        new_record.args = None
        new_record.exc_info = None
        new_record.exc_text = None
        return new_record

    def emit(self, record: logging.LogRecord) -> None:
        try:
            msg_data = pickle.dumps(self.prepare(record))
            msg_length_data = len(msg_data).to_bytes(4, 'big')
            self._socket.sendall(msg_length_data + msg_data)
        except Exception:
            self.handleError(record)
Beispiel #2
0
class DBClient(BaseAtomicDB):
    logger = logging.getLogger('trinity.db.client.DBClient')

    def __init__(self, sock: socket.socket):
        self._socket = BufferedSocket(sock)
        self._lock = threading.Lock()

    def __enter__(self) -> None:
        self._socket.__enter__()

    def __exit__(self, exc_type: Type[BaseException], exc_value: BaseException,
                 exc_tb: TracebackType) -> None:
        self._socket.__exit__(exc_type, exc_value, exc_tb)

    def __getitem__(self, key: bytes) -> bytes:
        with self._lock:
            self._socket.sendall(GET.value +
                                 len(key).to_bytes(LEN_BYTES, 'little') + key)
            result_byte = self._socket.read_exactly(1)

            if result_byte == SUCCESS_BYTE:
                value_size_data = self._socket.read_exactly(LEN_BYTES)
                value = self._socket.read_exactly(
                    int.from_bytes(value_size_data, 'little'))
                return value
            elif result_byte == FAIL_BYTE:
                raise KeyError(key)
            else:
                raise Exception(f"Unknown result byte: {result_byte.hex}")

    def __setitem__(self, key: bytes, value: bytes) -> None:
        with self._lock:
            self._socket.sendall(SET.value +
                                 struct.pack('<II', len(key), len(value)) +
                                 key + value)
            Result(self._socket.read_exactly(1))

    def __delitem__(self, key: bytes) -> None:
        with self._lock:
            self._socket.sendall(DELETE.value +
                                 len(key).to_bytes(4, 'little') + key)
            result_byte = self._socket.read_exactly(1)

        if result_byte == SUCCESS_BYTE:
            return
        elif result_byte == FAIL_BYTE:
            raise KeyError(key)
        else:
            raise Exception(f"Unknown result byte: {result_byte.hex}")

    def _exists(self, key: bytes) -> bool:
        with self._lock:
            self._socket.sendall(EXISTS.value +
                                 len(key).to_bytes(4, 'little') + key)
            result_byte = self._socket.read_exactly(1)

        if result_byte == SUCCESS_BYTE:
            return True
        elif result_byte == FAIL_BYTE:
            return False
        else:
            raise Exception(f"Unknown result byte: {result_byte.hex}")

    @contextlib.contextmanager
    def atomic_batch(self) -> Iterator['AtomicBatch']:
        batch = AtomicBatch(self)
        yield batch
        diff = batch.finalize()
        pending_deletes = diff.deleted_keys()
        pending_kv_pairs = diff.pending_items()

        kv_pair_count = len(pending_kv_pairs)
        delete_count = len(pending_deletes)

        kv_sizes = tuple(
            len(item) for item in itertools.chain(*pending_kv_pairs))
        delete_sizes = tuple(len(key) for key in pending_deletes)

        # We encode all of the *sizes* in one shot using `struct.pack` and this
        # dynamically constructed format string.
        fmt_str = '<II' + 'I' * (len(kv_sizes) + len(pending_deletes))
        kv_pair_count_and_size_data = struct.pack(
            fmt_str,
            kv_pair_count,
            delete_count,
            *kv_sizes,
            *delete_sizes,
        )
        kv_and_delete_data = b''.join(
            itertools.chain(*pending_kv_pairs, pending_deletes))
        with self._lock:
            self._socket.sendall(ATOMIC_BATCH.value +
                                 kv_pair_count_and_size_data +
                                 kv_and_delete_data)
            Result(self._socket.read_exactly(1))

    def close(self) -> None:
        try:
            self._socket.shutdown(socket.SHUT_WR)
        except OSError as e:
            # on mac OS this can result in the following error:
            # OSError: [Errno 57] Socket is not connected
            if e.errno != errno.ENOTCONN:
                raise
        self._socket.close()

    @classmethod
    def connect(cls, path: pathlib.Path, timeout: int = 5) -> "DBClient":
        wait_for_ipc(path, timeout)
        s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        cls.logger.debug("Opened connection to %s: %s", path, s)
        s.connect(str(path))
        return cls(s)