🗂 Tirer parti de la déduplication de BTRFS: cas concret du versionnage

| ~ 6 mins | 1182 mots

Le problème

Il m’est arrivé à plusieurs reprises de travailler sur des projets utilisant la librairie python filedepot, cette librairie est bien pratique pour gérer le stockage de fichier notamment en combinaison avec Sqlalchemy.

Un point qui ne reste néanmoins pas forcément évident à gérer par-dessus ce type de librairie est la question du versionnage. Un cas que j’ai eu l’occasion de voir est le versionnage dans Tracim ou un contenu à diverse révision qui peuvent ou non avoir un contenu fichier associé différent.

Le fonctionnement du champ UploadedFileField proposé pour Sqlalchemy par filedepot, fonctionne de sorte à considérer que le fichier est liée uniquement à une ligne de base de donnée et il peut être compliqué de tenter de partage un identifiant d’un même fichier entre divers contenus, car par exemple, la suppression d’une ligne avec le contenu 1 supprimera le fichier associé, 2 pointant vers le même contenu ne pourra plus y accéder.

Comment alors éviter que pour une modification mineure non liée au fichier, que le fichier soit dupliqué inutilement consommant ainsi de l’espace inutilement ?

Je vois alors plusieurs approches :

  1. Mettre cette intelligence de « versionnage » dans filedepot, mais cela demanderait un gros travail.
  2. Mettre cette intelligence de « versionnage » dans l’application qui utilise filedepot notamment une table association entre l’identifiant du fichier pour l’application et le champ. Ce qui réduirait un peu l’intérêt simplificateur de filedepot à mon sens.
  3. Créer une sorte de couche intermédiaire d’intelligence entre filedepot qui gère cela. Cela pourrait être aussi l’occasion de gérer d’autres features lié au traitement du fichier : indexation, génération de preview, …

Si la troisième solution me semble la plus intéressante a creusé, elle demande du temps à mettre en place.

Ayant envie de gagner un peu d’espace disponible sur mon serveur utilisant Tracim, j’ai trouvé une solution de contournement à partir du fait que btrfs supporte la fonctionnalité de déduplication.

La Déduplication Quézako ?

La déduplication est une fonctionnalité de système de fichier qui permet à un système de fichier de dire qu’un certain contenu utilisé par plusieurs fichiers est au même endroit. Ainsi, il est possible de copier via cp --reflink=always sans utiliser d’espace supplémentaire (sauf quelques meta-données). Seul certain système de fichier supporte cette fonctionnalité dont BTRFS.

À noter que cette fonctionnalité est activé par default dans cp quand c’est possible à partir de coreutils-9.0. Ainsi, si vous utilisez un simple cp dans un Gnu/Linux assez récent et utilisé un système de fichier comme btrfs, votre copie sera gérer automatiquement intelligemment sans réelle copie.

Revenons à nos moutons

Mais du coup, qu’est-ce que ça change pour nous ? Parce qu’on pourrait effectivement ajuster filedepot pour faire de la copie via reflink, mais il faudrait pour cela… Qu’il soit conscient qu’il réalise une copie. Et donc on retombe dans une problématique de développement plus ou moins complexe…

Oui … Sauf qu’il y a une astuce !

Il est possible de réaliser la déduplication a posteriori, quand on considère que c’est nécessaire grâce à un outil comme Duperemove. Ainsi, il m’est possible de récupérer de l’espace disque sans avoir à me soucier de l’optimalité ou non des solutions techniques de stockage utilisé par le logiciel utilisé.

L’autre grosse force est que cela fonctionne pour tous les fichiers sans la moindre distinction. Ainsi peu importe si la duplication est le fait d’un utilisateur qui aurait copié 2 fois le même fichier, d’un mécanisme de versionnage non intelligent, ou simplement le hasard de 2 utilisateurs qui ne se connaissent même pas qui téléverse l’exacte, même contenu, la déduplication s’effectuera.

Voici un exemple, Avec un tracim 4.5 tout neuf en conteneur et son contenu dans un système btrfs tout propre dans un fichier (grâce à ce commentaire) :

Ajout d’un 1 fichier un peu gros sur l’instance :

df -h .
Filesystem      Size  Used Avail Use% Mounted on
/dev/loop0        10G  219M  9,3G   3% /mnt/dsk

Création de 2 nouvelles révisions sans vraie modification du fichier :

Filesystem      Size  Used Avail Use% Mounted on
/dev/loop0        10G  639M  8,9G   7% /mnt/dsk

Let’s go la déduplication (en utilisant l’exemple de https://wiki.tnonline.net/w/Btrfs/Deduplication/Duperemove):

root@gnu:/mnt/dsk# chrt -i 0 duperemove -A -h -d -r -v -b128k --dedupe-options=noblock,same --lookup-extents=yes --io-threads=1 var/data/depot 
Using 128K blocks
Using hash: murmur3
Gathering file list...
Skipping small file /mnt/dsk/var/data/depot/2031c9c0-076e-11ee-9c12-0242ac110002/file
Skipping small file /mnt/dsk/var/data/depot/2031c9c0-076e-11ee-9c12-0242ac110002/metadata.json
Skipping small file /mnt/dsk/var/data/depot/2032025a-076e-11ee-9c12-0242ac110002/file
Skipping small file /mnt/dsk/var/data/depot/2032025a-076e-11ee-9c12-0242ac110002/metadata.json
Skipping small file /mnt/dsk/var/data/depot/cb00f218-076e-11ee-a461-0242ac110002/file
Skipping small file /mnt/dsk/var/data/depot/cb00f218-076e-11ee-a461-0242ac110002/metadata.json
Skipping small file /mnt/dsk/var/data/depot/cb0142b8-076e-11ee-a461-0242ac110002/file
Skipping small file /mnt/dsk/var/data/depot/cb0142b8-076e-11ee-a461-0242ac110002/metadata.json
Skipping small file /mnt/dsk/var/data/depot/e89f0f58-076e-11ee-827e-0242ac110002/metadata.json
Skipping small file /mnt/dsk/var/data/depot/0f1a60a6-076f-11ee-a200-0242ac110002/metadata.json
Skipping small file /mnt/dsk/var/data/depot/55639ca8-076f-11ee-a315-0242ac110002/metadata.json
Using 1 threads for file hashing phase
[1/3] (33.33%) csum: /mnt/dsk/var/data/depot/e89f0f58-076e-11ee-827e-0242ac110002/file
[2/3] (66.67%) csum: /mnt/dsk/var/data/depot/0f1a60a6-076f-11ee-a200-0242ac110002/file
[3/3] (100.00%) csum: /mnt/dsk/var/data/depot/55639ca8-076f-11ee-a315-0242ac110002/file
Total files:  3
Total extent hashes: 3
Loading only duplicated hashes from hashfile.
Found 3 identical extents.
Simple read and compare of file data found 1 instances of extents that might benefit from deduplication.
Showing 3 identical extents of length 209.8M with id 54f11aa1
Start		Filename
0.0	"/mnt/dsk/var/data/depot/e89f0f58-076e-11ee-827e-0242ac110002/file"
0.0	"/mnt/dsk/var/data/depot/0f1a60a6-076f-11ee-a200-0242ac110002/file"
0.0	"/mnt/dsk/var/data/depot/55639ca8-076f-11ee-a315-0242ac110002/file"
Using 1 threads for dedupe phase
[0x56351b1f30c0] (1/1) Try to dedupe extents with id 54f11aa1
[0x56351b1f30c0] Add extent for file "/mnt/dsk/var/data/depot/e89f0f58-076e-11ee-827e-0242ac110002/file" at offset 0.0 (3)
[0x56351b1f30c0] Add extent for file "/mnt/dsk/var/data/depot/0f1a60a6-076f-11ee-a200-0242ac110002/file" at offset 0.0 (4)
[0x56351b1f30c0] Add extent for file "/mnt/dsk/var/data/depot/55639ca8-076f-11ee-a315-0242ac110002/file" at offset 0.0 (5)
[0x56351b1f30c0] Dedupe 2 extents (id: 54f11aa1) with target: (0.0, 209.8M), "/mnt/dsk/var/data/depot/e89f0f58-076e-11ee-827e-0242ac110002/file"
Kernel processed data (excludes target files): 419.5M
Comparison of extent info shows a net change in shared extents of: 629.3M

Après deduplication :

df -h .
Filesystem      Size  Used Avail Use% Mounted on
/dev/loop0        10G  219M  9,3G   3% /mnt/dsk

On a ainsi gagné pas moins de 419.5M !

Autres points intéressant de Duperemove:

Aller plus loin…

La solution Duperemove se conjugue bien avec le fonctionnement de Tracim, mais comment peut-on faire autrement ? Quelles solutions les autres logiciels utilisent pour conserver un historique sans surcharger les disques ?

Nextcloud : De ce que je comprends de l’approche de nextcloud sur ce genre de problématique, elle consiste à tenter de réduire le nombre de versions sauvegardées ainsi le versionnage de nextcloud n’est à ce sens pas très fiable dans une logique d’archivage, une approche hybride à base de version par etiquette pour une gestion semi-automatique associé ou non ne sorte d’IA ou d’algorithme un peu malin pourrait faire l’affaire afin de limiter les versions stockée tout en gardant l’essentiel de l’historique.

Gestion de version : L’approche des logiciels de gestion de versions comme git est intéressante et est clairement optimale en espace de stockage pour du fichier texte. Elle permet réellement de l’archivage, de plus le principe de pouvoir associer un texte à une modification commit, permet de s’y retrouvé facilement. Néanmoins l’approche ne se conjugue pas très bien avec du stockage de fichier binaire.

Voili Voilà ! 😀