-
Notifications
You must be signed in to change notification settings - Fork 197
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Limit for inflight messages #167
Changes from 10 commits
5dbd7d8
3887666
02d41f9
a5edd20
58377de
cf0b2e6
3ee1da3
e8fe92a
68a8ad2
1b19ec0
79ca110
954e457
0826724
d02c62a
29ec9ca
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
package org.elasticmq.actor.queue | ||
|
||
import akka.actor.ActorRef | ||
import org.elasticmq.OverLimitError | ||
import org.elasticmq.actor.reply._ | ||
import org.elasticmq.msg.{DeleteMessage, LookupMessage, ReceiveMessages, SendMessage, UpdateVisibilityTimeout, _} | ||
import org.elasticmq.util.{Logging, NowProvider} | ||
|
@@ -105,36 +106,44 @@ trait QueueActorMessageOps extends Logging { | |
|
||
protected def receiveMessages(visibilityTimeout: VisibilityTimeout, | ||
count: Int, | ||
receiveRequestAttemptId: Option[String]): List[MessageData] = { | ||
implicit val np = nowProvider | ||
val messages = receiveRequestAttemptId | ||
.flatMap({ attemptId => | ||
// for a given request id, check for any messages we've dequeued and cached | ||
val cachedMessages = getMessagesFromRequestAttemptCache(attemptId) | ||
|
||
// if the cache returns an empty list instead of None, we still want to pull messages from | ||
// from the queue so return None in that case to properly process down stream | ||
cachedMessages.getOrElse(Nil) match { | ||
case Nil => None | ||
case default => Some(default) | ||
receiveRequestAttemptId: Option[String]): Either[ElasticMQError, List[MessageData]] = { | ||
if (inflightMessagesRegisty.size >= queueData.inflightMessagesLimit) | ||
Left(new OverLimitError(queueData.name)) | ||
else { | ||
implicit val np = nowProvider | ||
val messages = receiveRequestAttemptId | ||
.flatMap({ attemptId => | ||
// for a given request id, check for any messages we've dequeued and cached | ||
val cachedMessages = getMessagesFromRequestAttemptCache(attemptId) | ||
|
||
// if the cache returns an empty list instead of None, we still want to pull messages from | ||
// from the queue so return None in that case to properly process down stream | ||
cachedMessages.getOrElse(Nil) match { | ||
case Nil => None | ||
case default => Some(default) | ||
} | ||
}) | ||
.getOrElse(getMessagesFromQueue(visibilityTimeout, count)) | ||
.map { internalMessage => | ||
// Putting the msg again into the queue, with a new next delivery | ||
val newNextDelivery = computeNextDelivery(visibilityTimeout) | ||
internalMessage.trackDelivery(newNextDelivery) | ||
messageQueue += internalMessage | ||
|
||
logger.debug(s"${queueData.name}: Receiving message ${internalMessage.id}") | ||
internalMessage | ||
} | ||
}) | ||
.getOrElse(getMessagesFromQueue(visibilityTimeout, count)) | ||
.map { internalMessage => | ||
// Putting the msg again into the queue, with a new next delivery | ||
val newNextDelivery = computeNextDelivery(visibilityTimeout) | ||
internalMessage.trackDelivery(newNextDelivery) | ||
messageQueue += internalMessage | ||
|
||
logger.debug(s"${queueData.name}: Receiving message ${internalMessage.id}") | ||
internalMessage | ||
|
||
receiveRequestAttemptId.foreach { attemptId => | ||
receiveRequestAttemptCache.add(attemptId, messages) | ||
} | ||
|
||
receiveRequestAttemptId.foreach { attemptId => | ||
receiveRequestAttemptCache.add(attemptId, messages) | ||
} | ||
messages.foreach { message => | ||
inflightMessagesRegisty += message.id | ||
} | ||
|
||
messages.map(_.toMessageData) | ||
Right(messages.map(_.toMessageData)) | ||
} | ||
} | ||
|
||
private def getMessagesFromRequestAttemptCache(receiveRequestAttemptId: String)( | ||
|
@@ -177,6 +186,7 @@ trait QueueActorMessageOps extends Logging { | |
messageQueue.byId.get(msgId).foreach { msgData => | ||
if (msgData.deliveryReceipts.lastOption.contains(deliveryReceipt.receipt)) { | ||
// Just removing the msg from the map. The msg will be removed from the queue when trying to receive it. | ||
inflightMessagesRegisty -= msgId | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [typo] Registy. Though maybe simply name it sth like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great suggestion! Renamed |
||
messageQueue.remove(msgId) | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -42,7 +42,7 @@ trait QueueActorWaitForMessagesOps extends ReplyingActor with QueueActorMessageO | |
val result = super.receiveAndReplyMessageMsg(msg) | ||
val waitForMessages = | ||
waitForMessagesOpt.getOrElse(queueData.receiveMessageWait) | ||
if (result == ReplyWith(Nil) && waitForMessages.getMillis > 0) { | ||
if (result == ReplyWith(Right(Nil)) && waitForMessages.getMillis > 0) { | ||
val seq = assignSequenceFor(rm) | ||
logger.debug(s"${queueData.name}: Awaiting messages: start for sequence $seq.") | ||
scheduleTimeoutReply(seq, waitForMessages) | ||
|
@@ -67,13 +67,15 @@ trait QueueActorWaitForMessagesOps extends ReplyingActor with QueueActorMessageO | |
AwaitingData(originalSender, ReceiveMessages(visibilityTimeout, count, _, receiveRequestAttemptId), _))) => | ||
val received = super.receiveMessages(visibilityTimeout, count, receiveRequestAttemptId) | ||
|
||
if (received != Nil) { | ||
originalSender ! received | ||
logger.debug( | ||
s"${queueData.name}: Awaiting messages: replying to sequence $seq with ${received.size} messages.") | ||
awaitingReply.remove(seq) | ||
received match { | ||
case Right(receivedList) if receivedList.nonEmpty => | ||
originalSender ! received | ||
logger.debug( | ||
s"${queueData.name}: Awaiting messages: replying to sequence $seq with ${receivedList.size} messages.") | ||
awaitingReply.remove(seq) | ||
|
||
tryReply() | ||
tryReply() | ||
case _ => () | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. and what if there are requests waiting for receiving a message, but the limit is hit? Does deleting a message trigger responding to waiting requests as well? |
||
} | ||
case _ => // do nothing | ||
} | ||
|
@@ -87,7 +89,7 @@ trait QueueActorWaitForMessagesOps extends ReplyingActor with QueueActorMessageO | |
} | ||
|
||
private def scheduleTimeoutReply(seq: Long, waitForMessages: Duration): Unit = { | ||
schedule(waitForMessages.getMillis, ReplyIfTimeout(seq, Nil)) | ||
schedule(waitForMessages.getMillis, ReplyIfTimeout(seq, Right(Nil))) | ||
} | ||
|
||
private def scheduleTryReplyWhenAvailable(): Unit = { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe extract this to a method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did refactor receiving messages to private method