Amazon Dynamo ha evolucionado bastante a lo largo de los años. Y los primeros en adoptar la base de datos NoSQL de AWS ahora enfrentan algunos …
desafíos al trabajar alrededor de los límites de DynamoDB previamente establecidos.
Cuando empezamos con DynamoDB, nuestra empresa indexaba la tabla por publicación y luego por ID de historia, lo que me permitió buscar rápidamente todas las historias en una publicación. Sin embargo, esta configuración también incurrió en un límite de 10 GB por publicación.
Varios años después, AWS me notificó que tenía una publicación con demasiado contenido y tuve que esforzarme para averiguar qué estaba pasando. Resultó que solo tenía una publicación con dos veces más contenido que la siguiente publicación más grande. Pero no quería volver a encontrarme con estos límites de DynamoDB.
Mi error fue que elegí un ID de publicación como clave hash principal en lugar de un ID distribuido aleatoriamente y único globalmente para la clave de tabla principal. Si lo hiciera, me habría permitido agregar índices secundarios globales para otros elementos en los que quería buscar. También podría haber indexado todo en otro sistema como Amazon CloudSearch o Algolia.
Más tarde, Amazon agregó soporte para índices secundarios, sin el límite de 10 GB, pero ya era demasiado tarde para comenzar de nuevo con nuevas claves e identificadores. Necesitaba arreglar mi tabla de problemas y dar soporte a un sistema en producción que tenía que permanecer activo mientras migraba a la nueva estructura de la tabla.
Una forma de hacer esto es a través de una migración en caliente, adaptando las tablas sin ningún tiempo de inactividad del sistema. Durante una migración en caliente, hay dos tablas de DynamoDB que se utilizan activamente al mismo tiempo y varias fuentes de registro. Si dos sistemas escriben en el mismo registro pero en tablas diferentes, se producen problemas de integridad referencial. La migración en caliente también requiere mucho más tiempo para admitir, pero en última instancia, era necesaria para admitir un entorno de producción.
Migración en caliente de tablas de DynamoDB
DynamoDB no permite a los usuarios cambiar o eliminar índices secundarios locales después de crear una tabla. Tampoco le permite cambiar la clave principal de las tablas. Mi tabla tenía escrituras activas varias veces por segundo, por lo que no pude eliminarla y recrearla con una nueva estructura de tabla. En su lugar, creé una nueva estructura de tabla para hacer la transición de mi código para escribir en él mientras leía de las tablas nueva y vieja. Mi código llegó a DynamoDB en varios lugares, lo que significaba que tenía que migrar todo mi código antes de poder deshabilitar la tabla anterior.
Pero deshabilitar la tabla anterior no era una prioridad, solo necesitaba asegurarme de que las nuevas escrituras funcionaran correctamente. Como medida provisional, limpié la mesa vieja y eliminé las historias que tenían seis meses o más de la publicación del problema. Esto me dio suficiente tiempo para realizar una migración en caliente sin perder ningún contenido nuevo.
Convierta aplicaciones para leer de ambas tablas
Antes de permitir que las aplicaciones escribieran exclusivamente en la nueva tabla, necesitaba actualizar las aplicaciones para que pudieran leer desde varias tablas. Utilizo Algolia como motor de búsqueda, por lo que no necesitaba admitir un nuevo método de búsqueda, solo una nueva capacidad para buscar registros. Busqué en todo el código fuente cualquier consulta de DynamoDB o búsquedas en mi Resúmenes mesa.
Una vez que encontré una referencia al Resúmenes tabla, modifiqué el código para leer primero de la nueva tabla, que renombré Artículos. Después de leer, el código verifica el antiguo Resúmenes tabla si la referencia no existía en el nuevo Artículos mesa. Inicialmente, esto daría lugar a búsquedas fallidas y operaciones de doble búsqueda. Pero una vez que las operaciones de escritura se trasladan a la nueva tabla, se convierte en la fuente principal de datos. Si se produjera una operación de escritura en la nueva tabla pero no en la anterior, la tabla anterior estaría desactualizada. Por lo tanto, necesitaba usar la nueva tabla como autoridad principal para cualquier registro.
Así es como se ve el código fuente:
function lookupAbstract (params) {
docClient.get ({
TableName: ‘Artículos’,
Llave: {
guid: params.guid,
},
}, función (e, r) {
si (e) {
lanzar nuevo Error (e);
} más si (r.Item) {
devolución de llamada (r.Item);
}
docClient.get ({
TableName: ‘Resúmenes’,
Llave: {
pub: params.pub,
external_id: params.external_id,
},
}, función (e, r) {
si (e) {
lanzar nuevo Error (e);
} demás {
devolución de llamada (r.Item);
}
});
});
}
Este código intenta buscar un artículo por el GUID sobre el Artículos mesa. Si no está disponible, usa el pub y external_id en el viejo Resúmenes mesa. Anteriormente, necesitaba proporcionar un ID de pub y un ID externo, pero esta llamada requiere que me asegure de que todas las llamadas a esta función también proporcionen un GUID. Esto significa cambiar cualquier referencia a mi lookupAbstract llame para incluir estos datos adicionales.
Migrar escrituras a la nueva tabla de DynamoDB
Después de verificar que todo se está leyendo correctamente de las tablas nuevas y antiguas, escribí algunos registros de prueba en la tabla nueva que no existían en la tabla anterior. Luego comencé a cambiar todos los procesos que escribían contenido al antiguo Resúmenes tabla para migrarlos a la nueva Artículos mesa. Esto fue mucho más simple que el primer paso, porque no necesitaban operaciones de respaldo para realizar escrituras en la tabla anterior, con una excepción.
Al hacer actualizaciones, en lugar de guardar operaciones, necesitaba hacer una operación de búsqueda. Si el elemento no existía todavía en la nueva tabla, cópielo de la tabla anterior primero usando este código, que verifica si el registro existe verificando que un pub se establece el valor.
function updateAbstract (clave, actualizaciones) {
docClient.update ({
TableName: ‘Artículos’,
Llave: {
guid: key.guid,
},
AttributeUpdates: actualizaciones,
// Asegúrese de que este registro exista en
// la nueva tabla primero
Previsto: {
pub: {
Existe: cierto,
},
},
}, función (e, r) {
// Solo copia de la tabla anterior si
// esta es una excepción condicional
if (e.code === ‘ConditionalCheckFailedException’) {
copyItemFromOldTable (clave)
.entonces (updateAbstract.bind (esto, clave, actualizaciones));
} demás {
// Manejo estándar aquí …
}
});
}
Si este intento falla, se llama a otra función para copiar el elemento anterior a la nueva tabla y luego reintenta la operación.
Copiar todos los datos a una nueva tabla
Una vez que se produzcan nuevas escrituras en la nueva tabla, verifique que no se produzcan nuevas escrituras en la tabla anterior. Para hacer esto, suscriba una función de AWS Lambda a todas las operaciones de escritura en el Resúmenes tabla y verifique que no se haya perdido ninguno. El último paso es copiar los datos antiguos de la Resúmenes mesa a la nueva Artículos tabla, lo que me permite eventualmente eliminar el antiguo Resúmenes mesa. Dejar la vieja mesa no causa ningún daño, pero podría costar más dinero y causar confusión entre otros desarrolladores que acceden al contenido.
Usé un script simple para copiar cada elemento de la tabla anterior a la nueva, asegurándome de que no hubiera un nuevo registro en el Artículos tabla antes de realizar escrituras. Verifiqué que todo salió bien y pasó a la etapa de limpieza.
Limpiar
Antes de eliminar el Resúmenes tabla, elimine todas las referencias a ella en el código. Si el código no pudo encontrar un artículo en el Artículos tabla, lo registré. Sin embargo, no quería que mi código buscara el registro en una tabla que estaba a punto de eliminar. Eliminé esos segundos pasos y los reemplacé con una alerta para poder encontrar y recuperar el contenido perdido.
Finalmente, antes de eliminar el Resúmenes tabla, tomé una instantánea para almacenarla en Amazon Simple Storage Service, en caso de que algo saliera mal. Luego reduje el rendimiento de mi tabla al mínimo y la supervisé durante unas semanas antes de eliminarla finalmente.
Cuando empiece desde cero, tenga en cuenta los límites de DynamoDB. Es posible que deba realizar un método de migración en caliente un día para mover una tabla a otra. Y, debido a que es más complicado que diseñar inicialmente claves para la escalabilidad, solo use una migración en caliente cuando sea absolutamente necesario.