Listado de comandos de administración de Informix

Introducción

La idea de este post es listar algunos comandos útiles para el servidor de base de datos Informix, con el que algunos tenemos que sufrir trabajar día a día. Muchos de ellos se pueden ejecutar desde ambientes gráficos, pero generalmente no disponemos de dichos ambientes en servidores de testing o producción.

Intenteré ir actualizando la lista a medida que me vaya encontrando con otros comandos que considere que valgan la pena.

Los comandos

Bajar el motor

Desde la línea de comandos, ejecutar:

<br />
onmode -ky<br />

Podemos hacerlo de forma más «delicada», siguiendo los siguientes pasos:

Ejecutar

<br />
onmode -sy<br />

para dejar no permitir nuevas conexiones, pero permitir que las conexiones que ya estaban abiertas se cierren de forma normal. luego de cerrar todas las conexiones, el servidor queda en quiescent mode, algo
así como inactivo, pero no apagado.

El siguiente paso es movernos al siguiente log lógico, ejecutando:

<br />
onmode -l<br />

Luego forzamos un checkpoint, de forma de estar seguros de escribir todos los buffers a disco:

<br />
onmode -c<br />

Finalmente, ejecutamos el comando para dejar el servidor de Informix offline:

<br />
onmode -ky<br />

Subir el motor

Para subir el motor de Informix, ejecutamos sin parámetros el comando:

<br />
oninit<br />

En Linux/UNIX, debemos estar logueados como root o como informix para poder ejecutar oninit. En Windows, se debe ejecutar siendo miembro del grupo Informix-Admin.

Ver la descripción de un error

Dado un código de error, podemos obtener una descripción y posibles acciones correctivas ejecutando:

<br />
finderr numero#<br />

El resultado es similar al siguiente (cambiando para cada error, claro está):

<br />
infordb:~ # finderr 167<br />
-167    ISAM error: Storage-space size is not multiple of PAGESIZE.</p>
<p>The database server administrator sees this error. When you define a<br />
storage space, you must specify a page size that is an integral multiple of<br />
the system page size. The system page size is set in the Parameters<br />
screen when the database server is first initialized.<br />

Pasar una base de transaccional a no transaccional y viceversa

Al restaurar un respaldo de una base de datos, si no decimos explícitamente, la base restaurada no soportará transacciones, sino que quedará en modo no transaccional. Al intentar utilizarla desde nuestras aplicaciones, las sentencias de BEGIN WORK, COMMIT, ROLLBACK, etc. fallarán por estar trabajando la base en este modo.

En estos casos, podemos pasar la base de datos a transactional unbuffered ejecutando el siguiente comando en una terminal:

<br />
ontape -s -L 0 -U [database]<br />

nO el siguiente, para pasarla a transactional buffered:

<br />
ontape -s -L 0 -B [database]<br />

Si por el contrario, queremos pasar una base de modo transaccional a no transaccional, ejecutamos:

<br />
ontape -s -L 0 -N [database]<br />

Por información acerca de la diferencia los logging modes transactional unbuffered y transactional buffered, ver este otro post del blog.

Exportar una base de datos

El comando dbexport descarga todos los datos de cada tabla de una base de datos y genera un esquema de la base de datos. Para exportar una base de dato a archivos, debemos ejecutar el siguiente comando en una terminal:

<br />
dbexport [database]<br />

Esto generará un directorio [database].exp con los datos de cada tabla, y desplegará por salida estandar (pantalla,
al menos que se rediriga a un archivo) un script con la creación de los objetos de la base.

Importar una base de datos

Para importar una base de datos podemos ejecutar el comando dbimport. dbimport lee el archivo de esquema generado por el comando dbexport y crea una base de datos cargando los datos de los archivos. Se puede especificar un dbspace determinado, y el logging mode de la base importada:

<br />
dbimport [database] [-i directory] [-d dbspace] [-l [buffered]]<br />

En donde:

  • en [database].exp están ubicados los archivos de datos a importar
  • -d dbspace: especifica el nombre del dbspace en donde la base será creada. Por defecto, será rootdbs.
  • -l: Establece que la base importada funcionará en modo unbuffered transaction logging
  • -l buffered: Establece que la base importada funcionará en modo buffered transaction logging

El parámetro [database] es la ubicación
en de los archivos a importar; el comando buscará un directorio llamado [database].exp para leer los datos de las tablas. Si se especifica, se buscará ese directorio dentro de [directory]. Se asume que existirá un archivo [database].exp/[database].sql conteniendo la estructura de la base de datos (tablas, índices, contraints, etc.).

Mostrar información de uso de espacio físico

Ejecutando el siguiente comando desde una terminal, obtenemos un reporte de los diferentes dbspaces y el espacio utilizado en cada uno:

<br />
onstat -d<br />

La salida es similar a la siguiente:

<br />
infordb:~ # onstat -d</p>
<p>IBM Informix Dynamic Server Version 10.00.FC7     -- On-Line -- Up 1 days 13:15:39 -- 38720 Kbytes</p>
<p>Dbspaces<br />
address          number   flags      fchunk   nchunks  pgsize   flags    owner    name<br />
450c8e78         1        0x60001    1        1        2048     N  B     Informix rootdbs<br />
45253450         2        0x60001    2        1        2048     N<br />
 B     Informix datadbs<br />
 2 active, 2047 maximum</p>
<p>Chunks<br />
address          chunk/dbs     offset     size       free       bpages     flags pathname<br />
450c9028         1      1      0          250000     58385                 PO-B  /opt/IBM/Informix/CHUNKS/rootdbs.000<br />
45269db8         2      2      0          1048576    516245                PO-B  /opt/IBM/Informix/CHUNKS/datadbs.000<br />
 2 active, 32766 maximum</p>
<p>NOTE: The values in the &quot;size&quot; and &quot;free&quot; columns for DBspace chunks are<br />
      displayed in terms of &quot;pgsize&quot; of the DBspace to which they belong.</p>
<p>Expanded chunk capacity mode: always<br />

El tamaño y el espacio libre de cada chunk (columnas size y free) están expresadas en páginas. El tamaño de cada página está expresada en bytes en la columna pgsize, por lo que el tamaño de cada chunk está determinado por size x pgsize. Cada chunk está asociado a un dbspace, y esta asociación se detalla en la columna chunk/dbs (el primer número es el id del chunk, el segundo es el id del dbspace).

Este reporte también muestra información de replicación, rutas en dispositivos físicos, etc. Por una descripción completa, ver la página de onstat -d en la Administrator’s Reference de Informix.

Actualizar las estadísticas

Desde dbaccess u otro cliente de Informix, podemos ejecutar los siguientes comandos para actualizar las estadísticas de la base de datos:

Actualizar estadísticas de todas las tablas la base de datos:

<br />
update statistics<br />

Para actualizar además las estadísticas de distribución de cada tabla (es más lento):

<br />
update statistics high<br />

Para actualizar las estadísticas para una tabla:

<br />
UPDATE STATISTICS [HIGH] FOR TABLE [tabla]<br />

Lock mode de las tablas

En este post escribí acerca de los lock modes de Informix. Para obtener la información acerca del tipo de bloqueo que tienen las tablas de una base podemos ejecutar la sigiuente consulta:

<br />
SELECT tabname,locklevel FROM systables WHERE tabid&amp;gt;99<br />

La columna tabname contiene el nombre de la tabla, la columna locklevel contiene ‘P’ si el lock mode es por página, o ‘R’ si es por tupla.

Para crear una tabla con un lock mode determinado lo especificamos al ejecutar el comando CREATE TABLE:

<br />
CREATE TABLE customer(customer_num serial, lname char(20)...)<br />
LOCK MODE ROW|PAGE|TABLE;<br />

Para cambiar el lock mode de una tabla utilizamos el comando ALTER TABLE:

<br />
ALTER TABLE [tabla] MODIFY<br />
LOCK MODE (PAGE|ROW|TABLE)<br />

Referencias

Tagged

Lock modes de tablas de Informix

El lock mode de una tabla especifica el nivel de granularidad que se tendrá para acceder a los datos de forma concurrente. Hay 3 niveles: por tupla (row), por página (page), o por tabla (table).

Al menos que se explicite, al crear una tabla el modo de bloqueo (lock mode) será por página. Si la tabla tiene muchos accesos concurrentes, se puede aumentar la granularidad y usar el modo de bloqueo a nivel de tupla. Este aumento de la granularidad conlleva a un aumento en la cantidad de objetos bloqueados que se deben mantener, lo que redunda en mayor uso de memoria y una posible baja de performance del motor.

Un nivel de bloqueo por tupla sirve cuando se actualizan pocas tuplas de una tabla, o cuando es necesario actualizar de forma concurrente (desde distintas sesiones) distintas tuplas de la misma página. En este caso, si los bloqueos son por página, es posible que las actualizaciones fallen, o tengan que
esperar que se termine de actualizar una tupla de la misma página.

El tipo de bloqueo por tabla utiliza menos recursos que los bloqueos por página o tupla. Este tiplo de bloqueo se pueden utilizar en procesos batch, cuando es un único proceso que accede a cada tabla de la base de datos. De deben tener recaudos con este bloqueos de tablas en sistemas de tipo OLTP. Ante un bloqueo de una tabla, otros procesos de sólo lectura igual podrán acceder a los datos de la misma.

Para obtener la información acerca del tipo de bloqueo que tienen las tablas de una base podemos ejecutar la sigiuente consulta:

<br />
SELECT tabname,locklevel FROM systables WHERE tabid&amp;gt;99<br />

La columna tabname contiene el nombre de la tabla, la columna locklevel contiene ‘P’ si el lock mode es por página, o ‘R’ si es por tupla.

Para crear una tabla con un lock mode determinado lo especificamos al ejecutar el comando CREATE TABLE:

<br />
CREATE TABLE customer(customer_num serial, lname char(20)...)<br />
LOCK MODE ROW|PAGE|TABLE;<br />

Para cambiar el lock mode de una tabla utilizamos el comando ALTER TABLE:

<br />
ALTER TABLE [tabla]<br />
MODIFY LOCK MODE (PAGE|ROW|TABLE)<br />

Tagged

Diferencias entre transactional unbuffered y transactional buffered en Informix

Vale la pena destacar la diferencia entre estos dos modos de trabajo (logging modes) de las bases de datos de Informix.

Informix siempre persiste los datos escribiendo a alguno de sus buffers de log lógico. Si la base de datos en la cual se están haciendo cambios está trabajando en modo unbuffered, estos cambios se escriben al disco duro inmediatamente después de que un registro de commit o rollback llega al log lógico. Si la base de datos está trabajando en modo buffered, el servidor mantiene estos cambios se mantienen en el log el mayor tiempo posible, hasta que ocurre alguno de los siguiente eventos:

  • Un buffer se llena
  • Se ejecuta un checkpoint
  • Se cierra la conexión contra la base de datos
  • Se hace un commit a una base que está trabajando en modo unbuffered

En el modo buffered, existen problemas si el sistema se bloquea antes de
que el buffer de log lógico sea escrito al disco duro pero después de la transacción que el usuario completó ejecutando un commit. La transacción se considerará incompleta y se ejecutará un rollback cuando el motor sea reiniciado y se recupere. En modo unbuffered esto no sucede ya que luego del commit se escribirán los datos de la transacción en el disco duro y no se perderán.

Por defecto, se trabaja en modo unbuffered. Si bien enlententece un poco el motor, es más robusto que el modo buffered frente a caídas del motor en lo que a pérdida de datos respecta.

El tamaño de los buffers de logs lógicos se pueden averiguar en el archivo onconfig (parámetro LOGBUFF).

Referencias:

Tagged

Copiar archivo y particiones utilizando dd (Linux/UNIX)

Introducción

dd es de esos comandos de Linux y UNIX «para temer». Puede ser de mucha utilidad en algunas situaciones, pero también puede ser fatal si se usa mal, especialmente si no tenemos cuidado en los parámetros con los que lo invocamos.

En pocas palabras, dd permite copiar y convertir el contenido de un archivo a otro. El concepto de archivo es bastante amplio, ya que además de copiar archivos comunes, se pueden utilizar como entrada y salida los archivos de /dev, y así copiar una partición, generar archivos vacíos, generar archivos con contenido aleatorio, etc.

Formato

El formato es un poco diferente a los comandos de linux que conocemos, dado que dd se basa en parámetros del tipo opción=valor en lugar de utilizar el formato más común -op valor o –opción valor.

El archivo de entrada estará especificado por la opción if (por input file). Como es de suponer,
el archivo de salida está especificado por la opción of (por output file).

Con la opción bs configuramos el tamaño de cada bloque que se leerá/convertirá/escribirá (con las opciones ibs, cbs y obs se configuran los tamaños de los bloques de cada proceso por separado). A éstos parámetros se les puede pasar un valores que sean el resultado de una multiplicación, por ejemplo, 1024x1024x1024 para expresar 1 GB. También se pueden utilizar sufijos como ser c (1 byte), w (2 bytes), b (512 bytes, asociado por defecto del tamaño de un sector de disco), kB (1000 bytes), K (1024 bytes), MB (1000*1000), M (1024*1024), GB (1000*1000*1000), G (1024*1024*1024), y así para T, P, E, Z, Y.

Con el parámetro count configuramos la cantidad de bloques a copiar; por lo tanto, el tamaño de lo transferido será bs*count. Aunque la última porción del archivo no tenga el tamaño de un bloque exacto, ese restante se
transfirirá de todas formas.

Podemos utilizar el parámetro conv para hacer ciertas transformaciones además de la lectura y la escritura, y puede tener los siguientes valores:

  • lcase: transformar mayúsculas a minúsculas
  • ucase: transformar minúsculas a mayúsculas
  • swab: intercambiar cada par de bytes de entrada
  • excl: fallar si el archivo de salida existe
  • noerror: continuar aunque se encuentren errores de lectura
  • nocreat: no crea el archivo de salida

Otras opciones que me parecieron menos interesantes fueron: ascii, ebcdic, ibm, block, unblock, notrunc, sync, fdatasync y fsync (consultar las páginas man por más datos).

Ejemplos de uso

(De más está decir que no puedo asegurar que todos los ejemplos van a funcionar como deberían, ya que en algunos casos, ni siquiera puedo afirmar que «en mi máquina anda». Por favor usar con responsabilidad. Dicho
esto, continuamos…)

En los siguientes ejemplos veremos algunos ejemplos de uso del comando dd. En los casos que corresponda este comentario, se generarán archivos de 64MB, en 1000 bloques de a 64KB.

Duplicar una partición

En este ejemplo duplicaremos el contenido de una partición en otra. Creo que lo ideal es que sean las 2 del mismo tamaño, aunque si la partición de salida es más grande, también funcionaría.

<br />
dd if=/dev/sda2 of=/dev/sdb2 bs=4096 conv=notrunc,noerror<br />

Duplicar una partición en otro host (ésta me la enseñó MacGyver)

Es la misma idea que el ejemplo anterior, pero en lugar de duplicar la partición en un disco local, se duplica a través de la red, utilizando el comando netcat (también conocido como la navaja suiza de TCP/IP):

En el host de destino:

<br />
netcat -l -p 1234 | dd of=/dev/hdc bs=16065b<br />

El el host de origen:

<br />
dd if=/dev/sda2 bs=16065b | netcat [IP-destino] 1234<br />
n

También podemos duplicar una partición a un host remoto a través de SSH:

<br />
dd if=/dev/sda2 bs=16065b | ssh user@host &amp;quot;dd of=/dev/hdc bs=16065b&amp;quot;<br />

Generar un archivo con ceros o contenido aleatorio

Los archivos generados con ceros o contenido aleatorio nos pueden servir como material para transferir entre discos o por la red para diagnosticar posibles problemas.

Con ceros:

<br />
dd if=/dev/zero of=/tmp/zeros bs=64k count=1000<br />

Con contenido aleatorio:

<br />
dd if=/dev/urandom of=/tmp/myrandom bs=64k count=1000<br />

Copiar una partición a un archivo

<br />
dd if=/dev/sda2 of=/tmp/part.image bs=4096 conv=notrunc,noerror<br />

Si queremos copiar un CD/DVD a una imagen ISO, podemos ejecutar:

<br />
dd if=/dev/cdrom of=/tmp/myCD.iso bs=2048 conv=sync,notrunc<br />

Parecido al ejemplo de duplicar una partición a través de la red,
podemos respaldar nuestra partición a un archivo en un host remoto a través de SSH:

<br />
dd if=/dev/sda2 | ssh user@host &amp;quot;dd of=/home/user/partition.image&amp;quot;<br />

Escribir basura en una partición

Cuando borramos archivos o utilizamos un formato rápido, la mayoría de los archivos pueden ser recuperados. Este ejemplo puede servir para asegurarnos que no se puedan recuperar los datos de una partición entera:

<br />
dd if=/dev/urandom of=/dev/hda<br />

Convertir un archivo a MAYÚSCULAS

<br />
dd if=filename of=filename conv=ucase<br />

Referencias

Matar procesos en Windows (taskkill)

Introducción

La forma más común para matar procesos en Windows seguramente sea utilizando el administrador de tareas, en la pestaña de «Procesos», haciendo click derecho en el proceso que queremos cerrar por la fuerza:

Administrador de tareas

Si utilizamos Windows XP Professional (o Windows Vista), podemos utilizar el comando taskkill para terminar (de forma forzada si así lo necesitamos) un proceso por su id, su nombre o por filtros menos selectivos. Inclusive se puede utilizar para matar procesos de sistemas remotos. Entre las opciones para seleccionar el proceso a terminar, tenemos las más simples como ser por PID (process id) o nombre de imagen. También tenemos la posibilidad de utilizar un sistema de filtros, para los cuales el comando nos ofrece algunos nombres de variables y una lista de operadores para poder hacer comparaciones (por ejemplo, para terminar todos los procesos de un usuario, o para terminar los procesos que exceden cierto límite de uso de memoria RAM).

Ejemplos de uso

Veamos algunos ejemplos de uso:

  • TASKKILL /PID 1230 /PID 1241 /PID 1253: termina los procesos con PID 1230, 1241 y 1253.
  • TASKKILL /F /IM notepad.exe /IM mspaint.exe: termina de manera forzada los procesos con nombre de imagen «notepad.exe» o «mspaint.exe».
  • TASKKILL /S otra-pc /F /IM notepad.exe: termina los bloc de notas en la PC de la red llamada «otra-pc».
  • TASKKILL /F /FI «PID ge 1000» /FI «WINDOWTITLE ne untitle*»: termina de manera forzada los procesos que tengan un PID mayor o igual a 1000, y además el título de la ventana no empiece con «untitle». Notar que se puede utilizar un caracter de comodín (*) en los filtros, lo que facilita el uso de filtros menos selectivos. También se pueden utilizar varios filtros, para que el comando haga la intersección de los filtros.
  • TASKKILL /F /FI «USERNAME eq NT AUTHORITYSYSTEM» /IM notepad.exe: termina de manera forzada los procesos con nombre de imagen «notepad.exe» que fueron iniciados por el usuario «NT AUTHORITYSYSTEM».
  • TASKKILL /S otra-pc /U dominiousuario /FI «USERNAME ne NT*» /IM *: Utilizando el usuario «dominiousuario», termina de forma remota todos los procesos de los usuarios que su nombre no empiece con «NT» en la PC de la red llamada «otra-pc». Si es necesario, pedirá que se ingrese la contraseña del usuario.
  • TASKKILL /S otra-pc /U nombreusuario /P contraseña»: Si no se ingresan filtros al invocar taskkill para un sistema remoto, el comando apaga la computadora remota (siempre y cuando el usuario tenga permisos para hacerlo)

La salida del comando es un listado de los procesos terminados en caso de que alguno cumpliera con el filtro:

<br />
C:\&gt; TASKKILL /F /FI &quot;PID ge 3599&quot; /FI &quot;IMAGENAME eq fdm.exe*&quot;<br />
Correcto: se terminó el proceso con PID 3600.<br />

O un aviso si ningún proceso cumplió el filtro especificado:

<br />
C:\&gt; TASKKILL /F /FI &quot;PID ge 3599&quot; /FI &quot;IMAGENAME eq FOXIT*&quot;<br />
INFORMACIÓN: no hay tareas ejecutándose con los criterios especificados.<br />

¿Y para Windows NT o 2000?

Para sistemas basados en Windows NT y 2000 vale la pena ver PsKill (link), una utilidad similar a taskkill.

Acceso directo para «limpiar» procesos que no responden

Una idea muy interesante es crear un acceso directo para matar de forma forzada todos los procesos que hayan dejado de responder. Esto es bastante simple, lo hacemos poniendo como comando del acceso directo lo siguiente:

<br />
taskkill.exe /f /fi &quot;status eq not responding&quot;<br />

Sintaxis completa

La ayuda completa del comando taskkill es la siguiente:

<br />
C:\&gt; taskkill /?</p>
<p>TASKKILL [/S sistema] [/U usuario [/P contraseña]]<br />
         { [/FI filtro] [/PID IdProceso | /IM NombreImagen] } [/F] [/T]</p>
<p>Descripción:<br />
    Esta herramienta de la línea de comandos puede usarse en uno o más procesos.<br />
    Los procesos pueden terminarse a través del Id. o del nombre de imagen.</p>
<p>Lista de parámetros:<br />
    /S    sistema               Especifica el sistema remoto al que conectarse.<br />
    /U    [dominio]usuario    Especifica el contexto de usuario en el que se que el comando debe ejecutarse.<br />
    /P     contraseña           Especifica la contraseña para el contexto de usuario dado. Pide la entrada si se omite.<br />
    /F                          Especifica la terminación forzada de proceso(s).<br />
    /FI     filtro              Especifica un conjunto de tarea que coinciden con el criterio especificado en el filtro.<br />
    /PID    Id. de proceso      Especifica el ID. de proceso que se debe terminar.<br />
    /IM     nombre de imagen    Especifica el nombre de imagen del proceso que debe terminar. El carácter comodín &quot;*&quot; puede usarse para especificar todos los nombres de imagen.<br />
    /T                          Terminar árbol: termina el proceso especificado y todos los procesos secundarios iniciados por él.<br />
    /?                          Muestra el uso de la ayuda.</p>
<p>Filtro(s):<br />
    Nombre filtro   Operadores válidos         Valores válidos<br />
    -------------   ------------------         ---------------<br />
    STATUS          eq, ne                     RUNNING | NOT RESPONDING<br />
    IMAGENAME       eq, ne                     Nombre de imagen.<br />
    PID             eq, ne, gt, lt, ge, le     Valor de PID.<br />
    SESSION         eq, ne, gt, lt, ge, le     Número de sesión<br />
    CPUTIME         eq, ne, gt, lt, ge, le     Tiempo válido en el formato<br />
                                               hh:mm:ss.<br />
                                               hh - horas,<br />
                                               mm - minutos, ss - segundos<br />
    MEMUSAGE        eq, ne, gt, lt, ge, le     Uso de memoria en KB.<br />
    USERNAME        eq, ne                     Nombre de usuario en formato<br />
                                               [dominio]usuario.<br />
    MODULES         eq, ne                     Nombre de DLL<br />
    SERVICES        eq, ne                     Nombre de servicio.<br />
    WINDOWTITLE     eq, ne                     Título de ventana.</p>
<p>Nota: el carácter comodín &quot;*&quot; del modificador /IM se acepta solamente<br />
      con filtros.</p>
<p>Nota: los procesos remotos siempre se terminarán de manera forzada<br />
      sin tener en cuenta si la opción /F se ha especificado o no.<br />

64GB de RAM en un sistema operativo de 32 bits

Varias veces dije (¡ups!) y escuchado decir que por las limitaciones de un sistema operativo de 32 bits, no es posible utilizar más de 3GB o 3,5GB de RAM. Esto no es del todo cierto, y -desde la época de los Pentium Pro- ni siquiera es una limitación de hardware, sino que es una limitación impuesta en algunos sistemas operativos de Microsoft.

En este post veremos cómo aprovechar los megabytes de RAM por arriba de los 3GB utilizando PAE, una característica de la mayoría de los procesadores modernos, sin necesidad de instalar un sistema operativo de 64 bits. Veremos también algunas consideraciones a tener en cuenta a la hora de poner en la balanza desperdiciar la memoria RAM por encima de los 3GB, habilitar PAE en un sistema operativo de 32 bits, o de utilizar un sistema de 64 bits.

PAE

PAE (Physical Address Extension, Extensión de dirección física) es una característica de la mayoría de los procesadores x86 y x86-64, que
permite que sistemas de 32 bits puedan utilizar hasta 64GB de RAM. Esto es posible ampliando la cantidad de bits utilizados para direccionar memoria de 32 a 36 bits y por esto, la cantidad de memoria direccionable máxima pasa de 4GB (2^32) a 64GB (2^36).

PAE en Windows

Lo más importante a destacar si de Windows hablamos, es que el uso de PAE para extender el espacio de direccionamiento de memoria no está habilitado en ninguna versión «consumer» de Windows, por lo que hay que recurrir a una versión de servidor (NT, 2003, etc.). En realidad, en Windows XP SP1 se permitió habilitar PAE, pero en SP2 se restringió nuevamente, siendo la principal causa de esta restricción los problemas de compatibilidad e inestabilidad de los drivers de los dispositivos. Estos problemas se daban porque en la programación de dichos drivers se asumen cosas que al direccionar la memoria utilizando PAE dejan de ser ciertas (ver la parte de More information de éste artículo).

En la siguiente tabla se muestran los máximos de memoria RAM física soportados por cada versión de Windows:

Sistema operativo – Memoria máxima soportada con PAE
Windows 2000 Advanced Server – 8 GB de RAM física
Windows 2000 Datacenter Server – 32 GB de RAM física
Windows XP (todas las versiones) – 4 GB de RAM física*
Windows Server 2003 (y SP1), Standard Edition – 4 GB de RAM física*
Windows Server 2003, Enterprise Edition – 32 GB de RAM física
Windows Server 2003, Datacenter Edition – 64 GB de RAM física
Windows Server 2003 SP1, Enterprise Edition – 64 GB de RAM física
Windows Server 2003 SP1, Datacenter Edition – 128 GB de RAM física

* El espacio físico de memoria está limitado a 4 GB en estas versiones de Windows.

PAE en Linux

La historia con Linux es diferente, ya que contando con un kernel 2.6+ que tenga habilitado el soporte para PAE, se abre la posibilidad de usar hasta 64GB de RAM en sistemas de 32 bits. Los kernels 2.4
soportan hasta 4GB de memoria RAM física.

Veamos algunos ejemplos de configuración:

Habilitar PAE en Ubuntu

Para habilitar PAE en Ubuntu basta con instalar algunos paquetes que reemplazan el kernel genérico por uno con soporte para PAE. Estoy en un sistema con 4GB de RAM, con el kernel por defecto, y Ubuntu versión 9.04 para x86 (32 bits). La memoria RAM disponible anda en los 3GB:

<br />
jarrarte@nb-jarrarte:~$ free -m<br />
total       used       free     shared    buffers     cached<br />
Mem:          3023        428       2594          0         26        176<br />
-/+ buffers/cache:        225       2797<br />
Swap:         1906          0       1906<br />

Mi kernel:

<br />
jarrarte@nb-jarrarte:~$ uname -a<br />
Linux nb-jarrarte 2.6.28-15-generic #49-Ubuntu SMP Tue Aug 18 18:40:08 UTC 2009 i686 GNU/Linux<br />

Para instalar el soporte para PAE (este punto también se puede hacer desde el gestor de paquetes gráfico):

<br />
jarrarte@nb-<br />
jarrarte:~$ sudo sudo apt-get install linux-headers-server linux-image-server linux-server<br />

Luego de reinciar con el nuevo kernel, vemos que la actualización surgió efecto:

<br />
jarrarte@nb-jarrarte:~$ free -m<br />
total       used       free     shared    buffers     cached<br />
Mem:          4021        389       3631          0         26        166<br />
-/+ buffers/cache:        196       3824<br />
Swap:         1906          0       1906<br />

<br />
jarrarte@nb-jarrarte:~$ uname -a<br />
Linux nb-jarrarte 2.6.28-15-server #49-Ubuntu SMP Tue Aug 18 19:30:06 UTC 2009 i686 GNU/Linux<br />

Habilitar PAE en Gentoo

Para habilitar el soporte para PAE en Gentoo, debemos recompilar el kernel con la opción de «High memory support» activada. Para ello, ejecutamos

<br />
server ~ # cd /usr/src/linux<br />
server linux # make menuconfig<br />

Encontraremos esta configuración bajo Processor type and features —> High Memory
Support
:

<br />
Processor type and features  ---&gt;<br />
 High Memory Support  ---&gt;<br />
  ( ) off<br />
  ( ) 4GB<br />
  (X) 64GB<br />

La opción de 64GB activa el soporte para PAE. Luego de seleccionada, debemos recompilar el kernel, y reiniciar el sistema utilizando este nuevo kernel.

Habilitar PAE en CentOS, Fedora y similares

En estos sabores de Linux podemos habilitar el soporte para PAE instalando «kernel-PAE» utilizando el comando yum:

<br />
# yum install kernel-PAE<br />

Luego de instalado, debemos reiniciar el sistema utilizando este nuevo kernel.

¿PAE o sistema operativo de 64 bits?

Creo que la desición de utilizar un sistema operativo de 32 bits con PAE habilitado o de utilizar uno de 64 bits va un poco más allá de la dificultad o facilidad técnica de utilizar uno u otro; como generalmente pasa, «depende» será la mejor respuesta a la hora de hacernos esa pregunta:

  • Como primer punto, dependerá del tipo de
    aplicaciones que se vayan a correr, teniendo en cuenta especialmente si existen versiones optimizadas para CPUs de 64 bits. Si nos estamos planteando este cambio en un servidor de base de datos o de procesamiento multimedia, es muy posible que veamos incrementos de performance con un sistema operativo de 64 bits, ya sea porque aprovechan los registros más grandes, los registros adicionales, las nuevas instrucciones de 64 bits o los espacios de direccionamiento de memoria para los procesos mucho más grandes (pasan de 4GB a 1TB).
  • Por otro lado, también hay que poner en la balanza que una aplicación compilada para 64 bits va a ocupar más espacio en disco y más memoria, por tener que manejar variables y punteros del doble de tamaño. Este incremento en el tamaño repercute en un incremento en el ancho de banda ocupado entre el CPU y la memoria, que a su vez puede repercutir en una degradación de performance si no es bien aprovechado.
  • El no tener drivers de 64 bits para los dispositivos de
    nuestro sistema puede ser una razón de peso bastante importante para no poder pasarnos de arquitectura de 64 bits. También puede pasar que los drivers de 32 bits no funcionen bien con PAE habilitado.
  • Si tenemos un sistema Linux de 32 bits corriendo hace tiempo, aplicaciones instaladas, configuraciones personalizadas, etc. hay que ver si vale la pena reinstalar todo de cero para pasarnos a un sistema de 64 bits y aprovechar un poco más de RAM (si tenemos 4GB instalados, pasarmos a ver unos 800MB adicionales, menos el incremento de uso de memoria de las aplicaciones que vimos 2 puntos más arriba). En mi caso, cambiar el kernel de Ubuntu para aprovechar los 4GB completos implicó unos minutos bajando e instalando el paquete, y una reiniciada al sistema. Hasta ahora no he tenido problemas utilizando 32 bits con PAE.
  • Como último punto, el no poder pasar a sistemas de 64 bits por política organizacional también puede ser una respuesta válida a la pregunta. Si tenemos licencias de algunas
    aplicaciones que puedan tener problemas o directamente no corran en un servidor de 64 bits, la desición está tomada antes de plantearla (especialmente si estamos hablando de costos elevados o de aplicaciones críticas para el negocio de la organización).

Referencias

gotAPI: agregador de documentación de diferentes lenguajes y frameworks

gotAPI es un sitio con documentación y APIs de diferentes frameworks y lenguajes. Encontraremos documentación de referencia de Java, Spring framework, Hibernate, diferentes motores de bases de datos, XML y sus relacionados (XSL, XPath, etc.). La documentación sigue creciendo, y como vemos en la captura de pantalla más abajo, al día de hoy esta bastante completa:

gotapi-apis

Yendo a la estructura de la página, tenemos una presentación bien limpia, con un filtro por lenguaje/framework en la parte arriba, y el buscador de texto arriba a la izquierda. El buscador filtra los resultados a medida que escribimos, cosa que resulta muy
conveniente para este tipo de búsquedas. Abajo del buscador tenemos una estructura arbórea de todos los elementos en los que estamos buscando, y en el centro de la página se presenta el resultado.

En su blog hace unos meses anunciaron que estaban trabando en la versión 2.0, y que la beta está disponible para poder probarla. El 16 de junio anunciaron en su twitter que pasarían a producción esta beta, pero esto aún no ha pasado, siguen con la versión vieja.

De todas formas, la versión 2.0 beta está bastante usable, bastante ágil y -al igual que la versión en producción-, con abundante información.

Recomendable cualquiera de las dos.

Seleccionar y matar un proceso java determinado

Imaginemos el siguiente escenario: tenemos un servidor linux con varios procesos java corriendo, y quedemos matar a uno de ellos sin afectar el resto. En este post veremos cómo generar un script de linux para matar un proceso seleccionado según la clase que contiene el método main.
Si utilizamos el comando killall:

<br />
killall -9 java<br />

logramos parte de nuestro cometido (matar el proceso), pero seguramente nos traiga más problemas que soluciones, ya que matamos TODAS las máquinas virtuales java corriendo al momento de ejecutar el comando.

Podemos hacerlo de forma manual, haciendo un

<br />
ps -fea |grep java<br />

para luego seleccionar el PID (process id) correspondiente a nuestro proceso, tomando en cuenta la clase que inicia el proceso, el classpath utilizado o los parámetros de inicio y luego invocando el comando kill -9 PID.

Si vamos a matar un cierto proceso java bastante seguido, podemos crearnos un shell script que se tome el trabajo de buscar el proceso y matar únicamente al que nosotros querramos.

A este script únicamente le debemos configurar la variable MAIN_CLASS, que contendrá el nombre de la clase que contiene el método main (clase que inicia nuestra aplicación). Basándose en éste nombre, el script buscará todos los procesos que contengan ese nombre en la línea de comandos, extraerá el PID de las líneas que filtró, y ejecutará un kill -9 para cada proceso.

El script entonces, es el siguiente:

<br />
#!/bin/bash<br />
# Nombre del binario<br />
MAIN_CLASS=com.josearrarte.demo.MainClass</p>
<p># Cantidad de reintentos para matarlo<br />
CNT_INTENTOS=5</p>
<p># Funcion para matar el proceso<br />
matar(){<br />
INTENTO=1</p>
<p>while [ `ps -ef --cols=5000 |grep -v &quot;grep&quot;| grep -c ${MAIN_CLASS}` -gt 0 ] &amp;&amp; [ ${INTENTO} -le ${<br />
CNT_INTENTOS} ]; do<br />
  ps -ef --cols=5000 |grep -v &quot;grep&quot;| grep ${MAIN_CLASS}|awk '{ print $2 }'|xargs -i{} kill -9 {} &gt; /dev/null 2&gt; /dev/null<br />
  INTENTO=$(( ${INTENTO} + 1 ))<br />
done<br />
}</p>
<p>matar<br />

Queda abierta la sección de comentarios para proponer mejores formas de matar procesos java.

No hay perfiles. Elija el ícono ‘Correo y fax’ del panel de control para crear un nuevo perfil

Uso Mozilla Thunderbird como cliente de correo, y cada cierto tiempo al intentar adjuntar un archivo a un nuevo correo haciendo hacer click derecho en un archivo, «Enviar a», «Destinatario de correo» (o al intentar comprimir y enviar como adjunto) Windows me presenta el siguiente diálogo de error:

error-al-enviar-a-destinatario-de-correo

No tengo claro cuándo pasa, pero el tema es que Microsoft Outlook reemplaza a Thunderbird como cliente por defecto. Para solucionar este problema, vamos al Panel de Control, Agregar o quitar programas, seleccionamos Configurar acceso y programas predeterminados y en la parte de configuración personalizada, seleccionamos Mozilla Thunderbird como programa de correo electrónico
determinado:

agregar-o-quitar-programas

Luego de ese cambio, Windows debería volver a comportarse como se supone al enviar archivos a un correo electrónico.

Manejo transaccional de la base de datos con Spring Framework y AOP

Introducción

Manejar la transaccionabilidad contra una base de datos no es un tema complicado al utilizar JDBC, menos aún utilizando un bean TransactionTemplate de Spring. Sin embargo, siempre que hacemos tareas repetitivas -como escribir código para empezar una trasacción, hacer un commit en caso de éxito o hacer rollback en caso de error- abre la puerta a olvidos o errores de programación.

Spring pone a disposición buenas herramientas para el manejo transaccional contra una base de datos, ya sea de forma programática o de forma declarativa. Si elegimos el enfoque programático, contamos con la clase org.springframework.transaction.support.TransactionTemplate. Si preferimos manejar nuestras transacciones de forma declarativa, contamos con opciones de configuración mediante XMLs y AOP (tx:advice) o utilizando annotations (org.springframework.transaction.annotation.Transactional)
Veremos los diferentes atributos en la definición de una transacción, y las formas de uso de cada uno de los enfoques.

Atributos de una transacción

name

Nombre de la transacción. Por defecto no tienen nombre, y si tienen, este nombre se muestra en los monitores transaccionales, cuando el concepto es aplicable.

propagation

Es el comportamiento de la propagación de la transacción. Por propagación entendemos la forma de asociar la transacción al método que estamos ejecutando. Entre los posibles comportamientos tenemos:

  • REQUIRED: Comportamiento por defecto. Si existe una transacción en curso, el método corre dentro de esa transacción. Si no existe una en curso, creará una nueva transacción.
    tx_prop_required
  • REQUIRES_NEW: Indica que el método debe
    correr dentro de su propia transacción. Si existe una transacción en curso, se suspenderá durante la duración de la invocación al método.
    tx_prop_requires_new
  • MANDATORY: Indica que el método debe correr dentro de una transacción. Si no existe una transacción en curso, se lanzará una excepción.
  • NESTED: Indica que si existe una transacción en progreso, el método se ejecutará dentro de una transacción anidada. Sobre esta transacción se ejecutará commit o rollback sin afectar las otras, (utilizando savepoints) . Si no existe ninguna transacción en curso, se comporta de la misma forma que REQUIRED.
  • NEVER: Indica que el método no debe ser ejecutado utilizando una transacción en curso. Si existe una transacción en curso, se lanzará una
    excepción.
  • NOT_SUPPORTED: Indica que el método no debe ser ejecutado dentro de una transacción. Si existe una en progreso, se suspenderá durante la ejecución del método.
  • SUPPORTS: Indica que el método no requiere una transacción, pero se utilizará si existese alguna en curso.

isolation

El isolation level de la transacción. Por isolation level entendemos la configuración de qué tanto afectan los cambios del DBMS que son externos a nuestra transacción. En el nivel de aislación más bajo vemos cómo van cambiando los datos desde que comenzó nuestra transacción, inclusive si son resultados intermedios de otra transacción. En el nivel más alto, trabajamos con una foto de los datos que no cambia mientras nuestra transacción vive. Entre los posibles valores tenemos:

  • DEFAULT: Utiliza el nivel por defecto, lo determinará la fuente de datos.
  • READ_UNCOMMITED: Este nivel permite que en una transacción se puedan leer datos que aún no han sido commiteados en otras transacciones (lecturas sucias). Pueden suceder además lecturas fantasma (si se hace rollback de la otra transacción, los datos que leímos no fueron válidos) y lecturas no repetibles (una misma consulta puede retornar valores diferentes dentro de la misma transacción).
  • READ_COMMITED: Este nivel no expone datos de una tupla aún no commiteados por otras transacciones, pero permite que otras transacciones cambien datos de las tuplas recuperadas por nuestra transacción. De esta forma, las lecturas sucias se evitan, pero pueden suceder las lecturas fantasma y las lecturas no repetibles.
  • REPEATABLE_READ: Este nivel no permite leer datos aún no commiteados de una tupla, y tampoco permite modificar los datos leídos por una transacción en curso. Sin embargo, si hacemos 2 consultas (SELECTs) con una condición de rango en el WHERE, y otra transacción inserta tuplas entre las 2 consultas y que cumplan con la condición de rango, las consultas retornarán conjuntos de tuplas diferentes. Las lecturas sucias y las lecturas no repetibles se evitan, pero pueden suceder las lecturas fantasma.
  • SERIALIZABLE: Además de las condiciones de REPEATABLE_READ, evita el caso en que en nuestra transacción hacemos una consulta con una condición de rango, otra transacción inserta una tupla que cae cumple la condición, y luego repetimos la consulta en nuestra transacción. Si el es isolation level es SERIALIZABLE, ambas consultas retornarán el mismo resultado. Se evitan las lecturas sucias, las lecturas no repetibles y las lecturas fantasma.

timeout

El timeout de la transacción, expresado en segundos.

read-only

Indica si la transacción debe ser tratada como solo lectura, posibilitando así optimizaciones adicionales.

Uso de transacciones con Spring

Definición de forma programática

Para manejar la transaccionabilidad de forma programática, debemos
tener una referencia a org.springframework.transaction.support.TransactionTemplate en nuestra clase. Para ello, definiremos algunas relaciones entre nuestros beans:

<br />
&lt;bean name=&quot;hibernateTemplate&quot; class=&quot;org.springframework.orm.hibernate3.HibernateTemplate&quot;&gt;<br />
	&lt;constructor-arg ref=&quot;sessionFactory&quot;/&gt;<br />
&lt;/bean&gt;</p>
<p>&lt;bean name=&quot;transactionManager&quot; class=&quot;org.springframework.orm.hibernate3.HibernateTransactionManager&quot;&gt;<br />
	&lt;property name=&quot;dataSource&quot; ref=&quot;dataSource&quot; /&gt;<br />
	&lt;property name=&quot;sessionFactory&quot; ref=&quot;sessionFactory&quot; /&gt;<br />
&lt;/bean&gt;</p>
<p>&lt;bean name=&quot;transactionTemplate&quot; class=&quot;org.springframework.transaction.support.TransactionTemplate&quot;&gt;<br />
	&lt;constructor-arg ref=&quot;transactionManager&quot;/&gt;<br />
&lt;/bean&gt;<br />

En este ejemplo el framework de ORM utilizado es Hibernate, pero la idea aplica para cualquier framework ORM de los que Spring tiene soporte.
El bean hibernateTemplate entonces es el que agrupa funcionalidades de acceso a datos, apoyándose en las funcionalidades de Hibernate.
El bean transactionManager es una subclase de org.springframework.transaction.support.AbstractPlatformTransactionManager, implementando el manejo de transacciones (determinación de transacciones existentes, suspensión de transacciones, definición del comportamiento transaccional, etc.).
Por último, transactionTemplate simplifica el manejo transaccional y el manejo de excepciones por errores.

La forma de uso del transactionTemplate dentro de nuesta aplicación es la siguiente:

<br />
public class MyEntityDAOImpl implements MyEntityDAO {</p>
<p>	private HibernateTemplate hibernateTemplate;</p>
<p>	private TransactionTemplate transactionTemplate;	</p>
<p>	public List&lt;MyEntity&gt; getAll() {<br />
		return (List&lt;MyEntity&gt;)<br />
getTransactionTemplate().execute(new TransactionCallback() {</p>
<p>			@Override<br />
			public Object doInTransaction(TransactionStatus status) {<br />
				return getHibernateTemplate().loadAll(MyEntity.class);<br />
			}<br />
		});<br />
	}</p>
<p>	/*<br />
	...<br />
	setters y getters de hibernateTemplate y transactionTemplate<br />
	...<br />
	*/<br />
}<br />

Todo lo que pase dentro del método doInTransaction estará encapsulado dentro de una transacción.

Definición de forma declarativa – XMLs

El bean nos permite crear un aspecto (por más info acerca de AOP, ver este post) encargado de iniciar una transacción contra la base de datos al invocar un método que encaje en la definición del pointcut. En este pointcut se podría definir para un método de una clase, para toda la clase, o incluso para todo un paquete que identifique la capa de negocios o de acceso a datos de nuestra aplicación.

Para configurar un en nuestra aplicación, incluiremos en nuestros XMLs de Spring los siguientes beans:

<br />
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;<br />
&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;<br />
	xmlns:tx=&quot;http://www.springframework.org/schema/tx&quot;<br />
	xmlns:aop=&quot;http://www.springframework.org/schema/aop&quot;<br />
	xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd<br />
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd<br />
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd&quot;&gt;</p>
<p>	&lt;aop:config&gt;<br />
		&lt;aop:advisor pointcut=&quot;execution(public * demo.dao.*.create(..))&quot; advice-ref=&quot;tx-advice&quot; /&gt;<br />
	&lt;/aop:config&gt;</p>
<p>	&lt;tx:advice id=&quot;tx-advice&quot;&gt;<br />
		&lt;tx:attributes&gt;<br />
			&lt;tx:method name=&quot;*&quot; propagation=&quot;REQUIRED&quot; isolation=&quot;DEFAULT&quot; rollback-for=&quot;Throwable&quot; no-rollback-for=&quot;BusinessException&quot; /&gt;<br />
			&lt;tx:method name=&quot;get*&quot; propagation=&quot;SUPPORTS&quot; read-only=&quot;true&quot; /&gt;<br />
		&lt;/tx:attributes&gt;<br />
	&lt;/tx:advice&gt;</p>
<p>&lt;/beans&gt;<br />

Tendiendo hacia convención en lugar de configuración, asume que existe un bean llamado «transactionManager», instancia de la clase -en caso de Hibernate- org.springframework.orm.hibernate3.HibernateTransactionManager. Si nuestro transaction manager tiene un nombre diferente, debemos referenciarlo incluyendo un atributo transaction-manager al bean .

El atributo name es el único parámetro requerido, y tiene un significado diferente al que se le da en la configuración programática. En este caso, sirve para filtrar los nombres de los métodos que se asociarán a esta las transacciones. Soporta comodines (*) en los nombres, por ejemplo ‘get*’, ‘handle*’, ‘on*Event’.

Al bean le podemos configurar todos los atributos descritos al inicio del post (propagation, isolation leven, timeout, etc.), y además:

  • rollback-for: Lista de excepciones -separadas por comas- para las cuales se dispara un rollback de la transacción
  • no-rollback-for: Lista de excepciones -separadas por comas- para las cuales no se dispara un rollback de la transacción

Uso de forma declarativa – @Annotations

Si bien simplifica mucho el manejo de transacciones, podemos utilizar annotations para simplificarlo aún más.

Utilizando sólo una línea en la configuración XML de nuestra aplicación podemos dar lugar a configurar el manejo transaccional a nivel de annotations. Esta línea tiene el siguiente formato:

<br />
&lt;tx:annotation-driven transaction-manager=&quot;txManager&quot; /&gt;<br />

El atributo transaction-manager debe ser incluido si nuestro transaction manager no se llama «transactionManager».

El tener configurado este bean nos habilita a poder anotar nuestras clases y métodos con @Transactional y definir de esta forma nuestras transacciones de forma declarativa y dentro de nuestro código Java.

<br />
@Transactional(propagation=Propagation.SUPPORTS, readOnly=true)<br />
public class MyServiceImpl implements MyService {</p>
<p>	...</p>
<p>	@Transactional(propagation=Propagation.REQUIRED, readOnly=false)<br />
	public void addMyEntity(MyEntity e) {<br />
		...<br />
	}</p>
<p>	...<br />
}<br />

En este ejemplo, todos los métodos de la clase son anotados como de solo lectura y con el comportamiento de propagación SUPPORTS. Al método addMyEntity se le especifica un comportamiento diferente, anotándolo con el comportamiento de propagación REQUIRED, y eliminando la restricción de solo lectura.

Cabe destacar que @Transactional puede ser utilizado también en interfaces indicando el comportamiento de todas las implementaciones de dicha interface.

Conclusión

Un manejo transaccional apropiado en nuestras aplicaciones es cada día más importante para asegurar robustez, especialmente en ambientes inciertos (desconexiones, latencias) y de alta concurrencia.

Spring nos brinda posiblidades concretas y muy poderosas para aislarnos de la problemática del manejo transaccional, dándonos opciones para las diferentes preferencias de enfoque y opciones en el desarrollo de aplicaciones. Vimos desde cómo hacer para declarar transacciones en el código, teniendo total control acerca del inicio y fin de las mismas. También vimos cómo declarar el ciclo de vida de las transacciones utilizando AOP y Spring juntos, a través de configuración XML. Finalmente vimos aún más simplificada la tarea, habilitando la configuración transaccional mediante annotations, y de esta manera definir en nuestras clases -o interfaces- el comportamiento deseado.

Hoy en día podemos hacer uso de esta flexibilidad y potencia, que quizá hasta hace poco estaba restringida a EJBs, utilizando un framework no intrusivo como Spring, un concepto cada ves más utilizado como AOP y nuestros viejos y queridos POJOs para nuestra lógica.

Referencias