Colas de consulta de entidades

Las colas de consulta permiten a las aplicaciones crear una cola calificada por una consulta en el servidor o en eXtreme Scale local para una entidad. Las entidades del resultado de la consulta se almacenan en esta cola. Actualmente, la cola de consulta sólo se admite en una correlación que utilice la estrategia de bloqueo pesimista.

Varios clientes y transacciones comparten una cola de consulta. Una vez que la cola de consulta se queda vacía, la consulta de entidad asociada con esta cola se vuelve a ejecutar y los nuevos resultados se añaden a la cola. Una cola de consulta se identifica de forma exclusiva mediante la serie de consulta de entidad y los parámetros. Sólo hay una instancia para cada cola de consulta exclusiva en una instancia de ObjectGrid. Consulte la documentación de la API EntityManager para obtener más información.

Ejemplo de cola de consulta

El ejemplo siguiente muestra cómo se puede utilizar la cola de consulta.

/**
 * Obtener una tarea de tipo pregunta sin asignar
 */
private void getUnassignedQuestionTask() throws Exception {
    EntityManager em = og.getSession().getEntityManager();
    EntityTransaction tran = em.getTransaction();

    QueryQueue queue = em.createQueryQueue("SELECT t FROM Task t
				WHERE t.type=?1 AND t.status=?2", Task.class);
    queue.setParameter(1, new Integer(Task.TYPE_QUESTION));
    queue.setParameter(2, new Integer(Task.STATUS_UNASSIGNED));

    tran.begin();
    Task nextTask = (Task) queue.getNextEntity(10000);
    System.out.println("next task is " + nextTask);
    if (nextTask != null) {
        assignTask(em, nextTask);
    }
    tran.commit();
}

El ejemplo anterior crea una cola de consulta QueryQueue con una serie de consulta de entidad, "SELECT t FROM Task t WHERE t.type=?1 AND t.status=?2". A continuación, establece los parámetros del objeto QueryQueue. Esta cola de consulta representa todas las tareas no asignadas del tipo "question" (pregunta). El objeto QueryQueue es muy parecido al objeto Query de entidad.

Una vez creado QueryQueue, se inicia una transacción de entidad y se invoca el método getNextEntity, que recupera la siguiente entidad disponible con un valor de tiempo de espera establecido en 10 segundos. Una vez recuperada la entidad, se procesa en el método assignTask. El método assignTask modifica la instancia de la entidad Task y cambia el estado a "assigned" (asignado), lo cual la elimina eficazmente de la cola, puesto que ya no coincide con el filtro de QueryQueue. Una vez asignada, la transacción se confirma.

Con este ejemplo, puede ver que una cola de consulta es similar a una consulta de entidad. Se diferencia, no obstante, en lo siguiente:
  1. Las entidades de la cola de consulta pueden recuperarse de forma iterativa. La aplicación de usuario decide el número de entidades que se va a recuperar. Por ejemplo, si se utiliza QueryQueue.getNextEntity(timeout), sólo se recupera una entidad, y si se utiliza QueryQueue.getNextEntities(5, timeout), se recuperan 5 entidades. En un entorno distribuido, el número de entidades decide directamente el número de bytes que se transferirán del servidor al cliente.
  2. Cuando se recupera una entidad de la cola de consulta, se coloca un bloqueo U en la entidad de modo que ninguna otra transacción pueda acceder a ella.

Recuperación de entidades en un bucle

Puede recuperar entidades en un bucle. A continuación se muestra un ejemplo que ilustra cómo obtener todas las tareas de tipo pregunta sin asignar.

/**
 * Obtener todas las tareas de tipo pregunta sin asignar
 */
private void getAllUnassignedQuestionTask() throws Exception {
    EntityManager em = og.getSession().getEntityManager();
    EntityTransaction tran = em.getTransaction();

    QueryQueue queue = em.createQueryQueue("SELECT t FROM Task t WHERE 
			t.type=?1 AND t.status=?2", Task.class);
    queue.setParameter(1, new Integer(Task.TYPE_QUESTION));
    queue.setParameter(2, new Integer(Task.STATUS_UNASSIGNED));

    Task nextTask = null;

    do {
        tran.begin();
        nextTask = (Task) queue.getNextEntity(10000);
        if (nextTask != null) {
            System.out.println("next task is " + nextTask);
        }
        tran.commit();
    } while (nextTask != null);
}

Si hay 10 tareas de tipo pregunta sin asignar en la correlación de entidad, esperaría tener 10 entidades impresas en la consola. No obstante, si ejecuta este ejemplo, observará que el programa nunca sale, que es lo contrario de lo que esperaba.

Cuando se crea una cola de consulta y se llama a getNextEntity, la consulta de entidad asociada con la cola se ejecuta y en la cola se muestran 10 resultados. Al llamar a getNextEntity, una entidad se extrae de la cola. Después de ejecutar 10 llamadas a getNextEntity, la cola se queda vacía. La cola de la entidad se volverá a ejecutar automáticamente. Puesto que estas 10 entidades siguen existiendo y coinciden con el criterio del filtro de la cola de consulta, se vuelven a colocar en la cola.

Si se añade la línea siguiente después de la sentencia println(), sólo verá impresas 10 entidades.

em.remove(nextTask);

Para obtener información sobre cómo utilizar SessionHandle con QueryQueue en un despliegue de colocación por contenedor, lea la información sobre Integración de SessionHandle.

Colas de consulta desplegadas en todas las particiones

En un entorno distribuido de eXtreme Scale, una cola de consulta puede crearse para una partición o para todas las particiones. Si se crea una cola de consulta para todas las particiones, habrá una instancia de cola de consulta en cada partición.

Cuando un cliente intenta obtener la siguiente entidad mediante el método QueryQueue.getNextEntity o QueryQueue.getNextEntities, el cliente envía una solicitud a una de las particiones. Un cliente envía solicitudes PEEK y PIN al servidor.

  • Con una solicitud PEEK, el cliente envía una solicitud a una partición y el servidor responde inmediatamente. Si hay una entidad en la cola, el servidor envía una respuesta con la entidad; si no hay ninguna entidad, el servidor envía una respuesta sin ninguna entidad. En cualquier caso, el servidor responde inmediatamente.
  • Con una solicitud PIN, el cliente envía una solicitud a una partición y el servidor espera hasta que haya una entidad disponible. Si hay una entidad en la cola, el servidor envía una respuesta con la entidad inmediatamente; si no hay ninguna entidad, el servidor espera en la cola hasta que haya una entidad disponible o hasta que la solicitud exceda el tiempo de espera.

El ejemplo siguiente muestra cómo se recupera una entidad de una cola de consulta que se despliega en todas las particiones (n):

  1. Cuando se llama un método QueryQueue.getNextEntity o QueryQueue.getNextEntities, el cliente elige un número de partición aleatorio de 0 a n-1.
  2. El cliente envía una solicitud PEEK a la partición aleatoria.
    • Si hay una entidad disponible, el método QueryQueue.getNextEntity o QueryQueue.getNextEntities sale después de devolver la entidad.
    • Si no hay ninguna entidad disponible y no es la última partición sin visitar, el cliente envía una solicitud PEEK a la siguiente partición.
    • Si no hay ninguna entidad disponible y es la última partición sin visitar, el cliente envía una solicitud PIN.
    • Si la solicitud PIN a la última partición excede el tiempo de espera y sigue sin haber ningún dato disponible, el cliente enviará una solicitud PEEK a todas las particiones en serie una vez más. Por lo tanto, si hubiera una entidad disponible en las particiones anteriores, el cliente podría obtenerla.

Entidad de subconjunto y soporte de no entidad

El método para crear un objeto QueryQueue en el gestor de entidades es el siguiente:

public QueryQueue createQueryQueue(String qlString, Class entityClass);

El resultado de la cola de la consulta se debe proyectar en el objeto definido por el segundo parámetro en el método, Clase entityClass.

Si se especifica este parámetro, la clase debe tener el mismo nombre de entidad que el especificado en la serie de consulta. Esto resulta útil si desea proyectar una entidad en una entidad de subconjunto. Si se utiliza un valor nulo como clase de entidad, el resultado no se proyectará. El valor almacenado en la correlación tendrá un formato de tuple de entidad.

Colisión de claves de cliente

En un entorno distribuido de eXtreme Scale, la cola de consulta sólo se admite en correlaciones de eXtreme Scale con modalidad de bloqueo pesimista. Por lo tanto, no hay memoria caché cercana en el cliente. No obstante, un cliente podría tener datos (clave y valor) en la correlación transaccional. Esto podría desembocar potencialmente en una colisión de claves cuando una entidad recuperada del servidor comparte la misma clave que una entrada de la correlación transaccional.

Cuando se produce una colisión de claves, el tiempo de ejecución del cliente de eXtreme Scale utiliza la siguiente norma para lanzar una excepción o alterar temporalmente los datos de forma silenciosa.
  1. Si la clave de colisión es la clave de la entidad especificada en la consulta de entidad asociada con la cola de consulta, se emitirá una excepción. En este caso, la transacción se retrotrae, y el bloqueo U de esta clave de entidad se liberará en el servidor.
  2. Por el contrario, si la clave de colisión es la clave de la asociación de la entidad, los datos de la correlación transaccional se alterarán temporalmente sin aviso.

La colisión de claves sólo sucede cuando hay datos en la correlación transaccional. Es decir, sólo tiene lugar cuando se llama a getNextEntity o getNextEntities en una transacción que ya estaba sucia (se han insertado datos nuevos o se han actualizado datos). Si una aplicación prefiere que no se produzcan colisiones de claves, debe llamar siempre a getNextEntity o getNextEntities en una transacción que no se haya ensuciado.

Anomalías de cliente

Una vez que un cliente envía una solicitud getNextEntity o getNextEntities al servidor, se puede producir una anomalía en el cliente de la siguiente manera:
  1. El cliente envía una solicitud al servidor y concluye.
  2. El cliente obtiene una o más entidades del servidor y después concluye.

En el primer caso, el servidor descubre que el cliente va a concluir cuando intenta responder al cliente. En el segundo caso, cuando el cliente obtiene una o más entidades del servidor, se coloca un bloqueo X en estas entidades. Si el cliente concluye, la transacción excederá el tiempo de espera y se liberará el bloqueo X.

Consulta con la cláusula ORDER BY

Por norma, las colas de consulta no reconocen la cláusula ORDER BY. Si llama a getNextEntity o getNextEntities en la cola de consulta, no se garantiza que las entidades se devuelvan en función del orden. La razón es que las entidades no se pueden ordenar en las particiones. En el caso de que la cola de consulta se despliegue en todas las particiones, cuando se ejecuta una llamada getNextEntity o getNextEntities, se elige una partición aleatoria para procesar la solicitud. Por lo tanto, no se garantiza el orden.

ORDER BY se reconoce si se despliega una cola de consulta en una sola partición.

Si desea más información consulte API EntityManager Query.

Una llamada por transacción

Cada llamada a QueryQueue.getNextEntity o QueryQueue.getNextEntities recupera las entidades coincidentes de una partición aleatoria. Las aplicaciones deben llamar exactamente a una QueryQueue.getNextEntity o QueryQueue.getNextEntities en una transacción. De lo contrario, eXtreme Scale podría finalizar afectando a entidades de varias particiones, que provoca que se genere una excepción durante la confirmación.