
Hemos estado ejecutando nuestro sistema Java durante más de 2 años sin que ningún sistema se cuelgue. Tenemos 2 servidores físicos que ejecutan software Java similar (2 JVM en cada servidor) para formar un clúster. Hasta donde puedo decir, el bloqueo solo comenzó a ocurrir cuando introdujimos la fijación de núcleos y mappedbus.io para el acceso a la memoria compartida entre 2 JVM en uno de los servidores. El bloqueo del sistema solo ocurrió 4 veces en 2 semanas, y solo ocurre en la máquina donde configuramos la fijación del núcleo y el acceso a archivos asignados en memoria entre las JVM. Deshabilitamos esa configuración, por lo que no fijamos los núcleos para que giren al leer los archivos asignados en memoria y no fijamos el hilo de nuestra aplicación principal. Tenga en cuenta que cuando digo fijar, también estamos ocupados haciendo girar el hilo que se ejecuta en ese núcleo fijado.
Aunque esto es totalmente anecdótico. Como el sistema no se bloquea todos los días, no puedo decir con seguridad que tenga algo que ver con la fijación del núcleo o el acceso a la memoria compartida. Sin embargo, con la fijación (y el giro ocupado) deshabilitados y el acceso a la memoria compartida en un bucle con LockSupport.parkNanos(5000), no parece que tengamos ningún bloqueo en el sistema.
La latencia es fundamental para nosotros, por lo que esta configuración "no ocupada" es sólo una solución temporal.
Además, tenga en cuenta que moví la aplicación a un servidor idéntico y también pude experimentar este bloqueo completo del sistema. Entonces no veo que esto sea una falla de hardware.
Entonces, al investigar los registros antes o después de un accidente, esto es lo que me parece relevante. Hay varias de estas pilas. Solo estoy publicando el primero aquí (es decir, no creo que esto tenga nada que ver con postgres en sí)
kernel: [25738.874778] INFO: task postgres:2155 blocked for more than 120 seconds.
kernel: [25738.874833] Not tainted 5.4.0-050400-generic #201911242031
kernel: [25738.874878] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
kernel: [25738.874928] postgres D 0 2155 2056 0x00004000
kernel: [25738.874931] Call Trace:
kernel: [25738.874942] __schedule+0x2e3/0x740
kernel: [25738.874948] ? __wake_up_common_lock+0x8a/0xc0
kernel: [25738.874951] schedule+0x42/0xb0
kernel: [25738.874957] jbd2_log_wait_commit+0xaf/0x120
kernel: [25738.874961] ? wait_woken+0x80/0x80
kernel: [25738.874965] jbd2_complete_transaction+0x5c/0x90
kernel: [25738.874969] ext4_sync_file+0x38c/0x3e0
kernel: [25738.874974] vfs_fsync_range+0x49/0x80
kernel: [25738.874977] do_fsync+0x3d/0x70
kernel: [25738.874980] __x64_sys_fsync+0x14/0x20
kernel: [25738.874985] do_syscall_64+0x57/0x190
kernel: [25738.874991] entry_SYSCALL_64_after_hwframe+0x44/0xa9
kernel: [25738.874993] RIP: 0033:0x7f96dc24b214
kernel: [25738.875002] Code: Bad RIP value.
kernel: [25738.875003] RSP: 002b:00007fffb2abd868 EFLAGS: 00000246 ORIG_RAX: 000000000000004a
kernel: [25738.875006] RAX: ffffffffffffffda RBX: 00007fffb2abd874 RCX: 00007f96dc24b214
kernel: [25738.875007] RDX: 00005635889ba238 RSI: 00005635889a1490 RDI: 0000000000000003
kernel: [25738.875009] RBP: 00007fffb2abd930 R08: 00005635889a1480 R09: 00007f96cc1e1200
kernel: [25738.875010] R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
kernel: [25738.875011] R13: 0000000000000000 R14: 000056358899c5a0 R15: 0000000000000001
PD: esto sucedió en 16.04 y también en el kernel 4.15. La actualización a 18.04 y 5.0 fue un intento de resolver el bloqueo del sistema, pero no hizo ninguna diferencia.
La otra cosa que he considerado es que quizás este rastro sea solo un síntoma, no el problema. Es decir, mi aplicación ha vinculado el servidor y ha provocado que otros procesos se bloqueen en io y reciban estos errores. Pero como el servidor se congela por completo, no tengo forma de saber el estado de mi aplicación en ese momento.
Información adicional en respuesta a comentarios
Primero, solo para reiterar que no tengo evidencia sólida de que la fijación del núcleo + la memoria compartida sea la gota que colma el proverbial vaso, pero esta es mi mejor suposición basada en el historial de cambios y las interrupciones.
El modelo de CPU es CPU Intel(R) Xeon(R) E5-2620 v4 a 2,10 GHz con turbo boost. Hay 2 de estos en el servidor. Estoy fijando los números de CPU 2,4,6 que creo que están en la misma CPU física. Hyperthreading está activado.
La configuración es así. JVM-A tiene un hilo de giro ocupado fijado que escribe en el archivo X asignado en memoria y lee desde el archivo Y asignado en memoria. JVM-B tiene un hilo de giro ocupado fijado que lee desde el archivo X asignado en memoria y escribe de nuevo en el archivo Y asignado en memoria. En JVM- B, el hilo de lectura fijado publica el mensaje en un buffer de anillo disruptor con un trabajador de giro ocupado fijado. El mensaje es una instrucción de orden que finalmente se envía al mercado para este trabajador. Esta es una plataforma comercial de baja latencia.
Esta publicación ofrece una mejor exploración de LockSupport.parkNanos que la que puedo hacer aquíhttps://hazelcast.com/blog/locksupport-parknanos-under-the-hood-and-the-curious-case-of-parking/
Tengo 2 HDD de 10.000 rpm en RAID 1 con controlador RAID integrado.
Con respecto a la latencia objetivo, sí, en teoría podríamos fusionar las dos JVM en una y deshacernos por completo de este canal de archivos asignado en memoria. Sin embargo, hay otras consideraciones antes de hacer eso, por lo que me gustaría centrarme primero en comprender este problema técnico.
Finalmente, postgres en este servidor se ejecuta solo en modo de recuperación, no es el principal. Además, nuestro sistema no realiza muchas E/S de bases de datos. En realidad, solo se utiliza para arrancar y comenzar el día, y persistir la actividad comercial del día durante la noche. Uno de los fallos se produjo en un momento en el que no habría habido casi ninguna E/S de la base de datos.
Respuesta1
"Bloqueado" en el caso de hung_task_timeout_secs
significa que una tarea estuvo en estado D ininterrumpible durante ese tiempo. 120 segundos es una cantidad de tiempo bastante extraordinaria para realizar E/S.
Implemente un monitoreo que pueda obtener métricas de este host.datos de redes bueno para esto, recopila muchas cosas cada segundo en la memoria, por lo que no hay muchas E/S de disco. Y tiene buenos gráficos.
Revise la latencia del disco, como con iostat -xz 1
. Las esperas superiores a ms de un solo dígito no son buenas. Comparta cuál es ese almacenamiento: ejes, estado sólido, SAN LUN.
Con respecto a girar y fijar, sospecho que estás obligando al programador a morir de hambre. Comparta el modelo de CPU específico en cuestión y qué núcleos está asignando para hacer qué. ¿Cómo se LockSupport.parkNanos()
implementa?
Revisar vmstat 1
. Tener constantemente muchas tareas en estado de ejecución r
o ininterrumpibles b
no es bueno.
Considere instalar BPF y usar scripts para recopilar diagnósticos de tareas.runqslower
mostrará tareas en espera por encima de un cierto umbral. Lo ideal es muy rápido; tenga en cuenta que las unidades de umbral son microsegundos.
Retrocediendo un momento, consideremos el diseño de esta cosa.
¿Cuál es exactamente el objetivo de latencia, qué hacer y con qué rapidez?
¿Hay alguna razón por la que postgres se esté ejecutando en el mismo host? Si fuera remoto y se accediera a través de TCP, su E/S no sería un problema para la aplicación JVM.
Respuesta2
Bien, al final el problema fue bastante simple. Mis pruebas aisladas nunca pudieron bloquear la máquina porque me faltaba este elemento en mi código de prueba. El problema no tiene nada que ver con la memoria compartida o la fijación del núcleo en sí misma. Es solo que aislar los núcleos redujo ligeramente el recurso compartido disponible hasta el punto en que el programador podría quedarse sin recursos, porque...
Ambas JVM se configuraron con prioridad en tiempo real usando
sudo renice -n -20 $!
sudo chrt -r -a -p 99 $!
Toda la JVM se vio afectada, por lo que en total casi 300 subprocesos con prioridad máxima. Cambio de contexto superior a 150.000/s incluso con una utilización de CPU relativamente baja.
Hemos dejado la amabilidad y eliminado el cambio en tiempo real. Esto parece haberlo solucionado. El objetivo original de la configuración RT heredada se puede lograr cambiando la forma en que giramos/fijamos/estados-c/estados-p, etc.