Sistema de arquivos somente gravação virtual para armazenar arquivos no arquivo morto

8

Eu tenho um processo embaraçosamente paralelo que cria uma quantidade enorme de arquivos quase (mas não completamente) idênticos. Existe uma maneira de arquivar os arquivos "on the fly", para que os dados não consumam mais espaço do que o necessário?

O processo em si aceita parâmetros de linha de comando e imprime o nome de cada arquivo criado no stdout. Estou invocando-o com o parallel --gnuqual cuida da distribuição de entrada (que vem de outro processo) e da coleta de saída:

arg_generating_process | parallel --gnu my_process | magic_otf_compressor

EXEMPLO SIMPLES para a primeira parte do tubo em bash:

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

Como poderia magic_otf_compressorser? Ele deve tratar cada linha de entrada como nome do arquivo, copiar cada arquivo em um .tararquivo compactado (o mesmo arquivo para todos os arquivos processados!) E excluí-lo. (Na verdade, deve ser suficiente imprimir o nome de cada arquivo processado, outro | parallel --gnu rmpode cuidar da exclusão dos arquivos.)

Existe alguma ferramenta desse tipo? Não estou pensando em compactar cada arquivo individualmente, isso desperdiçaria muito espaço. Examinei archivemount(manterá o sistema de arquivos na memória -> impossível, meus arquivos são muito grandes e muitos) e avfs(não consegui fazê-lo funcionar em conjunto com o FUSE). O que eu perdi?

Estou a um passo de invadir essa ferramenta, mas alguém deve ter feito isso antes ...

EDIT : Essencialmente, acho que estou procurando um front-end stdin para libtar(em oposição ao front-end da linha de comando tarque lê argumentos da linha de comando).

krlmlr
fonte
Você já pensou em gravar arquivos em um formato que possui compactação nativa? Por exemplo, o hdf5 pode ser compactado à medida que são escritos com a compressão gzip ou szip. O Hdf5 também suporta MPI, por isso funciona bem com esses problemas embaraçosamente paralelos.
casey
2
Se você deseja compactação e desduplicação, o zfs vem à mente.
Stéphane Chazelas
@casey: É HTML, mas suponho que eu poderia usar um contêiner HDF5. Ainda não considerou isso.
krlmlr
@StephaneChazelas: Isso pode ser implementado na terra do usuário?
krlmlr

Respostas:

1

Parece que tarquer saber todos os nomes de arquivo antecipadamente. Portanto, é menos on-the-fly e mais after-the-fly. cpioparece não ter esse problema:

| cpio -vo 2>&1 > >(gzip > /tmp/arc.cpio.gz) | parallel rm
Ole Tange
fonte
Obrigado. Portanto, nem o RTFM é suficiente ;-) Eu até procurei no tarcódigo da empresa para ver que existe uma função que retorna o próximo nome de arquivo a ser processado, o que me fez ler a documentação novamente. - Então, stdouté direcionado ao gzipprocesso via substituição de processo e stderré redirecionado para o stdoutqual é processado pela próxima etapa no canal?
krlmlr
Sim. A construção>> () não funciona em todos os shells, mas funciona no Bash.
precisa
Posso confirmar que tarlê a lista de arquivos primeiro, usando o exemplo simples que adicionei à minha pergunta. No entanto, lendo taro código-fonte novamente, parece-me que ele deveria ler a lista de arquivos "on the fly" se não estiver criando um arquivo incremental. Infelizmente, tenho erros ao compilar a tarpartir da fonte ... :-(
krlmlr
Não encontrei uma maneira de suprimir a linha final na saída de cpio, exceto grep -v 'blocks$'. ( head -n -1Usa um buffer muito grande ...) torna esta solução um pouco de um truque, mas não importa ;-)
krlmlr
@krlmlr que é estranho: meu head -n -1usa apenas 16 MB quando executado em alguns GB de dados. Você sempre pode usar o perl: perl -ne 'print $ last; $ last = $ _'
Ole Tange
7

Um caso clássico de RTFM (tudo isso!) . A -Topção GNU tarirá ler os arquivos a serem arquivados de outro arquivo (no meu caso /dev/stdin, você também pode usar -), e há ainda uma --remove-filesopção:

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

(usando a versão paralela de xzpara compactação, mas você pode usar seu compressor preferido). Para ser usado como:

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

EDIT : Como Ole aponta, tarparece ler toda a lista de arquivos com a -Topção por algum motivo. O teste a seguir confirma isso:

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

Há um atraso de um segundo no meu sistema antes que todos os arquivos sejam impressos de uma só vez; por outro lado, se o tarcomando for substituído por cat, todos os arquivos serão impressos à medida que são criados. Eu registrei uma solicitação de suporte com o pessoal do alcatrão, vamos ver.

EDIT ^ 2 : O mais recente tarda fonte corrige isso. Ainda não está no Ubuntu 13.10, mas pode estar incluído no 14.04.

krlmlr
fonte
1

De alguma forma, isso não parece um bom trabalho para um compressor sólido (arquivadores baseados em fita + compressão). A inserção de arquivos um após o outro parece uma tarefa zipou outro formato que permite acesso aleatório a arquivos dentro do arquivo morto e inserção incremental.

O fato de os arquivos serem semelhantes não ajudará muito nos dois casos. Em zip, os arquivos são compactados separadamente e, em compressores sólidos, geralmente há uma janela na qual a compactação ocorre.

Se os arquivos forem baseados em texto, você poderá armazenar diferenças em comparação com um único arquivo de referência. Para binário, é um pouco mais complicado, mas pode ser feito.

Há também uma maneira formal (não apenas de gravação, mas sistemas de arquivos adequados). Por exemplo, os sistemas de arquivos ZFS e BTRFS oferecem compactação transparente. Você também pode usar este http://developer.berlios.de/projects/fusecompress

orion
fonte
Meus arquivos têm cerca de 100k cada. Não seria suficiente para permitir que o compressor usasse uma janela de, digamos, 1M? xzparece operar com um tamanho de dicionário padrão de 8M (no nível de compactação padrão -6), o que parece ser suficiente para o meu caso de uso. - As diferenças para um arquivo de referência são boas, mas requerem a construção de um arquivo de referência primeiro. Um sistema de arquivos compactado detectaria arquivos com conteúdo quase idêntico?
krlmlr
A compactação de sistemas de arquivos não compacta os arquivos (nem o zip), mas btrfspossui cópia na gravação; portanto, se você copiar um arquivo e modificar uma parte dele, ele salvará apenas as partes que você alterou. Se você não está criando arquivos dessa maneira, supostamente existem ferramentas de desduplicação , mas btrfsainda não é um sistema de arquivos estável e maduro, e a desduplicação está nos estágios iniciais de desenvolvimento. Mas agora penso nisso, e quanto a lessfs.com/wordpress
orion
Recebo taxas de compactação impressionantes com um compressor sólido para o meu caso de uso, mas, como você destacou, presumo que os resultados seriam piores se os arquivos fossem maiores que o tamanho do dicionário.
krlmlr
0

Pode não parecer óbvio, mas aposto squashfsque seria perfeito para isso - e até foi implementado no kernel. Uma vez que a versão 4.1 squashfspode manipular pseudo arquivos conforme especificado na mksquashlinha de comando ou através de um shell-script e mksquashfsirá gerar os arquivos à medida que cria o arquivo morto.

Ele pode lidar com tubos - por exemplo, você pode capturar outro processo stdoutem um arquivo squash montável - até quinos - é muito legal. No seu caso, se você pudesse trabalhar a logística de script de tubulação de saída do seu processo através dele, você poderia envolver seu processo inteiramente em mksquashfse acabar com um único arquivo. Aqui está um pouco readmesobre como funciona e há mais aqui :

O Mksquashfs 4.1 adiciona suporte para "pseudo arquivos dinâmicos" e uma operação de modificação. Os pseudo arquivos dinâmicos permitem que os arquivos sejam criados dinamicamente quando o Mksquashfs é executado, sendo o seu conteúdo o resultado da execução de um comando ou parte do script do shell. A operação de modificação permite que o modo / uid / gid de um arquivo existente no sistema de arquivos de origem seja modificado.

Criando exemplos de arquivos dinâmicos

Crie um arquivo "dmesg" contendo a saída do dmesg.

    dmesg f 444 root root dmesg

Crie um arquivo RELEASE contendo o nome do release, a data, o host de construção e um número de versão incremental. A versão incremental é um efeito colateral da execução do script do shell e garante que, sempre que o Mksquashfs for executado, um novo número de versão seja usado sem a necessidade de nenhum outro 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 do dispositivo / dev / sda1 para a entrada do arquivo. Normalmente, o Mksquashfs que recebe um dispositivo, fifo ou soquete nomeado coloca esse arquivo especial dentro do sistema de arquivos Squashfs, permitindo que a entrada desses arquivos especiais seja capturada e colocada no sistema de arquivos Squashfs.

        input f 444 root root dd if=/dev/sda1 bs=1024 count=10
mikeserv
fonte
Como isso funcionaria dentro da infraestrutura que descrevi?
krlmlr
Você precisaria que seu processo escrevesse seus nomes de arquivos no script de chamada do mksquash e continuasse a anexá-los enquanto ele é executado. Ou mesmo em um tmpfs que o squash lerá e comprimirá enquanto for executado. Ou, como outro mencionado, através de outra coisa - invoque o cpio exatamente como no exemplo dd acima, mas com o cpio use sua função de cópia talvez. De qualquer forma - ele definitivamente lê, cria e comprime em tempo real.
mikeserv
Comprimirá arquivos?
krlmlr
Ele comprime sua entrada em um fluxo - todos os inodes, tudo. Eu usei com dd e foi bem legal - eu sempre uso o tamanho de bloco de 1 MB e a compactação xz.
mikeserv
Isso parece uma opção, mas pela sua resposta, não consigo ver como criar, por exemplo, um arquivo squashfs com um diretório teste um arquivo filenesse diretório. Você poderia fornecer um breve exemplo?
krlmlr