Mapas como Filas FIFO

Com o WebSphere eXtreme Scale, é possível fornecer um recurso semelhante à fila first-in first-out (FIFO) para todos os mapas. O WebSphere eXtreme Scale controla a ordem de inserção para todos os mapas. Um cliente pode solicitar um mapa para a próxima entrada não-bloqueada em um mapa na ordem de inserção e bloqueia a entrada. Este processo permite que vários clientes consumam entradas do mapa de maneira eficiente.

Exemplo de FIFO

O trecho de código a seguir mostra um cliente entrando em um loop para processar entradas do mapa até que o mapa seja esvaziado. O loop inicia uma transação e, então, chama o método ObjectMap.getNextKey(5000). Este método retorna a chave da próxima entrada desbloqueada disponível e a bloqueia. Se a transação é bloqueada por mais de 5000 milissegundos, então, o método retorna null.
Session session = ...;
ObjectMap map = session.getMap("xxx");
// this needs to be set somewhere to stop this loop
boolean timeToStop = false;

while(!timeToStop)
{
  session.begin();
  Object msgKey = map.getNextKey(5000);
  if(msgKey == null)
  {
    // current partition is exhausted, call it again in
    // a new transaction to move to next partition
    session.rollback();
    continue;
  }
  Message m = (Message)map.get(msgKey);
  // now consume the message
  ...
  // need to remove it
  map.remove(msgKey);
  session.commit();
}

Modo Local versus Modo do Cliente

Se o aplicativo estiver utilizando um núcleo local, ou seja, se ele não for um cliente, então o mecanismo funcionará conforme descrito anteriormente.

Para o modo cliente, se a Java virtual machine (JVM) for um cliente, então, o cliente inicialmente se conecta ao primário de partição aleatório. Se não existir nenhum trabalho em tal partição, então, o cliente move para a próxima partição para procurar trabalho. O cliente localiza uma partição com entrada ou executa um loop pela partição aleatória inicial. Se o cliente executa um loop pela partição inicial, então, ele retorna um valor nulo para o aplicativo. Se o cliente localiza uma partição com um mapa que possui entradas, então ele consome entradas até que nenhuma esteja disponível para o período de tempo limite. Após o tempo limite passar, então, um nulo é retornado. Esta ação significa que quando um nulo é retornado e um mapa particionado é utilizado, então, ele deve iniciar uma nova transação e retomar o atendimento. O fragmento de amostra do código anterior possui este comportamento.

Exemplo

Quando você está executando com um cliente é uma chave é retornada, tal transação é vinculada à partição com a entrada para tal chave. Se não desejar atualizar nenhum outro mapa durante tal transação, então, não há um problema. Se você não desejar atualizar, então, será possívelapenas atualizar os mapas a partir da mesma partição que o mapa, a partir da qual obteve a chave. A entrada que é retornada do método getNextKey precisa fornecer ao aplicativo uma maneira de descobrir dados relevantes nesta partição. Como exemplo, se você possui dois mapas; um para eventos e um outro para tarefas que os eventos impactam. Você define os dois mapas com as seguintes entidades:
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;
}
A tarefa possui um ID, que é uma cadeia, e um estado, que é um número inteiro. Suponha que você deseja incrementar o estado sempre que chega um evento. Os eventos são armazenados no Mapa de JobEvent. Cada entrada possui uma referência para a tarefa que o evento tem interesse. O código para o listener fazer isto é semelhante ao exemplo a seguir:
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);
			// process the event, here we just increment the
			// job state
			event.job.jobState++;
			em.getTransaction().commit();
		}
	}
}

Um listener é iniciado em um encadeamento pelo aplicativo. O listener é executado até que o método stopListening seja chamado. O método processJobEvents é executado no encadeamento até que o método stopListening seja chamado. O loop bloqueia a espera por um eventKey a partir do Mapa JobEvent e, em seguida, utiliza o EntityManager para acessar o objeto de evento, aponta para a tarefa e incrementa o estado.

A API do EntityManager não possui um método getNextKey, mas o ObjectMap possui. Portanto, o código utiliza o ObjectMap para JobEvent para obter a chave. Se um mapa é utilizado com entidades, então ele não mais armazena objetos. Em vez disso, ele armazena Tuplas; um objeto Tupla para a chave e um objeto Tupla para o valor. O método EntityManager.find aceita uma Tupla para a chave.

O código para criar um evento é semelhante ao exemplo a seguir:
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); // insert it
em.getTransaction().commit();
Você localiza a tarefa para o evento, constrói um evento, aponta o evento para a tarefa, insere o evento no Mapa de JobEvent e consolida a transação.

Utilitários de Carga e Mapas FIFO

Se você desejar apoiar um mapa que é utilizado como uma fila FIFO com um Utilitário de Carga, então, pode ser necessário executar algum trabalho adicional. Se a ordem das entradas no mapa não for uma preocupação, não há trabalho extra. Se a ordem for importante, então, é necessário incluir um número de sequência em todos os registros inseridos quando você está persistindo os registros para o backend. O mecanismo de pré-carregamento deve ser escrito para inserir os registros na inicialização utilizando esta ordem.