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 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 _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 _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 __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 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 _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 _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 __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 setUp(self): """ Setup """ MysqlApi.reset_pools() Meters.reset() d_conf_root = { "host": "localhost", "port": 3306, "database": None, "user": "******", "password": "******", "autocommit": True, } # exec_n try: ar = MysqlApi.exec_n(d_conf_root, "DROP DATABASE IF EXISTS pysolmysql_test;") logger.info("ar=%s", ar) except Exception as e: logger.debug("Ex=%s", SolBase.extostr(e)) MysqlApi.exec_n(d_conf_root, "CREATE DATABASE IF NOT EXISTS pysolmysql_test;") # Full reset MysqlApi.reset_pools() Meters.reset()
def test_basic_ttl(self): """ Test :return: """ # Alloc self.redis_cache = RedisCache() # Put self.redis_cache.put(self.key_prefix + "keyA", b"valA", 60000) self.redis_cache.put(self.key_prefix + "keyB", b"valB", 1000) logger.info("ms cur=%s", SolBase.mscurrent()) logger.info("A : %s", self.redis_cache.get(self.key_prefix + "keyA")) logger.info("B : %s", self.redis_cache.get(self.key_prefix + "keyB")) # Wait a bit SolBase.sleep(2000) logger.info("ms after sleep=%s", SolBase.mscurrent()) # A : must be present # B : must be evicted (TTL elapsed) self.assertEqual(self.redis_cache.get(self.key_prefix + "keyA"), b"valA") self.assertIsNone(self.redis_cache.get(self.key_prefix + "keyB")) self.assertEqual(Meters.aig("rcs.cache_put"), 2) self.assertEqual(Meters.aig("rcs.cache_get_hit"), 3) self.assertEqual(Meters.aig("rcs.cache_get_miss"), 1) # Stop self.redis_cache.stop_cache() self.redis_cache = None
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 test_max_item_size(self): """ Test :return: """ # Alloc self.redis_cache = RedisCache(max_single_item_bytes=2) # Put self.assertTrue( self.redis_cache.put(self.key_prefix + "keyA", b"aa", 60000)) self.assertFalse( self.redis_cache.put(self.key_prefix + "keyB", b"aaa", 60000)) logger.info("ms cur=%s", SolBase.mscurrent()) logger.info("A : %s", self.redis_cache.get(self.key_prefix + "keyA")) logger.info("B : %s", self.redis_cache.get(self.key_prefix + "keyB")) # A : must be present # B : must be out of cache self.assertEqual(self.redis_cache.get(self.key_prefix + "keyA"), b"aa") self.assertIsNone(self.redis_cache.get(self.key_prefix + "keyB")) self.assertEqual(Meters.aig("rcs.cache_put"), 1) self.assertEqual(Meters.aig("rcs.cache_get_hit"), 2) self.assertEqual(Meters.aig("rcs.cache_get_miss"), 2) # Stop self.redis_cache.stop_cache() self.redis_cache = None
def tearDown(self): """ Setup (called on destroy) """ SolBase.logging_init(log_level='DEBUG', force_reset=True) Meters.write_to_logger() SolBase.logging_init(log_level='INFO', force_reset=True)
def _internal_test(self, client_count, ping_interval_ms, my_run_time_ms=20000): """ Test. :param client_count: Client count. :param ping_interval_ms: Ping interval in millis. :param my_run_time_ms: Run time in millis. :return: nothing. """ try: # Overrides statics (beuuurk but so gooood) self.clientMaxCount = client_count self.serverPingIntervalMs = ping_interval_ms self.clientPingIntervalMs = ping_interval_ms self.runTimeMs = my_run_time_ms # Expected server and client ping/src self.expectedPps = (float(self.clientMaxCount) / float(self.serverPingIntervalMs)) * 1000.0 logger.info("starting, client=%s, ping.ms=%s, expected.pps=%s", client_count, ping_interval_ms, self.expectedPps) # Instance self.tcp_server = None try: # Start server self._start_server_and_check() # Start client, check and stop client self._start_multi_client_checkallping_stop() # Stop server self._stop_server_and_check() except Exception as e: logger.error("Exception in test, ex=%s", SolBase.extostr(e)) raise finally: if self.tcp_server: self.tcp_server.stop_server() finally: # Final stats logger.info("Finished, final stats bellow") Meters.write_to_logger() # Reset self.tcp_server = None # Sleep Utility.test_wait()
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 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 test_pool_basic_x2(self): """ Test pool, basic """ MysqlApi.reset_pools() Meters.reset() d_conf = { "hosts": ["localhost", "127.0.0.1"], "port": 3306, "database": None, "user": "******", "password": "******", "autocommit": True, } for _ in range(0, 10): MysqlApi.exec_1(d_conf, "SELECT user, host FROM mysql.user LIMIT 1;") d_conf = { "hosts": ["localhost", "localhost"], "port": 3306, "database": None, "user": "******", "password": "******", "autocommit": True, } for _ in range(0, 10): MysqlApi.exec_1(d_conf, "SELECT user, host FROM mysql.user LIMIT 1;") # Check it self.assertEquals(Meters.aig("k.db_pool.hash.cur"), 1 * 2) self.assertEquals(Meters.aig("k.db_pool.base.cur_size"), 1 * 2) self.assertEquals(Meters.aig("k.db_pool.base.call.connection_acquire"), 10 * 2) self.assertEquals(Meters.aig("k.db_pool.base.call.connection_release"), 10 * 2) self.assertEquals(Meters.aig("k.db_pool.mysql.call.__init"), 1 * 2) self.assertEquals( Meters.aig("k.db_pool.mysql.call._connection_create"), 1 * 2) self.assertEquals(Meters.aig("k.db_pool.mysql.call._get_connection"), 1 * 2) self.assertEquals(Meters.aig("k.db_pool.mysql.call._connection_ping"), 10 * 2)
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 get(self, key): """ Get from cache. :param key: Any key :type key: str :return An obj or null if not in cache :rtype bytes, None """ ms_start = SolBase.mscurrent() try: if not isinstance(key, (bytes, str)): raise Exception("Key must be (bytes, str)") # Use read redis v = self._read_redis.get(key) if v: Meters.aii(self.meters_prefix + "rcs.cache_get_hit") else: Meters.aii(self.meters_prefix + "rcs.cache_get_miss") return v except Exception as e: logger.warning("Exception, ex=%s", SolBase.extostr(e)) Meters.aii(self.meters_prefix + "rcs.cache_ex") return None finally: Meters.dtci(self.meters_prefix + "rcs.cache_dtc_read", SolBase.msdiff(ms_start))
def __str__(self): """ Str override :return str :rtype: str """ return "id={0}*put/bypass/hit/miss={1}/{2}/{3}/{4}*ex={5}".format( id(self), Meters.aig(self.meters_prefix + "rcs.cache_put"), Meters.aig(self.meters_prefix + "rcs.cache_put_too_big"), Meters.aig(self.meters_prefix + "rcs.cache_get_hit"), Meters.aig(self.meters_prefix + "rcs.cache_get_miss"), Meters.aig(self.meters_prefix + "rcs.cache_ex"), )
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 test_basic_eviction_max_capacity_lru(self): """ Test :return: """ # Alloc self.mem_cache = MemoryCache(max_item=3, cb_evict=self.eviction_callback) # Put self.mem_cache.put("keyA", b"valA", 60000) self.mem_cache.put("keyB", b"valB", 60000) self.mem_cache.put("keyC", b"valC", 60000) # Use A and C => B becomes the older to be used self.mem_cache.get("keyA") self.mem_cache.get("keyC") # We are maxed (3 items) # Add D => B must be kicked self.mem_cache.put("keyD", b"valD", 60000) self.assertEqual(self.mem_cache.get("keyA"), b"valA") self.assertEqual(self.mem_cache.get("keyC"), b"valC") self.assertEqual(self.mem_cache.get("keyD"), b"valD") self.assertIsNone(self.mem_cache.get("keyB")) self.assertEqual(self.evict_count, 1) self.assertEqual(self.evict_last_key, "keyB") self.assertEqual(self.evict_last_value, b"valB") self.assertEqual(Meters.aig("mcs.cache_evict_lru_put"), 1) # Stop self.mem_cache.stop_cache() self.mem_cache = None
def test_basic_eviction_with_get_ttl(self): """ Test :return: """ # Alloc self.mem_cache = MemoryCache(cb_evict=self.eviction_callback) # Put self.mem_cache.put("keyA", b"valA", 60000) self.mem_cache.put("keyB", b"valB", 500) logger.info("ms cur=%s", SolBase.mscurrent()) logger.info("A : %s", self.mem_cache.get_raw("keyA")) logger.info("B : %s", self.mem_cache.get_raw("keyB")) # Wait a bit SolBase.sleep(600) logger.info("ms after sleep=%s", SolBase.mscurrent()) # A : must be present # B : must be evicted (TTL elapsed) self.assertEqual(self.mem_cache.get("keyA"), b"valA") self.assertIsNone(self.mem_cache.get("keyB")) self.assertEqual(self.evict_count, 1) self.assertEqual(self.evict_last_key, "keyB") self.assertEqual(self.evict_last_value, b"valB") self.assertEqual(Meters.aig("mcs.cache_evict_ttl_get"), 1) # Stop self.mem_cache.stop_cache() self.mem_cache = None
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_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_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 setUp(self): """ Setup (called before each test) """ # Initialize asap SolBase.voodoo_init() SolBase.set_compo_name("CompoNotSet") self.assertTrue(SolBase._voodoo_initialized) self.assertTrue(SolBase._logging_initialized) # Reset Meters.reset() # Bench self.per_loop = 10000 self.max_ms = 2000
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()