SCALE 21x
Julien RIOU
March 15, 2024
Data definition language (DDL)
ALTER TABLE customer
ADD COLUMN archived_at TIMESTAMP WITH TIME ZONE;
psql database
The DBA forgot on which host the database is
The DBA misunderstood what developer really wanted
The DBA executed the query in Europe but forgot Canada
The DBA forgot to create a transaction and one query failed in the middle
The DBA started a transaction but forgot to commit
The DBA created a query that runs for too long and locks production
The DBA forgot to use SET ROLE
and permissions are now broken
Let’s write migrations in a ticketing system
SQL schema migration tool for Go
Developers create code:
v0001.sql
-- +migrate Up
create table x (...);
-- +migrate Down
drop table x;
notransaction
hint
CREATE INDEX CONCURRENTLY
DBAs deploy with a command:
sql-migrate-wrapper
SET ROLE
# sql-migrate-wrapper status -config /etc/sqlmigrate/test.json
INFO[2024-02-13T10:28:47Z] Processing 'test' database
INFO[2024-02-13T10:28:47Z] Processing normal migration path
+-----------+--------------------------------------+
| MIGRATION | APPLIED |
+-----------+--------------------------------------+
| v0001.sql | 2022-03-03 15:33:39.709067 +0100 CET |
| v0002.sql | 2022-11-30 12:43:34.947511 +0100 CET |
| v0003.sql | 2022-11-30 12:53:27.437182 +0100 CET |
| v0004.sql | 2023-01-24 17:20:22.330472 +0100 CET |
| v0005.sql | 2023-05-17 12:32:39.87181 +0200 CEST |
+-----------+--------------------------------------+
CDS is an Enterprise-Grade Continuous Delivery & DevOps Automation Open Source Platform.
lock_timeout (2000)
Abort any statement that waits longer than the specified amount of time while attempting to acquire a lock on a table, index, row, or other database object.
Where should I connect to run the migrations?
In a database of databases:
$ curl https://<api>/database?name=test
{
"code": 200,
"data": [
{
"schemarepository": "<uuid>",
"name": "test",
"id": "<uuid>",
"cluster": "<uuid>"
}
]
}
server_type
postgresql
, mysql
, filer
, …role
node
, lb
, backup
, …cluster
identifierprimary
, replica
Special file in the repository meta.json
{
"databases": {
"US": [
"test"
]
}
}
How to apply migrations safely?
Ansible is a radically simple IT automation system.
- name: Check arguments
hosts: all
run_once: true
delegate_to: localhost
tasks:
- name: Check variable schema_url # fail fast
- name: Check variable database_name # fail fast
- name: Update database to the latest schema migration
hosts: "{{ database_name }}:&subrole_primary"
tasks:
- name: Create sql-migrate directories
- name: Create sql-migrate configuration file
- name: Clone schema
- name: Run migrations
- name: Create sql-migrate directories
ansible.builtin.file:
path: "{{ item }}"
state: directory
loop:
- /etc/sqlmigrate
- /var/lib/sqlmigrate
- name: Create sql-migrate configuration file
ansible.builtin.template:
src: sqlmigrate/database.yml.j2
dest: "/etc/sqlmigrate/{{ database_name }}.yml"
- name: Clone schema repository
ansible.builtin.git:
repo: "{{ schema_url }}"
dest: "/var/lib/sqlmigrate/{{ database_name }}"
version: "{{ branch|default('master') }}" # branch or tag
force: true
environment:
TMPDIR: /run
- name: Run migrations
ansible.builtin.command:
cmd: sql-migrate-wrapper up -config /etc/sqlmigrate/{{ database_name }}.json
$ ansible-playbook \
--extra-vars \
'{"database_name": "test",
"branch": "master",
"schema_url": "ssh://git@hostname:port/project/repository.git"
}' \
database-primary-schema-update.yml
Ansible Wrapper
[ssh_connection]
pipelining = True
private_key_file = ~/.ssh/id_ed25519
ssh_executable = /usr/share/ansible/plugins/bastion/sshwrapper.py
sftp_executable = /usr/share/ansible/plugins/bastion/sftpbastion.sh
transfer_method = sftp
retries = 3
AWX provides a web-based user interface, REST API, and task engine built on top of Ansible.
awx -f human job_templates launch --monitor --extra_vars \
'{"database_name": "test",
"branch": "master",
"schema_url": "ssh://git@hostname:port/project/repository.git"
}' \
database-primary-schema-update
How to deploy without human intervention?
Use a declarative model:
Event-Driven Ansible