Single Page Interface, Google App Engine y 28Kb

En el eterno debate entre los enfoques céntricos en el cliente y en el servidor en aplicaciones intensivas en AJAX, un aspecto que se resalta siempre en contra del enfoque céntrico en el servidor es su alto consumo de memoria, puesto que el estado visual se guarda de alguna manera en el servidor.
En aplicaciones de intranet este consumo de memoria no es un gran problema puesto que el número máximo de usuarios suele ser discreto y acotado.
Sin embargo si queremos construir sitios web para el gran público tipo Single Page Interface (o en general muy intensivos en AJAX) el uso de memoria en el servidor pasa a ser muy relevante pues el número de usuarios simultáneos puede ser extremadamente variable en el tiempo y puede ser sin duda muy alto si hablamos de una web muy popular.
A esto hay que añadirle la proliferación de opciones de cloud computing en donde la aplicación funcionará en varios nodos a la vez y con posibilidad de "escalabilidad elástica", es decir añadiéndose nodos automáticamente a medida que aumenta la carga de la aplicación.
El problema del consumo de memoria en el servidor es especialmente relevante en el caso de que no dispongamos de afinidad de sesiones (sticky sessions), como ocurre en Google App Engine y por tanto el estado visual guardado en el servidor es susceptible de ser serializado y replicado entre los nodos en los que se está ejecutando la aplicación; a mayor estado visual del cliente almacenado en el servidor mayor será la cantidad de datos que tendrán que transportarse entre nodos.
Google App Engine (GAE) es uno de los talones de aquiles de los frameworks céntricos en el servidor debido en buena parte a la replicación de sesiones como única opción, estos frameworks funcionan con mucha más "soltura" con afinidad de sesiones (aunque también hay que considerar otras limitaciones de GAE que dificultan su uso). Si observamos la lista de frameworks que funcionan en GAE, no hay muchos céntricos en el servidor y su presencia en la lista no implica necesariamente que sean efectivos si se hace un uso intensivo de AJAX.
ItsNat también soporta Google App Engine desde la versión 0.7 como se puede ver en ese listado, como se sabe ItsNat es claramente céntrico en el servidor (aunque se puede combinar con librerías cliente como se mostró hace poco).
Tenemos por tanto un problema, el enfoque céntrico en el servidor, por ejemplo en el caso de ItsNat, es quizás la mejor opción para crear sitios web Single Page Interface duales, es decir que puedan funcionar también como paginados si desactivamos JavaScript o que sean indexables por los buscadores sin recurrir a hacer dos versiones, sin embargo el enfoque céntrico en el servidor es aparentemente un gran consumidor de memoria y por tanto dichos datos en memoria han de ser serializados para ser compartidos entre los nodos de un cloud como GAE.
Otra opción posible se mostró aquí en javaHispano, y consistía en no guardar el estado visual en la sesión tal que en el cliente (en URLs) se guardara la información necesaria para poder reconstruir el estado visual en el servidor cuando se produjera un cambio de nodo dentro de las peticiones web de un mismo cliente.
Aunque esa opción es viable (y funciona en el caso de ItsNat) suponía una recarga de página cuando GAE decidía cambiar de nodo (lo cual no es muy habitual si las peticiones son bastante seguidas), y por tanto con esa estrategia podríamos hablar de sitios web semi Single Page Interface pero no verdaderamente Single Page Interface.
Por tanto si queremos verdadero Single Page Interface en GAE debemos replantearnos el problema de la memoria.
¿Verdaderamente necesitamos mucha memoria en el servidor?
Es sabido que en ItsNat podemos prescindir de aquellas partes que son estáticas, dichas partes no son manipuladas por código del programador y por tanto pueden ser cacheadas como texto plano compartido entre todos los usuarios, evitando ser instanciadas como DOM para cada client. El sistema de templates de ItsNat facilita este cacheado selectivo con un simple atributo especial.
Sin embargo esto no es suficiente, basta que cambiemos un nodo de texto para que el subárbol necesario para albergar dicho nodo esté instanciado (al menos parcialmente), aunque dicho nodo no vuelva a necesitarse más.
Con la introducción de la técnica de "desconexión de nodos" en ItsNat v1.1 todo cambia, ahora podemos eliminar un subárbol DOM en el servidor y dejarlo intacto en el cliente (pudiendo volver a sincronizar ambos lados de nuevo si se quiere), de esa manera podemos evitar guardar zonas enteras de la página como DOM cuando dichas zonas no van a volver a ser cambiadas (lo normal es que sean substituidas por otras). Esta técnica se mostró ya en el artículo de programación híbrida cliente/servidor citado anteriormente aunque el artículo no hacía énfasis en el asunto de la memoria sino más bien en la colaboración con código JavaScript cliente.
El paso siguiente es
¿Es posible conseguir una drástica reducción de memoria en sitios web Single Page Interface y conseguir alto rendimiento en Google App Engine con replicación de sesiones?
La respuesta es ¡¡SI!!
Sin embargo ha sido necesario publicar ItsNat v1.1.1 para solucionar algunos problemas importantes de ItsNat en GAE con replicación de sesiones, el más importante es deshacerse por fin de la necesidad del hilo limpiador de nodos perdidos de Batik DOM (en el cual ItsNat se basa extendiéndolo). Dicho hilo no puede crearse en GAE porque GAE no admite la creación de hilos, por otra parte la técnica de caché usada por Batik daba lugar a que nodos que en teoría estaban sin uso a punto de ser recolectados por el GC, fueran serializados al serializar la sesión aumentando inútilmente el tamaño de la misma. ItsNat v1.1.1 reescribe esa parte de Batik DOM evitando sujetar con referencias débiles nodos definitivamente perdidos y por fin evitando la necesidad de este hilo en background.
ItsNat v1.1.1 añade otro método interesante ItsNatServletContext.setSessionSerializeCompressed(boolean) que sirve para comprimir (con GZIP) automáticamente el resultado de la serialización de la sesión, reduciendo fuertemente la cantidad de datos que GAE (o cualquier otro entorno de replicación de sesiones o serialización a disco para failover) transportará entre nodos. Por otra parte el método ItsNatServletContext.setSessionExplicitSerialize(boolean) tiene el fin de simular el comportamiento de entornos como GAE pero en instancia única.
¿Y si llevamos todo esto a la práctica?
Es conocida esta demo prototipo de sitio web Single Page Interface, aplicamos las técnicas anteriormente citadas para salvar memoria y permitimos que el estado visual se serialice en la sesión, además activando la compresión de datos.
Navegando al estado que más consume memoria: Electrónica/Televisión/TV LCD en donde se muestra la lista de productos, si salvamos dicha página, el markup plano resultante ocupa 151Kb, que en forma de objetos DOM ocuparía significativamente más memoria por razones obvias.
Sin embargo a través del Datastore Viewer de la consola de administración de GAE podemos conocer cual es el tamaño de la sesión serializada y compartida entre instancias y dicho valor es tan sólo de 28Kb
Conclusión: ¿puede tu sitio web Single Page Interface céntrico en el servidor ser infinitamente escalable? ¿puede vivir en las nubes?
¡SI!
Nota: me hubiera gustado incluir imágenes pero me temo que el "uploader" de javaHispano está roto.

Reader Comments