Redis Python Client opens many connections

4.8k views Asked by At

I am using the below code to connect with the Redis server. I am seeing huge connections are on the TIME_WAIT state. What could be wrong ?

root@ubuntu:~$ netstat | grep :6479 | grep TIME_WAIT |wc -l
9061
root@ubuntu:~$ netstat | grep :6479 | grep ESTABLISHED |wc -l
7

I thought of closing the connection once the operation is done with Redis server using the below code. But I am getting the error with this.

@staticmethod
def disconnectRedisConnection(r_server):
    if r_server is not None and r_server:
        r_server.connection.disconnect()

I am getting the below error,

r_server.connection.disconnect()
AttributeError: 'Redis' object has no attribute 'connection'

Any thoughts on the huge TIME_WAIT connections / Closing the connection once the operation is done with Redis ? Code:

import threading
from time import sleep
import time, datetime
import traceback
import CACHE_CONST
import json
import os

import MySQLdb
import redis

# Static methods to interact with the Redis cache server
class CacheUtil(object):

    # Log Errors
    @staticmethod
    def log_message(msg):
        log_file = None
        log_file = open (os.path.abspath(CACHE_CONST.REDIS_LOG_FILE), "a")
        print(msg)
        if (log_file):
            message = time.strftime("%d-%m-%Y %H:%M:%S")    
            message += " :: " + str(msg)
            log_file.write(message + "\n")

    @staticmethod
    def saveToCache(hashName, hashValue):
        r_server = CacheUtil.getRedisConnection()
        r_server.hmset(hashName, hashValue)
        CacheUtil.disconnectRedisConnection(r_server)

    @staticmethod
    def getTrackerDetailsByID(trackerId):
        trackerDetail = None
        r_server = None
        hashName = "tDetails:" + str(trackerId)
        try:
            if trackerId is not None:
                print("getTrackerDetailsByID ")
                r_server = CacheUtil.getRedisConnection()
                trackerDetail = r_server.hgetall(hashName)
            else:
                CacheUtil.log_message("getDetailsByID failed with empty trackerId ")
        except:
            CacheUtil.log_message("getDetailsByID failed, ll fetch from DB " + str(traceback.format_exc()))
        finally:
            CacheUtil.disconnectRedisConnection(r_server)
        return trackerDetail

    @staticmethod
    def getRedisConnection():
        print("Get Redis Connection on Util ")
        r_server = redis.Redis(host=CACHE_CONST.REDIS_SERVER_URL, port=CACHE_CONST.REDIS_SERVER_PORT, db=0, password=CACHE_CONST.REDIS_PASS_PHRASE, socket_connect_timeout=2, socket_timeout=2)
        return r_server;

    @staticmethod
    def disconnectRedisConnection(r_server):
        if r_server is not None and r_server:
            r_server.connection.disconnect()
1

There are 1 answers

3
GuangshengZuo On BEST ANSWER

Actually, when you call the redis.Redis() , it will create "client" for you which has a connection pool, not just a connection.

Each time you send a command like redis.set() or something other, it will retrieve a connection from its connection pool, and use this connection to send this command and wait for reply. when the request has done, it put back the connection to connection pool for reusing. so you do not need to manage the connection yourself. check out this https://github.com/andymccurdy/redis-py for more infos.

Just like this:

import threading
from time import sleep
import time, datetime
import traceback
import CACHE_CONST
import json
import os

import MySQLdb
import redis
r_server = redis.Redis(host=CACHE_CONST.REDIS_SERVER_URL, port=CACHE_CONST.REDIS_SERVER_PORT, db=0, password=CACHE_CONST.REDIS_PASS_PHRASE, socket_connect_timeout=2, socket_timeout=2)

# Static methods to interact with the Redis cache server
class CacheUtil(object):

    # Log Errors
    @staticmethod
    def log_message(msg):
        log_file = None
        log_file = open (os.path.abspath(CACHE_CONST.REDIS_LOG_FILE), "a")
        print(msg)
        if (log_file):
            message = time.strftime("%d-%m-%Y %H:%M:%S")    
            message += " :: " + str(msg)
            log_file.write(message + "\n")

    @staticmethod
    def saveToCache(hashName, hashValue):
        r_server.hmset(hashName, hashValue)

    @staticmethod
    def getTrackerDetailsByID(trackerId):
        hashName = "tDetails:" + str(trackerId)
        try:
            if trackerId is not None:
                print("getTrackerDetailsByID ")
                trackerDetail = r_server.hgetall(hashName)
            else:
                CacheUtil.log_message("getDetailsByID failed with empty trackerId ")
        except:
            CacheUtil.log_message("getDetailsByID failed, ll fetch from DB " + str(traceback.format_exc()))
        return trackerDetail

UPDATE

Every time you use redis instance send command, it will call this method:

    # COMMAND EXECUTION AND PROTOCOL PARSING
def execute_command(self, *args, **options):
    "Execute a command and return a parsed response"
    pool = self.connection_pool
    command_name = args[0]
    connection = pool.get_connection(command_name, **options)
    try:
        connection.send_command(*args)
        return self.parse_response(connection, command_name, **options)
    except (ConnectionError, TimeoutError) as e:
        connection.disconnect()
        if not connection.retry_on_timeout and isinstance(e, TimeoutError):
            raise
        connection.send_command(*args)
        return self.parse_response(connection, command_name, **options)
    finally:
        pool.release(connection)

Any thoughts on the huge TIME_WAIT connections / Closing the connection once the operation is done with Redis

enter image description here

This is an image about TCP conection termination process. when the client(Initiator) send ACK for server(Reciever) FIN, and it enters into the Time_WAIT status.

The words quoted from TCP/IP illustrated vol 1:

When TCP performs an active close and sends the final ACK, that connection must stay in the TIME_WAIT state for twice the MSL. This lets TCP resend the final ACK in case it is lost. The final ACK is resent not because the TCP retransmits ACKs (they do not consume sequence numbers and are not retransmitted by TCP), but because the other side will retransmit its FIN (which does consume a sequence number). Indeed, TCP will always retransmit FINs until it receives a final ACK.

So it will be in TIME_WAIT state for four minutes, after that, it will close the connection automatically. Because you open new tcp connection and close it frequently, then many closed connection will be in TIME_WAIT status.

Also There is a more detailed article about the purpose about TIME_WAIT