GNU/Linux >> Tutoriales Linux >  >> Linux

¿Cómo copiar un archivo transaccionalmente?

rsync hace este trabajo. Un archivo temporal es O_EXCL creado por defecto (solo deshabilitado si usa --inplace ) y luego renamed sobre el archivo de destino. Usa --ignore-existing para no sobrescribir B si existe.

En la práctica, nunca tuve ningún problema con esto en montajes ext4, zfs o incluso NFS.


No te preocupes, noclobber es una función estándar.


Usted preguntó acerca de NFS. Es probable que este tipo de código se rompa bajo NFS, ya que la verificación de noclobber involucra dos operaciones NFS separadas (compruebe si el archivo existe, cree un nuevo archivo) y dos procesos de dos clientes NFS separados pueden entrar en una condición de carrera donde ambos tienen éxito (ambos verifican que B.part aún no existe, ambos proceden a crearlo con éxito, como resultado, se sobrescriben entre sí).

Realmente no hay que hacer una verificación genérica para saber si el sistema de archivos en el que está escribiendo admitirá algo como noclobber atómicamente o no. Puede verificar el tipo de sistema de archivos, ya sea NFS, pero eso sería una heurística y no necesariamente una garantía. Es probable que los sistemas de archivos como SMB/CIFS (Samba) sufran los mismos problemas. Los sistemas de archivos expuestos a través de FUSE pueden o no comportarse correctamente, pero eso depende principalmente de la implementación.

Un enfoque posiblemente mejor es evitar la colisión en el B.part paso, utilizando un nombre de archivo único (a través de la cooperación con otros agentes) para que no necesite depender de noclobber . Por ejemplo, podría incluir, como parte del nombre de archivo, su nombre de host, PID y una marca de tiempo (+posiblemente un número aleatorio). Dado que debe haber un solo proceso ejecutándose bajo un PID específico en un host en un momento dado, esto debería garantía de exclusividad.

Así que cualquiera de:

test -f B && continue  # skip already existing
unique=$(hostname).$$.$(date +%s).$RANDOM
cp A B.part."$unique"
# Maybe check for existance of B again, remove
# the temporary file and bail out in that case.
mv B.part."$unique" B
# mv (rename) should always succeed, overwrite a
# previously copied B if one exists.

O:

test -f B && continue  # skip already existing
unique=$(hostname).$$.$(date +%s).$RANDOM
cp A B.part."$unique"
if ln B.part."$unique" B ; then
    echo "Success creating B"
else
    echo "Failed creating B, already existed"
fi
# Both cases require cleanup.
rm B.part."$unique"

Entonces, si tiene una condición de carrera entre dos agentes, ambos procederán con la operación, pero la última operación será atómica, por lo que B existe con una copia completa de A o B no existe.

Puede reducir el tamaño de la carrera comprobando de nuevo después de la copia y antes del mv o ln operación, pero todavía hay una pequeña condición de carrera allí. Pero, independientemente de la condición de carrera, el contenido de B debe ser coherente, suponiendo que ambos procesos intenten crearlo desde A (o una copia de un archivo válido como origen).

Tenga en cuenta que en la primera situación con mv , cuando existe una carrera, el último proceso es el que gana, ya que rename(2) reemplazará atómicamente un archivo existente:

Si nueva ruta ya existe, se reemplazará atómicamente, de modo que no haya ningún punto en el que otro proceso intente acceder a nueva ruta encontrará que falta. [...]

Si nueva ruta existe pero la operación falla por algún motivo, rename() garantiza dejar una instancia de newpath en su lugar.

Por lo tanto, es muy posible que los procesos que consumen B en ese momento puedan ver diferentes versiones de él (diferentes inodos) durante este proceso. Si todos los escritores están tratando de copiar el mismo contenido, y los lectores simplemente están consumiendo el contenido del archivo, eso podría estar bien, si obtienen diferentes inodos para archivos con el mismo contenido, estarán felices de la misma manera.

El segundo enfoque que utiliza un enlace fijo parece mejor, pero recuerdo haber hecho experimentos con enlaces duros en un ciclo cerrado en NFS de muchos clientes simultáneos y contando el éxito y todavía parecía haber algunas condiciones de carrera allí, donde parecía que si dos clientes emitían una operación de enlace duro al mismo tiempo, con el mismo destino, ambos parecían tener éxito. (Es posible que este comportamiento esté relacionado con la implementación particular del servidor NFS, YMMV). En cualquier caso, es probable que sea el mismo tipo de condición de carrera, en la que podría terminar obteniendo dos inodos separados para el mismo archivo en casos donde hay mucho concurrencia entre escritores para desencadenar estas condiciones de carrera. Si sus escritores son consistentes (ambos copian A a B) y sus lectores solo consumen el contenido, eso podría ser suficiente.

Finalmente, mencionaste el bloqueo. Desafortunadamente, falta mucho el bloqueo, al menos en NFSv3 (no estoy seguro acerca de NFSv4, pero apuesto a que tampoco es bueno). Si está considerando bloquear, debe buscar diferentes protocolos para el bloqueo distribuido, posiblemente fuera de banda con el copias de archivos reales, pero eso es disruptivo, complejo y propenso a problemas como interbloqueos, por lo que diría que es mejor evitarlo.

Para obtener más información sobre el tema de la atomicidad en NFS, es posible que desee leer sobre el formato de buzón Maildir, que se creó para evitar bloqueos y funcionar de manera confiable incluso en NFS. Lo hace manteniendo nombres de archivo únicos en todas partes (para que ni siquiera obtenga una B final al final).

Quizás algo más interesante para su caso particular, el formato de Maildir ++ extiende Maildir para agregar soporte para la cuota del buzón y lo hace mediante la actualización atómica de un archivo con un nombre fijo dentro del buzón (por lo que podría estar más cerca de su B). Creo que Maildir ++ intenta para agregar, que no es realmente seguro en NFS, pero hay un enfoque de recálculo que usa un procedimiento similar a este y es válido como un reemplazo atómico.

¡Esperemos que todos estos consejos sean útiles!


Linux
  1. ¿Cómo copiar archivos recursivamente por extensión de archivo?

  2. ¿Cómo cambiar el nombre de un archivo en Linux?

  3. ¿Cómo hacer que el archivo sea disperso?

  4. Cómo grep \n en el archivo

  5. Cómo ordenar un archivo en el lugar

Cómo vincular un archivo en Linux

Cómo copiar archivos con una extensión de archivo específica recursivamente

Cómo copiar archivos y directorios en la terminal de Linux

Cómo copiar un directorio en Linux

Cómo hacer eco en un archivo

Cómo copiar un archivo en el Administrador de archivos