atom.gregg
atom.gregg

Reputation: 1007

Scala closing over val when asking

I want to know if the following code will close over the id value in the ask 'callback' in the map function.

val id = if (model.id.isEmpty) UUID.randomUUID().toString else model.id
val result = couchbaseActor ? SetDoc(s"user:$id", model.toJson.compactPrint)
   result map {
      case true => sender ! Right(Success(id))
      case false => sender ! Left(makeFailureFromErrorEnum(ErrorCode.DbSaveFailed, List("User", "Error occurred while saving to Couchbase")))
  }

Thanks, Aaron

Eventual Solution:

Message Handler:

case SaveUserReq(model) => saveDocument[User](sender, "User", model.id, model)

Definition:

def saveDocument[T:JsonWriter](requester: ActorRef, prefix: String, id: String, model: T): Unit = {
  couchbaseActor ? SetDoc(s"${prefix.toLowerCase}:$id", model.toJson.compactPrint) map {
    case true => requester ! Right(Success(id))
    case false => requester ! Left(makeFailureFromErrorEnum(ErrorCode.DbSaveFailed, List(prefix, errorCouchbaseSaveFailed)))
  }
}

Thank you to all who assisted.

Upvotes: 2

Views: 225

Answers (3)

Arseniy Zhizhelev
Arseniy Zhizhelev

Reputation: 2401

  1. Well, I think that id is used as is. It is a constant, isn't it?

  2. Victor Klang gives the source of error: the access to sender method of this actor. Your code actually looks like

    val id = if (model.id.isEmpty) UUID.randomUUID().toString else model.id
    val result = couchbaseActor ? SetDoc(s"user:$id", model.toJson.compactPrint)
    result map {
      case true => this.sender.tell(Right(Success(id)), self)
      case false => this.sender.tell(Left(makeFailureFromErrorEnum(ErrorCode.DbSaveFailed, List("User", "Error occurred while saving to Couchbase"))), self)
    }
    

    It seems that it closes over this.

    Probably rewriting the code the following way:

    val id = if (model.id.isEmpty) UUID.randomUUID().toString else model.id
    val theSender = sender
    val result = couchbaseActor ? SetDoc(s"user:$id", model.toJson.compactPrint)
    result map {
      case true => theSender ! Right(Success(id))
      case false => theSender ! Left(makeFailureFromErrorEnum(ErrorCode.DbSaveFailed, List("User", "Error occurred while saving to Couchbase")))
    }
    

    can help.

Upvotes: 2

Viktor Klang
Viktor Klang

Reputation: 26579

sender/getSender() disappears when I use Future in my Actor, why?

"When using future callbacks, inside actors you need to carefully avoid closing over the containing actor’s reference, i.e. do not call methods or access mutable state on the enclosing actor from within the callback. This breaks the actor encapsulation and may introduce synchronization bugs and race conditions because the callback will be scheduled concurrently to the enclosing actor. Unfortunately there is not yet a way to detect these illegal accesses at compile time.

Read more about it in the docs for Actors and the JMM"

Upvotes: 3

leipie
leipie

Reputation: 122

Yes it should

By definition of a closure

Upvotes: 1

Related Questions