sexta-feira, 9 de março de 2012

Escritas duráveis (ou: o mal que o EXT3 causou)

Os dados do sistema de arquivos são divididos em dados e metadados (dados dos dados). O dados são o conteúdo em si que as aplicações mandam gravar no disco. Os metadados são dados que o sistema de arquivos usa para controle e correção.

Para obter bom desempenho, os sistemas de arquivos usam técnicas que não gravam os dados e/ou metadados imediamente no disco quando um aplicativo começa a escrever algo. Existem casos legítmos, contudo, nos quais um aplicativo precisa que os dados sejam imediatamente gravados no disco. Para isso existem duas funções na libc: fsync() e fdatasync() — são ligeiramente diferentes, mas considerarei que fazem a mesma coisa. Pense num editor de texto. Quando clica no botão "Salvar", você espera que o arquivo esteja salvo. Ele só estará salvo no momento que os dados e metadados associados atingirem a mídia física do disco. Isso — a durabilidade — é garantido com um fsync().

Aplicações que se preocupam com integridade de dados são programadas cuidadosamente levando em consideração tal comportamento. Programadores competentes sabem que a única forma garantida de terem os dados na mídia física é usando as funções adequadas. Aplicações sérias, como banco de dados (exemplo: MySQL + InnoDB), são cautelosas nesse sentido.

Aí que entra o EXT3. O EXT3 no modo data=ordered, que foi padrão nas distribuições durante muito tempo, é uma aberração que foge totalmente a regra. Nele, os dados são alocados antes dos metadados associados, sem a necessidade de fsync(). A atualização dos metadados fica pendente para o próximo commit (quando o journal é atualizado), que é feito a cada cinco segundos sem intervenção da aplicação. Ou seja, na prática, a cada cinco segundos, qualquer dado pendente é gravado na marra na mídia.

Para piorar mais: quando algum fsync() é executado, todas as dirty pages (dados pendentes que estão na memória RAM) são gravadas no disco, ao invés de apenas as associadas ao arquivo para o qual a função foi chamada. Claro que a função fsync() passou a ter uma péssima reputação por causa disso, pois pode introduzir latência além do esperado (a função é síncrona). Vamos supor que você esteja gravando uma grande quantidade de dados no sistema de arquivos (um aplicativo de edição de vídeo, por exemplo) e ao mesmo tempo clica no botão "Salvar" do seu editor de texto. O fsync() do editor de texto não fará apenas o arquivo de texto ir parar na mídia imediatamente, mas ele e em todos os demais dados pendentes.

Foi a receita para a tragédia. Programadores preguiçosos passaram a programar código que considera esse comportamento como padrão (quando nunca foi — é exceção!) e passaram a evitar o uso de fsync(). Uma legião de programas bugados nasceu por culpa do EXT3 no modo data=ordered.

Surgiu o EXT4 e os problemas apareceram. O EXT4 tem o mesmo modo data=ordered, mas tem um recurso novo: alocação atrasada (que o XFS tem desde sempre). Aquela garantia dos dados serem alocados antes não existe mais e o intervalo de cada commit aumentou para trinta segundos. No EXT4 data=ordered, o comportamento do fsync() voltou à normalidade, atuando apenas nos dados e metadados referentes ao arquivo especificado na função.

A fama de perder arquivos em desligamentos incorretos que o XFS (e agora EXT4) tem é por causa das aplicações mal programadas. Parte do problema, pelo menos, foi remediada com hacks que detectam comportamentos comuns das aplicações mal escritas e abrem uma exceção que desativa a alocação atrasada nos dados associados. Os hacks existem no EXT4 (1, 2, 3) e XFS (1).

BARRIERS E WRITE CACHE

A problemática dos aplicavos mal escritos está longe de ter fim. Então vamos esquecê-la por enquanto.

Supondo que todos seus aplicativos sejam bem escritos, ainda assim existe outra questão que diz respeito à durabilidade das operações de escrita.

        APLICAÇÃO
            |
          KERNEL
            |
       WRITE CACHE
       (hardware)
            |
          MÍDIA

O sistema de arquivos requer que a ordem de gravação do que for enviado ao disco não seja alterada, para ser possível manter sua consistência. O problema reside no que o firmware do dispositivo de armazenamento faz. Geralmente existe um cache de gravação (write cache) para melhorar o desempenho. Ele serve como um reservatório temporário de dados que estão pendentes de serem gravados. Porém no momento que o sistema de arquivos manda gravar alguma coisa, a gravação pode não atingir a mídia imediatamente, ficando temporariamente no write cache. Se o sistema de arquivos considerar que essa escrita atingiu a mídia e continuar, caso aconteça um desligamento incorreto antes do firmware efetivamente gravar os dados na mídia, a estrutura do sistema de arquivos acabou de ficar corrompida.

Dependendo do montante e tipo de dado que teve sua ordenação violada pelo write cache, pode ser um dano menor, facilmente consertável pela ferramenta de verificação do sistema de arquivos. Ou não...

Discos voltados para o mercado de servidores possuem um mecanismo que os sistemas operacionais podem usar para certificar que dados que requeiram ordenamento atingiram a mídia na ordem esperada. Trata-se do comando Force Unit Access (FUA) do protocolo SCSI (usado também em discos e controladores SAS). Esse comando diz para o hardware que a operação de E/S (intervalo de blocos) deve ser feita diretamente na mídia, sem passar pelo cache do dispositivo. Discos SATA não suportam FUA nem existe um equivalente no protocolo ATA.

Mas então como fazer com hardware de pobres mortais? É... o pessoal do kernel teve que fazer alguma coisa: Write Barriers. Elas são um FUA para os desvalidos. Quando montados com a opção barrier (que é padrão no EXT4 e XFS), os sistemas de arquivos têm grantido pela block layer do kernel que todas as operações de E/S no disco efetuadas antes da barreira iniciar serão terminadas e as que forem efetuadas depois apenas começarão depois da barreira terminar. Barriers funcionam (simplificando) assim: FLUSH_CACHE_EXT, operação de E/S, FLUSH_CACHE_EXT novamente. FLUSH_CACHE_EXT é um comando do protocolo ATA que diz para o dispositivo gravar na mídia tudo que estiver pendente no write cache — o que pode incluir dados não pertencentes à operação de E/S feita naquele momento.

Isso garante ordenação de escrita com o write cache habilitado. Não, entretanto, sem um custo: desempenho piorado por causa dos constantes flushes feitos no cache do dispositvo. Note que HDs SATA atualmente têm cache de 16MB, 32MB, 64MB.

Uma segunda possibilidade é desativar o write cache e montar o sistema de arquivos sem barriers. Também é uma configuração que garante a integridade do sistema de arquivos e, dependendo do hardware, pode oferecer melhor desempenho. As operações de E/S feitas com barriers não podem ser agrupadas e/ou reordenadas pelo escalonador de E/S do kernel. Quando você tem várias pequenas operações sequenciais, com barriers, cada uma delas será uma operação de E/S separada (entre comandos FLUSH_CACHE_EXT), enquanto que sem barriers o escalonador de E/S pode juntar o máximo possível delas e realizar apenas uma grande operação quando possível.

O write cache é desativado com:

# hdparm -W0 /dev/<disco>

A configuração não se mantém entre desligamentos, nem ao voltar do modo de espera. Ou seja, sendo o disco desligado/ligado, reseta a configuração. Veja aqui como resolver.

Em distribuições que usem o systemd pode usar um arquivo unit como esse para torná-la permanente:

/etc/systemd/system/desativa-write-cache.service

[Unit]
Description=Desativa Write Cache
DefaultDependencies=no
Before=local-fs-pre.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/sbin/hdparm -W0 /dev/sda

[Install]
WantedBy=basic.target

E ativá-lo com:

# systemctl daemon-reload
# systemctl enable --now desativa-write-cache.service

Para remontar o sistema de arquivos (para testes) sem barriers:

# mount -o remount,nobarrier /<ponto de montagem>

(barrier=1/0 é outra forma válida de especificar barrier/nobarrier)

Edite o /etc/fstab para deixar permanente.

Ao usar o arquivo unit e editar o fstab, depois de desligar e ligar novamente, confira:

# systemctl status desativa-write-cache.service
desativa-write-cache.service - Desativa Write Cache
          Loaded: loaded (/etc/systemd/system/desativa-write-cache.service; enabled)
          Active: active (exited) since Fri, 09 Mar 2012 08:31:52 -0300; 38s ago
         Process: 365 ExecStart=/sbin/hdparm -W0 /dev/sda (code=exited, status=0/SUCCESS)
          CGroup: name=systemd:/system/desativa-write-cache.service

# hdparm -W /dev/sda

/dev/sda:
 write-caching =  0 (off)

# findmnt /dev/sda1
TARGET SOURCE    FSTYPE OPTIONS
/      /dev/sda1 ext4   rw,relatime,seclabel,nobarrier,data=ordered

Como disse, se melhorará ou não o desempenho depende do hardware.

O importante é que com commodity hardware as duas formas de ter a integridade do sistema de arquivos garantida no caso de desligamentos incorretos são:

→ WRITE CACHE ATIVADO + BARRIER

→ WRITE CACHE DESATIVADO + NOBARRIER

3 comentários:

  1. Ola
    Tenho aqui um servidor com um disco que constantemente apresenta status busy pelo comando atop ou nmon e qdo isso acontece, meu cpu load fica extremamente alto. Quando o status busy persiste durante muito tempo, já notei que volta ao normal quando executado o comando 'sync' (porém volta e meia apresenta esse problema) Já verifiquei o frag com xfs_db e o filesystem não está muito fragmentado.
    No boot.msg tenho a seguinte informação para esse disco:
    "Write cache: enabled, read cache: enabled, doesn't support DPO or FUA"
    O disco foi formatado com XFS e nobarrier. Ou seja, não segue nenhuma das opções indicadas acima. Essa partição XFS é usada basicamente para HOMEDIR de usuário (que é montado via NFS nos clientes) e ainda é usado DRBD no servidor para replicação destes dados. Sendo assim, sua recomendação para melhor performance seria desabilitar o write cache OU ativar o barrier? Esse servidor é um Dell PowerEdge 1900 (creio q tem uma controladora SAS) e o SO não identifica do modelo do disco pelo sginfo nem hdparm (o hwinfo o identifica o disco apenas como Dell Virtual Disk e não tenho como abrir a máquina pois estou dando suporte remoto ao servidor)

    ResponderExcluir
    Respostas
    1. Habilitando barriers o desempenho piorará. Controladoras RAID por hardware com BBWC costumam desativar por padrão os caches dos HDs (confira mesmo assim, isso varia de acordo com o modelo) e o cache usado passa a ser o seu (da própria controladora, que é coberto pela bateria). Neste caso, é seguro (e recomendado) usar nobarrier para ter melhor desempenho.

      http://xfs.org/index.php/XFS_FAQ#Q._Should_barriers_be_enabled_with_storage_which_has_a_persistent_write_cache.3F

      Sugiro que você dê uma olhada, além da FAQ, na lista de discussão do XFS, que tem gente especialista. Em alguns casos, a recomendação é simplesmente atualizar o kernel. A propósito, qual distro/kernel você roda aí?

      Excluir
    2. ola.. obrigada pela resposta!!
      nesse servidor tem o sles11 sp1. mas o i/o nesse disco está sofrível (e tenho apenas uns 15 usuários ali) como estava tenho muito wait i/o resolvi investigar a causa e descobri que o disco em questão é que fica praticamente busy o tempo todo... tenho outro disco nesse servidor (um EAGATE ST3300656SS 15k rpm) que está write cache desativado e read cache habilitado, também com XFS e nobarries e que comporta a grande massa de dados de usuários sem problemas , enquanto o outro disco com os demais usuários fica o tempo todo busy (mas talvez por ser mais lento, não sei.. pois não consigo ver as informações deste disco pelo s.o.)

      Excluir