Cambios de Esquema de Base de Datos

Gustavo Pajuelo, Backend Lead

Los servidores no responden! ¡Estamos lanzando timeouts! ¿Se lanzó algo a producción?

¿Qué pasaba?

En algún momento, durante el crecimiento acelerado que está teniendo Crehana, nos encontramos con una situación como esa, la cuál en un inicio no podíamos determinar a qué se debía. En ese momento, la solución fue solo darle un reboot a nuestra base de datos, y todo solucionado.

Cuándo volvió a ocurrir, nos dimos cuenta de algo: las sesiones de base de datos aumentaban drásticamente, y esto se debía a una operación de base de datos que estaba bloqueando todas las demás. Investigando un poco, aprendimos que existen ciertas operaciones que generan locks en la base de datos, como pueden ver en el siguiente cuadro.

Screen Shot 2021-01-19 at 22.22.40.png

(En Crehana usamos postgres, sobre el cual aplica el cuadro mostrado. En otros motores de base de datos, las reglas pueden ser distintas).

¡La solución!

En ese momento, el equipo estableció como regla de todo cambio de esquema de base de datos, que pudiera contener algún cambio indicado en la tabla, agregar sentencias adicionales, como por ejemplo, para agregar índices se tiene que poner:

CREATE INDEX CONCURRENTLY “nuevo campo” on ……

en lugar de:

CREATE INDEX “nuevo campo” on ……

La solución Crehana

En Crehana, usamos principalmente Django para definir los modelos de base de datos, y realizar modificaciones en las mismas usando el sistema de migraciones que viene con el framework.

Django viene con un comando llamado sqlmigrate que puede ser usado de la siguiente forma:

./manage.py sqlmigrate <nombre del app> <nombre de la migración>

Esto lanza en la terminal las sentencias sql que ejecuta el archivo de migración. Esto es para modificar el archivo de migraciones para agregar y/o eliminar las sentencias que podrían lanzar locks. 

Como ejemplo, el archivo de migración se genera así:

class Migration(migrations.Migration): 

    dependencies = [ ('<Nombre del app>', '<nombre del archivo>'), ] 

    operations = [ migrations.AddField( model_name='<Nombre del modelo>', name='<nombre del campo>', field=models.CharField(blank=True, max_length=200, null=True), ) ]

Con las modificaciones que se tendrían que agregar para las sentencias sql, quedaría así:

class Migration(migrations.Migration): dependencies = [ ('<Nombre del app>', '<nombre del archivo>'), ] operations = [ migrations.SeparateDatabaseAndState( state_operations=[ migrations.AddField( model_name='<Nombre del modelo>',name='<nombre del campo>',field=models.CharField(blank=True, max_length=200, null=True) ], database_operations=[ migrations.RunSQL(sql='SET LOCAL lock_timeout = "3s"; '), migrations.RunSQL(sql='<sentencia sql de sqlmigrate>'),migrations.RunSQL(sql='<sentencia sql de sqlmigrate>'), migrations.RunSQL(sql='<sentencia sql de sqlmigrate>'), migrations.RunSQL(sql='<sentencia sql de sqlmigrate>'), ) ]

Una sentencia interesante que siempre agregamos en Crehana es lock timeout, para cortar la ejecución de la sentencia(s) en caso ya estén tomando mucho tiempo.

Final feliz

Ya con estos cambios implementados, el problema de los locks sobre la base de datos, debido a las sentencias indicadas en el cuadro, fue solucionado. Luego de eso, han aparecido otra clase de situaciones, pero será motivo de otro post.

Si quieres dar solución a situaciones como éstas, que aparecen en un producto en constante crecimiento como Crehana, ¡tenemos posiciones abiertas para que te puedas unir al equipo!

Se vienen retos muy interesantes, ¿te atreves a ser parte?

Sé el próximo Backend Developer
Teamtailor

Inicio de Teamtailor