viernes, 24 de septiembre de 2010

Paths relativos, especificaciones, Java y un bug en "Internetes"

Hoy en el trabajo me he encontrado con “un bug en Java e Internetes”. Bueno, más que un bug es un comportamiento extraño por especificaciones obsoletas, implementaciones independientes de contexto etc. etc. Pero es algo que te puedes encontrar en la vida real, así que he pensado que sería interesante contarlo.
El problema se da al leer un documento HTML con Java y tener que interpretar las direcciones relativas que contiene dicho documento. Si una de las direcciones únicamente contiene un “query string”... ¿Cómo la interpretarías vosotros?
Es decir, si en la página www.host.com/dir/loquesea.do encontramos un enlace tal que así href=”?param=value”... ¿Dónde ha de ir la página?
La respuesta correcta es “depende” :).

¿Y cual de las dos respuestas es incorrecta? En realidad “ninguna”. La clase java.net.Uri implementa correctamente la resolución de caminos relativos según la RFC2396, de agosto de 1998, y por ello elimina el “loquesea.do” antes de añadir el camino relativo. En cambio HTML 4.1 está basado en la especificación RFC1808, de junio de 1995, la cual dice que si hay “query string” se mantiene la dirección completa como base. Lo curioso es que HTML 4.1 es de diciembre de 1999, más de un año después de que la RFC1808 fuera “sobre-escrita” por la RFC2396 pero sin embargo, parece al escribir la especificación de HTML no se fijaron en que la RFC1808 ya no estaba en vigor. De todas formas, el comportamiento de los navegadores es correcto ya que HTML 4.1 se basa en la RFC1808 y ésta es la que hay que seguir. Y la clase java.net.Uri tampoco hace nada “incorrecto” ya que en realidad sigue una especificación más reciente. En realidad podría hacerlo “mejor” si permitiera especificar si el método resolve() ha de funcionar según HTML 4.1 o el futuro HTML 5, y puestos a tener un comportamiento por defecto... HTML 4.1 es muy común.... en fin, que la cosa es bastante ambigua así que la mejor solución es no escribir nunca enlaces de esa forma y escribirlos al menos un poco más explícitos, o si hay que tratar en Java las páginas HTML que escribe un tercero... si esas páginas son HTML 4.1 hay que tener en cuenta que java.net.Uri no resuelve las direcciones relativas como lo hacen los navegadores, así que hay que tratar la dirección antes de pasársela para obtener el resultado esperado.

Y ahí queda escrito ese aviso para navegantes, por si a alguien le ahorra un buen rato de investigación leyendo especificaciones y el código del OpenJDK como he tenido que hacer yo.



Happy coding! EJ

lunes, 20 de septiembre de 2010

Dime tu lista de prioridades y te diré lo profesional que me pareces

Un tema interesante y que da para muchas discusiones es “¿qué convierte/define a un buen informático?” Sin ánimo de sentar cátedra ni de que los demás piensen igual, voy a dar mi opinión personal e intrasferible de lo que define a un profesional del desarrollo de software (no a cualquier tipo de informático) pero siendo un pelín original. En vez de decir la típica lista de atributos, simplemente me fijaré en un aspecto que es: El orden de prioridades a la hora de afrontar un trabajo.
Así que sin más dilaciones, la pregunta crítica: “¿A que le das más importancia a la hora de desarrollar un software?
  • A aplicar los patrones X, Y y Z. Si esa es tu prioridad más alta o está entre las más altas, lo siento pero entras dentro de la categoría empezar-la-casa-por-el-tejado. Los patrones son soluciones comunes y conocidas para ciertos tipos de problemas, y si tu interés se centra en usarlos sin saber si son aplicables a tu problema, ciertamente tienes el orden de las prioridades un poco confundidas. El desarrollo no es un concurso para aplicar el mayor número de patrones posibles, ni aplicar patrones significa garantía de nada (de ahí el invento de los anti-patrones) así que la hora de pensar en aplicar patrones está bastante más abajo, una vez sepamos a qué problemas no estamos enfrentando y en caso de reconocer algún problema habitual que se solucionar con un patrón, nada mejor que aplicarlo. Pero no antes.
  • A aplicar las más modernas y “más mejores” técnicas de desarrollo. Si este objetivo lo tienes muy alto significa que te dejas llevar por las modas y que eres una “fashion-victim”, de las que se tragan eso de que todo lo nuevo es mejor y que lo viejo huele a rancio. Como pez en el agua en el mercado consumista, pero en versión tecnológica. Si es solo producto de la juventud y no de la falta de neuronas, no te preocupes que se cura con la experiencia :). Cuando veas a la misma gente contando cada X tiempo mentiras nuevas, por que con las viejas no se hace negocio, aprenderás a ver detrás del humo y distinguir lo aprovechable de las novedades y lo que hay que conservar de lo que ya existe, siempre en función del tipo de trabajo que tengas.
  • A que el diseño sea orientado a objetos (sustituible por cualquier concepto purista). Este tipo de objetivos están muy bien para el mundo teórico, pero tienen ese ligero problemilla de que a la tozuda realidad a veces no le gusta jugar con reglas perfectas. Seguro que eres de los que creen que la tierra es perfectamente redonda y el mar es azul, al fin y al cabo los pintan así en todos los libros ¿verdad? Pues no, la perfección teórica está muy bien pero sirve a un fin, no es la finalidad en sí misma. Es decir, que el diseño sea lo más orientado a objetos posible persigue un fin, que es obtener los beneficios de la OO etc. pero si no vamos a conseguir esos beneficios, por diversas razones, entonces ya no tiene utilidad.
  • A usar el lenguaje/framework X.Seamos sinceros, la mayoría de los proyectos sabemos en que lenguaje/arquitectura los vamos a realizar por que es en el que realizamos casi todos, si no todos, nuestros proyectos y no vamos a re-evaluar nuestra cartera de soluciones en cada proyecto (en la mayoría de casos, en algunas empresas lo hacen pero por necesidad). Aun así, a la hora de cambiar de lenguaje/arquitectura en un proyecto hay que recordar qué es lo realmente importante, y no, no es usar una solución específica a no ser que el proyecto tenga ese objetivo concreto.
  • A cumplir lo que se me mande hacer, que para eso me pagan. Esta actitud conformista y muy habitual no es que sea terriblemente mala, pero no le convierte a uno en un gran profesional. Especialmente en nuestro campo donde muchos mandos intermedios no saben lo que significan la mitad de las siglas que usan y se limitan a distribuir la mierda que cae desde arriba entre los de abajo. Repito, es algo comprensible pero por hacer un examen justito a uno no le ponen un 10.
  • A disfrutar trabajando. Sí, numerosos estudios demuestran que la gente trabaja mejor cuando está más contenta y en trabajos creativos como el nuestro es muy importante la mentalidad y la actitud, pero hombre, “un hombre ha de hacer lo que tiene que hacer” y la vida no es una fiesta constante. Si toca hacer un mantenimiento, se hace, aunque hayamos hecho mil. Es importante que el trabajo sea gratificante, pero tambien hay que tratar de disfrutar con nuestro trabajo. Como dicen los sabios “no es más feliz quien más tiene, si no quien menos desea”. O dicho de otro modo: “Señor, dame fuerza para aceptar las cosas que no puedo cambiar, valor para cambiar las cosas que puedo y sabiduría para poder diferenciar entre unas y otras.”
  • A hacer lo que el cliente pide. Esta es otra actitud comprensible, decente... pero estamos hablando de informática, no de una tienda de ropa, y aquí el cliente no tiene siempre la razón, simplemente por que el experto en manejo de la información se supone que eres tú, y no él. Hacer lo que el cliente dice sirve para cubrir el expediente y salvaguardar el culo, pero no se lleva el máximo galardón en mi lista.
  • A solucionar “El Problema”. Obviamente, como último en la lista, esta es la que yo considero que es la más profesional de las prioridades. Hay que entender cual es “El Problema” que queremos solucionar, ayudados por el cliente, usando el lenguaje y framework adecuados, que normalmente será los que conocemos ya que que los conozca el equipo de desarrollo da muchos puntos, utilizado las técnicas y patrones adecuados y si encima podemos disfrutar haciéndolo, mejor que mejor. Con esto lo que quiero decir es que la prioridad es dar respuesta al problema. Los programas, de momento, no son obras de arte para ser admiradas sin más, tienen una finalidad y cumplirla debería ser la máxima prioridad, la cual muchas veces se combina con el resto ya que ayudan a cumplirla, pero siempre hay que tener el orden claro.
Y esa es mi lista. Como se puede ver soy del modelo pragmático, pero es lo que hay. La lista no será del agrado de todos pero lo que está claro es que es la mía y sobre eso no hay discusión. ¿Y la tuya?

Happy coding!
EJ

jueves, 2 de septiembre de 2010

Arquitectura ligera y uso de cachés

Después de explicar un poco la arquitectura ligera que estoy empleando en un proyecto y dado que estoy usando una caché, tema sobre el cual ya desvarié un poco, he pensado que también podría ser interesante explicar cómo estoy usando la caché en este proyecto, basándome en las reflexiones del mensaje sobre uso de cachés.

Como ya expliqué, el proyecto consiste en mostrar los resultados de las partidas de un juego online, quien participó, cómo lo hizo cada piloto y estadísticas recopilatorias sobre el historial de cada piloto. Respondamos entonces a las preguntas clave:

¿Merecía la pena introducir una caché? La respuesta en este caso es que claramente sí, dado que vamos a mostrar resultados acumulados de varias tablas, especialmente en el caso de las estadísticas históricas. Incluso la “simple” lista de partidas jugadas es una consulta que cruza 4 tablas ( no por que esté mal el esquema de BDD si no por que ya muestra datos acumulados) y las primeras pruebas ya indican un incremento en el rendimiento de x30 en las consultas más simples.

¿En que capa he introducido la caché? Dejar que la BDD se encargue de ello implicaría que todavía tendríamos que viajar por la red hasta la BDD. La cache de MyBatis no es algo con lo que he experimentado y no se que control me da (creo que no suficiente por lo que he leído en las listas de distribución), así que seguimos subiendo. El control de caché del framework ligero lo conozco bien y se que control me da: más que suficiente. Podría intentar subir aun más y usar la caché a nivel de filtro de servlets en la salida final (HTML o JSON), pero dado que más tarde introduciremos personalizaciones para que los usuarios vean sus propias páginas y eso implica que puede que la salida final de cada usuario sea ligeramente diferente, me quedo justo en el nivel de abajo. De todas formas, el salto gordo en el rendimiento se produce en este caso, como en muchos, en la consulta a la BDD y la creación de los objetos correspondientes en el servidor, así que haciendo caché de eso ya obtengo un gran beneficio.

¿Como vamos a mantener actualizada la caché? En la aplicación hay 3 tipos de páginas, clasificándolas en relación de dependencia con datos actualizables:

  • Grupo 1: Las que dependen de todas las partidas jugadas: Lista de partidas, estadísticas globales, clasificaciones de todos los jugadores, etc.

  • Grupo 2: Las que dependen de un piloto en particular: Estadísticas generales de cada piloto, detalles sobre los elementos utilizados por el piloto en sus partidas...

  • Grupo 3: Las que no dependen de nada: Datos de una partida y detalles de participación de cada piloto en esa partida.
Una vez sabemos esto, podemos ver que cada vez que haya una o más partidas nuevas, tenemos que invalidar todos los elementos de la caché del grupo 1 e invalidar los elementos del grupo 2 de los pilotos que hayan participado en las partidas nuevas. Los elementos del grupo 3 pueden estar en caché “indefinidamente”. Para llevar a cabo esta tarea lo que hemos hecho es implementar una tarea periódica con Quartz que a intervalos regulares comprueba si ha habido partidas nuevas y en caso afirmativo, procede a marcar como caducados los elementos de la caché según las reglas definidas.

¿Cual es el perfil de nuestra aplicación? Se presupone una aplicación donde habrá muchas más lecturas que actualizaciones, y donde las actualizaciones de datos no son críticas en absoluto. Por tanto el tipo de funcionamiento definido cuadra perfectamente.

Puntos extra: Para controlar mejor el uso que se hace de la caché y verificar que todo funciona correctamente, tenemos varias ayudas:

  • Por un lado, el framework que utilizamos de caché nos puede decir en cada momento el grado de utilización que le estamos dando (Hit/miss ratio) por lo que podemos saber si realmente se está leyendo mucho más de lo que se está actualizando la cache.

  • Por otro lado, podemos controlar la periodicidad de la tarea de control de la cache y si vemos que la el grado de utilización es bajo y la caché no se usa lo suficiente, podemos aumentar el período y así aumentar el grado de utilización, protegiendo a la vez la BDD de una carga excesiva.

  • Ambos elementos se pueden retocar en tiempo de ejecución sin necesidad de reiniciar la aplicación, lo cual nos permite una gestión muy cómoda sin miedo a molestar a los usuarios.
Espero que este ejemplo sirva para ilustrar lo que quería decir en el mensaje de reflexiones sobre el uso de cachés, y si os da algunas ideas para vuestras aplicaciones, mejor que mejor.

Happy coding! EJ