Move inlined example to its own class (#22453)
Issue: #22453 Mode inlined example for Shared Mutable State to its own class Add additional wrong case when the message is mutable Includes auto reformated code
This commit is contained in:
parent
0367812e40
commit
5102d14168
4 changed files with 87 additions and 35 deletions
|
|
@ -228,7 +228,7 @@ private[akka] class Shard(
|
||||||
|
|
||||||
def passivate(entity: ActorRef, stopMessage: Any): Unit = {
|
def passivate(entity: ActorRef, stopMessage: Any): Unit = {
|
||||||
idByRef.get(entity) match {
|
idByRef.get(entity) match {
|
||||||
case Some(id) => if (!messageBuffers.contains(id)) {
|
case Some(id) ⇒ if (!messageBuffers.contains(id)) {
|
||||||
log.debug("Passivating started on entity {}", id)
|
log.debug("Passivating started on entity {}", id)
|
||||||
|
|
||||||
passivating = passivating + entity
|
passivating = passivating + entity
|
||||||
|
|
@ -237,7 +237,7 @@ private[akka] class Shard(
|
||||||
} else {
|
} else {
|
||||||
log.debug("Passivation already in progress for {}. Not sending stopMessage back to entity.", entity)
|
log.debug("Passivation already in progress for {}. Not sending stopMessage back to entity.", entity)
|
||||||
}
|
}
|
||||||
case None => log.debug("Unknown entity {}. Not sending stopMessage back to entity.", entity)
|
case None ⇒ log.debug("Unknown entity {}. Not sending stopMessage back to entity.", entity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,34 +72,6 @@ Since Akka runs on the JVM there are still some rules to be followed.
|
||||||
|
|
||||||
* Closing over internal Actor state and exposing it to other threads
|
* Closing over internal Actor state and exposing it to other threads
|
||||||
|
|
||||||
.. code-block:: scala
|
.. includecode:: ../scala/code/docs/actor/SharedMutableStateDocSpec.scala#mutable-state
|
||||||
|
|
||||||
class MyActor extends Actor {
|
|
||||||
var state = ...
|
|
||||||
def receive = {
|
|
||||||
case _ =>
|
|
||||||
//Wrongs
|
|
||||||
|
|
||||||
// Very bad, shared mutable state,
|
|
||||||
// will break your application in weird ways
|
|
||||||
Future { state = NewState }
|
|
||||||
anotherActor ? message onSuccess { r => state = r }
|
|
||||||
|
|
||||||
// Very bad, "sender" changes for every message,
|
|
||||||
// shared mutable state bug
|
|
||||||
Future { expensiveCalculation(sender()) }
|
|
||||||
|
|
||||||
//Rights
|
|
||||||
|
|
||||||
// Completely safe, "self" is OK to close over
|
|
||||||
// and it's an ActorRef, which is thread-safe
|
|
||||||
Future { expensiveCalculation() } onComplete { f => self ! f.value.get }
|
|
||||||
|
|
||||||
// Completely safe, we close over a fixed value
|
|
||||||
// and it's an ActorRef, which is thread-safe
|
|
||||||
val currentSender = sender()
|
|
||||||
Future { expensiveCalculation(currentSender) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
* Messages **should** be immutable, this is to avoid the shared mutable state trap.
|
* Messages **should** be immutable, this is to avoid the shared mutable state trap.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
|
||||||
|
*/
|
||||||
|
package docs.actor
|
||||||
|
|
||||||
|
class SharedMutableStateDocSpec {
|
||||||
|
|
||||||
|
//#mutable-state
|
||||||
|
import akka.actor.{ Actor, ActorRef }
|
||||||
|
import akka.pattern.ask
|
||||||
|
import akka.util.Timeout
|
||||||
|
import scala.concurrent.Future
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
import scala.language.postfixOps
|
||||||
|
import scala.collection.mutable
|
||||||
|
|
||||||
|
case class Message(msg: String)
|
||||||
|
|
||||||
|
class EchoActor extends Actor {
|
||||||
|
def receive = {
|
||||||
|
case msg => sender() ! msg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CleanUpActor extends Actor {
|
||||||
|
def receive = {
|
||||||
|
case set: mutable.Set[_] => set.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyActor(echoActor: ActorRef, cleanUpActor: ActorRef) extends Actor {
|
||||||
|
var state = ""
|
||||||
|
val mySet = mutable.Set[String]()
|
||||||
|
|
||||||
|
def expensiveCalculation(actorRef: ActorRef): String = {
|
||||||
|
// this is a very costly operation
|
||||||
|
"Meaning of live is 42"
|
||||||
|
}
|
||||||
|
|
||||||
|
def expensiveCalculation(): String = {
|
||||||
|
// this is a very costly operation
|
||||||
|
"Meaning of live is 42"
|
||||||
|
}
|
||||||
|
|
||||||
|
def receive = {
|
||||||
|
case _ =>
|
||||||
|
|
||||||
|
//Wrong ways
|
||||||
|
implicit val ec = context.dispatcher
|
||||||
|
implicit val timeout = Timeout(5 seconds) // needed for `?` below
|
||||||
|
|
||||||
|
// Very bad, shared mutable state,
|
||||||
|
// will break your application in weird ways
|
||||||
|
Future { state = "This will race" }
|
||||||
|
((echoActor ? Message("With this other one")).mapTo[Message])
|
||||||
|
.foreach { received => state = received.msg }
|
||||||
|
|
||||||
|
// Very bad, shared mutable object,
|
||||||
|
// the other actor cand mutate your own state,
|
||||||
|
// or worse, you might get weird race conditions
|
||||||
|
cleanUpActor ! mySet
|
||||||
|
|
||||||
|
// Very bad, "sender" changes for every message,
|
||||||
|
// shared mutable state bug
|
||||||
|
Future { expensiveCalculation(sender()) }
|
||||||
|
|
||||||
|
//Right ways
|
||||||
|
|
||||||
|
// Completely safe, "self" is OK to close over
|
||||||
|
// and it's an ActorRef, which is thread-safe
|
||||||
|
Future { expensiveCalculation() } foreach { self ! _ }
|
||||||
|
|
||||||
|
// Completely safe, we close over a fixed value
|
||||||
|
// and it's an ActorRef, which is thread-safe
|
||||||
|
val currentSender = sender()
|
||||||
|
Future { expensiveCalculation(currentSender) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//#mutable-state
|
||||||
|
}
|
||||||
|
|
@ -4,11 +4,11 @@
|
||||||
|
|
||||||
package akka.remote.serialization
|
package akka.remote.serialization
|
||||||
|
|
||||||
import akka.actor.{Actor, ActorRef, ActorSystem, Address, Deploy, ExtendedActorSystem, OneForOneStrategy, Props, SupervisorStrategy}
|
import akka.actor.{ Actor, ActorRef, ActorSystem, Address, Deploy, ExtendedActorSystem, OneForOneStrategy, Props, SupervisorStrategy }
|
||||||
import akka.remote.{DaemonMsgCreate, RemoteScope}
|
import akka.remote.{ DaemonMsgCreate, RemoteScope }
|
||||||
import akka.routing.{FromConfig, RoundRobinPool}
|
import akka.routing.{ FromConfig, RoundRobinPool }
|
||||||
import akka.serialization.SerializationExtension
|
import akka.serialization.SerializationExtension
|
||||||
import akka.testkit.{AkkaSpec, TestKit}
|
import akka.testkit.{ AkkaSpec, TestKit }
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue