

# LWLock:SubtransSLRU (LWLock:SubtransControlLock)
<a name="wait-event.lwlocksubtransslru"></a>

Los eventos de espera `LWLock:SubtransSLRU` y `LWLock:SubtransBuffer` indican que una sesión está esperando para acceder a la caché simple de uso menos reciente (SLRU) para la información de subtransacciones. Esto ocurre al determinar la visibilidad de las transacciones y las relaciones entre principales y secundarios.
+ `LWLock:SubtransSLRU`: un proceso está esperando para acceder a la caché simple de uso menos reciente (SLRU) para una subtransacción. En RDS para PostgreSQL anterior a la versión 13, este evento de espera se llama `SubtransControlLock`.
+ `LWLock:SubtransBuffer`: un proceso está esperando la E/S en un búfer simple de uso menos reciente (SLRU) para una subtransacción. En RDS para PostgreSQL anterior a la versión 13, este evento de espera se llama `subtrans`.

**Topics**
+ [Versiones del motor admitidas](#wait-event.lwlocksubtransslru.supported)
+ [Contexto](#wait-event.lwlocksubtransslru.context)
+ [Causas probables del aumento de las esperas](#wait-event.lwlocksubtransslru.causes)
+ [Acciones](#wait-event.lwlocksubtransslru.actions)

## Versiones del motor admitidas
<a name="wait-event.lwlocksubtransslru.supported"></a>

Esta información de eventos de espera es compatible con todas las versiones de RDS para PostgreSQL.

## Contexto
<a name="wait-event.lwlocksubtransslru.context"></a>

**Descripción de las subtransacciones**: una subtransacción es una transacción dentro de una transacción en PostgreSQL. También se conoce como transacción anidada.

Por lo general, las subtransacciones se crean cuando se utiliza:
+ `SAVEPOINT`Comandos de 
+ Bloques de excepciones (`BEGIN/EXCEPTION/END`)

Las subtransacciones le permiten revertir partes de una transacción sin que ello afecte a toda la transacción. Esto le brinda un control minucioso sobre la administración de transacciones.

**Detalles de implementación**: PostgreSQL implementa subtransacciones como estructuras anidadas dentro de las transacciones principales. Cada subtransacción recibe su propio ID de transacción.

Aspectos de implementación claves:
+ Los ID de las transacciones se rastrean en `pg_xact`
+ Las relaciones principal-secundario se almacenan en el subdirectorio `pg_subtrans` en `PGDATA`
+ Cada sesión de base de datos puede mantener hasta `64` subtransacciones activas
+ Si se supera este límite se produce un desbordamiento de subtransacciones, lo que requiere acceder a la caché simple de uso menos reciente (SLRU) para la información de subtransacciones

## Causas probables del aumento de las esperas
<a name="wait-event.lwlocksubtransslru.causes"></a>

Entre las causas más comunes de la contención de SLRU en las subtransacciones se incluyen:
+ **Uso excesivo del manejo de SAVEPOINT y EXCEPTION**: los procedimientos PL/pgSQL con controladores `EXCEPTION` crean automáticamente puntos de almacenamiento implícitos, independientemente de si se producen o no excepciones. Cada `SAVEPOINT` inicia una nueva subtransacción. Cuando una sola transacción acumula más de 64 subtransacciones, desencadena un desbordamiento de SLRU de subtransacciones.
+ **Configuraciones de controladores y ORM**: el uso de `SAVEPOINT` puede ser explícito en el código de la aplicación o implícito en las configuraciones de los controladores. Muchas de las herramientas ORM y los marcos de aplicaciones más utilizados admiten transacciones anidadas de forma nativa. Estos son algunos ejemplos comunes:
  + El parámetro del controlador JDBC `autosave`, si está establecido en `always` o `conservative`, genera puntos de almacenamiento antes de cada consulta.
  + Definiciones de transacciones de Spring Framework cuando se establece en `propagation_nested`.
  + Registros de seguimiento cuando `requires_new: true` está configurado.
  + SQLAlchemy cuando `session.begin_nested` se usa.
  + Django cuando se usan bloques de `atomic()` anidados.
  + GORM cuando se usa `Savepoint`.
  + psqlODBC cuando la configuración por reversión se establece en la reversión por instrucción (por ejemplo, `PROTOCOL=7.4-2`).
+ **Cargas de trabajo simultáneas altas con transacciones y subtransacciones de larga duración**: cuando se produce un desbordamiento de la SLRU de subtransacciones durante cargas de trabajo simultáneas altas y transacciones y subtransacciones de larga duración, PostgreSQL experimenta una mayor contención. Esto se manifiesta como eventos de espera elevados para bloqueos `LWLock:SubtransBuffer` y `LWLock:SubtransSLRU`.

## Acciones
<a name="wait-event.lwlocksubtransslru.actions"></a>

Recomendamos diferentes acciones en función de las causas del evento de espera. Algunas acciones proporcionan un alivio inmediato, mientras que otras requieren una investigación y una corrección a largo plazo.

**Topics**
+ [Supervisión del uso de subtransacciones](#wait-event.lwlocksubtransslru.actions.monitor)
+ [Configuración de los parámetros de memoria](#wait-event.lwlocksubtransslru.actions.memory)
+ [Acciones a largo plazo](#wait-event.lwlocksubtransslru.actions.longterm)

### Supervisión del uso de subtransacciones
<a name="wait-event.lwlocksubtransslru.actions.monitor"></a>

Para las versiones 16.1 y posteriores de PostgreSQL, utilice la siguiente consulta para supervisar los recuentos de subtransacciones y el estado de desbordamiento por backend. Esta consulta combina las estadísticas del backend con la información de la actividad para mostrar qué procesos utilizan subtransacciones:

```
SELECT a.pid, usename, query, state, wait_event_type,
       wait_event, subxact_count, subxact_overflowed
FROM (SELECT id, pg_stat_get_backend_pid(id) pid, subxact_count, subxact_overflowed
      FROM pg_stat_get_backend_idset() id
           JOIN LATERAL pg_stat_get_backend_subxact(id) AS s ON true
     ) a
JOIN pg_stat_activity b ON a.pid = b.pid;
```

Para las versiones 13.3 y posteriores de PostgreSQL, supervise la vista `pg_stat_slru` para comprobar la presión de la caché de las subtransacciones. La siguiente consulta SQL recupera las estadísticas de la caché de la SLRU para el componente Subtrans:

```
SELECT * FROM pg_stat_slru WHERE name = 'Subtrans';
```

Un valor `blks_read` que aumenta de forma coherente indica el acceso frecuente al disco para las subtransacciones no almacenadas en caché, lo que indica una posible presión en la memoria caché de la SLRU.

### Configuración de los parámetros de memoria
<a name="wait-event.lwlocksubtransslru.actions.memory"></a>

Para PostgreSQL 17.1 y versiones posteriores, puede configurar el tamaño de la caché de SLRU de la subtransacción mediante el parámetro `subtransaction_buffers`. En el siguiente ejemplo de configuración, se muestra cómo establecer el parámetro de búfer de subtransacciones:

```
subtransaction_buffers = 128
```

Este parámetro especifica la cantidad de memoria compartida que se utiliza para almacenar en caché el contenido de las subtransacciones (`pg_subtrans`). Si se especifica sin unidades, el valor representa bloques de `BLCKSZ` bytes, normalmente de 8 KB cada uno. Por ejemplo, si se establece el valor en 128, se asigna 1 MB (128 \$1 8 KB) de memoria para la caché de subtransacciones.

**nota**  
Puede establecer este parámetro por clúster para que todas las instancias se mantengan coherentes. Pruebe y ajuste el valor para que se adapte a los requisitos de carga de trabajo específicos y a la clase de instancia. Debe reiniciar la instancia de escritor para que los cambios de parámetro tengan efecto.

### Acciones a largo plazo
<a name="wait-event.lwlocksubtransslru.actions.longterm"></a>
+ **Examine el código y las configuraciones de la aplicación**: revise las configuraciones del código de la aplicación y del controlador de la base de datos para ver el uso explícito e implícito de `SAVEPOINT` y el uso de subtransacciones en general. Identifique las transacciones que podrían generar más de 64 subtransacciones.
+ **Reduzca el uso de puntos de almacenamiento**: minimice el uso de puntos de almacenamiento en las transacciones:
  + Revise los procedimientos y las funciones de PL/pgSQL con bloques EXCEPTION. Los bloques EXCEPTION crean automáticamente puntos de almacenamiento implícitos, que pueden contribuir al desbordamiento de las subtransacciones. Cada cláusula EXCEPTION crea una subtransacción, independientemente de si realmente se produce una excepción durante la ejecución.  
**Example**  

    Ejemplo 1: Uso problemático del bloque EXCEPTION

    En el siguiente ejemplo de código, se muestra el uso problemático del bloque EXCEPTION que crea varias subtransacciones:

    ```
    CREATE OR REPLACE FUNCTION process_user_data()
    RETURNS void AS $$
    DECLARE
        user_record RECORD;
    BEGIN
        FOR user_record IN SELECT * FROM users LOOP
            BEGIN
                -- This creates a subtransaction for each iteration
                INSERT INTO user_audit (user_id, action, timestamp)
                VALUES (user_record.id, 'processed', NOW());
                
                UPDATE users 
                SET last_processed = NOW() 
                WHERE id = user_record.id;
                
            EXCEPTION
                WHEN unique_violation THEN
                    -- Handle duplicate audit entries
                    UPDATE user_audit 
                    SET timestamp = NOW() 
                    WHERE user_id = user_record.id AND action = 'processed';
            END;
        END LOOP;
    END;
    $$ LANGUAGE plpgsql;
    ```

    El ejemplo de código mejorado siguiente reduce el uso de subtransacciones mediante el uso de UPSERT en lugar del manejo de excepciones:

    ```
    CREATE OR REPLACE FUNCTION process_user_data()
    RETURNS void AS $$
    DECLARE
        user_record RECORD;
    BEGIN
        FOR user_record IN SELECT * FROM users LOOP
            -- Use UPSERT to avoid exception handling
            INSERT INTO user_audit (user_id, action, timestamp)
            VALUES (user_record.id, 'processed', NOW())
            ON CONFLICT (user_id, action) 
            DO UPDATE SET timestamp = NOW();
            
            UPDATE users 
            SET last_processed = NOW() 
            WHERE id = user_record.id;
        END LOOP;
    END;
    $$ LANGUAGE plpgsql;
    ```  
**Example**  

    Ejemplo 2: Controlador de excepciones STRICT

    En el ejemplo de código siguiente, se muestra un manejo problemático de EXCEPTION con NO\$1DATA\$1FOUND:

    ```
    CREATE OR REPLACE FUNCTION get_user_email(p_user_id INTEGER)
    RETURNS TEXT AS $$
    DECLARE
        user_email TEXT;
    BEGIN
        BEGIN
            -- STRICT causes an exception if no rows or multiple rows found
            SELECT email INTO STRICT user_email 
            FROM users 
            WHERE id = p_user_id;
            
            RETURN user_email;
            
        EXCEPTION
            WHEN NO_DATA_FOUND THEN
                RETURN 'Email not found';
        END;
    END;
    $$ LANGUAGE plpgsql;
    ```

    El ejemplo de código mejorado siguiente evita subtransacciones mediante el uso de IF NOT FOUND en lugar del manejo de excepciones:

    ```
    CREATE OR REPLACE FUNCTION get_user_email(p_user_id INTEGER)
    RETURNS TEXT AS $$
    DECLARE
        user_email TEXT;
    BEGIN
         SELECT email INTO user_email 
         FROM users 
         WHERE id = p_user_id;
            
         IF NOT FOUND THEN
             RETURN 'Email not found';
         ELSE
             RETURN user_email;
         END IF;
    END;
    $$ LANGUAGE plpgsql;
    ```
  + Controlador JDBC: el parámetro `autosave`, si está establecido en `always` o `conservative`, genera puntos de almacenamiento antes de cada consulta. Evalúe si la configuración de `never` sería aceptable para la aplicación.
  + Controlador ODBC de PostgreSQL (psqlODBC): la configuración del nivel de reversión (para la reversión por instrucción) crea puntos de almacenamiento implícitos para habilitar la funcionalidad de reversión de instrucciones. Evalúe si el nivel de la transacción o sin reversión sería aceptable para la aplicación. 
  + Examen de las configuraciones de transacciones de ORM
  + Consideración de estrategias alternativas de manejo de errores que no requieran puntos de almacenamiento
+ **Optimización del diseño de las transacciones**: reestructure las transacciones para evitar el anidamiento excesivo y reducir la probabilidad de que se produzcan desbordamientos de las subtransacciones.
+ **Reduzca las transacciones de larga duración**: las transacciones de larga duración pueden agravar los problemas relacionados con las subtransacciones al conservar la información de las subtransacciones durante más tiempo. Supervise las métricas de información de rendimiento y configure el parámetro `idle_in_transaction_session_timeout` para terminar automáticamente las transacciones inactivas.
+ Supervise las métricas de información de rendimiento: realice un seguimiento de las métricas, incluido `idle_in_transaction_count` (número de sesiones inactivas en estado de transacción) y `idle_in_transaction_max_time` (duración de la transacción inactiva más prolongada) para detectar transacciones de larga duración.
+ Configure `idle_in_transaction_session_timeout`: defina este parámetro en el grupo de parámetros para terminar automáticamente las transacciones inactivas después de un periodo especificado.
+ Supervisión proactiva: supervise los eventos de espera de alta frecuencia `LWLock:SubtransBuffer` y `LWLock:SubtransSLRU` para detectar conflictos relacionados con las subtransacciones antes de que se conviertan en críticos.