Migración a Java 5 en walmart.com

Recientemente, la tienda en línea de Wal-Mart en Estados Unidos (walmart.com) fue migrada hacia la versión 1.5 de Java (también conocido como Java 5). Esto no fue una tarea trivial, dado que el sitio debe soportar 7 millones de sesiones y más de 100 millones de hits al día. Afortunadamente el proyecto se ha estado llevando a cabo con éxito. Eugenio Ciurana, quien participó en él, nos cuenta un poco sobre como se llevó a cabo esta migración.

Motivación
Las razones para migrar a Java 1.5 fueron las siguientes:
  • Mejorar herramientas de monitoreo
  • Mejorar capacidad de la máquina virtual (JVM)
  • Poder utilizar tecnologías cuyas versiones más recientes requieren Java 1.5
  • Petición de los desarrolladores para aprovechar las nuevas capacidades del lenguaje

Arquitectura de la Aplicación
walmart.com es una aplicación compuesta por diversos subsistemas que combina componentes comerciales, de código abierto, y desarrollados internamente. Funciona sobre Apache y Tomcat, y se apoya en Apache Axis, APIs comerciales, procedimientos almacenados (stored procedures) en la base de datos, y diversas tecnologías de presentación.

La aplicación se ejecuta en un cluster formado por más de cien servidores. El manejador de base de datos es masivamente paralelo. Adicionalmente, la aplicación provee y consume web services sobre HTTP y SOAP, y también realiza transferencias de archivos ad hoc.

Calendario
Dado que todo esta desarrollado en Java, fue imperativo realizar la migración de forma que se minimizara el impacto operacional. Por ello, se planeó llevarla a cabo en 3 ciclos de 8 semanas cada uno, para un total de 24 semanas. Un ciclo típico cubre:
  • Desarrollo
  • Pruebas (unitarias, integración e interoperación)
  • Pre-producción (Staging)
  • Despliegue (Deployment)

Las fechas fueron inamovibles debido a que estaban acordadas con una gran cantidad de proveedores, cuyas aplicaciones tienen fuertes interdependencias con walmart.com. La siguiente tabla muestra los objetivos establecidos para cada ciclo.

Ciclo Meta Notas
A Introducir en el ambiente de desarrollo las herramientas de Java 5, pero seguir compilando con Java 1.4

Definir plan de contingencia para regresar a 1.4 en caso de que surjan problemas en producción que no fueron detectados previamente o causados por sobrecarga del nuevo sistema
Identificar y corregir problemas en el ambiente y durante la compilación y generación de builds.

Entrenar a los desarrolladores en las nuevas capacidades de Java 5.
B Instalar en producción Java 5, pero mantener el código en Java 1.4. Esto es, todavía no introducir programas que utilicen las capacidades de Java 5.

Definir mejores prácticas de programación para los desarrolladores que pretenden utilizar las nuevas capacidades del lenguaje.

Resolver los problemas que hayan surgido como resultado del ciclo A, de manera permanente y compatible con Java 5.
Si hay problemas durante la liberación en producción, regresar a Java 1.4 y reevaluar.

Actualizar tecnología de terceros, como Apache Axis
C Liberar en producción código que utilice características exclusivas de Java 5. Migración completa

Este calendario fue diseñado para dar oportunidad a los ingenieros de atacar y resolver los problemas que surgieron durante el desarrollo e integración.

A continuación se describen algunos de los puntos finos que surgieron durante el proyecto, y como fueron resueltos por los grupos de desarrollo y operaciones.

Migración de compiladores
Previo al ciclo A, la aplicación utilizaba Jikes en lugar de javac. Jikes se utilizaba en el ambiente de desarrollo para compilar las clases y contenido estático, y en tiempo de ejecución (run-time) para generar las JSPs. Durante la migración se cambió el compilador a javac, configurándolo para que validara el código contra Java 1.4 y generara binarios de 1.5. Esto causó algunas complicaciones, ya que la organización no define un IDE obligatorio y los desarrolladores utilizan el IDE de su preferencia, siempre y cuando la aplicación se pueda compilar y ensamblar desde línea de comandos con make/Ant/javac. El cambio de compiladores afectó a los desarrolladores de las siguientes maneras:
  • Quienes solamente utilizaban herramientas *NIX estándar no fueron afectados. Simplemente obtuvieron el compilador y los archivos de ensamble (build files) desde el repositorio de CVS e hicieron un “make clean; make”.
  • Los usuarios de Eclipse 3.1 tuvieron mayores dificultades para lograr que la aplicación compilara y se ejecutara en el IDE. Sin embargo, ninguno de los problemas fue grave y en la gran mayoría de los casos se resolvieron en menos de 24 horas.
  • Los usuarios de IDEA y otras herramientas como jEdit no tuvieron mayores complicaciones para realizar el cambio.

El desempeño en la generación dinámica de JSPs se vio ligeramente afectado con el compilador javac. Sin embargo, juzgamos que su impacto era mínimo y se balanceaba con las ventajas de desempeño obtenidas con el uso del Hotspot de Java 1.5.

Soporte a XML
Java 1.5 incluye varios cambios y mejoras a su API de XML (SAX, DOM y XSLT particularmente). Por ejemplo, las versiones anteriores del API de XML permitían que un mismo atributo de XML fuera definido varias veces en la misma JSP, pero Java 1.5 no lo permite. En la aplicación se contaba con varias JSPs en esta situación, sobre todo los que tenían “imports”, y fallaron al correrlos bajo Java 1.5. Estas son las soluciones implementadas para corregir este problema:
  • Parsear los JSP que hacen referencia a atributos repetidos, y renombrar estos con un nombre único por archivo. Esto permitiría la anidación y causaría un impacto mínimo en el código existente, dado que los atributos en cuestión tienen un alcance limitado.
  • Reestructurar las JSP e imports utilizados para que los atributos fueran definidos una sola vez por página, evitando así las colisiones y manteniendo el nombre del atributo.

Descartamos la primera opción, ya que aunque era más rápida, no era óptima. Lo único que haría sería perpetuar una mala práctica de programación. Esto a la larga haría más difícil el mantenimiento y podría provocar que la aplicación fuera menos robusta, lo cual no es aceptable para un sitio de esta magnitud.

Así que optamos por la segunda opción, la cual era sencilla pero laboriosa. Los ingenieros participaron en una sesión de entrenamiento donde se les explicó las razones del cambio, y la manera en la que se debía adaptar el código. Varios cientos de páginas se actualizaron y probaron.

Web services SOAP
Algunos componentes de misión crítica se implementaron como servicios web Axis SOAP. Dichos elementos presentaron estos retos durante la migración:

Los desarrolladores de Axis 1.1 habían usado la palabra “enum” en uno de los nombres de los paquetes. Esto generó problemas, dado que “enum” ahora es una palabra reservada, utilizada para enumeraciones en base a la clase java.lang.Enum.

Los ingenieros de la plataforma básica migraron a Axis 1.2, la cual provee una nomenclatura de paquetes diferente. Esta solución funcionó bien para el compilador, pero generó errores al ejecutar los componentes que utilizan Axis.

Para simplificar el proceso de regresión en caso de ser necesario, minimizamos los cambios necesarios a las clases de Java existentes.

La manera correcta de corregir esto fue regenerar el código de la aplicación utilizando la nueva base de código y herramientas como WSDL2Java. Sin embargo, queríamos evitar alterar el código de las clases existentes para simplificar el proceso de regresión a Java 1.4 en caso de que fuera necesario. Fue por esto que en el ciclo A decidimos mantener las clases existentes y desarrollar una solución programática que le diera la vuelta a este problema, y mantuviera la compatibilidad con Java 1.4 y Axis 1.1. En caso de una regresión a 1.4, solamente sería necesario cambiar el JVM y las bibliotecas. La implementación completa para Axis 1.2 se dejó para el ciclo B ó C, cuando la migración se enfoque en aspectos de lenguaje y bibliotecas, no de la JVM.

Detalles con Hotspot
Los servidores implementan todas las optimizaciones posibles debido al volumen de tráfico que toleran. La aplicación se apoya en Hotspot para optimizar los métodos más frecuentemente utilizados. Al introducir la JVM 1.5 surgieron algunas excepciones en tiempo de ejecución en código que funcionaba bien bajo la JVM 1.4. La resolución de estos errores típicamente es sencilla, aunque laboriosa. Recomendamos lo siguiente:
  • De ser posible, utilizar herramientas de análisis de código (profiling)
  • Si la JVM deja de responder, analizar el core dump para averiguar cuales son los métodos que están provocando el error.

Un ejemplo del primer caso, fue un problema que teníamos con el garbage collector (GC) concurrente. Este fue resuelto al aumentar el stack de memoria disponible al GC a través de la configuración de parámetros como CMSMarkSTackSize y CMSParallelRemarEnabled, tal y como lo sugirió el proveedor comercial de la JVM.

En el segundo caso, algunos métodos arrojaban NullPointerExceptions durante la compilación de JIT, sin importar la configuración. La solución a corto plazo para este problema fue agregar el nombre de la clase/método al archivo de configuración .hotspot_compiler. Esto hará que el JVM no compile esos métodos. La solución a largo plazo es que el proveedor del JVM arregle el problema.

Transformaciones JSTL
Algunas partes del sitio están implementadas con JSTL. Varios errores aparecieron cuando ejecutamos el código de JSTL en un ambiente de Java 1.5. Se determinó que JSTL requiere JAXP 1.2, pero el J2SE de 1.5 provee JAXP 1.3.

La solución requirió dos pasos:
  • Indicar al run-time que utilice las clases de JAXP 1.2
  • Reestablecer el factory de transformación default de Xalan a XSLTC

En congruencia con nuestra intención de alterar el ambiente lo menos posible, estos cambios se hicieron como parámetros pasados por línea de comando al arrancar el JVM. De esta manera son fáciles de eliminar en caso de requerir una regresión en el ambiente de producción.

Consideraciones Adicionales
Los ejemplos mencionados reflejan algunos de los problemas de mayor impacto y visibilidad que encontramos con el nuevo JVM en un ambiente normalmente estable. Es posible que se encuentren otros problemas durante las pruebas de regresión. En este momento, la aplicación está estable y los miembros del equipo de desarrollo sentimos que hemos resuelto todos los problemas mayores que de otra manera hubieran requerido hacer una regresión.

Programación en Java 1.5
Para hablar con los desarrolladores sobre las nuevas características del lenguaje introducidas en Java 5, se llevaron a cabo varias sesiones de entrenamiento. La meta era familiarizar a los desarrolladores con las ventajas y desventajas (o cuidados que se deben tener) en el uso de estas características.

Las sesiones fueron divididas en tres y fueron planeadas de la siguiente manera:
  • Diciembre 2004 - Nuevas características en el lenguaje de programación: genéricos, autoboxing, tipos enumerados, ciclos mejorados, static imports.
  • Mayo 2005 – Anotaciones, API de concurrencia, mejoras en I/O
  • Junio 2005 – Herramientas para monitoreo y análisis (JMX)
Estas sesiones fueron desarrolladas por un equipo de ingenieros de walmart.com. Posteriormente recurriremos a terceros para tener entrenamiento a mayor profundidad.

La organización ya cuenta con unos lineamientos de programación no-restrictivos. Estos serán actualizados para incluir mejores prácticas específicas a las nuevas capacidades del lenguaje.

De acuerdo con estas políticas, solo se deberá recurrir a las nuevas capacidades del lenguaje cuando:
  • La API de un tercero que se va a integrar las requiere
  • La API de un tercero que ya existía pero se está actualizando las requiere
  • Desarrollo de nuevas applicaciones
  • Revisiones a código existente

No se realizará un esfuerzo dedicado para implementar las capacidades de Java 5 en cada elemento de código existente. La cantidad de clases y JSPs que habría que actualizar es enorme, y tal esfuerzo generaría muchos errores. La meta es ir agregando estas capacidades conforme se vaya necesitando.

Conclusión
Migrar un ambiente de producción a Java 1.5 en un sitio popular y con gran volumen es una tarea con diversos riesgos que pueden ser mitigados a través de una planeación adecuada. Este artículo mostró como fueron superados algunos problemas durante la migración de la JVM. Los ingenieros a cargo de esta migración están satisfechos con el proceso hasta ahora, aunque posiblemente hagan algunas cosas diferente para las próximas etapas. Las próximas actualizaciones del ambiente de ejecución se enfocarán en resolver problemas específicos al JVM, postergando la adición de nueva funcionalidad para otros ciclos.

Este artículo está limitado al ambiente de Java en walmart.com. Excepto por los problemas descritos aquí, el sentimiento en general es que la migración a Java 1.5 en un ambiente de producción complejo puede ser algo que no brinde mayores complicaciones. No se espera ningún problema mayor cuando se comiencen a introducir las nuevas capacidades de Java 5 en los programas. Los ingenieros a cargo sienten que esta actualización proveerá mejoras en desempeño, integridad, prácticas de programación y monitoreo para los usuarios, desarrolladores y equipo de operación a cargo del sitio.

#####
Acerca del Autor
Eugenio Ciurana tiene más de 20 años de experiencia en el diseño, implementación y desarrollo de sistemas de misión crítica en México, Estados Unidos y Europa. El Ing. Ciurana se graduó (y fue profesor) en la facultad de ingeniería en computación de la Universidad Autónoma de Guadalajara.
http://eugeneciurana.com


* Este artículo fue publicado originalmente en el sitio theserverside.com y está disponible en http://www.theserverside.com/articles/article.tss?l=MigratingtoJava5. Esta traducción fue hecha por Software Guru, con la autorización del autor.