Mappes comme files d'attente FIFO

Avec WebSphere eXtreme Scale, vous pouvez fournir une fonctionnalité FIFO (premier entré, premier sorti) similaire aux files d'attente pour toutes les mappes. WebSphere eXtreme Scale recherche l'ordre d'insertion de toutes les mappes. Un client peut demander à une mappe sa prochaine entrée déverrouillée, suivant l'ordre d'insertion, et verrouiller cette entrée. Ce processus permet à plusieurs clients d'utiliser les entrées de la mappe de manière efficace.

Exemple FIFO

Le fragment de code ci-après montre un client qui entre dans une boucle pour traiter les entrées de la mappe jusqu'à épuisement de cette dernière. La boucle démarre une transaction, puis appelle la méthode ObjectMap.getNextKey(5000). Cette méthode renvoie la clé de la prochaine entrée déverrouillée disponible et la verrouille. Si la transaction est bloquée pour plus de 5000 millisecondes, la méthode renvoie la valeur null.
Session session = ...;
ObjectMap map = session.getMap("xxx");
// cela doit être défini quelque part pour arrêter cette boucle
boolean timeToStop = false;

while(!timeToStop)
{
  session.begin();
  Object msgKey = map.getNextKey(5000);
  if(msgKey == null)
  {
    // la partition actuelle est épuisée ; appelez-la de nouveau dans
    // une nouvelle transaction pour passer à la partition suivante
    session.rollback();
    continue;
  }
  Message m = (Message)map.get(msgKey);
  // consommez maintenant le message
  ...
  // doit être supprimé
  map.remove(msgKey);
  session.commit();
}

Mode local versus mode client

Si l'application utilise un coeur local (il ne s'agit pas d'un client), le mécanisme fonctionne comme décrit précédemment.

Pour le mode client, si la machine virtuelle Java est un client, ce dernier se connecte d'abord à un fragment primaire aléatoire de partition. S'il n'existe pas de travaux dans cette partition, le client passe à la partition suivante pour en rechercher. Le client trouve une partition contenant des entrées ou boucle sur la partition initiale aléatoire. Dans ce second cas, il renvoie une valeur null à l'application. Si le client trouve une partition avec une mappe qui contient des entrées, il utilise ces dernières jusqu'à ce qu'aucune entrée ne soit disponible pour le délai d'expiration. Une fois le délai d'expiration dépassé, la valeur null est renvoyée. Cette action signifie que si la valeur null est renvoyée et qu'une mappe partitionnée est utilisée, vous devez démarrer une nouvelle transaction et reprendre l'écoute. L'exemple de fragment de code précédent possède ce comportement.

Exemple

Si votre système fonctionne comme client et qu'une clé est renvoyée, cette transaction est maintenant associée à la partition avec l'entrée de cette clé. Si vous ne souhaitez pas mettre à jour d'autres mappes lors de cette transaction, cela ne pose aucun problème. Si vous souhaitez en mettre à jour, vous ne pouvez mettre à jour que les mappes de la même partition que la mappe dont vous avez extrait la clé. L'entrée renvoyée par la méthode getNextKey doit fournir à l'application un moyen de détecter les données appropriées dans cette partition. Soit par exemple deux mappes ; une pour les événements et l'autre pour les travaux sur lesquels ces événements ont un impact. Vous définissez les deux mappes avec les entités suivantes :
Job.java
package tutorial.fifo;

import com.ibm.websphere.projector.annotations.Entity;
import com.ibm.websphere.projector.annotations.Id;

@Entity
public class Job
{
	@Id String jobId;

	int jobState;
}
JobEvent.java
package tutorial.fifo;

import com.ibm.websphere.projector.annotations.Entity;
import com.ibm.websphere.projector.annotations.Id;
import com.ibm.websphere.projector.annotations.OneToOne;

@Entity
public class JobEvent
{
	@Id String eventId;
	@OneToOne Job job;
}
Le travail possède un ID et un état, qui est un entier. Supposons que vous souhaitez incrémenter l'état chaque fois qu'un événement arrive. Les événements sont stockés dans la mappe JobEvent. Chaque entrée possède une référence au travail concerné par l'événement. Le code permettant au programme d'écoute d'effectuer cette opération ressemble au suivant :
JobEventListener.java
package tutorial.fifo;

import com.ibm.websphere.objectgrid.ObjectGridException;
import com.ibm.websphere.objectgrid.ObjectMap;
import com.ibm.websphere.objectgrid.Session;
import com.ibm.websphere.objectgrid.em.EntityManager;

public class JobEventListener
{
	boolean stopListening;

	public synchronized void stopListening()
	{
		stopListening = true;
	}

	synchronized boolean isStopped()
	{
		return stopListening;
	}

	public void processJobEvents(Session session)
		throws ObjectGridException
	{
		EntityManager em = session.getEntityManager();
		ObjectMap jobEvents = session.getMap("JobEvent");
		while(!isStopped())
		{
			em.getTransaction().begin();

			Object jobEventKey = jobEvents.getNextKey(5000);
			if(jobEventKey == null)
			{
				em.getTransaction().rollback();
				continue;
			}
			JobEvent event = (JobEvent)em.find(JobEvent.class, jobEventKey);
			// traitez l'événement ; ici nous nous contentons d'incrémenter
			// l'état du travail
			event.job.jobState++;
			em.getTransaction().commit();
		}
	}
}

Le programme d'écoute est démarré sur une unité d'exécution par l'application. Il est exécuté jusqu'à ce que la méthode stopListening soit appelée. La méthode processJobEvents est exécutée sur l'unité d'exécution jusqu'à ce que la méthode stopListening soit appelée. La boucle se bloque en attendant une clé d'événement de la mappe JobEvent, puis utilise l'API EntityManager pour accéder à l'objet d'événement, supprimer la référence au travail et incrémenter l'état.

L'API EntityManager ne possède pas de méthode getNextKey, contrairement à l'ObjectMap. Par conséquent, le code utilise l'ObjectMap du JobEvent pour extraire la clé. Si une mappe est utilisée avec les entités, elle ne stocke plus d'objets. Elle stocke à la place des nuplets ; un objet nuplet pour la clé et un autre pour la valeur. La méthode EntityManager.find accepte un nupplet pour la clé.

Le code permettant de créer un événement ressemble à celui de l'exemple suivant :
em.getTransaction().begin();
Job job = em.find(Job.class, "Job Key");
JobEvent event = new JobEvent();
event.id = Random.toString();
event.job = job;
em.persist(event); // insérez-le
em.getTransaction().commit();
Vous recherchez le travail de l'événement, construisez un événement, pointez ce dernier vers le travail, l'insérez dans la mappe JobEvent et validez la transaction.

Chargeurs et mappes FIFO

Si vous souhaitez assister une mappe utilisée comme une file d'attente FIFO d'un chargeur, des tâches supplémentaires peuvent être requises. Si l'ordre des entées dans la mappe n'est pas important, aucune autre tâche n'est requise. Si l'ordre est important, vous devez ajouter un numéro de séquence à tous les enregistrements insérés lorsque vous stockez les enregistrements sur le système dorsal. Le mécanisme de préchargement doit être écrit pour insérer les enregistrements au démarrage suivant cet ordre.