Member of The Internet Defense League Últimos cambios
Últimos Cambios
Blog personal: El hilo del laberinto Geocaching

Rastros de DBTab y productos ZOPE "mágicos"

Última Actualización: 29 de Agosto de 2004 - Domingo

Mi web personal tiene muchas secciones con perfiles de uso y requerimientos de recursos muy diferentes. Por ejemplo, aunque la inmensa mayoría del web es bastante estático y está formado por "objetos" pequeños (páginas HTML), tengo una sección de "WIKIS" muy activa (muchas modificaciones) y una sección de imágenes con objetos grandes y numerosos. Así que en 2003 decidí dividir mi ZODB en tres bases de datos distintas: web principal, sección fotográfica y WIKIS. Para ello utilicé un producto excepcional, llamado "DBTab".

"DBTab" es un producto ZOPE que permite particionar una ZODB en varias, utilizando "puntos de montaje" comparables a los "puntos de montaje" del mundo UNIX. De esta forma tenía mis ZODBs separadas, pudiendo hacer copias de seguridad o compactaciones de las bases de datos con las políticas más apropiadas en cada caso.

El problema surgió al migrar mi instalación ZOPE a la versión 2.7. Esta versión de ZOPE incluye de serie una funcionalidad muy similar a "DBTab", convirtiendo este producto en obsoleto. Pero no son compatibles, así que en su momento debí reunificar mis diferentes ZODB en una única base de datos, actualizar a ZOPE 2.7 y volver a separar las ZODBs nuevamente con la nueva opción.

Fácil y sencillo.

Pero las cosas no resultaron tan triviales. Aunque aparentemente ya no existía ningún directorio "DBTab" en mi ZOPE, este servidor de aplicaciones se "quejaba" al arrancar, con el siguiente mensaje de error: "PROBLEM(100) ZODB Could not import class "MountedObject" from module "Products.DBTab.MountedObject"". Este error no ocasionaba ningún problema funcional en mi web, pero su mera presencia era desconcertante y una prueba de que mis conocimientos y experiencia con ZOPE aún tienen camino por recorrer.

En Julio de 2004, tras pegarme con el problema durante semanas, envié el siguiente mensaje a la lista de correo de ZOPE España:

Subject: [Zope-es] Problemilla con objetos "stale" en la ZODB
Date: Tue, 13 Jul 2004 07:29:29 +0200
From: Jesus Cea Avion <jcea@argo.es>
To: zope-es <zope-es@aditel.org>

Os planteo una cuestión que me trae de cabeza.

Hace tiempo usaba la DBTab (ZOPE 2.6) para dividir la ZODB en varios ficheros separados. Esta funcionalidad está integrada ya en el Zope 2.7 de forma nativa, así que lo que hice en su día fue:

  1. Reunificar la ZODB en un único fichero.

  2. Eliminar el producto DBTab.

  3. Compactar la ZODB al instante actual, para asegurarme de que no haya ningún objeto antiguo en la ZODB.

Pero cuando lanzo ZOPE, se me queja de que no encuentra el producto DBTab. No lo encuentra porque lo he borrado, correcto, pero no debería existir ningún objeto DBTab en la ZODB.

Me aseguro de que la compactación esté bien hecha, pero el problema persiste.

Examinando la ZODB con diversas herramientas (muy cutres, todo sea dicho), observo que hay 8 instancias de objetos DBTab.

Por si acaso me los he dejado por la jerarquía zope, me hago un pequeño script python ZOPE que se recorre la jerarquía entera ZOPE y me muestra todos los tipos de objetos que encuentra, de forma recursiva. No encuentra ningún objeto DBTab.

Osea, existen al menos 7 objetos en la ZODB que no son referenciados por "nada", y que se les debería haber aplicado recogida de basuras y eliminarlos. Pero no los elimina, como constato volcando la ZODB y viendo los errores de arranque de ZOPE.

¿A qué puede ser debido el problema?. ¿Cómo lo puedo solucionar?. ¿Cómo puedo ver si hay otros objetos en la ZODB "inaccesibles"?. ¿Cómo puedo borrar esos objetos (posiblemente "a mano") sabiendo su OID?. ¿Conoceis herramientas "serias" para manejar la ZODB?.

Gracias anticipadas :).

PS: No, no tengo "versioning".

PPS: Sí, me aseguro de que el punto de corte de la compactación sea posterior al borrado de esos objetos DBTab.

PPPS: Por si a alguien le interesa, el script que uso es:

>>>>>

tipos={}

def arbol(obj) :
  a=[]
  for i in obj.objectValues() :
    id=i.getId()
    m=i.meta_type
    tipos[m]=tipos.get(m,0)+1
    try :
      arbol(i)
    except:
      pass
  return

arbol(context)

for a,b in tipos.items() :
  print "'%s': %d" %(a,b)

return printed

<<<<<

-- 
Jesus Cea Avion                         _/_/      _/_/_/        _/_/_/
jcea@argo.es http://www.argo.es/~jcea/ _/_/    _/_/  _/_/    _/_/  _/_/
                                      _/_/    _/_/          _/_/_/_/_/
PGP Key Available at KeyServ   _/_/  _/_/    _/_/          _/_/  _/_/
"Things are not so easy"      _/_/  _/_/    _/_/  _/_/    _/_/  _/_/
"My name is Dump, Core Dump"   _/_/_/        _/_/_/      _/_/  _/_/
"El amor es poner tu felicidad en la felicidad de otro" - Leibniz
_______________________________________________
Zope-es mailing list
Zope-es@aditel.org
http://listas.aditel.org/listinfo/zope-es

No recibí ninguna respuesta útil, y el tema quedó un poco en el limbo. Pero aprovechando una semana de vacaciones (que poco dura una semana :-) en Agosto decidí invertir algo de tiempo en investigar el problema en serio. Esa noche me acosté a las 7:30 de la madrugada, pero resolví el problema de una vez por todas :)

En primer lugar intenté localizar objetos "DBTab" "perdidos" en la ZODB, con un script similar al expuesto más arriba, pero no apareció nada útil. Seguidamente, tras decidir que era poco probable que se tratase de un problema con la "recogida de basuras" en la compactación de la ZODB, me escribí otro script python que accedía directamente a la ZODB (sin usar ZOPE) y examinaba todos los objetos, uno por uno, buscando referencias a objetos "DBTab". Y no tuve que buscar mucho hasta encontrar una serie de referencias en el propio objeto raíz de la ZODB, en un atributo "no documentado", llamado "_mount_points".

Al parecer ese atributo contiene una lista de "puntos de montaje" presentes en esa carpeta (en este caso, en el directorio raíz de ZOPE). En buena lógica al eliminar las carpetas "DBTab" el producto debería haber actualizado la lista de "_mount_points", pero no lo hace. Probablemente se trate de un bug, pero dado que "DBTab" ha quedado obsoleto con las nuevas funcionalidades de ZOPE 2.7, tampoco me he preocupado mucho de identificar exactamente al responsable.

Una vez diagnosticado, el problema se soluciona de forma trivial simplemente editando la ZODB y eliminando esas referencias anticuadas. Un posible script para ello, ejecutado desde el directorio de las ZODBs, sería:

import sys
sys.path.append("../../zope-binarios/lib/python")
import Zope
Zope.configure("../etc/zope.conf")
a=Zope.app()
del a._mount_points["prueba"]
a._mount_points=a._mount_points
get_transaction().commit()

El primer par de lineas permiten poder importar fácilmente el módulo ZOPE, que se hace en la linea siguiente. A continuación se configura el objeto y se obtiene la raíz del árbol ZODB.

El siguiente paso es eliminar los "puntos de montaje" inexistentes (en mi caso eran unos 7 u 8 diferentes). Dado que "_mount_points" es una lista python, que es un tipo "mutable", es necesario que se notifique que se ha modificado a ZOPE, para que almacene la versión actualizada en la ZODB. La forma más habitual de hacerlo es con "a._p_changed=1", pero he preferido hacerlo mediante la actualización directa del atributo del objeto raíz. Cuestión de gustos :-)

Por último, notificamos a la ZODB que la transacción ha finalizado y que debe actualizarse la base de datos.

Una vez ejecutado el script, relanzamos ZOPE y el error descrito desaparece por arte de magia.

Con fines de documentación, indico que hay un par de detalles interesantes en un objeto ZOPE que no están muy bien documentados en ningún sitio pero que resultan muy útiles:

  • "objeto._p_oid": Este atributo nos proporciona el OID (Object ID) de un objeto determinado.

  • "root._p_jar(oid)": Este método nos proporciona el objeto de la ZODB con el OID especificado.



Python Zope ©2004 jcea@jcea.es

Más información sobre los OpenBadges

Donación BitCoin: 19niBN42ac2pqDQFx6GJZxry2JQSFvwAfS