return;
}
HelixManager manager = changeContext.getManager();
HelixDataAccessor accessor = manager.getHelixDataAccessor();
Builder keyBuilder = accessor.keyBuilder();
if (messages == null || messages.size() == 0)
{
LOG.info("No Messages to process");
return;
}
// sort message by creation timestamp, so message created earlier is processed first
Collections.sort(messages, Message.CREATE_TIME_COMPARATOR);
// message handlers created
List<MessageHandler> handlers = new ArrayList<MessageHandler>();
// message read
List<Message> readMsgs = new ArrayList<Message>();
String sessionId = manager.getSessionId();
List<String> curResourceNames =
accessor.getChildNames(keyBuilder.currentStates(instanceName, sessionId));
List<PropertyKey> createCurStateKeys = new ArrayList<PropertyKey>();
List<CurrentState> metaCurStates = new ArrayList<CurrentState>();
Set<String> createCurStateNames = new HashSet<String>();
changeContext.add(NotificationContext.TASK_EXECUTOR_KEY, this);
for (Message message : messages)
{
// nop messages are simply removed. It is used to trigger onMessage() in
// situations such as register a new message handler factory
if (message.getMsgType().equalsIgnoreCase(MessageType.NO_OP.toString()))
{
LOG.info("Dropping NO-OP message. mid: " + message.getId() + ", from: "
+ message.getMsgSrc());
accessor.removeProperty(message.getKey(keyBuilder, instanceName));
continue;
}
String tgtSessionId = message.getTgtSessionId();
// if sessionId not match, remove it
if (!sessionId.equals(tgtSessionId) && !tgtSessionId.equals("*"))
{
String warningMessage =
"SessionId does NOT match. expected sessionId: " + sessionId
+ ", tgtSessionId in message: " + tgtSessionId + ", messageId: "
+ message.getMsgId();
LOG.warn(warningMessage);
accessor.removeProperty(message.getKey(keyBuilder, instanceName));
_statusUpdateUtil.logWarning(message,
HelixStateMachineEngine.class,
warningMessage,
accessor);
continue;
}
// don't process message that is of READ or UNPROCESSABLE state
if (MessageState.NEW != message.getMsgState())
{
// It happens because we don't delete message right after
// read. Instead we keep it until the current state is updated.
// We will read the message again if there is a new message but we
// check for the status and ignore if its already read
LOG.trace("Message already read. mid: " + message.getMsgId());
continue;
}
// create message handlers, if handlers not found, leave its state as NEW
try
{
List<MessageHandler> createHandlers =
createMessageHandlers(message, changeContext);
if (createHandlers.isEmpty())
{
continue;
}
handlers.addAll(createHandlers);
}
catch (Exception e)
{
LOG.error("Failed to create message handler for " + message.getMsgId(), e);
String error =
"Failed to create message handler for " + message.getMsgId()
+ ", exception: " + e;
_statusUpdateUtil.logError(message,
HelixStateMachineEngine.class,
e,
error,
accessor);
// Mark message state UNPROCESSABLE if we hit an exception in creating
// message handler. The message will stay on zookeeper but will not be processed
message.setMsgState(MessageState.UNPROCESSABLE);
accessor.updateProperty(message.getKey(keyBuilder, instanceName), message);
continue;
}
// update msgState to read
message.setMsgState(MessageState.READ);
message.setReadTimeStamp(new Date().getTime());
message.setExecuteSessionId(changeContext.getManager().getSessionId());
_statusUpdateUtil.logInfo(message,
HelixStateMachineEngine.class,
"New Message",
accessor);
readMsgs.add(message);
// batch creation of all current state meta data
// do it for non-controller and state transition messages only
if (!message.isControlerMsg()
&& message.getMsgType().equals(Message.MessageType.STATE_TRANSITION.toString()))
{
String resourceName = message.getResourceName();
if (!curResourceNames.contains(resourceName)
&& !createCurStateNames.contains(resourceName))
{
createCurStateNames.add(resourceName);
createCurStateKeys.add(keyBuilder.currentState(instanceName,
sessionId,
resourceName));
CurrentState metaCurState = new CurrentState(resourceName);
metaCurState.setBucketSize(message.getBucketSize());