Eliminar tiempo de inactividad nulo mediante actualizaciones de servicio con versiones

Tradicionalmente, los administradores necesitaban desconectar un servidor para actualizar y mejorar el software local. Sin embargo, el tiempo de inactividad se hace totalmente imposible para servicios globales ininterrumpidos. Muchos servicios en la nube modernos son una dependencia crítica para que los usuarios operen con sus empresas. Nunca es buen momento de quitar un sistema, así que ¿cómo puede un equipo prestar servicio continuo al instalar actualizaciones importantes de seguridad y funciones?

Mediante el uso de actualizaciones con versiones, estos servicios esenciales pueden transicionar sin problemas de una versión a otra mientras los clientes los usan activamente. No todas las actualizaciones son complejas. Las actualizaciones de diseños o estilos de front-end son fáciles. Los cambios en las funciones pueden ser complicados, pero hay procedimientos bien conocidos para mitigar los riesgos de migración. Sin embargo, los cambios que derivan del nivel de datos plantean una nueva clase de dificultades que merecen una consideración especial.

Actualizar capas por separado

Con un servicio en línea distribuido en varios centros de datos y un almacenamiento de datos independiente, no todo puede cambiar simultáneamente. Si el servicio normal se divide en código de aplicación y bases de datos, que probablemente tienen versiones independientes entre sí, uno de esos entornos debe absorber la complejidad del control de versiones.

A menudo, el control de versiones es más fácil de controlar en el código de la aplicación. Los sistemas más grandes suelen tener bastante código heredado, como SQL que reside dentro de sus bases de datos. En lugar de complicar aún más este CÓDIGO SQL, el código de la aplicación debe controlar la complejidad. En concreto, puede crear un conjunto de clases de fábrica que comprendan el control de versiones de SQL.

Durante cada sprint, cree una nueva interfaz con esa versión para que siempre haya código que coincida con cada versión de base de datos. Puede dar marcha atrás fácilmente los archivos binarios durante la implementación. Si algo va mal después de implementar los nuevos archivos binarios, vuelva al código anterior. Si la implementación binaria se realiza correctamente, inicie el mantenimiento de la base de datos.

¿Cómo funciona esto realmente? Por ejemplo, supongamos que el equipo implementa actualmente el Sprint 123. Los archivos binarios comprenden el esquema de la base de datos de Sprint 123 y el esquema de Sprint 122. El patrón general es trabajar con ambas versiones o sprints N y N-1 del esquema SQL. Los archivos binarios hacen una consulta en la base de datos, determinan a qué versión de esquema se están dirigiendo y luego cargan el enlace adecuado. A continuación, el código de la aplicación controla la incidencia cuando el nuevo esquema de datos aún no está disponible. Una vez disponible la nueva versión, el código de aplicación puede empezar a usar la nueva funcionalidad habilitada por la versión de base de datos más reciente.

Puesta al día solo con el nivel de datos

Una vez actualizadas las bases de datos, el servicio entrará en estado de puesta al día si se produce un problema. Las migraciones de bases de datos en línea son complejas y, a menudo, necesitan varias fases, por lo que la fase de puesta al día suele ser la mejor manera de solucionar un problema. En otras palabras, si se produce un error en la actualización, es probable que también se produzca un error en la reversión. No tiene mucho sentido invertir en la tarea de crear y probar código de reversión que el equipo no tenga previsto usar nunca.

Secuencia de implementación

Piense en una situación en la que necesita añadir un conjunto de columnas a una base de datos y transformar algunos datos. Esta transición no debe ser visible para los usuarios, lo que significa evitar bloqueos de tabla tanto como sea posible y, a continuación, mantener los bloqueos durante el menor tiempo posible para que no sean perceptibles.

Lo primero que hacemos es manipular los datos, posiblemente en tablas paralelas mediante un desencadenador SQL para tener los datos sincronizados. A veces, las migraciones y transformaciones de datos de gran tamaño tienen que superar varias implementaciones en varios sprints.

Una vez creados los datos adicionales o el nuevo esquema en paralelo, el equipo activa el modo de implementación para el código de la aplicación. En el modo de implementación, cuando el código realiza una llamada a la base de datos, primero toma un bloqueo en el esquema y, a continuación, lo libera después de ejecutar el procedimiento almacenado. La base de datos no puede cambiar entre el momento en que se realice la llamada a la base de datos y cuando se ejecuta el procedimiento almacenado.

El código de actualización actúa como escritor de esquemas y solicita un bloqueo de escritura en el esquema. El código de aplicación tiene prioridad al tomar un bloqueo del lector y el código de actualización se activa en segundo plano intentando obtener el bloqueo del escritor. En el bloqueo del sistema de escritura, solo se permite un pequeño número de operaciones muy rápidas en las tablas. A continuación, se libera el bloqueo y la aplicación registra la nueva versión de la base de datos en uso y usa la interfaz que es la misma que la nueva versión de la base de datos.

Todas las actualizaciones de la base de datos se realizan mediante un patrón de migración. Un conjunto de código y scripts examina la versión de la base de datos y, a continuación, realiza cambios graduales para migrar el esquema de la versión anterior a la nueva. Todas las migraciones se automatizan e implementan a través del servicio de administración de versiones.

La interfaz de usuario web también debe actualizarse sin afectar a los usuarios. Al actualizar archivos de JavaScript, hojas de estilos o imágenes, evite mezclar versiones antiguas y nuevas cargadas por el cliente. Esto puede llevar a errores que podrían hacer perder el trabajo en curso, como un campo que edita un usuario. Por lo tanto, debe crear versiones de todos los archivos JavaScript, CSS y de imagen colocando todos los archivos asociados a una implementación en una carpeta independiente con versiones. Cuando la interfaz de usuario web realiza llamadas de nuevo al nivel de la aplicación, se cargan los recursos con una versión especificada. Solo cuando la acción de un usuario da como resultado una actualización de página completa, la nueva interfaz de usuario web se carga en el explorador. La actualización no afecta a la experiencia del usuario.

Pasos siguientes

Microsoft ha sido una de las empresas de desarrollo de software más grandes del mundo durante décadas. Descubra cómo Microsoft opera en sistemas fiables con DevOps.