Faltan eventos de inotify (en el directorio .git)

11

Estoy mirando los archivos para ver los cambios usando eventos inotify (como sucede, desde Python, llamando a libc).

Para algunos archivos durante un git clone, veo algo extraño: veo un IN_CREATEevento, y veo lsque el archivo tiene contenido, sin embargo, nunca veo IN_MODIFYo IN_CLOSE_WRITE. Esto me está causando problemas ya que me gustaría responder IN_CLOSE_WRITEen los archivos: específicamente, para iniciar una carga del contenido del archivo.

Los archivos que se comportan de manera extraña están en el .git/objects/packdirectorio y terminan en .packo .idx. Otros archivos que crea git tienen una cadena más regular IN_CREATE-> IN_MODIFY-> IN_CLOSE_WRITE(no estoy buscando IN_OPENeventos).

Esto está dentro de Docker en MacOS, pero he visto evidencia de lo mismo en Docker en Linux en un sistema remoto, por lo que sospecho que el aspecto de MacOS no es relevante. Estoy viendo esto si estoy mirando y estoy git cloneen el mismo contenedor acoplable.

Mis preguntas:

  • ¿Por qué faltan estos eventos en estos archivos?

  • ¿Qué se puede hacer al respecto? Específicamente, ¿cómo puedo responder a la finalización de las escrituras en estos archivos? Nota: idealmente me gustaría responder cuando la escritura esté "terminada" para evitar la carga innecesaria / (incorrecta) de la escritura "sin terminar".


Editar: Lectura https://developer.ibm.com/tutorials/l-inotify/ parece que lo que estoy viendo es coherente con

  • un archivo temporal separado, con nombre como tmp_pack_hBV4Alz, siendo creado, modificado y cerrado;
  • se crea un enlace duro a este archivo, con el .packnombre final ;
  • tmp_pack_hBV4AlzSe borra el nombre original .

Creo que mi problema, que es tratar de usar inotify como un desencadenante para cargar archivos, luego se reduce a notar que el .packarchivo es un enlace duro a otro archivo y cargarlo en este caso.

Michal Charemza
fuente
La respuesta podría estar en algún lugar aquí ...
choroba
@choroba Puede que tengas razón ... Veo muchas referencias a mmap, e inotify no informa el acceso de mmap a los archivos
Michal Charemza
1
Por cierto, ¿cuál es el problema original que estás tratando de resolver (con inotify)? ¿Puede existir alguna solución más robusta que tratar de adivinar qué está haciendo / ha hecho un proceso Git en un repositorio?
Kostix
@kostix Esto es parte de github.com/uktrade/mobius3 , que sincroniza las carpetas de inicio de los usuarios desde contenedores que ejecutan JupyterLab o RStudio en AWS Fargate, hacia y desde S3, y en esas carpetas de inicio puede haber carpetas .git. Sé que la solución inotify nunca será "robusta-robusta" ... pero espero que pueda ser "lo suficientemente robusta".
Michal Charemza
1
@tink Parece que la respuesta aceptada es un parche en el kernel de Linux? Funcionaría, sospecho en general, pero en mi caso en Fargate no tengo ese control. (Y admito que temo un poco las consecuencias de depender de un kernel parcheado a largo plazo incluso si tuviera ese poder ...)
Michal Charemza

Respuestas:

5

Para responder a su pregunta por separado para git2.24.1 en Linux 4.19.95:

  • ¿Por qué faltan estos eventos en estos archivos?

No ves IN_MODIFY/ IN_CLOSE_WRITEeventos porque git clonesiempre intentará usar enlaces duros para los archivos del .git/objectsdirectorio. Al clonar a través de la red o a través de los límites del sistema de archivos, estos eventos aparecerán nuevamente.

  • ¿Qué se puede hacer al respecto? Específicamente, ¿cómo puedo responder a la finalización de las escrituras en estos archivos? Nota: idealmente me gustaría responder cuando la escritura esté "terminada" para evitar la carga innecesaria / (incorrecta) de la escritura "sin terminar".

Para detectar modificaciones de enlaces duros, debe configurar un controlador para el CREATEevento inotify que sigue y realiza un seguimiento de esos enlaces. Tenga en cuenta que un simple CREATEtambién puede significar que se creó un archivo no vacío. Luego, en IN_MODIFY/ IN_CLOSE_WRITEa cualquiera de los archivos, también debe activar la misma acción en todos los archivos vinculados. Obviamente, también debes eliminar esa relación en el DELETEevento.

Probablemente, un enfoque más simple y robusto sería simplemente hacer hash periódicamente todos los archivos y verificar si el contenido de un archivo ha cambiado.


Corrección

Después de comprobar el gitcódigo fuente de cerca y funcionando gitcon strace, encontré que githace archivos de la memoria uso asignadas, pero sobre todo para la lectura de contenido. Vea el uso del xmmapcual siempre se llama con PROT_READsolo. . Por lo tanto, mi respuesta anterior a continuación NO es la respuesta correcta. Sin embargo, con fines informativos, todavía me gustaría mantenerlo aquí:

  • No ve IN_MODIFYeventos porque se packfile.cusa mmappara acceder a archivos y inotifyno informa modificaciones para mmaparchivos ed.

    Desde la página de manual de inotify :

    La API de inotify no informa los accesos a los archivos y las modificaciones que pueden ocurrir debido a mmap (2), msync (2) y munmap (2).

Ente
fuente
Mi mecanismo de detección de cambios depende de IN_CLOSE_WRITE, lo que creo que todavía se activaría al cerrar un archivo que se escribió para usar mmap, porque el archivo debería haber sido abierto en un modo de escritura.
Michal Charemza
Tengo que investigar esto, pero sospecharía que un archivo mapeado de memoria no activa ningún evento de inotificación. La mayoría de los eventos de intoify están vinculados a un estado del descriptor de archivo, pero cuando mmapun archivo las cosas pueden salir un poco fuera de orden. Por ejemplo, aún puede escribir en un descriptor de archivo cerrado cuando tiene el archivo asignado en la memoria.
Ente
Rasca eso, acabo de probar esta implementación de ejemplo y obtengo CLOSE_WRITE_CLOSEincluso si elimino el closey munmapal final. Tengo que profundizar en la implementación real de git entonces ...
Ente
Hmm, estoy luchando un poco para reproducir tu problema. En mis pruebas con inotifywaity git clone(2.24.1) obtengo un OPEN-> CLOSE_NOWRITE,CLOSEpara los *.idxarchivos. ¿Quizás olvidó configurar un controlador para CLOSE_NOWRITE,CLOSE? Nota: Obtendrá un *NOWRITE*porque todas las escrituras ocurridas a través de la memoria asignada son.
Ente
Sí, hay CLOSE_NOWRITE: el problema es que no veo IN_CLOSE_WRITE, y me gustaría responder a los "cambios" del archivo para activar una carga, pero ignorar las "lecturas" del archivo. Tenga en cuenta que realmente creo que en este momento la limitación mmap + inotify es un poco falsa. Creo que el problema es que los archivos .pack/ .idxse crean inicialmente como enlaces duros a otro archivo, por lo que solo se disparan IN_CREATE(y el OPEN-> CLOSE_NOWRITEocurre más tarde cuando git está leyendo los archivos).
Michal Charemza
2

Puedo especular que Git usa la mayoría de las veces actualizaciones de archivos atómicos que se hacen así:

  1. El contenido de un archivo se lee en la memoria (y se modifica).
  2. El contenido modificado se escribe en un archivo separado (generalmente ubicado en el mismo directorio que el original y con un nombre aleatorio ( mktempestilo).
  3. El nuevo archivo es entonces rename(2)d -d sobre el original; Esta operación garantiza que cada observador que intente abrir el archivo usando su nombre obtendrá el contenido anterior o el nuevo.

Tales cambios son vistos por inotify(7)como moved_toeventos-desde un archivo "reaparece" en un directorio.

kostix
fuente
Ah, para algunos archivos, creo que hace esto: veo los diversos IN_MOVED_FROMy IN_MOVED_TOeventos. Sin embargo, no veo que esto suceda para los archivos .packy.idx
Michal Charemza
Los archivos de paquete pueden ser enormes (varios gigabytes, hasta 2GiB al menos, creo); manejarlas usando actualizaciones atómicas podría ser rentable en el espacio de almacenamiento, por lo que podrían actualizarse usando alguna otra estrategia.
Kostix
2

Según esta respuesta aceptada , supongo que podría haber alguna diferencia en los eventos según el protocolo que se esté utilizando (es decir, ssh o https).

¿Observa el mismo comportamiento al monitorear la clonación desde el sistema de archivos local con la --no-hardlinksopción?

$ git clone git@github.com:user/repo.git
# set up watcher for new dir
$ git clone --no-hardlinks repo new-repo

Su comportamiento observado al ejecutar el experimento tanto en un host Linux como en Mac probablemente elimine este problema abierto que es la causa https://github.com/docker/for-mac/issues/896 pero agrega solo en caso.

hundido
fuente
2

Hay otra posibilidad (del hombre inotify):

Tenga en cuenta que la cola de eventos puede desbordarse. En este caso, los eventos se pierden. Las aplicaciones robustas deben manejar la posibilidad de eventos perdidos con gracia. Por ejemplo, puede ser necesario reconstruir parte o la totalidad del caché de la aplicación. (Un enfoque simple, pero posiblemente costoso, es cerrar el descriptor de archivo de inotify, vaciar la memoria caché, crear un nuevo descriptor de archivo de inotify y luego volver a crear relojes y entradas de caché para los objetos que se van a monitorear).

Y si bien git clonepuede generar un gran flujo de eventos, esto puede suceder.

Cómo evitar esto:

  1. Aumente el búfer de lectura, intente fcntl (F_SETPIPE_SZ) (este enfoque es una suposición, nunca lo he intentado).
  2. Lea eventos en un búfer grande en un hilo dedicado, procese eventos en otro hilo.
Yury Nevinitsin
fuente
2

Tal vez cometiste el mismo error que cometí hace años. Solo he usado inotify dos veces. La primera vez, mi código simplemente funcionó. Más tarde, ya no tenía esa fuente y comencé de nuevo, pero esta vez, me faltaban eventos y no sabía por qué.

Resulta que cuando estaba leyendo un evento, realmente estaba leyendo un pequeño lote de eventos. Analicé el que esperaba, pensando que era eso, eso era todo. Eventualmente, descubrí que hay más en los datos recibidos, y cuando agregué un pequeño código para analizar todos los eventos recibidos de una sola lectura, no se perdieron más eventos.

donjuedo
fuente