def __init__(self, conf_dict): """ Init :param conf_dict: dict :type conf_dict: dict """ Meters.aii("k.db_pool.mysql.call.__init") # Base super(MysqlConnectionPool, self).__init__(conf_dict) # Init us self.host_status = dict() # Check if "hosts" not in self.conf_dict and "host" not in self.conf_dict and "unix" not in self.conf_dict: raise Exception( "No server specified (hosts, host, unix not found in conf_dict" ) # Init, "hosts" first if "hosts" in self.conf_dict: for host in self.conf_dict["hosts"]: self.host_status[host] = 0.0 elif "host" in self.conf_dict: logger.warning( "Using deprecated entry, prefer using 'hosts', got host=%s", self.conf_dict["host"]) for host in self.conf_dict["host"].split(","): self.host_status[host] = 0.0 elif "unix" in self.conf_dict: for host in self.conf_dict["unix"].split(","): self.host_status[host] = 0.0
def _protocol_client_hello_server_reply(self, item): """ Received a hello reply :param item: Received server reply ("C.HI.REPLY PID=...") """ with self._protocol_lock: # Parse it self._server_pid = item.replace("C.HI.REPLY PID=", "") # Unschedule timeout self._unschedule_client_helloservertimeout() # Stats Meters.aii("ping.client.client_hello_server_reply") # Delay ms = SolBase.datediff(self.dt_hello_send) # Stats Meters.dtci("ping.client.delay_client_hello_toserver_reply", ms) # Initiate the ping loop, with random start self._schedule_clientping(True)
def _protocol_client_ping_send(self): """ Must send a client ping """ with self._protocol_lock: # Reset (we are called by it) self._ping_greenlet = None # Schedule a timeout self._schedule_client_pingservertimeout() # Timestamp self.dt_last_ping_send = SolBase.datecurrent() # Send a ping b = self.send_unicode_to_socket("C.PING") if not b: # Stat logger.error("send failed, fatal, disconnecting") Meters.aii("ping.client.client_send_error") # Disconnect self.disconnect() else: # Stats Meters.aii("ping.client.client_ping_sent")
def send_binary_to_socket_with_signal(self, signaled_buffer): """ Send to socket, asynch. Upon completion, signaled_buffer.send_event is set. Caution : Caller MUST check the boolean returned. If False, the event will NOT be set. :param signaled_buffer: A signaled_buffer instance. :type signaled_buffer: pysoltcp.tcpbase.SignaledBuffer.SignaledBuffer :return: True is send has been scheduled, false otherwise. :rtype bool """ # Check if not isinstance(signaled_buffer, SignaledBuffer): logger.error("signaled_buffer not a SignaledBuffer, class=%s, self=%s", SolBase.get_classname(signaled_buffer), self) return False # Check if not isinstance(signaled_buffer.binary_buffer, binary_type): logger.error("binary_buffer not a binary, class=%s, self=%s", SolBase.get_classname(signaled_buffer.binary_buffer), self) return False # Check if not self.__is_running(): logger.debug( "not connected, returning false, self=%s", self) return False # Enqueue self.__send_queue.put(signaled_buffer) # Stats Meters.aii("tcp.server.server_bytes_send_pending", len(signaled_buffer.binary_buffer)) return True
def connect(self): """ Connect to server. :return Return true upon success. """ # Stats Meters.aii("ping.client.client_connect_count") # Call base dt_start = SolBase.datecurrent() b = TcpSimpleClient.connect(self) if not b: # Stat logger.error("PingSimpleClient : connect failed, fatal, exiting") Meters.aii("ping.client.client_connect_error") # Exit return False # Stat Meters.dtci("ping.client.delay_client_connect", SolBase.datediff(dt_start)) # SSL stats ms = self._get_ssl_handshake_ms() if ms: Meters.dtci("ping.client.delay_client_sslhandshake", ms) # Send hello self._protocol_client_hello_send() return True
def __on_ssl_timeout(self): """ Called when SSL timeout. """ # Reset first (in LOCK) with self.__ssl_locker: self.__ssl_timeout_greenlet = None # Process (outside of LOCK, high level may call _unschedule_ssl_handshake_timeout in lock) logger.debug("__on_ssl_timeout, self=%s", self) # Check if not self.__ssl_handshake_pending: # Done, exit return elif not self.__is_running(): # Not connected, exit return # Not done, FATAL logger.warning("handshake timeout, fatal, ms=%s, self=%s", self.__ssl_handshake_timeout_ms, self) # Stat Meters.aii("tcp.server.ssl_handshake_timeout_count") # Timeout, Fatal, exit now self._disconnect_helper("__on_ssl_timeout")
def _connection_ping(self, conn): """ Ping connection This send a ping, write/read toward mysql. :param conn: pymysql.connections.Connection :type conn: pymysql.connections.Connection :return bool :rtype bool """ Meters.aii("k.db_pool.mysql.call._connection_ping") # noinspection PyBroadException try: # TODO : PING MODE MUST BE CONFIGURABLE conn.ping(reconnect=False) except Exception as e: Meters.aii("k.db_pool.mysql.ex_ping") logger.debug("Ping failed, obj=%s, ex=%s", conn, SolBase.extostr(e)) return False else: return True
def handle(self, data, address): """ Handle one udp message :param data: data :type data: str :param address: address :type address: str """ ms_start = SolBase.mscurrent() try: # Handle data pass # Stats Meters.aii("resolvusclient_udp_recv") except Exception as e: # Log logger.warning( "Handle failed, data_len=%s, address=%s, data=%s, ex=%s", len(data), repr(address), repr(data), SolBase.extostr(e)) # Stat Meters.aii("resolvusclient_udp_recv_ex") finally: Meters.dtci("resolvusclient_udp_recv_dtc", SolBase.msdiff(ms_start))
def start(self): """ Connect to server. :return Return true upon success. """ # Timestamp self.dtClientConnect = SolBase.datecurrent() # Call base b = TcpServerQueuedClientContext.start(self) if not b: # Stat logger.error("start failed, fatal, exiting") Meters.aii("ping.server.serverStartError") # Exit return False # Stat Meters.aii("ping.server.serverStartCount") # Schedule an hello timeout self._schedule_client_hello_timeout() return True
def _protocol_server_ping_client_reply(self): """ A client ping has been replied """ with self._protocolLock: # Must have a timeout ongoing if self._pingTimeOutGreenlet is None: # Ping reply received without ongoing ping Meters.aii( "ping.server.serverPingServerClientReplyNoPingOngoing") else: # Unschedule the timeout self._unschedule_server_ping_client_timeout() # Reschedule a ping self._schedule_server_ping() # Stats Meters.aii("ping.server.serverPingClientReply") # Delay Meters.dtci("ping.server.client_ping_server_reply", SolBase.datediff(self.dtLastPingSend))
def _protocol_server_ping_send(self): """ Must send a client ping """ with self._protocolLock: # Reset (we are called by it) self._pingGreenlet = None # Schedule a timeout self._schedule_server_ping_client_timeout() # Timestamp self.dtLastPingSend = SolBase.datecurrent() # Send a ping b = self.send_unicode_to_socket("S.PING") if not b: # Stat logger.error("send failed, fatal, disconnecting") Meters.aii("ping.server.serverSendError") # Disconnect self.stop_asynch() else: # Stats Meters.aii("ping.server.serverPingSent")
def send_binary_to_socket(self, buffer_to_send): """ Send to socket, asynch. :param buffer_to_send: The localBuffer to send (bytes) :type buffer_to_send: bytes :return: True is send has been scheduled, false otherwise. :rtype bool """ # Check if not isinstance(buffer_to_send, binary_type): logger.error("buffer_to_send not a binary, class=%s, self=%s", SolBase.get_classname(buffer_to_send), self) return False # Check if not self.__is_running(): logger.debug("not connected, returning false, self=%s", self) return False # Enqueue self.__send_queue.put(buffer_to_send) # Stats Meters.aii("tcp.server.server_bytes_send_pending", len(buffer_to_send)) return True
def _protocol_client_ping_server_reply(self): """ A client ping has been replied """ with self._protocol_lock: # Must have a timeout ongoing if self._ping_timeout_greenlet is None: # Ping reply received without ongoing ping Meters.aii( "ping.client.client_ping_server_reply_noping_ongoing") else: # Unschedule the timeout self._unschedule_client_pingservertimeout() # Reschedule a ping self._schedule_clientping() # Stats Meters.aii("ping.client.client_ping_server_reply") # Delay and stats Meters.dtci("ping.client.delay_client_ping_toserver_reply", SolBase.datediff(self.dt_last_ping_send))
def _schedule_clientping(self, randomize_delay=False): """ Schedule a ping :param randomize_delay: Randomize delay if true. """ with self._protocol_lock: # Check if self._ping_greenlet: logger.warning( "PingSimpleClient : _schedule_clientping : _ping_greenlet is not None, killing" ) Meters.aii("ping.client.schedule_client_ping_error") self._unschedule_client_ping() # Go if not randomize_delay: self._ping_greenlet = gevent.spawn_later( self._ping_interval_ms * 0.001, self._protocol_client_ping_send) else: # Randomize delay vmin = self._ping_interval_ms / 2 vmax = self._ping_interval_ms ms = randint(vmin, vmax) self._ping_greenlet = gevent.spawn_later( ms * 0.001, self._protocol_client_ping_send)
def close_all(self): """ Close all connections """ n = 0 while not self.pool.empty(): conn = self.pool.get_nowait() self._connection_close(conn) n += 1 Meters.aii("k.db_pool.base.cur_size", increment_value=-n) Meters.ai("k.db_pool.base.max_size").set(max(Meters.aig("k.db_pool.base.max_size"), Meters.aig("k.db_pool.base.cur_size"))) self.size = 0
def _protocol_client_hello_received(self): """ Must send a hello """ with self._protocolLock: # Un-schedule hello timeout self._unschedule_client_hello_timeout() # Stats Meters.aii("ping.server.clientHelloReceived") # Delay Meters.aii("ping.server.delayClientConnectToClientHello", SolBase.datediff(self.dtClientConnect)) # Send a reply b = self.send_unicode_to_socket("C.HI.REPLY PID={0}".format( os.getpid())) if not b: # Stat logger.error("send failed, fatal, disconnecting") Meters.aii("ping.server.serverSendError") # Disconnect self.stop_asynch() else: # Now connected, schedule a ping self._schedule_server_ping() # Stats Meters.aii("ping.server.client_hello_server_reply")
def _schedule_client_hello_timeout(self): """ Schedule a ping timeout """ with self._protocolLock: # Check if self._helloTimeOutGreenlet: logger.warning("_helloTimeOutGreenlet is not None, killing") Meters.aii("ping.server.scheduleClientHelloTimeOutError") self._unschedule_client_hello_timeout() # Go self._helloTimeOutGreenlet = gevent.spawn_later( self._helloTimeOutMs * 0.001, self._protocol_context_client_hello_timeout)
def _schedule_client_pingservertimeout(self): """ Schedule a ping timeout """ with self._protocol_lock: # Check if self._ping_timeout_greenlet: logger.warning("_ping_timeout_greenlet is not None, killing") Meters.aii("ping.client.schedule_client_pingtimeouterror") self._unschedule_client_pingservertimeout() # Go self._ping_timeout_greenlet = gevent.spawn_later( self._ping_timeout_ms * 0.001, self._protocol_client_pingserver_timeout)
def disconnect(self): """ Disconnect from server. :return Return true upon success. """ # Stat Meters.aii("ping.client.client_disconnect_count") # Clean self._unschedule_client_helloservertimeout() self._unschedule_client_ping() self._unschedule_client_pingservertimeout() # Base return TcpSimpleClient.disconnect(self)
def _schedule_server_ping_client_timeout(self): """ Schedule a ping timeout """ with self._protocolLock: # Check if self._pingTimeOutGreenlet: logger.warning("_ping_timeout_greenlet is not None, killing") Meters.aii("ping.server.scheduleServerPingTimeOutError") self._unschedule_server_ping_client_timeout() # Go self._pingTimeOutGreenlet = gevent.spawn_later( self._pingTimeOutMs * 0.001, self._protocol_server_ping_client_timeout)
def _read_from_socket(self): """ Read from the socket. Return a local_buf or None. """ # Check if not self.__is_running(): logger.debug("not connected, doing nothing, self=%s", self) return None try: # TODO : Socket local_buf size # TODO : Socket, upon server closure, will BLOCK on recv. Upon time, auto disconnect the client. # Try to read local_buf = self.current_socket.recv(1024) # If local_buf is empty string, socket is disconnected. if local_buf and len(local_buf) == 0: # Disconnect logger.debug("empty string received, disconnecting ourselves, self=%s", self) self._disconnect_helper("_read_from_socket : Empty string") return None # Stats Meters.aii("tcp.server.server_bytes_received", len(local_buf)) # Ok return local_buf except error as e: # [Errno 11] Resource temporarily unavailable # Means that nothing is available to read. # If not this, we raise. if e.args[0] != EWOULDBLOCK: # Raise :) logger.debug("_tryReadWrite : _read_from_socket : error, ex=%s, self=%s", SolBase.extostr(e), self) self._disconnect_helper("_read_from_socket : error / No EWOULDBLOCK") return None else: # Normal logger.debug("_tryReadWrite : _read_from_socket : normal exception/EWOULDBLOCK, ex=%s, self=%s", e, self) return None except Exception as e: logger.debug("_tryReadWrite : _read_from_socket : Exception, ex=%s, self=%s", SolBase.extostr(e), self) self._disconnect_helper("_read_from_socket : Exception / No EWOULDBLOCK") return None
def _protocol_context_client_hello_timeout(self): """ Hello timeout """ with self._protocolLock: logger.error("timeout, fatal, disconnecting") # Reset (we are called by it) self._helloTimeOutGreenlet = None # Stats Meters.aii("ping.server.clientHelloTimeOut") # Disconnect ourselves (this is fatal) self.stop_asynch()
def _protocol_server_ping_client_timeout(self): """ Called when a ping has time-out (ie no reply from server) """ with self._protocolLock: logger.warning("ping timeout, rescheduling ping") # Reset (we are called by it) self._pingTimeOutGreenlet = None # Reschedule a ping self._schedule_server_ping() # Stat Meters.aii("ping.server.serverPingClientTimeOut")
def _protocol_client_hello_server_timeout(self): """ Hello timeout """ with self._protocol_lock: logger.error("timeout, fatal, disconnecting") # Reset (we are called by it) self._hello_timeout_greenlet = None # Stats Meters.aii("ping.client.client_hello_server_timeout") # Disconnect ourselves (this is fatal) self.disconnect()
def test_meters(self): """ Test """ ai1a = Meters.ai("ai1") self.assertIsInstance(ai1a, AtomicIntSafe) ai1b = Meters.ai("ai1") self.assertEqual(id(ai1a), id(ai1b)) ai1a = Meters.aii("ai1") self.assertEqual(ai1a.get(), 1) ai1a = Meters.aii("ai1", 2) self.assertEqual(ai1a.get(), 3) self.assertEqual(ai1a.get(), Meters.aig("ai1")) af1a = Meters.af("af1") self.assertIsInstance(af1a, AtomicFloatSafe) af1b = Meters.af("af1") self.assertEqual(id(af1a), id(af1b)) af1a = Meters.afi("af1") self.assertEqual(af1a.get(), 1.0) af1a = Meters.afi("af1", 2.0) self.assertEqual(af1a.get(), 3.0) self.assertEqual(af1a.get(), Meters.afg("af1")) dtc1a = Meters.dtc("dtc1") self.assertIsInstance(dtc1a, DelayToCountSafe) dtc1b = Meters.dtc("dtc1") self.assertEqual(id(dtc1a), id(dtc1b)) Meters.dtci("dtc1", 0) Meters.dtci("dtc1", 50) Meters.dtci("dtc1", 100) self.assertEquals(Meters._hash_meter["dtc"]["dtc1#"]._sorted_dict[0].get(), 1) self.assertEquals(Meters._hash_meter["dtc"]["dtc1#"]._sorted_dict[50].get(), 1) self.assertEquals(Meters._hash_meter["dtc"]["dtc1#"]._sorted_dict[100].get(), 1) self.assertEquals(Meters._hash_meter["dtc"]["dtc1#"]._sorted_dict[500].get(), 0) Meters.dtc("dtc1").to_dict() # Write Meters.write_to_logger()
def stop_synch_internal(self): """ Disconnect from server. :return Return true upon success. """ # Stat Meters.aii("ping.server.serverStopSynchCount") # Clean self._unschedule_client_hello_timeout() self._unschedule_server_ping() self._unschedule_server_ping_client_timeout() # Called self.stop_synch_internalCalled = True # Base return TcpServerQueuedClientContext.stop_synch_internal(self)
def get_raw(self, key): """ Get from cache. Can evict if ttl expired and return None. :param key: Any key :type key: str :return tuple(ttl_ms, value as bytes or str) :rtype; tuple, None """ try: # Get it # [0] : Expired ms # [1] : Object cached tu_obj = self._hash_key.get(key) if not tu_obj: # Stat Meters.aii(self.meters_prefix + "mcs.cache_get_miss") return None # Got it, check TTL if self._is_expired(tu_obj[0], SolBase.mscurrent()): # Expired => kick it & exit self._safe_unhash(key) # Notify self._notify_eviction(key, tu_obj[1]) # Stat Meters.aii(self.meters_prefix + "mcs.cache_get_miss") Meters.aii(self.meters_prefix + "mcs.cache_evict_ttl_get") return None # Got a HIT : Must update it in the ordered dict : del & re-add del (self._hash_context[key]) self._hash_context[key] = tu_obj # Return the data Meters.aii(self.meters_prefix + "mcs.cache_get_hit") return tu_obj except Exception as e: logger.warning("Exception, ex=%s", SolBase.extostr(e)) Meters.aii(self.meters_prefix + "mcs.cache_ex") return None
def _connection_close(self, conn): """ Close a connection Must not raise anything. :param conn: pymysql.connections.Connection :type conn: pymysql.connections.Connection """ Meters.aii("k.db_pool.mysql.call._connection_close") # noinspection PyBroadException try: if conn: conn.close() except Exception as e: # Dont care of exception in case of closing Meters.aii("k.db_pool.mysql.ex_close") logger.debug("Close exception (non fatal), ex=%s", SolBase.extostr(e))
def _safe_unhash(self, key): """ Remove a key from cache (from both dict) :param key: Any key :type key: str """ try: # Kick from both hashes try: del (self._hash_key[key]) except KeyError: pass try: del (self._hash_context[key]) except KeyError: pass except Exception as e: logger.warning("Exception, ex=%s", SolBase.extostr(e)) Meters.aii(self.meters_prefix + "mcs.cache_ex")
def remove(self, key): """ Remove a key from cache. :param key: Any key :type key: str """ ms_start = SolBase.mscurrent() try: if not isinstance(key, (bytes, str)): raise Exception("Key must be (bytes, str)") # Use write redis self._write_redis.delete(key) except Exception as e: logger.warning("Exception, ex=%s", SolBase.extostr(e)) Meters.aii(self.meters_prefix + "rcs.cache_ex") finally: Meters.dtci(self.meters_prefix + "rcs.cache_dtc_write", SolBase.msdiff(ms_start))