String fillMessageMutation(MutationBatch mb, Message message) throws MessageQueueException {
    // Get the execution time from the message or set to current time so it runs immediately
    long curTimeMicros;
    if (!message.hasTrigger()) {
      curTimeMicros =
          TimeUnit.MICROSECONDS.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS);
    } else {
      curTimeMicros =
          TimeUnit.MICROSECONDS.convert(
              message.getTrigger().getTriggerTime(), TimeUnit.MILLISECONDS);
    }
    curTimeMicros += (counter.incrementAndGet() % 1000);

    // Update the message for the new token
    message.setToken(TimeUUIDUtils.getMicrosTimeUUID(curTimeMicros));

    // Set up the queue entry
    MessageQueueEntry entry =
        MessageQueueEntry.newMessageEntry(
            message.getPriority(), message.getToken(), MessageQueueEntryState.Waiting);

    // Convert the message object to JSON
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    try {
      mapper.writeValue(baos, message);
      baos.flush();
    } catch (Exception e) {
      throw new MessageQueueException("Failed to serialize message data: " + message, e);
    }

    // Write the queue entry
    String shardKey = getShardKey(message);
    mb.withRow(queueColumnFamily, shardKey)
        .putColumn(entry, new String(baos.toByteArray()), metadata.getRetentionTimeout());

    // Write the lookup from queue key to queue entry
    if (message.hasKey()) {
      mb.withRow(keyIndexColumnFamily, getCompositeKey(getName(), message.getKey()))
          .putEmptyColumn(
              MessageMetadataEntry.newMessageId(getCompositeKey(shardKey, entry.getMessageId())),
              metadata.getRetentionTimeout());
    }

    // Allow hook processing
    for (MessageQueueHooks hook : hooks) {
      hook.beforeSendMessage(message, mb);
    }

    // Update state and retun the token
    stats.incSendMessageCount();
    return getCompositeKey(shardKey, entry.getMessageId());
  }
 private void fillAckMutation(MessageContext context, MutationBatch mb) {
   queue.stats.incAckMessageCount();
   Message message = context.getMessage();
   // Token refers to the timeout event.  If 0 (i.e. no) timeout was specified
   // then the token will not exist
   if (message.getToken() != null) {
     MessageQueueEntry entry = MessageQueueEntry.newBusyEntry(message);
     // Remove timeout entry from the queue
     mb.withRow(queue.queueColumnFamily, queue.getShardKey(message)).deleteColumn(entry);
     // Remove entry lookup from the key, if one exists
     if (message.hasKey()) {
       mb.withRow(
               queue.keyIndexColumnFamily,
               queue.getCompositeKey(queue.getName(), message.getKey()))
           .putEmptyColumn(
               MessageMetadataEntry.newMessageId(
                   queue.getCompositeKey(queue.getShardKey(message), entry.getMessageId())),
               queue.metadataDeleteTTL);
       if (message.isKeepHistory()) {
         MessageHistory history = context.getHistory();
         if (history.getStatus() == MessageStatus.RUNNING) {
           history.setStatus(MessageStatus.DONE);
         }
         history.setEndTime(
             TimeUnit.MICROSECONDS.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS));
         try {
           mb.withRow(queue.historyColumnFamily, message.getKey())
               .putColumn(
                   history.getToken(),
                   queue.serializeToString(context.getHistory()),
                   queue.metadata.getHistoryTtl()); // TTL
         } catch (Exception e) {
           LOG.warn("Error serializing message history for " + message.getKey(), e);
         }
       }
     }
     // Run hooks
     for (MessageQueueHooks hook : queue.hooks) {
       hook.beforeAckMessage(message, mb);
     }
   }
   if (context.getNextMessage() != null) {
     try {
       queue.fillMessageMutation(mb, context.getNextMessage());
     } catch (MessageQueueException e) {
       LOG.warn("Error filling nextMessage for " + message.getKey(), e);
     }
   }
 }