Sistema de archivo virtual de solo escritura para almacenar archivos en el archivo

8

Tengo un proceso embarazosamente paralelo que crea una gran cantidad de archivos casi (pero no completamente) idénticos. ¿Hay alguna manera de archivar los archivos "sobre la marcha", para que los datos no consuman más espacio del necesario?

El proceso en sí mismo acepta parámetros de línea de comandos e imprime el nombre de cada archivo creado para stdout. Lo invoco con el parallel --gnuque se encarga de distribuir la entrada (que proviene de otro proceso) y recopilar la salida:

arg_generating_process | parallel --gnu my_process | magic_otf_compressor

EJEMPLO SIMPLE para la primera parte de la tubería en bash:

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

¿Cómo podría magic_otf_compressorverse? Se supone que debe tratar cada línea de entrada como nombre de archivo, copiar cada archivo a un .tararchivo comprimido (¡el mismo archivo para todos los archivos procesados!) Y luego eliminarlo. (En realidad, debería ser suficiente para imprimir el nombre de cada archivo procesado, otro | parallel --gnu rmpodría encargarse de eliminar los archivos).

¿Existe alguna herramienta de este tipo? No estoy considerando comprimir cada archivo individualmente, esto desperdiciaría demasiado espacio. He investigado archivemount(mantendrá el sistema de archivos en la memoria -> imposible, mis archivos son demasiado grandes y demasiados) y avfs(no pude hacer que funcione junto con FUSE). ¿Qué me he perdido?

Estoy a un paso de hackear una herramienta así, pero alguien debe haberlo hecho antes ...

EDITAR : Básicamente creo que estoy buscando un front-end stdin para libtar(a diferencia del front-end de la línea de comandos tarque lee argumentos de, bueno, la línea de comandos).

krlmlr
fuente
¿Has considerado escribir archivos en un formato que tenga compresión nativa? Por ejemplo, hdf5 se puede comprimir ya que se escriben con compresión gzip o szip. Hdf5 también es compatible con MPI, por lo que funciona bien con esos problemas embarazosamente paralelos.
Casey
2
Si desea compresión y deduplicación, zfs viene a la mente.
Stéphane Chazelas
@casey: Es HTML, pero supongo que podría usar un contenedor HDF5. No he considerado esto todavía.
krlmlr
@StephaneChazelas: ¿Se puede implementar esto en el área de usuarios?
krlmlr

Respuestas:

1

Parece que tarquiere saber todos los nombres de archivo por adelantado. Entonces es menos sobre la marcha y más después de la marcha. cpiono parece tener ese problema:

| cpio -vo 2>&1 > >(gzip > /tmp/arc.cpio.gz) | parallel rm
Ole Tange
fuente
Gracias. Entonces, incluso RTFM no es suficiente ;-) Incluso miré en tarel código para ver que hay una función que devuelve el siguiente nombre de archivo para ser procesado, lo que me hizo leer la documentación nuevamente. - Entonces, ¿ stdoutse dirige al gzipproceso a través de la sustitución del proceso y stderrse redirige a stdoutcuál se procesa en el siguiente paso en la tubería?
krlmlr
Sip. La construcción>> () no funciona en todos los shells, pero funciona en Bash.
Ole Tange
Puedo confirmar que tarlee la lista de archivos primero, usando el ejemplo simple que agregué a mi pregunta. Sin embargo, al leer tarel código fuente nuevamente, me parece que debería leer la lista de archivos "sobre la marcha" si no se crea un archivo incremental. Desafortunadamente, tengo errores al compilar tardesde la fuente ... :-(
krlmlr
No he encontrado una manera de suprimir la línea final en la salida de cpio, aparte de grep -v 'blocks$'. ( head -n -1usa un búfer muy grande ...) Hace que esta solución sea un poco hack, pero no importa ;-)
krlmlr
@krlmlr que es extraño: mi head -n -1solo usa 16 MB cuando se ejecuta con unos pocos GB de datos. Siempre puede usar perl: perl -ne 'print $ last; $ last = $ _'
Ole Tange
7

Un caso clásico de RTFM (¡todo!) . La -Topción de GNU tarleerá los archivos que se archivarán desde otro archivo (en mi caso, /dev/stdintambién puede usar -), e incluso hay una --remove-filesopción:

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

(usando la versión paralela de xzpara la compresión, pero puede usar su compresor preferido en su lugar). Para ser utilizado como:

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

EDITAR : Como señala Ole, tarparece leer la lista completa de archivos con la -Topción por alguna razón. La siguiente prueba confirma esto:

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

Hay un segundo retraso en mi sistema antes de que todos los archivos se impriman a la vez; en cambio, si el tarcomando se reemplaza por cat, todos los archivos se imprimen a medida que se crean. He presentado una solicitud de soporte con la gente de alquitrán, veamos.

EDITAR ^ 2 : El más reciente tarde la fuente corrige esto. Todavía no está en Ubuntu 13.10, pero podría incluirse con 14.04.

krlmlr
fuente
1

De alguna manera, esto no parece un buen trabajo para un compresor sólido (archivadores basados ​​en cinta + compresión). Insertar archivos uno tras otro parece un trabajo zipo algún otro formato que permita el acceso aleatorio a los archivos dentro del archivo y la inserción incremental.

El hecho de que los archivos sean similares no ayudará mucho en ninguno de los casos. En zip, los archivos se comprimen por separado, y en los compresores sólidos, generalmente hay una ventana dentro de la cual tiene lugar la compresión.

Si los archivos están basados ​​en texto, puede almacenar diferencias en comparación con un solo archivo de referencia. Para binario, es un poco más complicado pero se puede hacer.

También hay una forma formal (no solo de escritura, sino de sistemas de archivos adecuados). Por ejemplo, los sistemas de archivos ZFS y BTRFS ofrecen una compresión transparente. También puede usar este http://developer.berlios.de/projects/fusecompress

Orión
fuente
Mis archivos son de aproximadamente 100k cada uno. ¿No sería suficiente para permitir que el compresor use una ventana de, digamos, 1M? xzparece funcionar con un tamaño de diccionario predeterminado de 8M (en el nivel de compresión predeterminado -6), lo que parece ser suficiente para mi caso de uso. - Las diferencias en un archivo de referencia son buenas, pero primero se requiere construir un archivo de referencia. ¿Un sistema de archivos de compresión detectaría archivos con contenido casi idéntico?
krlmlr
Los sistemas de compresión de archivos no comprimen entre archivos (ni zip), pero btrfstienen copia en escritura, por lo que si copia un archivo y modifica una parte del mismo, solo guarda las partes que ha cambiado. Si no está creando archivos de esta manera, supuestamente existen herramientas de deduplicación , pero aún btrfsno es un sistema de archivos maduro y estable y la deduplicación se encuentra en las primeras etapas de desarrollo. Pero ahora que lo pienso, ¿qué pasa con lessfs.com/wordpress
orion
Obtengo unas relaciones de compresión impresionantes con un compresor sólido para mi caso de uso, pero, como lo describió, supongo que los resultados serían peores si los archivos fueran más grandes que el tamaño del diccionario.
krlmlr
0

Puede que no parezca obvio, pero apuesto a squashfsque sería perfecto para esto, e incluso está implementado en el núcleo. Desde la versión 4.1 squashfspuede manejar pseudo archivos como se especifica en la mksquashlínea de comando o mediante un script de shell y mksquashfsgenerará los archivos a medida que crea el archivo.

Puede manejar tuberías , por ejemplo, puede capturar otro proceso stdouten un archivo de squash montable, incluso quince , es bastante genial. En su caso, si se pudiera encontrar la logística de secuencia de comandos de la tubería de salida de su proceso a través de él, se puede envolver su proceso enteramente en mksquashfsy terminar con un único archivo. Aquí hay un poco de readmecómo funciona y hay más aquí :

Mksquashfs 4.1 agrega soporte para "pseudo archivos dinámicos" y una operación de modificación. Los pseudo archivos dinámicos permiten que los archivos se creen dinámicamente cuando se ejecuta Mksquashfs, siendo su contenido el resultado de ejecutar un comando o una secuencia de comandos de shell. La operación de modificación permite modificar el modo / uid / gid de un archivo existente en el sistema de archivos fuente.

Crear ejemplos de archivos dinámicos

Cree un archivo "dmesg" que contenga la salida de dmesg.

    dmesg f 444 root root dmesg

Cree una LIBERACIÓN de archivo que contenga el nombre de la versión, la fecha, el host de compilación y un número de versión incremental. La versión incremental es un efecto secundario de ejecutar el script de shell, y garantiza que cada vez que se ejecuta Mksquashfs se use un nuevo número de versión sin requerir ningún otro script de 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`

Copie 10K del dispositivo / dev / sda1 en la entrada del archivo. Normalmente, Mksquashfs dado un dispositivo, fifo o socket con nombre colocará ese archivo especial dentro del sistema de archivos Squashfs, esto permite que la entrada de estos archivos especiales se capture y se coloque en el sistema de archivos Squashfs.

        input f 444 root root dd if=/dev/sda1 bs=1024 count=10
mikeserv
fuente
¿Cómo funcionaría esto dentro de la infraestructura que describí?
krlmlr
Tendría que obtener su proceso para escribir sus nombres de archivo en el script de invocación de mksquash, y hacer que continúe agregándolos mientras se ejecuta. O incluso en un tmpfs que la calabaza leerá y comprimirá mientras se ejecuta. O, como otro mencionó, a través de otra cosa: invoque cpio como en el ejemplo dd anterior, pero con cpio use su función de copia tal vez. En cualquier caso, definitivamente lee, crea y comprime sobre la marcha.
mikeserv
¿Se comprimirá entre archivos?
krlmlr
Comprime su entrada en una secuencia, todos los inodos, todo. Lo he usado con dd y fue genial, siempre uso el tamaño de bloque de 1 MB y la compresión xz.
mikeserv
Esto parece una opción, pero por su respuesta no veo cómo crear, por ejemplo, un archivo squashfs con un directorio testy un archivofile en este directorio. ¿Podría por favor dar un breve ejemplo?
krlmlr