Les applications peuvent créer des files d'attente qualifiées par des requêtes sur une entité dans l'environnement eXtreme Scale local ou côté serveur. Les entités du résultat de la requête sont stockées dans cette file d'attente. Les files d'attente sont actuellement prises en charge uniquement dans les mappes utilisant la stratégie de verrouillage pessimiste.
Une même file d'attente de requêtes est partagée par plusieurs transactions et plusieurs clients. Une fois la file d'attente vide, la requête d'entité associée à cette file d'attente est exécutée à nouveau et de nouveaux résultats sont ajoutés à la file. La file d'attente est identifiée uniquement par la chaîne et les paramètres de la requête d'entité. Une instance ObjectGrid contient une seule instance de chaque file d'attente de requête unique. Pour plus d'informations, consultez la documentation relative à l'API EntityManager.
L'exemple suivant présente comment utiliser une file d'attente de requêtes.
/**
* 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();
}
Dans cet exemple, une file d'attente est créée avec une chaîne de requête d'entité : "SELECT t FROM Task t WHERE t.type=?1 AND t.status=?2". Les paramètres de l'objet QueryQueue sont ensuite définis. Cette file d'attente de requêtes représente toutes les tâches "non affectées" de type "question". L'objet QueryQueue est très semblable à un objet Query d'entité.
Une fois la file d'attente créée, la transaction d'entité démarre et la méthode getNextEntity est appelée. Cette méthode extrait l'entité disponible suivante dans un délai de 10 secondes. Une fois l'entité extraite, elle est traitée par la méthode assignTask. Celle-ci modifie l'instance d'entité de tâche et lui donne le statut "affecté". Comme elle ne correspond plus au filtre de la file d'attente, elle est supprimée. Une fois affectée, la transaction est validée.
Vous pouvez extraire des entités dans une boucle. L'exemple qui suit illustre comment obtenir toutes les tâches de type question non affectées.
/**
* 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);
}
Si la mappe d'entités contient 10 tâches de type question non affectées, vous vous attendez à ce que ces 10 entités vont apparaître sur la console. Toutefois, lors de l'exécution de la requête, vous constatez, contrairement à vos suppositions, que le programme ne se termine jamais.
Lorsqu'une file d'attente de requête est créée et que la méthode getNextEntity est appelée, la requête d'entité associée à la file d'attente est exécutée et les 10 résultats sont ajoutés à la file d'attente. Lors de l'appel de cette méthode, une entité est retirée de la file d'attente. Après 10 appels, la file d'attente est vide. La requête d'entité est automatiquement réexécutée. Comme ces 10 entités existent toujours et qu'elles correspondent toujours aux critères de filtre de la file d'attente de la requête, elles sont à nouveau ajoutées à la file d'attente.
Si la ligne suivante est ajoutée après l'instruction println(), 10 entités seulement apparaissent.
em.remove(nextTask);
Pour plus d'informations sur l'utilisation de SessionHandle avec QueryQueue dans un déploiement de positionnement par conteneur, consultez la section Intégration de l'objet SessionHandle.
Dans un environnement eXtreme Scale réparti, vous pouvez créer une file d'attente pour une partition ou pour toutes les partitions. Si la file d'attente est créée pour toutes les partitions, chaque partition sera associée à une instance de celle-ci.
Lorsque le client tente d'obtenir l'entité suivante à l'aide de la méthode QueryQueue.getNextEntity ou QueryQueue.getNextEntities, il envoie une requête à l'une des partitions. Un client envoie une demande d'exécution immédiate et une demande d'exécution différée au serveur :
L'exemple suivant montre comment récupérer une entité pour une file d'attente de requête déployée sur toutes les partitions (n) :
Voici la méthode permettant de créer un objet QueryQueue dans le gestionnaire d'entités :
public QueryQueue createQueryQueue(String qlString, Class entityClass);
Le résultat en file d'attente doit être projeté vers l'objet défini par le second paramètre de la méthode, Class entityClass.
Si ce paramètre est indiqué, le nom d'entité associé à la classe doit être identique à celui indiqué dans la chaîne de requête. Cela se révèle utile si vous souhaitez projeter une entité dans une entité de sous-ensemble. Si une valeur null est associée à la classe d'entités, le résultat n'est pas projeté. La valeur stockée dans la mappe aura le format d'un bloc de format d'entité.
Dans un environnement eXtreme Scale réparti, les files d'attente de requête sont uniquement prises en charge pour les mappes eXtreme Scale dont le mode de verrouillage est pessimiste. Aucun cache local n'existe côté client. La mappe transactionnelle peut toutefois contenir les données (clé et valeur) d'un client. Une collision de clé peut en résulter lorsqu'une entité extraite du serveur et une entrée déjà présente dans la mappe transactionnelle partagent la même clé.
La collision de clé produit uniquement lorsque la mappe transactionnelle contient des données. En d'autres termes, elle se produit uniquement lorsqu'un appel getNextEntity ou getNextEntities est émis dans une transaction ayant déjà été modifiée (une nouvelle donnée a été insérée ou une donnée a été mise à jour). Lorsqu'une application ne souhaite pas qu'une collision de clé se produise, elle doit appeler getNextEntity ou getNextEntities dans une transaction n'ayant pas encore été modifiée.
Dans le premier cas, le serveur découvre que l'échec du client se produit lorsqu'il tente de renvoyer la réponse à ce dernier. Dans le second cas, lorsque le client obtient une ou plusieurs entités du serveur, un verrou X est placé sur ces entités. En cas d'échec du client, la transaction expire et le verrou X est libéré.
Les files d'attente de requête n'honorent généralement pas la clause ORDER BY. Si vous appelez getNextEntity ou getNextEntities à partir de la file d'attente, rien ne garantit que les entités seront renvoyées dans l'ordre indiqué. Il est en effet impossible de classer les entités de plusieurs partitions. Au cas où la file d'attente serait déployée sur toutes les partitions, une partition est sélectionnée au hasard en vue du traitement de la demande lorsqu'un appel getNextEntity ou getNextEntities est émis. L'ordre des entités n'est donc pas garanti.
La clause ORDER BY est honorée si une file d'attente est déployée sur une seule partition.
Pour plus d'informations, voir API de requête EntityManager.
Chaque appel à QueryQueue.getNextEntity ou à QueryQueue.getNextEntities extrait les entités correspondantes d'une partition prise au hasard. Les applications doivent n'appeler par transaction qu'un seule QueryQueue.getNextEntity ou qu'un seul QueryQueue.getNextEntities. Sinon, eXtreme Scale se retrouverait avec des entités provenant d'une multiplicité de partitions, ce qui provoquerait une exception au moment de valider la transaction.