def _run_transaction(self, access_mode, unit_of_work, *args, **kwargs): if not callable(unit_of_work): raise TypeError("Unit of work is not callable") retry_delay = retry_delay_generator(INITIAL_RETRY_DELAY, RETRY_DELAY_MULTIPLIER, RETRY_DELAY_JITTER_FACTOR) last_error = None t0 = perf_counter() while True: try: self._connect(access_mode) self._create_transaction() self.__begin__() with self._transaction as tx: return unit_of_work(tx, *args, **kwargs) except (ServiceUnavailable, SessionExpired) as error: last_error = error except TransientError as error: if is_retriable_transient_error(error): last_error = error else: raise error t1 = perf_counter() if t1 - t0 > self._max_retry_time: break sleep(next(retry_delay)) raise last_error
def acquire_direct(self, address): """ Acquire a connection to a given address from the pool. The address supplied should always be an IP address, not a host name. This method is thread safe. """ if self.closed(): raise ServiceUnavailable("Connection pool closed") with self.lock: try: connections = self.connections[address] except KeyError: connections = self.connections[address] = deque() connection_acquisition_start_timestamp = perf_counter() while True: # try to find a free connection in pool for connection in list(connections): if connection.closed() or connection.defunct( ) or connection.timedout(): connections.remove(connection) continue if not connection.in_use: connection.in_use = True return connection # all connections in pool are in-use can_create_new_connection = self._max_connection_pool_size == INFINITE or len( connections) < self._max_connection_pool_size if can_create_new_connection: try: connection = self.connector( address, self.connection_error_handler) except ServiceUnavailable: self.remove(address) raise else: connection.pool = self connection.in_use = True connections.append(connection) return connection # failed to obtain a connection from pool because the pool is full and no free connection in the pool span_timeout = self._connection_acquisition_timeout - ( perf_counter() - connection_acquisition_start_timestamp) if span_timeout > 0: self.cond.wait(span_timeout) # if timed out, then we throw error. This time computation is needed, as with python 2.7, we cannot # tell if the condition is notified or timed out when we come to this line if self._connection_acquisition_timeout <= ( perf_counter() - connection_acquisition_start_timestamp): raise ClientError( "Failed to obtain a connection from pool within {!r}s" .format(self._connection_acquisition_timeout)) else: raise ClientError( "Failed to obtain a connection from pool within {!r}s". format(self._connection_acquisition_timeout))
def __init__(self, address, sock, protocol_version, error_handler, **config): self.address = address self.socket = sock self.protocol_version = protocol_version self.error_handler = error_handler self.server = ServerInfo(SocketAddress.from_socket(sock)) self.input_buffer = ChunkedInputBuffer() self.output_buffer = ChunkedOutputBuffer() self.packer = Packer(self.output_buffer) self.unpacker = Unpacker() self.responses = deque() self._max_connection_lifetime = config.get("max_connection_lifetime", default_config["max_connection_lifetime"]) self._creation_timestamp = perf_counter() # Determine the user agent and ensure it is a Unicode value user_agent = config.get("user_agent", default_config["user_agent"]) if isinstance(user_agent, bytes): user_agent = user_agent.decode("UTF-8") self.user_agent = user_agent # Determine auth details auth = config.get("auth") if not auth: self.auth_dict = {} elif isinstance(auth, tuple) and 2 <= len(auth) <= 3: from neo4j.v1 import basic_auth self.auth_dict = vars(basic_auth(*auth)) else: try: self.auth_dict = vars(auth) except (KeyError, TypeError): raise TypeError("Cannot determine auth details from %r" % auth) # Pick up the server certificate, if any self.der_encoded_server_certificate = config.get("der_encoded_server_certificate")
def _run_transaction(self, access_mode, unit_of_work, *args, **kwargs): if not callable(unit_of_work): raise TypeError("Unit of work is not callable") retry_delay = retry_delay_generator(INITIAL_RETRY_DELAY, RETRY_DELAY_MULTIPLIER, RETRY_DELAY_JITTER_FACTOR) errors = [] t0 = perf_counter() while True: try: self._create_transaction() self._connect(access_mode) self.__begin__() tx = self._transaction try: result = unit_of_work(tx, *args, **kwargs) except: if tx.success is None: tx.success = False raise else: if tx.success is None: tx.success = True finally: tx.close() except (ServiceUnavailable, SessionExpired) as error: errors.append(error) except TransientError as error: if is_retriable_transient_error(error): errors.append(error) else: raise else: return result t1 = perf_counter() if t1 - t0 > self._max_retry_time: break sleep(next(retry_delay)) if errors: raise errors[-1] else: raise ServiceUnavailable("Transaction failed")
def timedout(self): return 0 <= self._max_connection_lifetime <= perf_counter( ) - self._creation_timestamp