# Migration de SQLite vers MySQL

## Pourquoi

Sur le mutualisé OVH, le fichier SQLite vit sur un stockage réseau qui supporte mal
les écritures concurrentes. La base s'y est corrompue plusieurs fois le 5 août 2026,
et le site est resté indisponible une partie de la matinée. Déplacer le cache et les
sessions hors de la base a fortement réduit le rythme des écritures, mais pas
supprimé la cause : le journal des accès écrit encore à chaque visite, et son index
s'est de nouveau abîmé le même jour. MySQL supprime cette classe de panne.

## Ce qui a déjà été fait et vérifié

Tout ce qui suit a été exécuté et contrôlé en local, contre un vrai serveur MariaDB.

- **Les 39 migrations passent sur MySQL.** Deux index portaient un nom déduit de plus
  de 64 caractères, que MySQL refuse là où SQLite l'acceptait : ils sont désormais
  nommés explicitement. Un test verrouille ce point pour les migrations à venir.
- **Les 5 527 lignes se recopient**, 42 tables, tous les comptages identiques.
- **Les 328 tests passent sur les deux moteurs**, SQLite comme MySQL.
- **Les 21 pages publiques répondent** sur la base migrée, recherche comprise.
- Une différence silencieuse a été corrigée : `LENGTH()` compte les caractères en
  SQLite et les **octets** en MySQL. Sur un corpus français, tout classement par
  longueur, dont le tri de pertinence de la recherche, aurait divergé sans lever la
  moindre erreur. `App\Support\SqlPortable::longueur()` rétablit le compte exact.

## Ce qui reste à faire, et qui demande votre main

La création de la base exige un mot de passe, que je ne dois pas manipuler.

### 1. Créer la base dans le panel OVH

Espace client → Hébergements → `baumberger.me` → onglet **Bases de données** →
*Créer une base de données*.

- Moteur : **MySQL** (la version la plus récente proposée)
- Nom : `smefr` par exemple
- Utilisateur et mot de passe : à votre main

Notez le **serveur** indiqué par OVH (du type `smefr.mysql.db`), le nom de la base,
l'utilisateur et le mot de passe.

### 2. Renseigner le `.env` de production

En SSH, dans `~/www/sauvermonentreprise/.env`, **sans supprimer les lignes
existantes**, ajoutez ou modifiez :

```
DB_CONNECTION=mysql
DB_HOST=<serveur indiqué par OVH>
DB_PORT=3306
DB_DATABASE=<nom de la base>
DB_USERNAME=<utilisateur>
DB_PASSWORD=<mot de passe>

# Chemin figé du fichier SQLite, qui reste la source de la recopie et le
# point de retour en arrière. DB_DATABASE ne peut pas servir aux deux.
DB_SQLITE_SOURCE=/homez.608/baumberg/www/sauvermonentreprise/database/database.sqlite
```

`SESSION_DRIVER` et `CACHE_STORE` restent sur `file` : les remettre en base
réintroduirait des écritures que rien n'oblige à faire là.

**Ne lancez jamais `php artisan config:cache` sur ce serveur** : le compte est monté
à deux endroits et le cache fige des chemins absolus qui deviennent invalides côté
web. C'est ce qui a mis le site à terre le 5 août.

### 3. Créer le schéma, puis recopier

```bash
cd ~/www/sauvermonentreprise
php artisan migrate --database=mysql --force
php artisan base:migrer-vers-mysql
```

La commande recopie table par table puis **compare les comptages** et sort en erreur
au moindre écart. Elle vide chaque table cible avant de la remplir : on peut la
relancer autant de fois que nécessaire sans empiler de doublons, ce qui compte quand
le réseau lâche au milieu, comme il le fait régulièrement ici.

### 4. Vérifier avant de considérer la bascule faite

```bash
php artisan base:migrer-vers-mysql --verifier   # comparaison seule, rien n'est écrit
php artisan sauvegarde:base                     # la sauvegarde doit passer au vert
```

Puis, depuis n'importe où :

```bash
for u in / /glossaire /glossaire/cessation-des-paiements /code-commerce/livre-6/l631-1 /recherche?q=creance; do
  curl -s -o /dev/null -w "$u %{http_code}\n" "https://sauvermonentreprise.fr$u"
done
```

### 5. Retour en arrière, si besoin

Le fichier SQLite n'est jamais modifié par la migration. Pour revenir en arrière, il
suffit de remettre `DB_CONNECTION=sqlite` dans le `.env`. Gardez ce fichier au moins
quinze jours après la bascule, le temps de vérifier qu'aucun écran n'a de surprise.

## La sauvegarde suit toute seule

`php artisan sauvegarde:base` reconnaît le moteur en place et n'a rien à changer au
moment de la bascule : `VACUUM INTO` tant qu'on est sur SQLite, `mysqldump` en
transaction unique dès que MySQL prend la main. La tâche planifiée OVH reste la même.

Vérifié en local contre une base MySQL réellement peuplée : dump produit, **restauré
dans une base neuve**, et **sommes de contrôle identiques sur les 49 tables**, donc un
aller-retour exact.

Deux points à connaître :

- Le mot de passe passe par un fichier temporaire en 0600, jamais par la ligne de
  commande, où il serait lisible par les autres comptes du serveur mutualisé le temps
  du dump.
- Le contrôle avant conservation est **plus faible que côté SQLite**. Là-bas, on relit
  vraiment le fichier produit. Ici, on ne peut pas restaurer pour vérifier : un compte
  mutualisé n'a pas le droit de créer la base d'essai qu'il faudrait. On contrôle donc
  la ligne de fin que `mysqldump` n'écrit qu'en cas de succès, et le nombre de tables.
  Un dump tronqué échoue aux deux, mais une corruption silencieuse à l'intérieur d'une
  ligne ne serait pas vue. Une restauration d'essai à la main, une fois par trimestre,
  reste la seule vraie garantie.

## Après la bascule, à ne pas oublier

- Supprimer la connexion `sqlite_source` de `config/database.php` et le fichier
  SQLite une fois la période de retour en arrière refermée.
- La commande `base:migrer-vers-mysql` devient inutile : elle peut être supprimée.
- Pendant quelques jours, les deux formats d'archives cohabiteront dans le dossier de
  sauvegarde. C'est prévu : la purge reconnaît les deux et continue de n'en garder
  que quatorze.

## Ce qui change pour les visiteurs, en mieux

MySQL compare **sans tenir compte des accents**, là où SQLite les distingue. Une
recherche sur « creance » ne remontait rien et remontera désormais les 38 fiches qui
parlent de créance. En français, où l'on tape rarement les accents dans une barre de
recherche, c'est un gain réel et gratuit.
