Skip to content

1024pix/pix-db-replication

Repository files navigation

Réplication des données de production de Pix

Ce projet a pour but de répliquer tout ou une partie des données d'une base de données postgreSQL:

  • via une sauvegarde / restauration
  • d'une manière incrémentale

Des enrichissements peuvent être fait à la fin de l'import.

Les données de pix-editor/lcms sont également répliquées.

A la fin du processus, nous notifions par webhooks des systèmes externes.

Ces étapes se font dans l'ordre et sont executées sequentiellement dans des jobs bull.

Pré-requis

Ce projet est prévu pour être déployé sur une application Scalingo associée à une base de donnée PostgreSQL.

Des variables d'environnement sont mises en place afin de garder un seul repository partagé par les applications.

Utilisation sur Scalingo

Installation

Alimenter les variables d'environnement documentées dans le fichier sample.env

Pour satisfaire les contraintes de déploiement Scalingo, le Procfile déclare un conteneur de type web qui démarre un serveur Web "vide".

Une fois l'application créée et déployée une première fois, il faut :

  • mettre à 0 le nombre de conteneurs de type web
  • mettre à 1 le nombre de conteneurs de type background

Résolution de problèmes

Analyse de la cause

Connectez-vous à bull

scalingo --region osc-secnum-fr1 --app pix-datawarehouse-production run bull-repl
connect "Replication queue"
#connect "Incremental replication queue"
#connect "Learning Content replication queue"
failed
stats

Alternativement, se connecter à redis

scalingo --region osc-secnum-fr1 --app pix-datawarehouse-production redis-console
KEYS *
GET <KEY>

Relance

Une fois que la cause du problème a été corrigée:

  • s'il est important que les données soient disponibles le jour même, il est possible de lancer le traitement manuellement
  • sinon ne rien faire, le traitement sera exécuté la nuit prochaine

🧨 Le traitement peut avoir des impacts sur les temps de réponses des applications, car il utilise les ressources BDD. Monitorez le % CPU BDD et le temps de réponse des requêtes HTTP pour arrêter le traitement si besoin. Pour cela, stopper le conteneur background.

Si la sauvegarde/restauration/enrichissement a échoué :

npm run restart:full-replication

Si la réplication incrémentale a échoué :

npm run restart:incremental-replication

Si la réplication de LCMS a échoué :

npm run restart:learning-content-replication

Si les notifications de fin ont échoué :

npm run restart:notification

Développement et exécution en local

Installation

Installez le dépôt

git clone git@github.com:1024pix/pix-db-replication.git && cd pix-db-replication
nvm use
npm run preinstall

Démarrer le serveur de BDD

docker-compose up --detach

Créer et charger les BDD

npm run local:setup-databases

Vérifiez que la source et la cible sont accessibles et qu'elles contiennent des données

psql postgres://source_user@localhost/source_database
psql postgres://target_user@localhost/target_database

Paramétrage

Créer un fichier .env à partir du fichier sample.env

Exécution

Réplication complète

Modifier le .env

DATABASE_URL=postgresql://target_user@localhost/target_database
BACKUP_MODE={}
RESTORE_FK_CONSTRAINTS=true

Lancer la réplication

node -e "require('./src/steps/backup-restore').run(require ('./src/config/extract-configuration-from-environment')())"

Au bout de 5 minutes, vous devez obtenir le message

{"msg":"enrichment.add - Ended","time":"2021-01-08T08:26:13.000Z","v":0}
{"msg":"Import and enrichment done","time":"2021-01-08T08:26:13.000Z","v":0}

Pensez à recréer le backup sur le filesystem local, supprimé par la restauration

git checkout data/source.pgsql

Réplication incrémentale

Initialiser l'environnement

Supprimer les FK sortantes des tables à copier

psql postgresql://target_user@localhost/target_database
ALTER TABLE answers DROP CONSTRAINT "answers_assessmentid_foreign";
ALTER TABLE "knowledge-elements" DROP CONSTRAINT "knowledge_elements_answerid_foreign";
ALTER TABLE "knowledge-elements" DROP CONSTRAINT "knowledge_elements_assessmentid_foreign";
ALTER TABLE "knowledge-elements" DROP CONSTRAINT "knowledge_elements_userid_foreign";
Paramétrer

Modifier le .env

SOURCE_DATABASE_URL=postgresql://source_user@localhost/source_database
TARGET_DATABASE_URL=postgresql://target_user@localhost/target_database
BACKUP_MODE='{"knowledge-elements":"incremental", "knowledge-element-snapshots":"incremental","answers":"incremental"}'
RESTORE_FK_CONSTRAINTS=false

Ordonnanceur

Il est possible de faire tourner l'ordonnanceur en local.

Mettez la planification à toutes les minutes dans le fichier .env

SCHEDULE=* * * * *

Démarrez l'ordonnanceur

node ./src/main.js | ./node_modules/.bin/bunyan

Vérifiez que le traitement se lance

[2021-06-11T14:11:01.944Z]  INFO: pix-db-replication/83294 on OCTO-TOPI: Starting job in Learning Content replication queue: 10

Vérifiez que bull a pu joindre redis

redis-cli
keys bull:*

Connectez-vous au CLI Bull pour suivre l'avancement.

Pour se connecter via Scalingo, utiliser le connect avec les 4 options ci-dessous.

connect [options] <queue>
    -h, --host <host>      Redis host for connection
    -p, --port <port>      Redis port for connection
    -d, --db <db>          Redis db for connection
    --password <password>  Redis password for connection

Puis saisir le nom de la queue.

Pour la réplication par dump

bull-repl
connect "Replication queue"
stats

Pour la réplication incrémentale

bull-repl
connect "Incremental replication queue"
stats

Pour l'import LCMS

bull-repl
connect "Learning Content replication queue"
stats

Vous obtenez, par exemple

  • en cours d'exécution d'un traitement
  • après 14 exécutions avec succès
┌───────────┬────────┐
│  (index)  │ Values │
├───────────┼────────┤
│  waiting  │   0    │
│  active   │   1    │
│ completed │   14   │
│  failed   │   0    │
│  delayed  │   0    │
│  paused   │   0    │
└───────────┴────────┘

Tests

Une partie du code n'est pas testable de manière automatisée.

Il est donc important d'effectuer un test manuel en RA avant de merger une PR, même si la CI passe.

Manuels

Local

Récupérer les données de LCMS :

node -e "require('./src/steps/learning-content').run(require ('./src/config/extract-configuration-from-environment')())"

RA Scalingo

  • Faire un backup des données d'une application Scalingo hors osc-secnum-fr1 pour éviter les considérations de sécurité des données

  • Vérifier les données présentes dans la BDD à exporter (exemple pour les données d'une review app)

scalingo -a pix-api-review-prxxx pgsql-console
  • Lancer un backup (ou ne rien faire, le dernier est utilisé par défaut)

  • Déterminer le nom de l'application de RA Scalingo de db-replication

NOM_APPLICATION=pix-datawarehouse-pr<NUMERO-PR>
  • Lancer le process de création et d'import du backup sur cette RA
scalingo run --region osc-fr1 --app $NOM_APPLICATION npm run restart:full-replication
  • Vérifier le résultat dans la bdd répliquée
scalingo -a $NOM_APPLICATION pgsql-console
SELECT id, email FROM "users" LIMIT 5;

Automatisés

Local

Intégration

Déroulement :

  • une BDD est créée en local sur l'URL $TEST_POSTGRES_URL (par défaut : postgres://postgres@localhost), instance pix_replication_test
  • la table test_table est créée et chargée avec 100 000 enregistrements (1 colonne, PK)
  • un export est effectué par pg_dump --c dans un dossier temporaire
  • la restauration à tester est appelée depuis steps.js/restoreBackup
  • les assertions SQL sont effectuées par un runSql, un wrapper autour de psql

le dump Scalingo est créé avec des options pg_dump différentes

  • Se connecter à la BDD de test :
psql postgres://postgres@localhost/pix_replication_test

CI

La CI exécute l'intégralité des tests (unitaire et intégration).

Parser les logs

Datadog

Les logs en production sont parsés sur Datadog, et l'ensemble des éléments remontent dans des logs structurés. Il est ainsi possible de filtrer sur les status des logs pour obtenir les informations désirées.

A la main

L'analyse de ce qui prend du temps est complexe sur les logs brutes s'il y a :

  • plusieurs jobs de restauration (variable d'environnementPG_RESTORE_JOBS)
  • beaucoup de tables.

Pour faciliter l'analyse, utilisez le script d'analyse de log.

Étapes :

  • récupérer les logs
scalingo --region osc-secnum-fr1 --app <NOM_APPLICATION> logs --lines 100000 > /tmp/logs.txt
  • déterminer la date d'exécution au format YYYY-MM-DDDD, par exemple : 2020-10-13

  • exécuter

node utils/parse-replication-logs.js ./logs.txt <DATE_EXECUTION>

Exemples de résultat sur pix-datawarehouse-production le 22/10/2020

node utils/parse-replication-logs.js ./logs.txt 2020-10-22
Durée de récupération du backup: 1h 27min 42s
Durée de réplication: 8h 51min 17s
Durée de l'enrichissement: 1h 39min 42s
Durée totale: 11h 58min 41s
FK CONSTRAINT total duration : 7h 57min 25s
	FK CONSTRAINT schooling-registrations students_organizationid_foreign : 2h 11min 4s
	FK CONSTRAINT competence-evaluations competence_evaluations_assessmentid_foreign : 2h 10min 41s
	FK CONSTRAINT knowledge-elements knowledge_elements_answerid_foreign : 0h 58min 52s
CONSTRAINT total duration : 2h 49min 60s
	CONSTRAINT answers answers_pkey : 1h 26min 32s
	CONSTRAINT knowledge-elements knowledge-elements_pkey : 1h 13min 36s
	CONSTRAINT knowledge-element-snapshots knowledge-element-snapshots_pkey : 0h 2min 59s
INDEX total duration : 10h 21min 34s
	INDEX knowledge-elements_assessmentId_idx : 4h 16min 55s
	INDEX knowledge_elements_userid_index : 3h 49min 46s
	INDEX answers_assessmentid_index : 2h 7min 20s
SEQUENCE total duration : 0h 0min 28s
	SEQUENCE SET assessments_id_seq : 0h 0min 4s
	SEQUENCE SET user-orga-settings_id_seq : 0h 0min 3s
	SEQUENCE SET assessment-results_id_seq : 0h 0min 3s
TABLE DATA total duration : 4h 8min 7s
	TABLE DATA knowledge-element-snapshots : 1h 51min 60s
	TABLE DATA knowledge-elements : 1h 11min 25s
	TABLE DATA answers : 0h 46min 55s

S'il y a eu :

  • plusieurs exécutions le même jour
  • une exécution incomplète (pas de message Start restore ou Restore done)

Alors vous obtiendrez le message suivant TypeError: Cannot read property '0' of null

Duplication des schémas uniquement

Afin de pouvoir alimenter une base de données contenant uniquement le schéma de BDD, notamment pour des besoins de Data Catalog, le script db-schema-exporter.sh peut être utilisé.

En définissant les variables suivantes :

DB_SCHEMA_EXPORTER_ENABLED=true
DB_SCHEMA_EXPORTER_DATABASE_TARGET=postgres://user:password@database:port/db

Tous les jours à midi, le schéma de la base actuelle sera dupliqué sur la BDD distante.