Filas de Consulta da Entidade

Filas de consulte permitem que aplicativos criem uma fila qualificada por uma consulta no lado do servidor ou eXtreme Scale local sobre uma entidade. As entidades do resultado da consulta são armazenadas nesta fila. Atualmente, a fila de consulta é suportada apenas em um mapa que está utilizando a estratégia de bloqueio pessimista.

Uma fila de consulta é compartilhada por várias transações e clientes. Após a fila de consulta ficar vazia, a consulta da entidade associada a esta fila é executada novamente e novos resultados são incluídos na fila. Uma fila de consulta é identificada exclusivamente pela cadeia e os parâmetro de consulta da entidade. Há apenas uma instância para cada fila de consulta exclusiva em uma instância do ObjectGrid. Consulte a documentação da API do EntityManager para obter mais informações.

Exemplo de Fila de Consulta

O exemplo a seguir mostra como a fila de consulta pode ser utilizada.

/**
 * Get a unassigned question type task 
 */
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();
}

O exemplo anterior primeiro cria um QueryQueue com uma cadeia de consulta de entidade, "SELECT t FROM Task t WHERE t.type=?1 AND t.status=?2". Depois ele configura os parâmetros para o objeto QueryQueue. Esta fila de consulta representa todas as tarefas "não-designadas" do tipo "questão". O objeto QueryQueue é muito semelhante a um objeto Query da entidade.

Após o QueryQueue ser criado, uma transação de entidade é iniciada e o método getNextEntity é chamado, que recupera a próxima entidade disponível sem um valor de tempo limite de 10 segundos. Após a entidade ser recuperada, ela é processada no método assignTask. O assignTask modifica a instância da entidade Task e altera o status para "designado" que efetivamente a remove da fila já que não corresponde mais ao filtro do QueryQueue. Uma vez designada, ocorre o commit da transação.

A partir deste exemplo, é possível visualizar uma fila de consulta que é semelhante a uma consulta de entidade. Entretanto, elas diferem nas seguintes maneiras:
  1. As entidades na fila de consulta podem ser recuperadas de uma maneira iterativa. O aplicativo de usuário decide o número de entidades a ser recuperado. Por exemplo, se QueryQueue.getNextEntity(timeout) é utilizado, apenas uma entidade é recuperada e se QueryQueue.getNextEntities(5, timeout) é utilizado, 5 entidades são recuperadas. Em um ambiente distribuído, o número de entidades decide diretamente o número de bytes a ser transferido do servidor para o cliente.
  2. Quando uma entidade é recuperada da fila de consulta, um bloqueio U é colocado na entidade, assim nenhuma outra transação pode acessá-la.

Recuperar Entidades em um Loop

É possível recuperar entidades em um loop. A seguir está um exemplo que ilustra como obter todas as tarefas não-designadas do tipo question concluídas.

/**
 * Get all unassigned question type tasks 
 */
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);
}

Se houver 10 tarefas não-designadas do tipo question no mapa de entidade, é possível esperar que você terá 10 entidades impressas no console. Entretanto, se este exemplo for executado, visualizará que o programa nunca sai, o que pode ser contrário ao que você assumiu.

Quando uma fila de consulta é criada e o getNextEntity é chamado, a consulta de entidade associada com a fila é executada e os 10 resultados são colocadas na fila. Quando o getNextEntity é chamado, uma entidade é retirada da fila. Após 10 chamadas do getNextEntity serem executadas, a fila fica vazia. A consulta da entidade será novamente executada automaticamente. Como estas 10 entidades ainda existem e correspondem aos critérios de filtro da fila de consulta, elas são colocadas na fila novamente.

Se a linha a seguir for incluída após a instrução println(), você verá apenas 10 entidades impressas.

em.remove(nextTask);

Para obter informações sobre o uso de SessionHandle com QueryQueue em uma implementação de posicionamento por contêiner, leia sobre Integração de SessionHandle.

Filas de Consulta Implementadas em todas as Partições

Em um eXtreme Scale distribuído, uma fila de consulta pode ser criada para uma partição ou todas as partições. Se uma fila de consulta é criada para todas as partições, haverá uma instância de fila de consulta em cada partição.

Quando um cliente tenta obter a próxima entidade utilizando o método QueryQueue.getNextEntity ou QueryQueue.getNextEntities, o cliente envia um pedido para uma das partições. Um cliente envia pedidos de peek e pin para o servidor:

  • Com um pedido de peek, o cliente envia um pedido para uma partição e o servidor retorna imediatamente. Se houver uma entidade na fila, o servidor envia uma resposta com a entidade; se não houver, o servidor envia uma resposta sem nenhuma entidade. Em qualquer um dos casos, o servidor retornará imediatamente.
  • Com um pedido de pin, o cliente envia um pedido para uma partição e o servidor aguarda até que uma entidade esteja disponível. Se houver uma entidade na fila, o servidor envia uma resposta com a entidade imediatamente; se não houver, o servidor aguarda na fila até que uma entidade esteja disponível ou o pedido atinja o tempo limite.

A seguir, está um exemplo de como uma entidade é recuperada para uma fila de consulta que é implementada em todas as partições (n):

  1. Quando um método QueryQueue.getNextEntity ou QueryQueue.getNextEntities é chamado, o cliente seleciona um número de partição aleatório de 0 a n-1.
  2. O cliente envia o pedido de peek para a partição aleatória.
    • Se uma entidade estiver disponível, o método QueryQueue.getNextEntity ou QueryQueue.getNextEntities sai retornando a entidade.
    • Se uma entidade não estiver disponível e não for a última partição não-visitada, o cliente envia um pedido de peek para a próxima partição.
    • Se uma entidade não estiver disponível e for a última partição não-visitada, o cliente envia um pedido de pin.
    • Se o pedido de pin para a última partição atingir o tempo limite e ainda não houver dados disponíveis, o cliente fará um último esforço enviado o pedido de peek para todas as partições serialmente um ciclo a mais. Portanto, se alguma entidade estiver disponível nas partições anteriores, o cliente estará a obtê-la.

Suporte a Entidade e Não-entidade de Subconjunto

A seguir está o método para criar um objeto QueryQueue no entity manager:

public QueryQueue createQueryQueue(String qlString, Class entityClass);

O resultado na fila de consulta deve ser projetado para o objeto definido pelo segundo parâmetro para o método, Class entityClass.

Se este parâmetro for especificado, a classe deve ter o mesmo nome da entidade conforme especificado na cadeia de consultas. Isto é útil se você desejar projetar uma entidade em uma entidade de subconjunto. Se um valor nulo é utilizado com a classe de entidade, então, o resultado não será projetado. O valor armazenado no mapa estará em um formato de tupla da entidade.

Colisão de Chaves do Lado do Cliente

No ambiente eXtreme Scale distribuído, a fila de consulta é suportada apenas para mapas do eXtreme Scale com o modo de bloqueio pessimista. Portanto, não há um cache local no lado do cliente. Entretanto, um cliente poderia ter dados (chave e valor) no mapa transacional. Isto potencialmente poderia levar a uma colisão de chaves quando uma entidade recuperada do servidor compartilha a mesma chave que uma entrada já no mapa da transação.

Quando ocorre uma colisão de chaves, o tempo de execução do cliente do eXtreme Scale utiliza a seguinte regra para emitir uma exceção ou substituir os dados silenciosamente.
  1. Se a chave colidida for a chave da entidade especificada na consulta de entidade associada com a fila de consulta, então, uma exceção é lançada. Neste caso, ocorre o rollback da transação e o bloqueio U nesta entidade será liberado no lado do servidor.
  2. Caso contrário, se a chave colidida for a chave de uma associação de entidade, os dados no mapa transacional serão substituídos sem aviso.

A colisão de chaves acontece apenas quando há dados no mapa transacional. Em outras palavras, isto ocorre apenas quando uma chamada getNextEntity ou getNextEntities é chamada em uma transação que já foi suja (um novo dado foi inserido ou um dado foi atualizado). Se um aplicativo não deseja que aconteça uma colisão de chaves, ele deve sempre chamar getNextEntity ou getNextEntities em uma transação que não foi suja.

Falhas do Cliente

Após um cliente enviar um pedido getNextEntity ou getNextEntities para o servidor, o cliente poderia falhar da seguinte maneira:
  1. O cliente envia um pedido para o servidor e, então, fica inativo.
  2. O cliente obtém uma ou mais entidades do servidor e, então, fica inativo.

No primeiro caso, o servidor descobre que o cliente está ficando inativo quando ele tenta enviar de volta a resposta para o cliente. No segundo caso, quando o cliente obtém uma ou mais entidades do servidor, um bloqueio X é colocado nestas entidades. Se o cliente fica inativo, a transação eventualmente atingirá o tempo limite e o bloqueio X será liberado.

Consulta com a cláusula ORDER BY

Geralmente, as filas de consulta não honram a cláusula ORDER BY. Se você chamar getNextEntity ou getNextEntities a partir da fila de consulta, não há garantia de que as entidades serão retornadas de acordo com a ordem. O motivo é que as entidades não podem ser ordenadas entre partições. No caso em que a fila de consulta é implementada em todas as partições, quando uma chamada getNextEntity ou getNextEntities é executada, uma partição aleatória é selecionada para processar o pedido. Portanto, a ordem não é garantida.

ORDER BY será honrado se uma fila de consulta for implementada em uma partição única.

Para obter mais informações, consulte API de Consulta EntityManager.

Uma Chamada Por Transação

Cada chamada QueryQueue.getNextEntity ou QueryQueue.getNextEntities recupera entidades correspondentes de uma partição aleatória. Os aplicativos devem chamar exatamente uma QueryQueue.getNextEntity ou QueryQueue.getNextEntities em uma transação. Caso contrário, o eXtreme Scale pode encerrar o toque a entidades de várias partições, fazendo com que uma exceção seja lançada na hora da confirmação.