Bloqueos de entrada de correlación con consultas e índices

Este tema describe cómo las API de consulta de eXtreme Scale y el plug-in de indexación MapRangeIndex interactúan con bloqueos y algunos procedimientos recomendados para aumentar la simultaneidad y reducir los puntos muertos al utilizar la estrategia de bloqueo pesimista para correlaciones.

Visión general

La API de consulta de ObjectGrid permite consultas SELECT en entidades y objetos de la memoria caché ObjectMap. Cuando se ejecuta una consulta, el motor de consultas utiliza un índice MapRangeIndex, siempre que es posible, para buscar claves coincidentes con los valores de la cláusula WHERE de la consulta o para servir de puente de las relaciones. Si no hay ningún índice disponible, el motor de consultas explorará cada entrada en una o más correlaciones para buscar las entradas correspondientes. El motor de consultas y los plug-ins de índices adquirirán bloqueos para comprobar los datos coherentes, en función de la estrategia de bloqueo, el nivel de aislamiento y el estado de la transacción.

Bloqueo con el plug-in HashIndex

El plug-in HashIndex de eXtreme Scale permite encontrar claves basadas en un único atributo almacenado en el valor de la entrada de la memoria caché. El índice almacena el valor indizado en una estructura de datos independiente de la correlación de memoria caché. El índice valida las claves respecto a las entradas de correlación antes de volver al usuario para intentar conseguir un conjunto de resultados precisos. Cuando se utiliza la estrategia de bloqueo pesimista y se usa el índice en una instancia local de ObjectMap (versus un ObjectMap de cliente/servidor), el índice adquirirá bloqueos para cada entrada coincidente. Si utiliza un bloqueo optimista o un objeto ObjectMap remoto, los bloqueos se liberan de forma inmediata.

El tipo de bloqueo que se adquiere depende del argumento forUpdate pasado al método ObjectMap.getIndex. El argumento forUpdate especifica el tipo de bloqueo que debe adquirir el índice. Si es false, se adquiere un bloqueo compartible (S) y si es true, se adquiere un bloqueo actualizable (U).

Si el bloqueo es de tipo compartible, se aplica el valor del aislamiento de la transacción de la sesión y afecta a la duración del bloqueo. Consulte el tema que trata sobre el aislamiento de transacciones para obtener más detalles sobre cómo se utiliza el aislamiento de transacciones para añadir simultaneidad a las aplicaciones.

Bloqueos compartidos con consulta

El motor de consultas de eXtreme Scale adquiere los bloqueos S cuando se necesita realizar una introspección de las entradas de la memoria caché para descubrir si cumplen los criterios del filtro de la consulta. Si se utiliza el aislamiento de transacciones de lectura repetible con bloqueo pesimista, los bloqueos compartibles S sólo se retienen en los elementos incluidos en el resultado de la consulta y se liberan en todas las entradas que no se incluyen en el resultado. Si utiliza un nivel de aislamiento de transacciones más bajo u optimista, los bloqueos S no se retienen.

Bloqueos compartidos con una consulta de cliente a servidor

Al utilizar la consulta de eXtreme Scale de un cliente, normalmente, la consulta se ejecuta en el servidor, a menos que todas las correlaciones o entidades a las que se hace referencia en la consulta son locales respecto al cliente (por ejemplo: una correlación replicada por el cliente o una entidad de resultado de consulta). Todas las consultas que se ejecutan en una transacción de lectura/grabación retendrán bloqueos S, como se ha descrito en el apartado anterior. Si la transacción no es una transacción de lectura/grabación, una sesión no se retiene en el servidor y los bloqueos S se liberan.

Una transacción de lectura/grabación sólo se direcciona a una partición primaria, y una sesión se mantiene en el servidor para la sesión de cliente. Una transacción puede promocionarse a lectura/grabación de acuerdo con las condiciones siguientes:
  1. Se accede a cualquier correlación configurada para usar un bloqueo pesimista mediante los métodos de API get y getAll de ObjectMap o los métodos EntityManager.find.
  2. La transacción se vacía, lo que ocasiona que se envíen actualizaciones al servidor.
  3. Se accede a cualquier correlación configurada para usar un bloqueo optimista mediante los métodos ObjectMap.getForUpdate o EntityManager.findForUpdate.

Bloqueos actualizables con consulta

Los bloqueos compartidos son útiles cuando es importante la coherencia y simultaneidad. Garantizan que el valor de la entrada no cambie durante la vida de la transacción. Ninguna otra transacción podrá cambiar el valor mientras se mantengan otros bloqueos S, y sólo una transacción puede intentar actualizar la entrada. Para obtener más información sobre las modalidades de bloqueo S, U y X, consulte el tema sobre la modalidad de bloqueo pesimista.

Los bloqueos actualizables se utilizan para identificar el intento de actualizar una entrada de la memoria caché cuando se usa la estrategia de bloqueo pesimista. Permite la sincronización entre las transacciones que desean modificar una entrada de la memoria caché. Las transacciones pueden ver la entrada mediante un bloqueo S, pero otras transacciones no pueden adquirir un bloqueo U o un bloqueo X. En numerosos escenarios, es necesario adquirir un bloqueo U sin adquirir primero un bloqueo S para evitar situaciones de punto muerto. Consulte el tema sobre la modalidad de bloqueo pesimista para obtener ejemplos de situaciones comunes de punto muerto.

Las interfaces ObjectQuery y EntityManager Query proporcionan el método setForUpdate para identificar el uso previsto para el resultado de la consulta. De forma específica, el motor de consultas adquiere bloqueos U en lugar de bloqueos S para cada entrada de correlación del resultado de la consulta:
ObjectMap orderMap = session.getMap("Order");
ObjectQuery q = session.createQuery("SELECT o FROM Order o WHERE o.orderDate=?1");
q.setParameter(1, "20080101");
q.setForUpdate(true);
session.begin();
// Ejecutar la consulta. Cada orden tiene un bloqueo U
Iterator result = q.getResultIterator();
// Para cada orden, actualice el estado.
while(result.hasNext()) {
    Order o = (Order) result.next();
    o.status = "shipped";
    orderMap.update(o.getId(), o);
}
// Cuando se confirma,
session.commit();
Query q = em.createQuery("SELECT o FROM Order o WHERE o.orderDate=?1");
q.setParameter(1, "20080101");
q.setForUpdate(true);
emTran.begin();
// Ejecutar la consulta. Cada orden tiene un bloqueo U
Iterator result = q.getResultIterator();
// Para cada orden, actualice el estado.
while(result.hasNext()) {
    Order o = (Order) result.next();
    o.status = "shipped";
}
tmTran.commit();

Cuando se habilita el atributo setForUpdate, la transacción se convierte automáticamente en una transacción de lectura/grabación y los bloqueos se mantienen en el servidor, como se esperaba. Si la consulta no puede utilizar índices, la correlación debe explorarse, lo cual resultará en bloqueos U temporales para las entradas de correlación que no satisfagan el resultado de la consulta, y mantendrá bloqueos U para las entradas incluidas en el resultado.