This is an automated email from the ASF dual-hosted git repository.
He-Pin pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pekko.git
The following commit(s) were added to refs/heads/main by this push:
new 2e5c450633 feat: add timeout duration to ReceiveTimeout message (#3399)
2e5c450633 is described below
commit 2e5c45063393b65a6af3300f74650fda6ed147ea
Author: He-Pin(kerr) <[email protected]>
AuthorDate: Fri Aug 7 20:48:27 2026 +0800
feat: add timeout duration to ReceiveTimeout message (#3399)
* feat: add timeout duration to ReceiveTimeout message
Motivation:
ReceiveTimeout was a case object singleton carrying no information about
the configured timeout duration, making it impossible to log or inspect
the timeout value from the message handler without accessing
context.receiveTimeout separately (#2569).
Modification:
Convert ReceiveTimeout from a case object to a final case class with a
`timeout: FiniteDuration` field. The scheduler now sends
ReceiveTimeout(duration) instead of the singleton. All pattern matches
updated from stable identifier patterns to type patterns. Java API
updated from matchEquals(getInstance()) to match(ReceiveTimeout.class).
Result:
Users can now access the timeout duration directly from the message:
case timeout: ReceiveTimeout => log.info("timeout: {}", timeout.timeout)
* chore: apply scalafmt and fix MiMa filters
Apply code formatting and update MiMa exclusion filters to match the
exact binary compatibility problems reported by CI.
* fix: add Scala 3 MiMa filters for ReceiveTimeout and cover getTimeout
Motivation:
The Binary Compatibility CI job failed on the Scala 3 lane: converting
ReceiveTimeout from case object to case class produced three unfiltered
problems for pekko-actor_3 (fromProduct result type, productElementNames
and productIterator generic signatures). The new Java API getTimeout also
lacked test coverage.
Modification:
Add the three Scala 3 MiMa exclusion filters reported by CI and assert
getTimeout in the ReceiveTimeoutSpec timeout-duration test.
Result:
MiMa passes on both Scala 2.13 and Scala 3 and the Java API is covered.
Tests:
- sbt "actor-tests / Test / testOnly
org.apache.pekko.actor.ReceiveTimeoutSpec" - 15 tests pass
- sbt "++2.13.18!" "actor/mimaReportBinaryIssues" "++3.3.8!"
"actor/mimaReportBinaryIssues" - pass
- sbt checkMimaFilterDirectories - pass
References:
Refs #2569
* fix: use scala.jdk.DurationConverters in ReceiveTimeout.getTimeout
Motivation:
Address review feedback on #3399 to use the standard Scala/Java duration
conversion, and document the ReceiveTimeout breaking change for 2.x users.
Modification:
- Replace java.time.Duration.ofNanos(timeout.toNanos) with timeout.toJava
via scala.jdk.DurationConverters in Actor.scala
- Add a ReceiveTimeout entry to migration-guide-1.x-2.x.md
Result:
The Java API conversion follows the established codebase convention and
the 2.x migration guide documents the pattern matching and getInstance
breaking changes.
Tests:
- sbt "actor-tests / Test / testOnly
org.apache.pekko.actor.ReceiveTimeoutSpec" - 15 tests passed
- sbt "actor-typed-tests / Test / testOnly
org.apache.pekko.actor.typed.CancelReceiveTimeoutSpec" - passed
- sbt actor/mimaReportBinaryIssues - no issues
- scalafmt --mode diff-ref=origin/main - no extra changes
- git diff --check - clean
References:
Refs #3399
---
.../test/java/org/apache/pekko/actor/JavaAPI.java | 1 -
.../apache/pekko/actor/ReceiveTimeoutSpec.scala | 57 ++++++++++++++--------
.../actor/typed/CancelReceiveTimeoutSpec.scala | 2 +-
.../typed/internal/adapter/ActorAdapter.scala | 2 +-
.../receive-timeout-with-duration.excludes | 43 ++++++++++++++++
.../main/scala/org/apache/pekko/actor/Actor.scala | 13 +++--
.../pekko/actor/dungeon/ReceiveTimeout.scala | 2 +-
.../scala/org/apache/pekko/io/TcpConnection.scala | 2 +-
.../apache/pekko/io/TcpOutgoingConnection.scala | 6 +--
.../pekko/actor/ReceiveTimeoutBenchmark.scala | 2 +-
.../cluster/metrics/sample/StatsService.scala | 2 +-
.../pekko/cluster/sharding/ShardCoordinator.scala | 6 +--
.../cluster/sharding/ClusterShardingSpec.scala | 12 ++---
.../pekko/cluster/client/ClusterClient.scala | 4 +-
.../org/apache/pekko/cluster/SeedNodeProcess.scala | 6 +--
.../apache/pekko/cluster/ddata/Replicator.scala | 10 ++--
.../paradox/migration/migration-guide-1.x-2.x.md | 6 +++
.../java/jdocs/actor/FaultHandlingDocSample.java | 4 +-
.../java/jdocs/sharding/ClusterShardingTest.java | 2 +-
docs/src/test/scala/docs/actor/ActorDocSpec.scala | 2 +-
.../scala/docs/actor/FaultHandlingDocSample.scala | 2 +-
.../circuitbreaker/CircuitBreakerDocSpec.scala | 2 +-
.../scala/docs/cluster/FactorialFrontend.scala | 2 +-
.../persistence/journal/AsyncWriteProxy.scala | 2 +-
.../journal/PersistencePluginProxy.scala | 6 +--
.../pekko/remote/artery/MaxThroughputSpec.scala | 2 +-
.../apache/pekko/testkit/TestActorRefSpec.scala | 2 +-
27 files changed, 133 insertions(+), 69 deletions(-)
diff --git a/actor-tests/src/test/java/org/apache/pekko/actor/JavaAPI.java
b/actor-tests/src/test/java/org/apache/pekko/actor/JavaAPI.java
index 1b813aa6e6..13d2126537 100644
--- a/actor-tests/src/test/java/org/apache/pekko/actor/JavaAPI.java
+++ b/actor-tests/src/test/java/org/apache/pekko/actor/JavaAPI.java
@@ -43,7 +43,6 @@ public class JavaAPI {
public void mustCompile() {
final Kill kill = Kill.getInstance();
final PoisonPill pill = PoisonPill.getInstance();
- final ReceiveTimeout t = ReceiveTimeout.getInstance();
final LocalScope ls = LocalScope.getInstance();
final NoScopeGiven noscope = NoScopeGiven.getInstance();
diff --git
a/actor-tests/src/test/scala/org/apache/pekko/actor/ReceiveTimeoutSpec.scala
b/actor-tests/src/test/scala/org/apache/pekko/actor/ReceiveTimeoutSpec.scala
index 72002a693e..4c2c11a1a0 100644
--- a/actor-tests/src/test/scala/org/apache/pekko/actor/ReceiveTimeoutSpec.scala
+++ b/actor-tests/src/test/scala/org/apache/pekko/actor/ReceiveTimeoutSpec.scala
@@ -56,8 +56,8 @@ object ReceiveTimeoutSpec {
restarting.set(true)
probe ! "crashing"
throw TestException("boom bang")
- case ReceiveTimeout =>
- probe ! ReceiveTimeout
+ case timeout: ReceiveTimeout =>
+ probe ! timeout
case other =>
probe ! other
}
@@ -94,7 +94,7 @@ class ReceiveTimeoutSpec extends PekkoSpec() {
context.setReceiveTimeout(500.milliseconds)
def receive = {
- case ReceiveTimeout => timeoutLatch.open()
+ case _: ReceiveTimeout => timeoutLatch.open()
}
}))
@@ -102,6 +102,23 @@ class ReceiveTimeoutSpec extends PekkoSpec() {
system.stop(timeoutActor)
}
+ "carry the configured timeout duration" taggedAs TimingTest in {
+ val probe = TestProbe()
+
+ val timeoutActor = system.actorOf(Props(new Actor {
+ context.setReceiveTimeout(500.milliseconds)
+
+ def receive = {
+ case timeout: ReceiveTimeout => probe.ref ! timeout
+ }
+ }))
+
+ val msg = probe.expectMsgType[ReceiveTimeout]
+ msg.timeout should ===(500.milliseconds)
+ msg.getTimeout should ===(java.time.Duration.ofMillis(500))
+ system.stop(timeoutActor)
+ }
+
"reschedule timeout after regular receive" taggedAs TimingTest in {
val timeoutLatch = TestLatch()
val messageCount = 100
@@ -111,8 +128,8 @@ class ReceiveTimeoutSpec extends PekkoSpec() {
context.setReceiveTimeout(1.second)
def receive = {
- case Tick => processedLatch.countDown()
- case ReceiveTimeout => timeoutLatch.open()
+ case Tick => processedLatch.countDown()
+ case _: ReceiveTimeout => timeoutLatch.open()
}
}))
@@ -132,8 +149,8 @@ class ReceiveTimeoutSpec extends PekkoSpec() {
context.setReceiveTimeout(500.milliseconds)
def receive = {
- case Tick => ()
- case ReceiveTimeout =>
+ case Tick => ()
+ case _: ReceiveTimeout =>
count.incrementAndGet
timeoutLatch.open()
context.setReceiveTimeout(Duration.Undefined)
@@ -152,7 +169,7 @@ class ReceiveTimeoutSpec extends PekkoSpec() {
val timeoutActor = system.actorOf(Props(new Actor {
def receive = {
- case ReceiveTimeout => timeoutLatch.open()
+ case _: ReceiveTimeout => timeoutLatch.open()
}
}))
@@ -167,8 +184,8 @@ class ReceiveTimeoutSpec extends PekkoSpec() {
context.setReceiveTimeout(1.second)
def receive = {
- case ReceiveTimeout => timeoutLatch.open()
- case TransparentTick =>
+ case _: ReceiveTimeout => timeoutLatch.open()
+ case TransparentTick =>
}
}))
@@ -189,7 +206,7 @@ class ReceiveTimeoutSpec extends PekkoSpec() {
context.setReceiveTimeout(1.second)
def receive = {
- case ReceiveTimeout =>
+ case _: ReceiveTimeout =>
self ! TransparentTick
timeoutLatch.countDown()
case TransparentTick =>
@@ -210,7 +227,7 @@ class ReceiveTimeoutSpec extends PekkoSpec() {
context.setReceiveTimeout(1.second)
def receive: Receive = {
- case ReceiveTimeout =>
+ case _: ReceiveTimeout =>
timeoutLatch.open()
case TransparentTick =>
count.incrementAndGet()
@@ -229,8 +246,8 @@ class ReceiveTimeoutSpec extends PekkoSpec() {
val timeoutActor = system.actorOf(Props(new Actor {
def receive = {
- case TransparentTick => context.setReceiveTimeout(500.milliseconds)
- case ReceiveTimeout => timeoutLatch.open()
+ case TransparentTick => context.setReceiveTimeout(500.milliseconds)
+ case _: ReceiveTimeout => timeoutLatch.open()
}
}))
@@ -247,8 +264,8 @@ class ReceiveTimeoutSpec extends PekkoSpec() {
context.setReceiveTimeout(500.milliseconds)
def receive = {
- case TransparentTick => context.setReceiveTimeout(Duration.Inf)
- case ReceiveTimeout => timeoutLatch.open()
+ case TransparentTick => context.setReceiveTimeout(Duration.Inf)
+ case _: ReceiveTimeout => timeoutLatch.open()
}
}))
@@ -266,8 +283,8 @@ class ReceiveTimeoutSpec extends PekkoSpec() {
context.setReceiveTimeout(initialTimeout)
def receive: Receive = {
- case TransparentTick => context.setReceiveTimeout(Duration.Undefined)
- case ReceiveTimeout => timeoutLatch.open()
+ case TransparentTick =>
context.setReceiveTimeout(Duration.Undefined)
+ case _: ReceiveTimeout => timeoutLatch.open()
}
}))
@@ -292,7 +309,7 @@ class ReceiveTimeoutSpec extends PekkoSpec() {
count += 1 // do some work then
context.setReceiveTimeout(initialTimeout)
- case ReceiveTimeout => probe.ref ! ReceiveTimeout
+ case timeout: ReceiveTimeout => probe.ref ! timeout
}
}))
@@ -320,7 +337,7 @@ class ReceiveTimeoutSpec extends PekkoSpec() {
probe.expectMsg("crashing")
probe.expectMsg("stopping")
probe.expectMsg("restarting")
- probe.expectMsg(ReceiveTimeout)
+ probe.expectMsgType[ReceiveTimeout]
}
"Will cancel receive timeout task if stopped" in {
diff --git
a/actor-typed-tests/src/test/scala/org/apache/pekko/actor/typed/CancelReceiveTimeoutSpec.scala
b/actor-typed-tests/src/test/scala/org/apache/pekko/actor/typed/CancelReceiveTimeoutSpec.scala
index c342ba9cfd..e227e624e8 100644
---
a/actor-typed-tests/src/test/scala/org/apache/pekko/actor/typed/CancelReceiveTimeoutSpec.scala
+++
b/actor-typed-tests/src/test/scala/org/apache/pekko/actor/typed/CancelReceiveTimeoutSpec.scala
@@ -96,7 +96,7 @@ class CancelReceiveTimeoutSpec extends
ScalaTestWithActorTestKit with AnyWordSpe
// This is what happens when the scheduler fires ReceiveTimeout before
// cancelReceiveTimeout() is processed but the actor dequeues them in the
// opposite order.
- ref.toClassic ! pekko.actor.ReceiveTimeout
+ ref.toClassic ! pekko.actor.ReceiveTimeout(1.second)
// Step 3: verify the actor is still alive and responsive (no NPE crash).
ref ! Ping
diff --git
a/actor-typed/src/main/scala/org/apache/pekko/actor/typed/internal/adapter/ActorAdapter.scala
b/actor-typed/src/main/scala/org/apache/pekko/actor/typed/internal/adapter/ActorAdapter.scala
index 7bee9f44f1..2b9ae0bd84 100644
---
a/actor-typed/src/main/scala/org/apache/pekko/actor/typed/internal/adapter/ActorAdapter.scala
+++
b/actor-typed/src/main/scala/org/apache/pekko/actor/typed/internal/adapter/ActorAdapter.scala
@@ -99,7 +99,7 @@ import pekko.util.OptionVal
ChildFailed(ActorRefAdapter(ref), ex)
} else Terminated(ActorRefAdapter(ref))
handleSignal(msg)
- case classic.ReceiveTimeout =>
+ case _: classic.ReceiveTimeout =>
// cancelReceiveTimeout() sets receiveTimeoutMsg to null, but a
classic ReceiveTimeout
// that was already enqueued in the mailbox before the cancel cannot
be retracted.
// Discard the stale timeout to avoid passing null into the typed
behavior stack (#3084).
diff --git
a/actor/src/main/mima-filters/2.0.x.backwards.excludes/receive-timeout-with-duration.excludes
b/actor/src/main/mima-filters/2.0.x.backwards.excludes/receive-timeout-with-duration.excludes
new file mode 100644
index 0000000000..8b5bb33515
--- /dev/null
+++
b/actor/src/main/mima-filters/2.0.x.backwards.excludes/receive-timeout-with-duration.excludes
@@ -0,0 +1,43 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# ReceiveTimeout changed from case object to case class with a timeout field
+# https://github.com/apache/pekko/issues/2569
+ProblemFilters.exclude[FinalClassProblem]("org.apache.pekko.actor.ReceiveTimeout")
+ProblemFilters.exclude[StaticVirtualMemberProblem]("org.apache.pekko.actor.ReceiveTimeout.canEqual")
+ProblemFilters.exclude[StaticVirtualMemberProblem]("org.apache.pekko.actor.ReceiveTimeout.productIterator")
+ProblemFilters.exclude[StaticVirtualMemberProblem]("org.apache.pekko.actor.ReceiveTimeout.productElement")
+ProblemFilters.exclude[StaticVirtualMemberProblem]("org.apache.pekko.actor.ReceiveTimeout.productArity")
+ProblemFilters.exclude[StaticVirtualMemberProblem]("org.apache.pekko.actor.ReceiveTimeout.productPrefix")
+ProblemFilters.exclude[StaticVirtualMemberProblem]("org.apache.pekko.actor.ReceiveTimeout.productElementNames")
+ProblemFilters.exclude[StaticVirtualMemberProblem]("org.apache.pekko.actor.ReceiveTimeout.productElementName")
+ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.actor.ReceiveTimeout.this")
+ProblemFilters.exclude[MissingTypesProblem]("org.apache.pekko.actor.ReceiveTimeout$")
+ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.actor.ReceiveTimeout.productElementName")
+ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.actor.ReceiveTimeout.productElementNames")
+ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.actor.ReceiveTimeout.productPrefix")
+ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.actor.ReceiveTimeout.productArity")
+ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.actor.ReceiveTimeout.productElement")
+ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.actor.ReceiveTimeout.productIterator")
+ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.actor.ReceiveTimeout.canEqual")
+ProblemFilters.exclude[MissingClassProblem]("org.apache.pekko.actor.ReceiveTimeout$")
+ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.actor.ReceiveTimeout.getInstance")
+ProblemFilters.exclude[IncompatibleTemplateDefProblem]("org.apache.pekko.actor.ReceiveTimeout")
+# Scala 3 only: Mirror/product members of the former case object
+ProblemFilters.exclude[IncompatibleResultTypeProblem]("org.apache.pekko.actor.ReceiveTimeout.fromProduct")
+ProblemFilters.exclude[IncompatibleSignatureProblem]("org.apache.pekko.actor.ReceiveTimeout.productElementNames")
+ProblemFilters.exclude[IncompatibleSignatureProblem]("org.apache.pekko.actor.ReceiveTimeout.productIterator")
diff --git a/actor/src/main/scala/org/apache/pekko/actor/Actor.scala
b/actor/src/main/scala/org/apache/pekko/actor/Actor.scala
index 3ba8abafc0..3b73d71729 100644
--- a/actor/src/main/scala/org/apache/pekko/actor/Actor.scala
+++ b/actor/src/main/scala/org/apache/pekko/actor/Actor.scala
@@ -18,6 +18,7 @@ import java.util.Optional
import scala.annotation.nowarn
import scala.annotation.tailrec
import scala.beans.BeanProperty
+import scala.jdk.DurationConverters._
import scala.util.control.NoStackTrace
import org.apache.pekko
@@ -144,19 +145,17 @@ private[pekko] final case class
AddressTerminated(address: Address)
with PossiblyHarmful
with DeadLetterSuppression
-abstract class ReceiveTimeout extends PossiblyHarmful
-
/**
- * When using ActorContext.setReceiveTimeout, the singleton instance of
ReceiveTimeout will be sent
- * to the Actor when there hasn't been any message for that long.
+ * When using ActorContext.setReceiveTimeout, a ReceiveTimeout message
carrying the configured
+ * timeout duration will be sent to the Actor when there hasn't been any
message for that long.
*/
@SerialVersionUID(1L)
-case object ReceiveTimeout extends ReceiveTimeout {
+final case class ReceiveTimeout(timeout:
scala.concurrent.duration.FiniteDuration) extends PossiblyHarmful {
/**
- * Java API: get the singleton instance
+ * Java API: get the timeout duration
*/
- def getInstance = this
+ def getTimeout: java.time.Duration = timeout.toJava
}
/**
diff --git
a/actor/src/main/scala/org/apache/pekko/actor/dungeon/ReceiveTimeout.scala
b/actor/src/main/scala/org/apache/pekko/actor/dungeon/ReceiveTimeout.scala
index 2c897e2872..21b4ce216d 100644
--- a/actor/src/main/scala/org/apache/pekko/actor/dungeon/ReceiveTimeout.scala
+++ b/actor/src/main/scala/org/apache/pekko/actor/dungeon/ReceiveTimeout.scala
@@ -73,7 +73,7 @@ private[pekko] trait ReceiveTimeout { this: ActorCell =>
private def rescheduleReceiveTimeout(data: State, timeout: FiniteDuration):
Unit = {
data.task.cancel() // Cancel any ongoing future
- data.task = system.scheduler.scheduleOnce(timeout, self,
pekko.actor.ReceiveTimeout)(this.dispatcher)
+ data.task = system.scheduler.scheduleOnce(timeout, self,
pekko.actor.ReceiveTimeout(timeout))(this.dispatcher)
data.version += 1
}
diff --git a/actor/src/main/scala/org/apache/pekko/io/TcpConnection.scala
b/actor/src/main/scala/org/apache/pekko/io/TcpConnection.scala
index b866f99ed0..3ae1366e13 100644
--- a/actor/src/main/scala/org/apache/pekko/io/TcpConnection.scala
+++ b/actor/src/main/scala/org/apache/pekko/io/TcpConnection.scala
@@ -100,7 +100,7 @@ private[io] abstract class TcpConnection(val tcp: TcpExt,
val channel: SocketCha
val info = ConnectionInfo(registration, commander, keepOpenOnPeerClosed
= false, useResumeWriting = false)
handleClose(info, Some(sender()), cmd.event)
- case ReceiveTimeout =>
+ case _: ReceiveTimeout =>
// after sending `Register` user should watch this actor to make sure
// it didn't die because of the timeout
log.debug("Configured registration timeout of [{}] expired, stopping",
RegisterTimeout)
diff --git
a/actor/src/main/scala/org/apache/pekko/io/TcpOutgoingConnection.scala
b/actor/src/main/scala/org/apache/pekko/io/TcpOutgoingConnection.scala
index 34c678ce20..2d6b1dfaee 100644
--- a/actor/src/main/scala/org/apache/pekko/io/TcpOutgoingConnection.scala
+++ b/actor/src/main/scala/org/apache/pekko/io/TcpOutgoingConnection.scala
@@ -87,7 +87,7 @@ private[io] class TcpOutgoingConnection(
register(remoteAddress, registration)
}
}
- case ReceiveTimeout =>
+ case _: ReceiveTimeout =>
connectionTimeout()
}
@@ -96,7 +96,7 @@ private[io] class TcpOutgoingConnection(
reportConnectFailure {
register(new InetSocketAddress(resolved.address(),
remoteAddress.getPort), registration)
}
- case ReceiveTimeout =>
+ case _: ReceiveTimeout =>
connectionTimeout()
case Failure(ex) =>
// async-dns responds with a Failure on DNS server lookup failure
@@ -141,7 +141,7 @@ private[io] class TcpOutgoingConnection(
reportConnectFailure {
channelRegistry.register(channel, SelectionKey.OP_CONNECT)
}
- case ReceiveTimeout =>
+ case _: ReceiveTimeout =>
connectionTimeout()
}
}
diff --git
a/bench-jmh/src/main/scala/org/apache/pekko/actor/ReceiveTimeoutBenchmark.scala
b/bench-jmh/src/main/scala/org/apache/pekko/actor/ReceiveTimeoutBenchmark.scala
index 67556b08f1..5757e0de18 100644
---
a/bench-jmh/src/main/scala/org/apache/pekko/actor/ReceiveTimeoutBenchmark.scala
+++
b/bench-jmh/src/main/scala/org/apache/pekko/actor/ReceiveTimeoutBenchmark.scala
@@ -90,7 +90,7 @@ object ReceiveTimeoutBenchmark {
override def receive: Receive = {
case Message =>
case finished: Finished => finished.latch.countDown()
- case ReceiveTimeout =>
+ case _: ReceiveTimeout =>
}
}
}
diff --git
a/cluster-metrics/src/multi-jvm/scala/org/apache/pekko/cluster/metrics/sample/StatsService.scala
b/cluster-metrics/src/multi-jvm/scala/org/apache/pekko/cluster/metrics/sample/StatsService.scala
index eef45b2c1a..9aaf98690a 100644
---
a/cluster-metrics/src/multi-jvm/scala/org/apache/pekko/cluster/metrics/sample/StatsService.scala
+++
b/cluster-metrics/src/multi-jvm/scala/org/apache/pekko/cluster/metrics/sample/StatsService.scala
@@ -51,7 +51,7 @@ class StatsAggregator(expectedResults: Int, replyTo:
ActorRef) extends Actor {
replyTo ! StatsResult(meanWordLength)
context.stop(self)
}
- case ReceiveTimeout =>
+ case _: ReceiveTimeout =>
replyTo ! JobFailed("Service unavailable, try again later")
context.stop(self)
}
diff --git
a/cluster-sharding/src/main/scala/org/apache/pekko/cluster/sharding/ShardCoordinator.scala
b/cluster-sharding/src/main/scala/org/apache/pekko/cluster/sharding/ShardCoordinator.scala
index 1d9baa46dc..752b02fc03 100644
---
a/cluster-sharding/src/main/scala/org/apache/pekko/cluster/sharding/ShardCoordinator.scala
+++
b/cluster-sharding/src/main/scala/org/apache/pekko/cluster/sharding/ShardCoordinator.scala
@@ -595,7 +595,7 @@ object ShardCoordinator {
shardRegionFrom,
regions.size)
- timers.startSingleTimer("hand-off-timeout", ReceiveTimeout, handOffTimeout)
+ timers.startSingleTimer("hand-off-timeout",
ReceiveTimeout(handOffTimeout), handOffTimeout)
def receive: Receive = {
case BeginHandOffAck(`shard`) =>
@@ -610,7 +610,7 @@ object ShardCoordinator {
shard)
acked(shardRegion)
}
- case ReceiveTimeout =>
+ case _: ReceiveTimeout =>
if (isRebalance)
log.debug("{}: Rebalance of [{}] from [{}] timed out", typeName,
shard, shardRegionFrom)
else
@@ -632,7 +632,7 @@ object ShardCoordinator {
def stoppingShard: Receive = {
case ShardStopped(`shard`) => done(ok
= true)
- case ReceiveTimeout => done(ok
= false)
+ case _: ReceiveTimeout => done(ok
= false)
case RebalanceWorker.ShardRegionTerminated(`shardRegionFrom`) =>
log.debug(
"{}: ShardRegion [{}] terminated while waiting for ShardStopped for
shard [{}].",
diff --git
a/cluster-sharding/src/multi-jvm/scala/org/apache/pekko/cluster/sharding/ClusterShardingSpec.scala
b/cluster-sharding/src/multi-jvm/scala/org/apache/pekko/cluster/sharding/ClusterShardingSpec.scala
index b939a1b4c9..1653dcf8cb 100644
---
a/cluster-sharding/src/multi-jvm/scala/org/apache/pekko/cluster/sharding/ClusterShardingSpec.scala
+++
b/cluster-sharding/src/multi-jvm/scala/org/apache/pekko/cluster/sharding/ClusterShardingSpec.scala
@@ -70,11 +70,11 @@ object ClusterShardingSpec {
}
override def receiveCommand: Receive = {
- case Increment => persist(CounterChanged(+1))(updateState)
- case Decrement => persist(CounterChanged(-1))(updateState)
- case Get(_) => sender() ! count
- case ReceiveTimeout => context.parent ! Passivate(stopMessage = Stop)
- case Stop => context.stop(self)
+ case Increment => persist(CounterChanged(+1))(updateState)
+ case Decrement => persist(CounterChanged(-1))(updateState)
+ case Get(_) => sender() ! count
+ case _: ReceiveTimeout => context.parent ! Passivate(stopMessage = Stop)
+ case Stop => context.stop(self)
}
}
// #counter-actor
@@ -499,7 +499,7 @@ abstract class ClusterShardingSpec(multiNodeConfig:
ClusterShardingSpecConfig)
runOn(second) {
region ! Get(2)
expectMsg(3)
- region ! EntityEnvelope(2, ReceiveTimeout)
+ region ! EntityEnvelope(2, ReceiveTimeout(120.seconds))
// let the Passivate-Stop roundtrip begin to trigger buffering of
subsequent messages
Thread.sleep(200)
region ! EntityEnvelope(2, Increment)
diff --git
a/cluster-tools/src/main/scala/org/apache/pekko/cluster/client/ClusterClient.scala
b/cluster-tools/src/main/scala/org/apache/pekko/cluster/client/ClusterClient.scala
index e953c151ff..1c087eac9d 100644
---
a/cluster-tools/src/main/scala/org/apache/pekko/cluster/client/ClusterClient.scala
+++
b/cluster-tools/src/main/scala/org/apache/pekko/cluster/client/ClusterClient.scala
@@ -924,8 +924,8 @@ object ClusterReceptionist {
}
def receive = {
- case Ping => // keep alive from client
- case ReceiveTimeout =>
+ case Ping => // keep alive from client
+ case _: ReceiveTimeout =>
log.debug("ClientResponseTunnel for client [{}] stopped due to
inactivity", client.path)
context.stop(self)
case msg =>
diff --git
a/cluster/src/main/scala/org/apache/pekko/cluster/SeedNodeProcess.scala
b/cluster/src/main/scala/org/apache/pekko/cluster/SeedNodeProcess.scala
index 9656b14020..36b905b95d 100644
--- a/cluster/src/main/scala/org/apache/pekko/cluster/SeedNodeProcess.scala
+++ b/cluster/src/main/scala/org/apache/pekko/cluster/SeedNodeProcess.scala
@@ -313,8 +313,8 @@ private[cluster] final class JoinSeedNodeProcess(
// first InitJoinAck reply, but incompatible
receiveInitJoinAckIncompatibleConfig(joinTo = address, origin =
sender(), behavior = Some(done))
- case InitJoinNack(_) => // that seed was uninitialized
- case ReceiveTimeout =>
+ case InitJoinNack(_) => // that seed was uninitialized
+ case _: ReceiveTimeout =>
if (attempt >= 2)
logWarning(
ClusterLogMarker.joinFailed,
@@ -332,6 +332,6 @@ private[cluster] final class JoinSeedNodeProcess(
def done: Actor.Receive = {
case InitJoinAck(_, _) => // already received one, skip rest
- case ReceiveTimeout => context.stop(self)
+ case _: ReceiveTimeout => context.stop(self)
}
}
diff --git
a/distributed-data/src/main/scala/org/apache/pekko/cluster/ddata/Replicator.scala
b/distributed-data/src/main/scala/org/apache/pekko/cluster/ddata/Replicator.scala
index 0546661029..957f5b108b 100644
---
a/distributed-data/src/main/scala/org/apache/pekko/cluster/ddata/Replicator.scala
+++
b/distributed-data/src/main/scala/org/apache/pekko/cluster/ddata/Replicator.scala
@@ -2639,7 +2639,7 @@ final class Replicator(settings: ReplicatorSettings)
extends Actor with ActorLog
import context.dispatcher
private val sendToSecondarySchedule =
context.system.scheduler.scheduleOnce(timeout / 5, self, SendToSecondary)
- private val timeoutSchedule = context.system.scheduler.scheduleOnce(timeout,
self, ReceiveTimeout)
+ private val timeoutSchedule = context.system.scheduler.scheduleOnce(timeout,
self, ReceiveTimeout(timeout))
var remaining = nodes.iterator.map(_.address).toSet
@@ -2801,7 +2801,7 @@ final class Replicator(settings: ReplicatorSettings)
extends Actor with ActorLog
}
}
secondaryNodes.foreach { replica(_) ! writeMsg }
- case ReceiveTimeout =>
+ case _: ReceiveTimeout =>
reply(isTimeout = true)
}
@@ -2920,7 +2920,7 @@ final class Replicator(settings: ReplicatorSettings)
extends Actor with ActorLog
reply(ok = true)
case SendToSecondary =>
secondaryNodes.foreach { replica(_) ! readMsg }
- case ReceiveTimeout => reply(ok = false)
+ case _: ReceiveTimeout => reply(ok = false)
}
def reply(ok: Boolean): Unit =
@@ -2947,7 +2947,7 @@ final class Replicator(settings: ReplicatorSettings)
extends Actor with ActorLog
case _: ReadResult =>
// collect late replies
remaining -= sender().path.address
- case SendToSecondary =>
- case ReceiveTimeout =>
+ case SendToSecondary =>
+ case _: ReceiveTimeout =>
}
}
diff --git a/docs/src/main/paradox/migration/migration-guide-1.x-2.x.md
b/docs/src/main/paradox/migration/migration-guide-1.x-2.x.md
index 4b7ebd32a1..2cbb360a14 100644
--- a/docs/src/main/paradox/migration/migration-guide-1.x-2.x.md
+++ b/docs/src/main/paradox/migration/migration-guide-1.x-2.x.md
@@ -42,3 +42,9 @@ The Java DSL `SourceWithContext` graph shape now uses
`pekko.japi.Pair` instead
Java code that passes a `SourceWithContext` directly to `GraphDSL` must use
`Pair`-typed stages.
Code that converts it with `asSource()` already uses `Pair` and requires no
changes.
([PR3388](https://github.com/apache/pekko/pull/3388))
+
+`ReceiveTimeout` changed from a `case object` singleton to a `final case
class` that carries the configured
+timeout duration. Scala pattern matches must use a type pattern (`case
timeout: ReceiveTimeout =>`) instead of a
+stable identifier pattern, and the `ReceiveTimeout.getInstance()` method has
been removed from the Java API;
+Java users should match on `ReceiveTimeout.class` instead.
+([PR3399](https://github.com/apache/pekko/pull/3399))
diff --git a/docs/src/test/java/jdocs/actor/FaultHandlingDocSample.java
b/docs/src/test/java/jdocs/actor/FaultHandlingDocSample.java
index 48593c90d3..14daa885d0 100644
--- a/docs/src/test/java/jdocs/actor/FaultHandlingDocSample.java
+++ b/docs/src/test/java/jdocs/actor/FaultHandlingDocSample.java
@@ -84,8 +84,8 @@ public class FaultHandlingDocSample {
getContext().getSystem().terminate();
}
})
- .matchEquals(
- ReceiveTimeout.getInstance(),
+ .match(
+ ReceiveTimeout.class,
x -> {
// No progress within 15 seconds, ServiceUnavailable
log().error("Shutting down due to unavailable service");
diff --git a/docs/src/test/java/jdocs/sharding/ClusterShardingTest.java
b/docs/src/test/java/jdocs/sharding/ClusterShardingTest.java
index 7db1c69131..328cd0fbb9 100644
--- a/docs/src/test/java/jdocs/sharding/ClusterShardingTest.java
+++ b/docs/src/test/java/jdocs/sharding/ClusterShardingTest.java
@@ -218,7 +218,7 @@ public class ClusterShardingTest {
.match(Get.class, this::receiveGet)
.matchEquals(CounterOp.INCREMENT, msg -> receiveIncrement())
.matchEquals(CounterOp.DECREMENT, msg -> receiveDecrement())
- .matchEquals(ReceiveTimeout.getInstance(), msg -> passivate())
+ .match(ReceiveTimeout.class, msg -> passivate())
.build();
}
diff --git a/docs/src/test/scala/docs/actor/ActorDocSpec.scala
b/docs/src/test/scala/docs/actor/ActorDocSpec.scala
index 7cde553ee9..2f4f22d623 100644
--- a/docs/src/test/scala/docs/actor/ActorDocSpec.scala
+++ b/docs/src/test/scala/docs/actor/ActorDocSpec.scala
@@ -555,7 +555,7 @@ class ActorDocSpec extends PekkoSpec("""
case "Hello" =>
// To set in a response to a message
context.setReceiveTimeout(100.milliseconds)
- case ReceiveTimeout =>
+ case _: ReceiveTimeout =>
// To turn it off
context.setReceiveTimeout(Duration.Undefined)
throw new RuntimeException("Receive timed out")
diff --git a/docs/src/test/scala/docs/actor/FaultHandlingDocSample.scala
b/docs/src/test/scala/docs/actor/FaultHandlingDocSample.scala
index 08db47715b..67014af3fd 100644
--- a/docs/src/test/scala/docs/actor/FaultHandlingDocSample.scala
+++ b/docs/src/test/scala/docs/actor/FaultHandlingDocSample.scala
@@ -67,7 +67,7 @@ class Listener extends Actor with ActorLogging {
context.system.terminate()
}
- case ReceiveTimeout =>
+ case _: ReceiveTimeout =>
// No progress within 15 seconds, ServiceUnavailable
log.error("Shutting down due to unavailable service")
context.system.terminate()
diff --git
a/docs/src/test/scala/docs/circuitbreaker/CircuitBreakerDocSpec.scala
b/docs/src/test/scala/docs/circuitbreaker/CircuitBreakerDocSpec.scala
index 3cf1f37599..7d4e0d2932 100644
--- a/docs/src/test/scala/docs/circuitbreaker/CircuitBreakerDocSpec.scala
+++ b/docs/src/test/scala/docs/circuitbreaker/CircuitBreakerDocSpec.scala
@@ -75,7 +75,7 @@ class TellPatternActor(recipient: ActorRef) extends Actor
with ActorLogging {
case err: Throwable => {
breaker.fail()
}
- case ReceiveTimeout => {
+ case _: ReceiveTimeout => {
breaker.fail()
}
}
diff --git a/docs/src/test/scala/docs/cluster/FactorialFrontend.scala
b/docs/src/test/scala/docs/cluster/FactorialFrontend.scala
index f645efdd44..fe257c2727 100644
--- a/docs/src/test/scala/docs/cluster/FactorialFrontend.scala
+++ b/docs/src/test/scala/docs/cluster/FactorialFrontend.scala
@@ -46,7 +46,7 @@ class FactorialFrontend(upToN: Int, repeat: Boolean) extends
Actor with ActorLog
if (repeat) sendJobs()
else context.stop(self)
}
- case ReceiveTimeout =>
+ case _: ReceiveTimeout =>
log.info("Timeout")
sendJobs()
}
diff --git
a/persistence/src/main/scala/org/apache/pekko/persistence/journal/AsyncWriteProxy.scala
b/persistence/src/main/scala/org/apache/pekko/persistence/journal/AsyncWriteProxy.scala
index 7e4b175340..139e42e599 100644
---
a/persistence/src/main/scala/org/apache/pekko/persistence/journal/AsyncWriteProxy.scala
+++
b/persistence/src/main/scala/org/apache/pekko/persistence/journal/AsyncWriteProxy.scala
@@ -156,7 +156,7 @@ private class ReplayMediator(
case ReplayFailure(cause) =>
replayCompletionPromise.failure(cause)
context.stop(self)
- case ReceiveTimeout =>
+ case _: ReceiveTimeout =>
replayCompletionPromise.failure(
new AsyncReplayTimeoutException(s"replay timed out after
${replayTimeout.toSeconds} seconds inactivity"))
context.stop(self)
diff --git
a/persistence/src/main/scala/org/apache/pekko/persistence/journal/PersistencePluginProxy.scala
b/persistence/src/main/scala/org/apache/pekko/persistence/journal/PersistencePluginProxy.scala
index ba02fb9ad4..f9228dc55c 100644
---
a/persistence/src/main/scala/org/apache/pekko/persistence/journal/PersistencePluginProxy.scala
+++
b/persistence/src/main/scala/org/apache/pekko/persistence/journal/PersistencePluginProxy.scala
@@ -176,9 +176,9 @@ final class PersistencePluginProxy(config: Config) extends
Actor with Stash with
context.watch(target)
unstashAll()
context.become(active(target, address == selfAddress))
- case _: ActorIdentity => // will retry after ReceiveTimeout
- case Terminated(_) =>
- case ReceiveTimeout =>
+ case _: ActorIdentity => // will retry after ReceiveTimeout
+ case Terminated(_) =>
+ case _: ReceiveTimeout =>
sendIdentify(address)
}: Receive).orElse(init)
diff --git
a/remote-tests/src/multi-jvm/scala/org/apache/pekko/remote/artery/MaxThroughputSpec.scala
b/remote-tests/src/multi-jvm/scala/org/apache/pekko/remote/artery/MaxThroughputSpec.scala
index 5a788df1a4..c7712d3bdd 100644
---
a/remote-tests/src/multi-jvm/scala/org/apache/pekko/remote/artery/MaxThroughputSpec.scala
+++
b/remote-tests/src/multi-jvm/scala/org/apache/pekko/remote/artery/MaxThroughputSpec.scala
@@ -208,7 +208,7 @@ object MaxThroughputSpec extends MultiNodeConfig {
runWarmup()
} else
target.tell(Warmup(payload), self)
- case ReceiveTimeout =>
+ case _: ReceiveTimeout =>
target.tell(Warmup(payload), self)
}
diff --git
a/testkit/src/test/scala/org/apache/pekko/testkit/TestActorRefSpec.scala
b/testkit/src/test/scala/org/apache/pekko/testkit/TestActorRefSpec.scala
index d448189423..fe5342b220 100644
--- a/testkit/src/test/scala/org/apache/pekko/testkit/TestActorRefSpec.scala
+++ b/testkit/src/test/scala/org/apache/pekko/testkit/TestActorRefSpec.scala
@@ -103,7 +103,7 @@ object TestActorRefSpec {
class ReceiveTimeoutActor(target: ActorRef) extends Actor {
context.setReceiveTimeout(1.second)
def receive = {
- case ReceiveTimeout =>
+ case _: ReceiveTimeout =>
target ! "timeout"
context.stop(self)
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]