Merge pull request #29873 from akka/wip-29854-RequestNext-bug-patriknw
Fix duplicate RequestNext messages in durable WorkPullingProducerController, #29854
This commit is contained in:
commit
feec7aa9b1
2 changed files with 37 additions and 12 deletions
|
|
@ -191,7 +191,7 @@ import akka.util.Timeout
|
||||||
case Some(p) =>
|
case Some(p) =>
|
||||||
becomeActive(p, load.state)
|
becomeActive(p, load.state)
|
||||||
case None =>
|
case None =>
|
||||||
// waiting for LoadStateReply
|
// waiting for Start
|
||||||
waitingForStart(producerId, context, stashBuffer, durableQueue, settings, producer, Some(load.state))
|
waitingForStart(producerId, context, stashBuffer, durableQueue, settings, producer, Some(load.state))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -365,7 +365,7 @@ private class WorkPullingProducerControllerImpl[A: ClassTag](
|
||||||
// wait until more demand
|
// wait until more demand
|
||||||
false
|
false
|
||||||
} else if (s.requested && wasStashed) {
|
} else if (s.requested && wasStashed) {
|
||||||
// msg was unstashed, but pending request alread in progress
|
// msg was unstashed, but pending request already in progress
|
||||||
true
|
true
|
||||||
} else if (durableQueue.isDefined && !s.requested && !wasStashed) {
|
} else if (durableQueue.isDefined && !s.requested && !wasStashed) {
|
||||||
// msg ResendDurableMsg, and stashed before storage
|
// msg ResendDurableMsg, and stashed before storage
|
||||||
|
|
@ -460,14 +460,18 @@ private class WorkPullingProducerControllerImpl[A: ClassTag](
|
||||||
replyTo ! Done
|
replyTo ! Done
|
||||||
}
|
}
|
||||||
|
|
||||||
s.handOver.get(seqNr).foreach {
|
val wasHandOver =
|
||||||
case HandOver(oldConfirmationQualifier, oldSeqNr) =>
|
s.handOver.get(seqNr) match {
|
||||||
durableQueue.foreach { d =>
|
case Some(HandOver(oldConfirmationQualifier, oldSeqNr)) =>
|
||||||
d ! StoreMessageConfirmed(oldSeqNr, oldConfirmationQualifier, System.currentTimeMillis())
|
durableQueue.foreach { d =>
|
||||||
}
|
d ! StoreMessageConfirmed(oldSeqNr, oldConfirmationQualifier, System.currentTimeMillis())
|
||||||
}
|
}
|
||||||
|
true
|
||||||
|
case None =>
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
val newState = onMessage(m, wasStashed = false, replyTo = None, seqNr)
|
val newState = onMessage(m, wasStashed = wasHandOver, replyTo = None, seqNr)
|
||||||
active(newState.copy(replyAfterStore = newState.replyAfterStore - seqNr, handOver = newState.handOver - seqNr))
|
active(newState.copy(replyAfterStore = newState.replyAfterStore - seqNr, handOver = newState.handOver - seqNr))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
package akka.persistence.typed.delivery
|
package akka.persistence.typed.delivery
|
||||||
|
|
||||||
|
import scala.concurrent.duration._
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
|
|
@ -72,6 +73,8 @@ class WorkPullingWithEventSourcedProducerQueueSpec
|
||||||
testKit.stop(consumerController)
|
testKit.stop(consumerController)
|
||||||
consumerProbe.expectTerminated(consumerController)
|
consumerProbe.expectTerminated(consumerController)
|
||||||
|
|
||||||
|
system.log.info("------------------ Start 2")
|
||||||
|
|
||||||
val producerController2 = spawn(
|
val producerController2 = spawn(
|
||||||
WorkPullingProducerController[String](
|
WorkPullingProducerController[String](
|
||||||
producerId,
|
producerId,
|
||||||
|
|
@ -82,6 +85,13 @@ class WorkPullingWithEventSourcedProducerQueueSpec
|
||||||
val consumerController2 = spawn(ConsumerController[String](serviceKey))
|
val consumerController2 = spawn(ConsumerController[String](serviceKey))
|
||||||
consumerController2 ! ConsumerController.Start(consumerProbe.ref)
|
consumerController2 ! ConsumerController.Start(consumerProbe.ref)
|
||||||
|
|
||||||
|
// start two consumers (same consumerProbe) to reproduce issue #29854
|
||||||
|
val consumerController3 = spawn(ConsumerController[String](serviceKey))
|
||||||
|
consumerController3 ! ConsumerController.Start(consumerProbe.ref)
|
||||||
|
|
||||||
|
val requestNext4 = producerProbe.receiveMessage()
|
||||||
|
producerProbe.expectNoMessage()
|
||||||
|
|
||||||
val delivery1 = consumerProbe.receiveMessage()
|
val delivery1 = consumerProbe.receiveMessage()
|
||||||
delivery1.message should ===("a")
|
delivery1.message should ===("a")
|
||||||
delivery1.confirmTo ! ConsumerController.Confirmed
|
delivery1.confirmTo ! ConsumerController.Confirmed
|
||||||
|
|
@ -94,7 +104,7 @@ class WorkPullingWithEventSourcedProducerQueueSpec
|
||||||
delivery3.message should ===("c")
|
delivery3.message should ===("c")
|
||||||
delivery3.confirmTo ! ConsumerController.Confirmed
|
delivery3.confirmTo ! ConsumerController.Confirmed
|
||||||
|
|
||||||
val requestNext4 = producerProbe.receiveMessage()
|
producerProbe.expectNoMessage()
|
||||||
requestNext4.sendNextTo ! "d"
|
requestNext4.sendNextTo ! "d"
|
||||||
|
|
||||||
val delivery4 = consumerProbe.receiveMessage()
|
val delivery4 = consumerProbe.receiveMessage()
|
||||||
|
|
@ -153,6 +163,7 @@ class WorkPullingWithEventSourcedProducerQueueSpec
|
||||||
delivery3.confirmTo ! ConsumerController.Confirmed
|
delivery3.confirmTo ! ConsumerController.Confirmed
|
||||||
|
|
||||||
val requestNext4 = producerProbe.receiveMessage()
|
val requestNext4 = producerProbe.receiveMessage()
|
||||||
|
producerProbe.expectNoMessage()
|
||||||
requestNext4.sendNextTo ! "d"
|
requestNext4.sendNextTo ! "d"
|
||||||
|
|
||||||
// TODO Should we try harder to deduplicate first?
|
// TODO Should we try harder to deduplicate first?
|
||||||
|
|
@ -208,7 +219,10 @@ class WorkPullingWithEventSourcedProducerQueueSpec
|
||||||
val batch1 = 15
|
val batch1 = 15
|
||||||
val confirmed1 = 10
|
val confirmed1 = 10
|
||||||
(1 to batch1).foreach { n =>
|
(1 to batch1).foreach { n =>
|
||||||
producerProbe.receiveMessage().sendNextTo ! s"msg-$n"
|
val reqNext = producerProbe.receiveMessage()
|
||||||
|
if (n == 1 || n == 7 || n == 13) // not checking all because takes too much time
|
||||||
|
producerProbe.expectNoMessage(50.millis) // issue #29854
|
||||||
|
reqNext.sendNextTo ! s"msg-$n"
|
||||||
}
|
}
|
||||||
|
|
||||||
(1 to confirmed1).foreach { _ =>
|
(1 to confirmed1).foreach { _ =>
|
||||||
|
|
@ -229,6 +243,10 @@ class WorkPullingWithEventSourcedProducerQueueSpec
|
||||||
val consumerController4 = spawn(ConsumerController[String](serviceKey))
|
val consumerController4 = spawn(ConsumerController[String](serviceKey))
|
||||||
consumerController4 ! ConsumerController.Start(consumerProbe.ref)
|
consumerController4 ! ConsumerController.Start(consumerProbe.ref)
|
||||||
|
|
||||||
|
// start two consumers (same consumerProbe) to reproduce issue #29854
|
||||||
|
val consumerController5 = spawn(ConsumerController[String](serviceKey))
|
||||||
|
consumerController5 ! ConsumerController.Start(consumerProbe.ref)
|
||||||
|
|
||||||
val producerController2 = spawn(
|
val producerController2 = spawn(
|
||||||
WorkPullingProducerController[String](
|
WorkPullingProducerController[String](
|
||||||
producerId,
|
producerId,
|
||||||
|
|
@ -238,7 +256,10 @@ class WorkPullingWithEventSourcedProducerQueueSpec
|
||||||
|
|
||||||
val batch2 = 5
|
val batch2 = 5
|
||||||
(batch1 + 1 to batch1 + batch2).foreach { n =>
|
(batch1 + 1 to batch1 + batch2).foreach { n =>
|
||||||
producerProbe.receiveMessage().sendNextTo ! s"msg-$n"
|
val reqNext = producerProbe.receiveMessage()
|
||||||
|
if (n == batch1 + 1 || n == batch1 + 3) // not checking all because takes too much time
|
||||||
|
producerProbe.expectNoMessage(50.millis) // issue #29854
|
||||||
|
reqNext.sendNextTo ! s"msg-$n"
|
||||||
}
|
}
|
||||||
|
|
||||||
consumerProbe.fishForMessage(consumerProbe.remainingOrDefault) { delivery =>
|
consumerProbe.fishForMessage(consumerProbe.remainingOrDefault) { delivery =>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue