Cancelar sockets postgresql

Cancelar sockets postgresql

Nota : los comandos enunciados a continuación están funcionado en Linux Centos  7.0 java 1.8 y postgres 9.0

En el desarrollo de aplicaciones y el uso de Postgres me he encontrado con un inconveniente por demás muy molesto de los sockets IDLE,  (ociosos), que aunque no consumen mucho recuros de CPU y muy poca memoria , tiene el inconveniente de ir sumando en la cuenta de el máximo numero de sockets abiertos, que en general es un numero limitado casi nunca superior a 3 0 4 centenares, podría decirse que son bastantes en general pero si tiene una pagina de mucho trafico estos valores limite pueden ser rebasados rápidamente, y dejar el postgres inoperante por un largo periodo de tiempo, el caso es que aunque uno se esfuerce mucho en abrir y cerrarlos con comando como rt.close() o st.close() por ejemplo, no faltan los usuarios indisciplinados que no terminan los procesos pues cancelan la session de forma imprevista al salirse sin dejar terminar los procesos de forma natural en el navegador, por esta razón me ha tocado implementar algún método que me permita eliminar estos sockets ociosos problemáticos.
 
La solucion la he implementado en dos formatos básicos pero siempre partiendo del hecho de que tengo un (job) trabajo ejecutándose de forma periódica cada cierto tiempo , yo suelo usarlo cada 5 minutos mediante la utilidad cron (ver comando cron en linux para programar tareas repetidas sin intervención).

Se creó un sencillo scrip que solo difere en un comando dependindo si deseo que se ejecute como un comando del sistema operativo un poco mas burdo en el control de tiempo o uno mas afinado con verificacion de tiempo de socket inactivo, ambos funcionan pero dependen de la necesidad especifica.


1. Modo usando solo comandos del sistema operativo.
El comando central es el siguiente:
 
ps -ef | grep postgres | grep idle |sort -n -k5.1 -k5.2 -k5.4 -k5.5|awk 'NR < 355 {print $2}'| xargs kill -15

 a. ps -ef : lista los sockets disponibles en el sistema
 b. grep postgres : selecciona solo los sockets postgres
 c. grep idle : solo lo que están inactivos
 c. grep idle |sort -n -k5.1 -k5.2 -k5.4 -k5.5 : organiza la lista de sockets IDLE por el 5 campo que es donde está la hora/fecha de lanzamiento del socket (5.1=primr digito de 5.2=segundo digito de hora 5.4= primer digito de minutos 5.5= segundo digito de minutos,
-r organización de mayor menor y -k=Posix1

 d. awk '{print $2}' : awk es un shell de linux para la busqueda y tratamiento de archivos de texto, con el comando print $2 le indico a awk que solo estoy interesado en la segunda columna listada por ps -ef , donde esta los numeros de los sockets
 e.  awk 'NR < 355{print}' : Muestra las primeras 355 primeras lineas, como solo deseo borrar las primeras 355 primeras ocurrencias de sockets inactivos , y en mi script  MAXIMO está en 450 dejo una salvaguarda los los últimos 45 (10%), sockets que pueden estar siendo procesados en el momento de la ejecución del script para evitar cancelar alguna tarea que se esta pueda estar ejecutando, en mi caso he visto que un margen 10% aproximadamente me ha funcionado bien. en este caso especifico 450*(10/100)=355. sockets a borrar.
 f. xarg : recorre de forma secuencial el resultado entregado por awk y ejecuta un kill sobre cada una de las entradas, eliminado cada socket que se ajuste al requerimiento de ser de postgres y estar en estado IDLE. el kill -15 evita que cancele también el proceso padre de postgres que sería un efecto indeseado ya que me apaga la base de datos.

#*************************************************
# el script /usr/bin/sos_postgresql 
# Define el MAXIMO numero de sockets abiertos
# USUARIOS son los sockets IDLE actuales
#*************************************************
MAXIMO=450
USUARIOS=`ps -e | fgrep "postmaster" | wc -l`

if [  ${USUARIOS} -gt ${MAXIMO}  ]
then
 
   ps -ef | grep postgres | grep idle |sort -n -k5.1 -k5.2 -k5.4 -k5.5|awk 'NR < 355 {print $2}'| xargs kill -15
 
fi

Un pequeño script que implementa no un conteo de lineas sino un retardo de
10 segundos antes del borrado para dar tiempo a que los sockets listados
se cierren

el comando ping con:
    con -i 1    : espere un segundo
           -c 10 : haga 10 veces el ping

Se podría haber colocado -i 10, pero lo que se dice es que genere un ping con un intervalo de 10 segundos, no hay garantía de que sean 10, depende del linux, no trate de usar el ping en una sola línea como en el script anterior porque el awk lo que recibe es la salida del ping y no el conenido de los sockets a eliminar, no funciona, por eso se necesita el archivo intermedio sos.txt para almacenar los sockets que están IDLE, esperamos que terminen normalmente los que están en proceso, durante unos 10 segundos en este caso y procedemos a eliminarlos los que quedaron registrados en sos.txt

ping -i 1 -c 10 127.0.0.1 > null

Horrible, horrible pero efectivo como feo.



#*************************************************
# el script /usr/bin/sos_postgresql 
# Define el MAXIMO numero de sockets abiertos
# USUARIOS son los sockets IDLE actuales
#*************************************************
MAXIMO=450
USUARIOS=`ps -e | fgrep "postmaster" | wc -l`

if [  ${USUARIOS} -gt ${MAXIMO}  ]
then
 
  ps -ef | grep postgres | fgrep 'idle' > sos.txt
  ping -i 1 -c 10 127.0.0.1 > null
  awk '{print $2}' sos.txt | xargs kill -15

fi



2. Modo usando java
#*************************************************
# el script /usr/bin/sos_postgresql 
# Define el MAXIMO numero de sockets abiertos
# USUARIOS son los sockets IDLE actuales

#*************************************************

MAXIMO=450
USUARIOS=`ps -e | fgrep "postmaster" | wc -l`

if [  ${USUARIOS} -gt ${MAXIMO}  ]
then
 

   java SosSocket
 
fi

 -------------------------------------------------------------------------------------------------
Script java para cancelar sockets

El principio básico es consultar una vista definida de postgres, sobre una base cualquiera que exista. en este ejemplo "mibase", con el usurio de la base "miusuario", llamada pg_stat_activity que informa el estado de los sockets postgres consulta el campo procpid donde esta el ID del socket y verifica
medinte current_query='<IDLE>' el estado del socket , nota la consulta '<IDLE>' depende de la versión de postgres puede tener o no corchetes, paréntesis, comillas dobles o sencillas, verifique
su sistema especifico como presenta el campo current_query para sockets inactivos, recuerde solo debe cancelar los sockets IDLE, pues hay diferentes versiones de IDLE, dentro de lo cuales están
lo que se encuentran en waiting y transation, esos no deberán ser eliminados porque abortan un proceso que está en ejecución

now()-query_start > '00:05:00', garantiza que solo elimina los que tiene mas de 5 minutos inactivos, es decir que es casi seguro que no van a entrar en waiting. La pareja de comando listados a continuación efectúan un llamado al comando del sistema operativo que es el encargado de cancelar sockets.

socketIDLE = "kill -15 "  ;
Sos.Run(socketIDLE)    ;

Sos.Run efectúa el procedimiento

Runtime command = Runtime.getRuntime()   ;

que permite llevar a cabo la ejcucion de comandos del sistema
operativo desde java.

/*
 -----------------------------------------------------------------------------------------------------------------
  javac SosSocket.java
 -----------------------------------------------------------------------------------------------------------------
*/

import java.util.Calendar                                               ;
import java.io.*                                                              ;
import java.sql.*                                                            ;
import java.net.*                                                            ;
import java.util.*                                                            ;
import java.text.*                                                           ;
              
public class SosSocket
{

   public static void main(String [ ] args)
    {
       SocketCancelar() ;
    }

    static public void SocketCancelar()
    {
      Connection  conexion   = null                                 ;
      Statement   st                                                           ;

      Vector      sockets    = new Vector()                         ;
      String      socketIDLE = new String()                      ;
      int         i          = 0                                                    ;
      int         j          = 0                                                    ;
    try
        {

         st = Conexion()                                                     ;
         sockets = ConseguirSocketsIDLE(st)                   ;
                                                                                 
       
         if (sockets.size() > 0)
           {
              socketIDLE = "kill -15 "                                   ;
              for (i=0 ; i < sockets.size(); i++)
                  {
                      socketIDLE += sockets.elementAt(i).toString()           ;
                      Sos.Run(socketIDLE)                                ;
                   }

           }
         st.close()                                                                 ;
        }

    catch (Exception e)
        {
          System.out.println (e.toString())                                   ;
        }
    }

 static public Vector ConseguirSocketsIDLE(Statement st)         
   {current_query='<IDLE>'

      ResultSet    rt                                                                    ;
      StringBuffer SqlSb   = new StringBuffer()                      ;
      Vector       sockets = new Vector()                                    ;

      SqlSb.append(" SELECT  ")                                             ;
      SqlSb.append(" procpid ")                                               ;
      SqlSb.append(" FROM ")                                                 ;
      SqlSb.append(" pg_stat_activity ")                                 ;
      SqlSb.append(" WHERE ")                                              ;
      SqlSb.append(" current_query='<IDLE>' ")                 ;
      SqlSb.append("  AND ")                                                   ;
      SqlSb.append(" now()-query_start > '00:01:00' ")         ;

      try
        {
          rt = st.executeQuery(SqlSb.toString())                          ;
          while (rt.next())                                               
            {
               sockets.addElement(rt.getString(1))                         ;
               System.out.println (rt.getString(1)) ;
            }
          rt.close()                                                                         ;
          return (sockets)                                                              ;
        }
        catch(Exception e)
        {
          System.out.println (e.toString())                                    ;
          return (sockets)                                                               ;
        }
   }
  static public Statement Conexion()         
   {

    Connection  conexion   = null                                              ;
    Statement   st                                                                        ;
    StringBuffer SqlSbC  = new StringBuffer()                        ;

    try
      {
           SqlSbC  = new StringBuffer()                                       ;
           SqlSbC.append("jdbc:postgresql:")                               ;
           SqlSbC.append("movimiento")                                     ;
           String dataBase = SqlSbC.toString()                             ;

           Close()                                                                            ;
 
           Class.forName("org.postgresql.Driver")                         ;
           conexion = DriverManager.getConnection(
                                   "miBase","postgres","miusuario")          ;
           st = conexion.createStatement()                                      ;

           return(st)                                                                          ;
     
      }
     catch(Exception e)
      {
           return (null)                                                                       ;
      }

  static public void Run(String comando)
    {Runtime command = Runtime.getRuntime()                         ;

        Runtime command = Runtime.getRuntime()                  ;
        Process proceso = null                                                          ;
        try
            {
                proceso = command.exec(comando)                            ;
                proceso.waitFor()                                                          ;
            }
        catch (Exception e)
            {
              System.out.println (e.toString())                                     ;
            }
    }








Comentarios

  1. hola

    El kill -9 no ocasiona que toda la instancia PostgreSQL se caiga?? pensaría que debería emplear un kill -15.

    ResponderEliminar
  2. Gracias por el comentario efectivamente eso ocurre , ya implemente esta observacion

    ResponderEliminar

Publicar un comentario

Entradas populares