Système de fichiers virtuel en écriture seule pour stocker des fichiers dans des archives

8

J'ai un processus parallèle embarrassant qui crée une énorme quantité de fichiers presque (mais pas complètement) identiques. Existe-t-il un moyen d'archiver les fichiers "à la volée", afin que les données ne consomment pas plus d'espace que nécessaire?

Le processus lui-même accepte les paramètres de ligne de commande et imprime le nom de chaque fichier créé sur stdout. Je l'invoque avec parallel --gnuqui s'occupe de distribuer les entrées (qui proviennent d'un autre processus) et de collecter les sorties:

arg_generating_process | parallel --gnu my_process | magic_otf_compressor

EXEMPLE SIMPLE pour la première partie du tuyau dans bash:

for ((f = 0; $f < 100000; f++)); do touch $f; echo $f; done

À quoi pourrait magic_otf_compressorressembler? Il est censé traiter chaque ligne d'entrée comme un nom de fichier, copier chaque fichier dans une .tararchive compressée (la même archive pour tous les fichiers traités!), Puis la supprimer. (En fait, il devrait suffire d'imprimer le nom de chaque fichier traité, un autre | parallel --gnu rmpourrait se charger de supprimer les fichiers.)

Existe-t-il un tel outil? Je ne pense pas à compresser chaque fichier individuellement, cela gaspillerait beaucoup trop d'espace. J'ai examiné archivemount(gardera le système de fichiers en mémoire -> impossible, mes fichiers sont trop gros et trop nombreux) et avfs( je n'ai pas pu le faire fonctionner avec FUSE). Qu'est-ce que j'ai raté?

Je ne suis qu'à un pas de pirater un tel outil moi-même, mais quelqu'un doit l'avoir fait avant ...

EDIT : Essentiellement, je pense que je cherche un frontal stdin pour libtar(par opposition au frontal de ligne de commande tarqui lit les arguments de, eh bien, la ligne de commande).

krlmlr
la source
Avez-vous envisagé d'écrire des fichiers dans un format doté d'une compression native? Par exemple, hdf5 peut être compressé car il est écrit avec une compression gzip ou szip. Hdf5 prend également en charge MPI afin qu'il fonctionne bien avec ces problèmes parallèlement embarrassants.
casey
2
Si vous voulez la compression et la déduplication, zfs vous vient à l'esprit.
Stéphane Chazelas
@casey: C'est du HTML, mais je suppose que je pourrais utiliser un conteneur HDF5.? Je n'ai pas encore considéré cela.
krlmlr
@StephaneChazelas: Cela peut-il être mis en œuvre dans l'espace utilisateur?
krlmlr

Réponses:

1

Il semble tarvouloir connaître tous les noms de fichiers à l'avance. C'est donc moins à la volée et plus après la volée. cpione semble pas avoir ce problème:

| cpio -vo 2>&1 > >(gzip > /tmp/arc.cpio.gz) | parallel rm
Ole Tange
la source
Merci. Donc, même RTFM ne suffit pas ;-) J'ai même regardé tarle code de pour voir qu'il y a une fonction qui retourne le prochain nom de fichier à traiter, ce qui m'a fait relire la documentation. - Donc, stdoutest dirigé vers le gzipprocessus via la substitution de processus, et stderrest redirigé vers stdoutlequel est traité par la prochaine étape dans le tuyau?
krlmlr
Ouaip. La construction>> () ne fonctionne pas dans tous les shells, mais elle fonctionne dans Bash.
Ole Tange
Je peux confirmer que tarlit la liste des fichiers en premier, en utilisant l'exemple simple que j'ai ajouté à ma question. Cependant, en relisant tarle code source , il me semble qu'il devrait lire la liste des fichiers "à la volée" s'il ne crée pas une archive incrémentielle. Malheureusement, j'ai des erreurs de compilation à tarpartir de la source ... :-(
krlmlr
Je n'ai pas trouvé de moyen de supprimer la dernière ligne dans la sortie de cpio, autre que grep -v 'blocks$'. ( head -n -1utilise un très grand tampon ...) Rend cette solution un peu un hack, mais tant
pis
@krlmlr qui est étrange: Mon head -n -1utilise uniquement 16 Mo lorsqu'il est exécuté sur quelques Go de données. Vous pouvez toujours utiliser perl: perl -ne 'print $ last; $ last = $ _'
Ole Tange
7

Un cas classique de RTFM (tout ça!) . L' -Toption GNU tarlira les fichiers à archiver à partir d'un autre fichier (dans mon cas /dev/stdin, vous pouvez également utiliser -), et il y a même une --remove-filesoption:

alias magic_otf_compressor='tar --create -T - --remove-files -O | pixz'

(en utilisant la version parallèle de xzpour la compression, mais vous pouvez utiliser votre compresseur préféré à la place). A utiliser comme:

arg_generating_process |
  parallel --gnu my_process |
  magic_otf_compressor > file.tar.xz

EDIT : Comme le souligne Ole, tarsemble lire la liste complète des fichiers avec l' -Toption pour une raison quelconque. Le test suivant le confirme:

for ((f = 0; $f < 1000; f++)); do
    touch $f; echo $f;
done | tar -c -f otf.tar -T - -v

Il y a un délai d'une seconde sur mon système avant que tous les fichiers soient imprimés en même temps; en revanche, si la tarcommande est remplacée par cat, tous les fichiers sont imprimés lors de leur création. J'ai déposé une demande d'assistance auprès des utilisateurs de tar, voyons.

EDIT ^ 2 : le plus récent tarde la source corrige cela. Ce n'est pas encore dans Ubuntu 13.10, mais pourrait être inclus avec 14.04.

krlmlr
la source
1

D'une certaine manière, cela ne semble pas être un bon travail pour un compresseur solide (archiveurs sur bande + compression). L'insertion de fichiers l'un après l'autre ressemble à un travail zipou à un autre format qui permet un accès aléatoire aux fichiers dans l'archive et une insertion incrémentielle.

Le fait que les fichiers soient similaires n'aidera pas beaucoup dans les deux cas. Dans zip, les fichiers sont compressés séparément et dans les compresseurs solides, il y a généralement une fenêtre dans laquelle la compression a lieu.

Si les fichiers sont basés sur du texte, vous pouvez stocker des différences par rapport à un seul fichier de référence. Pour le binaire, c'est un peu plus délicat mais cela peut être fait.

Il existe également un moyen formel (non pas en écriture seule, mais des systèmes de fichiers appropriés). Par exemple, les systèmes de fichiers ZFS et BTRFS offrent une compression transparente. Vous pouvez également utiliser ce http://developer.berlios.de/projects/fusecompress

orion
la source
Mes fichiers font environ 100k chacun. Ne serait-ce pas suffisant pour permettre au compresseur d'utiliser une fenêtre de, disons, 1M? xzsemble fonctionner avec une taille de dictionnaire par défaut de 8M (au niveau de compression par défaut -6), ce qui semble être suffisant pour mon cas d'utilisation. - Les différences avec un fichier de référence sont agréables, mais nécessitent de construire d'abord un fichier de référence. Un système de fichiers compressé détecterait-il des fichiers dont le contenu est presque identique?
krlmlr
La compression des systèmes de fichiers ne compresse pas entre les fichiers (ni le zip), mais btrfsa une copie sur écriture, donc si vous copiez un fichier et modifiez une partie de celui-ci, il enregistre uniquement les parties que vous avez modifiées. Si vous ne créez pas de fichiers de cette façon, il existe supposément des outils de déduplication , mais ce btrfsn'est pas encore un système de fichiers mature et stable et la déduplication en est aux premiers stades de développement. Mais maintenant j'y pense, qu'en est-il de lessfs.com/wordpress
orion
J'obtiens des taux de compression impressionnants avec un compresseur solide pour mon cas d'utilisation, mais, comme vous l'avez souligné, je suppose que les résultats seraient pires si les fichiers étaient plus grands que la taille du dictionnaire.
krlmlr
0

Cela peut ne pas sembler évident, mais je parie que ce squashfsserait parfait pour cela - et il est même implémenté dans le noyau. Depuis la version 4.1 squashfspeut gérer les pseudo-fichiers comme spécifié sur la mksquashligne de commande ou via un shell-script et mksquashfsgénérera les fichiers lors de la création de l'archive.

Il peut gérer des tuyaux - par exemple, vous pouvez capturer un autre processus stdoutdans une archive squash montable - même fifos - c'est plutôt cool. Dans votre cas, si vous pouviez travailler la logistique de script de la tuyauterie de sortie de votre processus à travers elle, vous pouvez envelopper votre processus entièrement dans mksquashfset le vent avec une seule archive. Voici un peu de la readmefaçon dont cela fonctionne et il y en a plus ici :

Mksquashfs 4.1 ajoute la prise en charge des "pseudo fichiers dynamiques" et une opération de modification. Les pseudo-fichiers dynamiques permettent la création dynamique de fichiers lors de l'exécution de Mksquashfs, leur contenu étant le résultat de l'exécution d'une commande ou d'un morceau de script shell. L'opération de modification permet de modifier le mode / uid / gid d'un fichier existant dans le système de fichiers source.

Création d'exemples de fichiers dynamiques

Créez un fichier "dmesg" contenant la sortie de dmesg.

    dmesg f 444 root root dmesg

Créez un fichier RELEASE contenant le nom de la version, la date, l'hôte de génération et un numéro de version incrémentiel. La version incrémentielle est un effet secondaire de l'exécution du script shell, et garantit que chaque fois que Mksquashfs est exécuté, un nouveau numéro de version est utilisé sans nécessiter d'autre script shell.

    RELEASE f 444 root root \
        if [ ! -e /tmp/ver ]; then \
        echo 0 > /tmp/ver; \
        fi; \
        ver=`cat /tmp/ver`; \
            ver=$((ver +1)); \
            echo $ver > /tmp/ver; \
            echo -n "release x.x"; \
            echo "-dev #"$ver `date` "Build host" `hostname`

Copiez 10K de l'appareil / dev / sda1 dans l'entrée de fichier. Ordinairement, Mksquashfs étant donné un périphérique, un fifo ou un socket nommé placera ce fichier spécial dans le système de fichiers Squashfs, cela permet à l'entrée de ces fichiers spéciaux d'être capturée et placée dans le système de fichiers Squashfs.

        input f 444 root root dd if=/dev/sda1 bs=1024 count=10
mikeserv
la source
Comment cela fonctionnerait-il dans l'infrastructure que j'ai décrite?
krlmlr
Vous devriez obtenir que votre processus écrive ses noms de fichiers dans le script d'invocation de mksquash et qu'il continue à les ajouter pendant son exécution. Ou même dans un tmpfs que squash lira et compressera pendant son exécution. Ou, comme un autre l'a mentionné, par quelque chose d'autre - appelez cpio comme dans l'exemple dd ci-dessus, mais avec cpio utilisez peut-être sa fonction de copie. Dans tous les cas, il lit, crée et compresse à la volée.
mikeserv
Sera-t-il compressé entre les fichiers?
krlmlr
Il comprime son entrée dans un flux - tous les inodes, tout cela. Je l'ai utilisé avec dd et c'était plutôt cool - j'utilise toujours la taille de bloc de 1 Mo et la compression xz.
mikeserv
Cela ressemble à une option, mais d'après votre réponse, je ne vois pas comment créer, disons, une archive squashfs avec un répertoire testet un fichier filedans ce répertoire. Pourriez-vous s'il vous plaît fournir un bref exemple?
krlmlr