El lado oscuro del DRM en Oracle RAC: Micro-cortes y el evento 'gcs drm freeze'

En un entorno Oracle RAC, la "magia" de la coherencia de caché tiene un precio.
Hoy analizaré un caso real (los que me gustan!) donde el mecanismo de Dynamic Remastering (DRM) causó un micro-delay de casi un segundo, demorando transacciones críticas.
El síntoma: El evento de espera "fantasma"
Al revisar la ASH (dba_hist_active_sess_history) debido a una demora puntual de menos de 1 segundo, detecto algo que me llama la atención.
SYS@cdbxxx1> SELECT
event,
count(*) as total_capturas,
round(avg(wait_time+time_waited)/1000, 2) as avg_wait_ms
FROM dba_hist_active_sess_history
WHERE sample_time BETWEEN TO_TIMESTAMP('22/02/26 06:13:00', 'DD/MM/YY HH24:MI:SS')
AND TO_TIMESTAMP('22/02/26 06:14:00', 'DD/MM/YY HH24:MI:SS')
GROUP BY event
ORDER BY 3 DESC;
EVENT TOTAL_CAPTURAS AVG_WAIT_MS
---------------------------------------------------------------- -------------- -----------
gcs drm freeze in enter server mode 1 919.47
LGWR worker group ordering 1 33.48
log file sync 31 26.52
LGWR any worker group 2 17.67
4 .75
db file sequential read 1 .7
buffer busy waits 1 .39
log file parallel write 2 .38
rdbms ipc reply 1 0
Evento: gcs drm freeze in enter server mode
Tiempo de espera: 919.47 ms (¡Casi 1 segundo!)
¿Qué estaba pasando internamente? (La traza de LMON)
La traza del proceso LMON nos da la respuesta. El sistema detectó que ciertos objetos tenían una carga de lectura/escritura que no era óptima para su ubicación actual y decidió "remasterizarlos" (mover la gestión del objeto de un nodo a otro).
*** 2026-02-22T06:13:24.101236+01:00 (CDB$ROOT(1))
* DRM RCFG called (swin 0)
* kjfcdrmrfg: cap sync timeout at 3 * default (752 -> 489 secs, res limit 7518552)
Begin DRM(29029) (swin 0)
AFFINITY pdb 3 tsn 13 object id 720963, * -> 2 obj size:7333
AFFINITY pdb 3 tsn 12 object id 720969, 1 -> * obj size:449893
AFFINITY pdb 3 tsn 11 object id 720996, 1 -> * obj size:217838
AFFINITY pdb 3 tsn 52 object id 731023, * -> 2 obj size:92115
AFFINITY pdb 3 tsn 13 object id 731026, * -> 2 obj size:6331
AFFINITY pdb 3 tsn 13 object id 731027, * -> 2 obj size:12668
AFFINITY pdb 3 tsn 13 object id 731028, * -> 2 obj size:5780
AFFINITY pdb 3 tsn 13 object id 731029, * -> 2 obj size:6685
AFFINITY pdb 3 tsn 13 object id 731030, * -> 2 obj size:6077
AFFINITY pdb 3 tsn 13 object id 731031, * -> 2 obj size:5386
AFFINITY pdb 3 tsn 13 object id 731032, * -> 2 obj size:7677
AFFINITY pdb 3 tsn 13 object id 731033, * -> 2 obj size:5352
AFFINITY pdb 3 tsn 13 object id 731034, * -> 2 obj size:3425
AFFINITY pdb 3 tsn 13 object id 731035, * -> 2 obj size:4113
AFFINITY pdb 3 tsn 13 object id 731036, * -> 2 obj size:3602
AFFINITY pdb 3 tsn 13 object id 731037, * -> 2 obj size:7983
AFFINITY pdb 3 tsn 13 object id 731038, * -> 2 obj size:5376
AFFINITY pdb 3 tsn 13 object id 731039, * -> 2 obj size:3714
AFFINITY pdb 3 tsn 14 object id 329632, * -> 2 obj size:295
AFFINITY pdb 3 tsn 13 object id 731040, * -> 2 obj size:5576
AFFINITY pdb 3 tsn 13 object id 731041, * -> 2 obj size:7776
AFFINITY pdb 3 tsn 13 object id 731042, * -> 2 obj size:2757
AFFINITY pdb 3 tsn 13 object id 706860, 1 -> * obj size:131057
AFFINITY pdb 3 tsn 13 object id 690745, * -> 2 obj size:634
AFFINITY pdb 3 tsn 51 object id 733055, * -> 2 obj size:4095
AFFINITY pdb 3 tsn 14 object id 447506, * -> 2 obj size:257
AFFINITY pdb 3 tsn 13 object id 718971, * -> 2 obj size:1709
Max cumultv sz 2624716 sz limit 4294967295 pk cnt 27 total objsz 1005504
individual max sizes [ 1005504 2624716 ]
Estimated time 6.2 secs (avgtime 0.9 secs, avgsize 384055)
* drm freeze
drm (29029) freeze step (window 1) - posted lmses/lmds for mtobe
drm (29029) freeze step (window 1) - apifrz = true
drm (29029) freeze step (window 1) - all lms procs frozen
* drm sync 1
* drm cleanup
* kjfclmsync: lmses complete parallel work in step 0.33.0
* drm sync 2
* drm replay
* kjfclmsync: lmses complete parallel work in step 0.35.0
send ftd(35) to 2:
* drm sync 3
* drm fix writes
* kjfclmsync: lmses complete parallel work in step 0.37.0
* drm sync 4
* drm end
* window 1 time 0.9 secs)
* DRM RCFG called (swin 0)
* kjfcdrmrfg: cap sync timeout at 3 * default (768 -> 489 secs, res limit 7673832)
* drm freeze
drm (29029) freeze step (window 2) - posted lmses/lmds for mtobe
drm (29029) freeze step (window 2) - apifrz = true
drm (29029) freeze step (window 2) - all lms procs frozen
* drm sync 1
* drm cleanup
*** 2026-02-22T06:13:25.234977+01:00 (CDB$ROOT(1))
* kjfclmsync: lmses complete parallel work in step 0.33.0
* drm sync 2
* drm replay
* kjfclmsync: lmses complete parallel work in step 0.35.0
send ftd(35) to 2:
* drm sync 3
* drm fix writes
* kjfclmsync: lmses complete parallel work in step 0.37.0
* drm sync 4
* drm end
* window 2 time 1.0 secs)
window 1 time 0.9 secs: Exactamente los 919 ms que capturó el ASH
El momento del "Freeze"
Lo más crítico de la traza es cuando Oracle congela los procesos LMS (los encargados de mover los bloques de datos por el interconnect):
* drm freeze drm (29029) freeze step (window 1)
* apifrz = true
* all lms procs frozen
¿Por qué es esto un problema? Cuando los procesos LMS se congelan para reorganizar las afinidades de los objetos, toda la comunicación de bloques entre nodos se detiene. Si tu aplicación está intentando leer un dato que está en el otro nodo en ese instante, la sesión se queda colgada en el evento gcs drm freeze.
Los objetos afectados: Identificando a los culpables
Cruzando los datos de gv$policy_history y dba_objects, veo que el sistema estuvo moviendo afinidades de particiones críticas:
select h.*,o.object_name, o.SUBOBJECT_NAME
from gv$Policy_History h, dba_objects o
where h.data_object_id = o.data_object_id
and o.object_id IN (564682, 391009, 504609, 626248, 716966, 720969, 720996)
and TO_DATE(EVENT_DATE, 'MM/DD/YYYY HH24:MI:SS') BETWEEN TO_DATE('02/22/2026 06:00:00', 'MM/DD/YYYY HH24:MI:SS') AND TO_DATE('02/22/2026 07:00:00', 'MM/DD/YYYY HH24:MI:SS')
order by TO_DATE(EVENT_DATE, 'MM/DD/YYYY HH24:MI:SS') desc;
INST_ID POLICY_EVENT DATABASE_ID TABLESPACE_ID DATA_OBJECT_ID TARGET_INSTANCE_NUMBER EVENT_DATE CON_ID OBJECT_NAME SUBOBJECT_NAME
---------- ---------------------- ----------- ------------- -------------- ---------------------- -------------------- ---------- -------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------
1 initiate_affinity 3 12 720969 2 02/22/2026 06:15:37 0 TABLA1 SYS_P16222
1 initiate_affinity 3 51 716966 2 02/22/2026 06:15:37 0 TABLA2 SYS_P16177
1 initiate_affinity 3 11 720996 2 02/22/2026 06:15:37 0 TABLA3 SYS_P16226
1 push_affinity 3 14 564682 2 02/22/2026 06:05:37 0 TABLA4 SYS_P15690
1 push_affinity 3 14 504609 2 02/22/2026 06:05:37 0 TABLA4 SYS_P15471
1 push_affinity 3 14 391009 2 02/22/2026 06:05:37 0 TABLA4 SYS_P15123
1 push_affinity 3 14 626248 2 02/22/2026 06:05:37 0 TABLA4 SYS_P15914
El sistema "empujó" (push_affinity) la gestión de los objetos hacia la instancia 2.
El "Culpable" Inesperado: El Job de Estadísticas
En nuestro caso real, tras investigar los horarios y el module de las sesiones, descubrimos al responsable. No era un error de la aplicación, sino el Job automático de estadísticas que entró por el nodo 2. La tormenta perfecta
La Aplicación está trabajando intensamente en el Nodo 1, leyendo y escribiendo. Oracle ha establecido la afinidad de esos objetos en el Nodo 2 para optimizar el rendimiento.
El Job de Estadísticas se lanza desde el Nodo 2. Para calcular las estadísticas, el Nodo 12empieza a pedir masivamente bloques .
El Conflicto: El DRM de Oracle ve que el Nodo 2 ahora tiene una actividad brutal sobre esos objetos. Consulta su parámetro gcaffinity_ratio y dice: "Oye, el Nodo 2 está pidiendo esto mucho más que antes, voy a mover la maestría del objeto del Nodo 1 al Nodo 2".
El Resultado: Durante ese cambio de "dueño", se produce el GCS Freeze. La aplicación en el Nodo 1 se detiene en seco durante 919 ms esperando a que el Nodo 2 termine de reclamar la propiedad de los datos.
El "Secuestro" de la Afinidad
Lo que ocurrió en este caso es un secuestro de afinidad. Las estadísticas engañaron al DRM haciéndole creer que el nodo donde se ejecutaban era el más importante. El freeze de 919 ms fue el precio que pagó la aplicación mientras Oracle le "quitaba" las llaves de la tabla para dárselas al proceso de estadísticas.
Opciones de Ajuste Fino (Tuning) del DRM
Si decides mantener el DRM activo para aprovechar la optimización del Interconnect, pero quieres evitar los freezes causados por procesos como las estadísticas, tienes estos tres niveles de configuración:
El "Filtro de Ruido" (_gc_affinity_ratio)
Este es el parámetro que determina cuánta "presión" debe ejercer un nodo remoto para robarle la maestría a otro.
Valor por defecto: 50.
Ajuste recomendado: 1000 o superior.
Efecto: Al subirlo a 1000, el proceso de estadísticas tendría que realizar muchísimas más solicitudes que la aplicación para que el DRM considere que vale la pena mover el objeto. Esto hace que el sistema ignore "picos" de trabajo de mantenimiento y se mantenga fiel a la aplicación OLTP.
La "Ventana de Paciencia" (_gc_policy_time)
Controla cada cuánto tiempo (en minutos) Oracle evalúa si debe remasterizar objetos.
Valor por defecto: 10.
Ajuste recomendado: 60.
Efecto: Si tu job de estadísticas dura 30 minutos, pero la ventana de evaluación es de 60, el job terminará antes de que el DRM decida que el cambio de maestría es necesario. Evitas el freeze simplemente siendo más paciente.
El "Límite de Agresión" (_gc_affinity_limit)
Define el número mínimo de accesos necesarios para que un objeto sea siquiera candidato a DRM.
Ajuste recomendado: Incrementar el valor actual (puedes ver el actual en V$GC_POLICY_MODIFICATIONS).
Efecto: Evita que tablas pequeñas o medianas sean movidas de nodo constantemente por consultas aleatorias o jobs rápidos, reservando el DRM solo para objetos donde el ahorro de tráfico sea masivo y justificado.
Segmentación y Afinidad de Servicios
Si tu entorno tiene múltiples aplicaciones, la solución no es solo mover las estadísticas, sino separar las cargas de trabajo por nodos. De esta forma, el DRM deja de ser un problema para convertirse en un aliado silencioso.
Segmentación de Aplicaciones
Imagina que tienes la App A y la App B. Si ambas conectan a ambos nodos, el Interconnect será un caos de tráfico cruzado.
Nodo 1: Configurado como PREFERRED para el servicio de la App A.
Nodo 2: Configurado como PREFERRED para el servicio de la App B.
El lugar de las Estadísticas en un entorno segmentado
En este escenario, tienes dos opciones inteligentes para el mantenimiento:
Alineación Total: Lanzas las estadísticas de las tablas de la App A en el Nodo 1, y las de la App B en el Nodo 2 Resultado: El DRM no se inmuta. Los bloques ya están donde deben estar. Es la opción más eficiente en red.
Nodo de Mantenimiento Dedicado: Si prefieres no cargar los nodos donde están las Apps, puedes usar un tercer nodo (si lo tienes) o un nodo con menos carga. Pero aquí debes usar el tuning de parámetros (_gc_affinity_ratio) para que el DRM no intente "robar" la maestría de las tablas hacia el nodo de mantenimiento.
Conclusión
El DRM es una herramienta de optimización, pero es ciega. No sabe qué proceso es más importante; solo ve volumen de tráfico. Si dejas que las estadísticas corran en el nodo donde menos impacto tendrá, el DRM castigará a tu aplicación para favorecer a un proceso de mantenimiento.



