Nota: é do meu entendimento que revisão de código (code review) faz parte do nosso foco (é on topic) atualmente. Se alguém não concorda, favor se manifestar no meta. – mgibsonbr 14/02/14 às 5:55
Não quis colocar isso como resposta, já que é só um link, mas acho interessante documentar. Essa solução te satisfaz? – Felipe Avelar 14/02/14 às 12:38
@FelipeAvelar A maioria dos recursos que achei ao buscar por "python journal" se referia não a journaling [no contexto de sistema de arquivos] mas à implementação de alguma espécie de "diário"... Creio que esse link também se trata disso - já que é um plugin para o plone (sistema de CMS). – mgibsonbr 14/02/14 às 22:40
A técnica básica é escrever dois arquivos no mínimo, primeiro um, depois o outro, em modo síncrono, conforme foi descrito em outras respostas.
O "tempero" do esquema é adicionar timestamp e um somatório (hash) nos dois arquivos, porque assim você sabe qual é o arquivo mais recente (pelo timestamp), e pode verificar se o arquivo está íntegro (pelo hash).
Exatamente o que eu estava procurando! Melhor do que reinventar a roda... Vou testar essa implementação que você citou, mas já dei uma olhada nos fontes e me parece que está tudo ok. – mgibsonbr 15/02/14 às 0:00
Acabei por utilizar sua solução: agora eu salvo o próprio args.json como um ACIDFile - assegurando de só atualizá-lo após me certificar que a tarefa anterior completou com sucesso (ver resposta do @utluiz). Simplificou bastante o código. – mgibsonbr 15/02/14 às 2:43
1.42311 medalhas de ouro99 medalhas de prata1919 medalhas de bronze
Uma dúvida sobre o item #1: o try já não faz exatamente isso, solicitando a leitura do arquivo? Deveria então haver um outro bloco try aninhado? – utluiz 14/02/14 às 13:18
Não precisa, você pode simplesmente retirar o bloco if já que se ocorrer exceção dentro do try o arquivo provavelmente não existe. Seguindo o bullet seguinte, você poderia checar por exceções diferentes e tratar casos diferentes (p.ex. arquivo não existe, acesso negado, etc). – Rodrigo Deodoro 14/02/14 às 13:21
Sobre o item #3, acho que a ideia é não executar os passos que já foram executados. Por exemplo, se há 3 passos gravados no journal.txt, as tarefas serão executadas da 4ª em diante. Nesse caso, a não ser que o índice seja usado para alguma outra coisa no código real, talvez pudesse ser feito algo como for tarefa in args['tarefas'][len(passos):]:. – utluiz 14/02/14 às 13:36
Ou ainda: já que o seu arquivo journal.txt só guarda o número de tarefas que foram executadas com sucesso, você poderia gravar simplesmente o número, incrementando-o a cada sucesso e utilizando-o ao invés de len(passos) para pular as tarefas que foram executadas com sucesso. – Rodrigo Deodoro 14/02/14 às 13:44
[sobre o último comentário] isso não é possível, pois há o risco de eu corromper o journal.txt se houver falha durante a manipulação do mesmo. No modo que eu fiz, ou uma linha nova será adicionada ao arquivo (confirmando a tarefa) ou não (exigindo que ela seja refeita depois). [sobre #1 e #2] Boa sugestão, vou fazer! [sobre #3] A sugestão do @utluiz é boa. Eu preciso ignorar tarefas já feitas pois, embora cada tarefa individual seja indepotente no estado correto do sistema, ela não o é se o sistema já mudou de estado (i.e. se a tarefa seguinte já começou). – mgibsonbr 14/02/14 às 22:19
Boa observação! Vou fazer isso e, pra garantir, também uma verificação dos arquivos recém-escritos para confirmar que está tudo ok antes de confirmar a tarefa. (e se entendi bem, é importante que o os.fsync seja usado também depois de se escrever o journal - para evitar que a tarefa seguinte comece sem que a registro de conclusão da tarefa anterior esteja pronto, certo?) – mgibsonbr 14/02/14 às 22:26
@mgibsonbr Sim, acredito que a sincronização deva ser feita após todas as gravações. – utluiz 14/02/14 às 23:44
Li ainda algum lugar (acho que foi no Linux System Programming) que o sync deveria ser feito 2 ou 3 vezes, como é o costume dos admins UNIX na linha de comando. Não lembro a razão exata, é algo como: o primeiro sync só inicia a gravação física, mas o segundo sync efetivamente bloqueia antes do primeiro terminar (se ainda não terminou). – epx 27/02/14 às 2:52
@epx Wow! Aí o nível baixou muito pra mim... rsrs – utluiz 27/02/14 às 12:59
crie um journaling baseado em arquivos no filesystem, sugiro usar uma nomenclatura baseada em epoch... escrever e apagar itens de uma unica vez é sempre mais seguro que manipular um aquivo milhares de vezes.. o proprio journaling pode ser corrompido ja que atomicidade em python é dificil de garantir pra processos longos