Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

Atomic File Writes: grave arquivos sem corromper

Atualizado em: 30 de outubro de 2026

Estação de trabalho usada no desenvolvimento de aplicações Node.js

Atomic File Writes no Node.js evitam que leitores encontrem arquivos parcialmente gravados. O padrão escreve o conteúdo em um arquivo temporário no mesmo diretório, sincroniza os dados quando necessário e substitui o destino com rename.

Uma chamada direta a writeFile pode truncar o arquivo antes de terminar. Se o processo cair, a configuração, índice ou checkpoint pode ficar vazio ou incompleto.

Padrão básico

import { open, rename, rm } from 'node:fs/promises';
import path from 'node:path';
import crypto from 'node:crypto';

async function writeAtomic(filePath, data) {
  const directory = path.dirname(filePath);
  const temporary = path.join(
    directory,
    `.${path.basename(filePath)}.${crypto.randomUUID()}.tmp`
  );

  let handle;
  try {
    handle = await open(temporary, 'wx', 0o600);
    await handle.writeFile(data);
    await handle.sync();
    await handle.close();
    handle = null;
    await rename(temporary, filePath);
  } finally {
    await handle?.close().catch(() => {});
    await rm(temporary, { force: true }).catch(() => {});
  }
}

O modo wx evita sobrescrever um temporário existente. Um nome aleatório reduz colisão.

Mesmo filesystem

O rename atômico depende de origem e destino estarem no mesmo filesystem. Criar o temporário em /tmp e mover para outro volume pode gerar EXDEV e exigir cópia, que não é atômica.

Atomicidade não é durabilidade

Rename atômico significa que leitores veem o arquivo antigo ou novo, não metade. Isso não garante que dados sobreviverão a perda de energia. Para maior durabilidade, sincronize arquivo e, em cenários exigentes, o diretório pai.

Sincronizando o diretório

const dirHandle = await open(directory, 'r');
try {
  await dirHandle.sync();
} finally {
  await dirHandle.close();
}

O suporte e comportamento variam por plataforma. Teste no ambiente real.

Permissões

Crie o temporário com permissões restritas. Depois do rename, ele mantém seu modo. Se o destino precisa de outro modo, aplique explicitamente antes da publicação.

Preservando metadados

Substituir o arquivo pode alterar owner, mode, ACLs e atributos. Quando isso importa, leia e reaplique metadados de forma controlada. Não copie permissões inseguras automaticamente.

JSON

const payload = JSON.stringify(value, null, 2) + '\n';
await writeAtomic(configPath, payload);

Valide o objeto antes de gravar. Depois, opcionalmente releia e faça parse para detectar erro de serialização ou filesystem.

Checkpoints

Checkpoints devem incluir versão e checksum:

{
  "version": 1,
  "sequence": 1500,
  "updatedAt": "2026-07-26T12:00:00Z",
  "checksum": "sha256:..."
}

Na inicialização, rejeite versão desconhecida ou checksum inválido.

Concorrência

Atomic write impede arquivo parcial, mas não evita lost update. Dois processos podem ler a mesma versão e publicar resultados diferentes. Use lock, compare versão ou mantenha a verdade em um banco.

if (current.version !== expectedVersion) {
  throw new Error('Conflito de versão');
}

Leitores

Leitores devem abrir o caminho final normalmente. Um descritor já aberto pode continuar apontando para o arquivo antigo após rename. Reabra quando precisar da nova versão.

Windows

Semântica de rename e arquivos abertos difere do Unix. Antivírus e processos podem bloquear substituição. Teste retries limitados para erros transitórios, sem loop infinito.

Backup

Para arquivos importantes, mantenha uma versão anterior:

  1. grave e sincronize o temporário;
  2. mova o destino atual para backup;
  3. mova o temporário para destino;
  4. sincronize diretório;
  5. remova backups antigos conforme retenção.

Mais etapas significam mais estados de recuperação. Documente o algoritmo de startup.

Recuperação de temporários

No startup, procure temporários antigos. Não publique automaticamente sem validar. Remova depois de uma idade segura ou encaminhe para quarentena.

Streaming

Para dados grandes, escreva em stream no temporário, aguarde pipeline, sincronize e renomeie:

await pipeline(source, createWriteStream(temporary, {
  flags: 'wx',
  mode: 0o600
}));

Garanta que o descritor foi fechado antes do rename quando a plataforma exigir.

Cancelamento

Se a operação for cancelada, destrua o stream e remova o temporário. Não toque no arquivo final até o conteúdo estar completo.

Disk full

ENOSPC pode ocorrer durante a escrita ou sync. Mantenha o destino antigo intacto e alerte. Monitore espaço e inodes.

Configuração observada

Watchers podem receber eventos do temporário e do rename. Ignore o padrão *.tmp e processe somente o caminho final. Consulte fs.watch no Node.js.

Segurança de caminho

O caminho deve vir de configuração confiável. Resolva a raiz, bloqueie traversal e avalie symlinks. Um atacante não deve redirecionar o rename para arquivo sensível.

Observabilidade

Monitore duração, bytes, falhas por código, sync, conflitos, temporários removidos, espaço disponível e idade do último checkpoint.

Testes

  • escrita bem-sucedida;
  • queda simulada antes do rename;
  • erro durante write;
  • disk full;
  • dois escritores;
  • destino aberto por leitor;
  • temporário antigo;
  • filesystem diferente;
  • permissões;
  • Windows e Linux.

Erros comuns

  • temporário em outro volume;
  • confundir atomicidade com durabilidade;
  • não fechar antes do rename;
  • temporário com permissão ampla;
  • não limpar falhas;
  • ignorar concorrência;
  • não testar Windows;
  • publicar temporário sem validação;
  • usar caminho controlado pelo usuário.

Conclusão

Atomic File Writes no Node.js protegem leitores contra conteúdo parcial usando temporário e rename. O padrão é adequado para configurações geradas, checkpoints e pequenos índices locais.

Use o mesmo filesystem, sincronize conforme o requisito de durabilidade e trate concorrência separadamente. Mantenha o arquivo antigo intacto até a nova versão estar completa e validada.

Consulte a documentação de fsPromises.rename e a documentação de FileHandle.sync.

10 melhores cursos de programação em 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita