3

Hey I read this jdbc docs https://www.playframework.com/documentation/2.1.0/ScalaDatabase and this question Is it good to put jdbc operations in actors?

Now I have an ActorClass for my mysql transaction, and this actor instantiated several times, whenever request comes. So each request would instantiate new actor. Is it safe for connection pool?

Can I use

val connection = DB.getConnection()

is connection object could handle async transaction? So I could just a singleton to handle mysql connection and used it in all instantiated actors. Also if I want to use anorm, how do I make an implicit connection object?

Thanks

Community
  • 1
  • 1
ans4175
  • 432
  • 1
  • 9
  • 23

4 Answers4

3

Your DB.getConnection() should be a promise[Connection] or a future[Connection] if you don't want to block the actor. (caveats at the end of the answer)

If your DB.getConnection() is synchronous (returning only connection without wrapping type) your actor will hang until it actually gets a connection from the pool while processing the actual message. It doesn't matter your DB being singleton or not, in the end it will hit the connection pool.

That being said, you can create actors to handle the messaging and other actors to handle persistence in the database, put them in different thread dispatchers giving more thread to database intensive. This is suggested also in the PlayFramework.

Caveats:

If you run futures inside the actor you are not ensure of the thread/timing it will run, I'm assuming you did something in the line of these (read the comments)

def receive = {
  case aMessage => 
    val aFuture = future(db.getConnection)
    aFuture.map { theConn => //from previous line when you acquire the conn and when you execute the next line
                             //it could pass a long time they run in different threads/time 
                             //that's why you should better create an actor that handles this sync and let
                             //akka do the async part
      theConn.prepareStatemnt(someSQL)
      //omitted code...
    }
}

so my suggestion would be

//actor A receives, 
//actor B proccess db (and have multiple instances of this one due to slowness from db)

class ActorA(routerOfB : ActorRef) extends Actor {
  def recieve = {
    case aMessage =>
      routerOfB ! aMessage
  }
}

class ActorB(db : DB) extends Actor {
  def receive = {
    case receive = {
      val conn = db.getConnection //this blocks but we have multiple instances 
                                  //and enforces to run in same thread
      val ps = conn.prepareStatement(someSQL)
    }
  }
}

You will need routing: http://doc.akka.io/docs/akka/2.4.1/scala/routing.html

Paul Roub
  • 36,322
  • 27
  • 84
  • 93
  • how to handle connection close? `java.sql.SQLException: Connection is closed!` – ans4175 Jan 17 '16 at 12:42
  • I improved the answer please check it out. Problem with previous approach is your connection could be closed when the future is run by another thread. – Luis Ramirez-Monterosa Jan 19 '16 at 14:59
  • aah, nice points. So it's instantiated within actor but limited by routing to few actor instances? – ans4175 Jan 20 '16 at 03:23
  • so you can instantiate a bunch of actors by using `val workers = context.actorOf(Props[ItemProcessingWorker].withRouter(RoundRobinRouter(100)))` workers is an actor ref and you can send a message to it `workers ! aMessage` and that means will be sent only to one of workers in round robin fashion. Read more about routing and dispatchers, akka helps you to isolate components, you don't want your message handler share threads with your db handlers, db requires more thread since will block everytime gets a conn – Luis Ramirez-Monterosa Jan 20 '16 at 14:46
1
  • As I know you couldn't run multiple concurrent query on a single connection for RDBMS ( I've not seen any resource/reference for async/non-blocking call for mysql even in C-API; ). To run your queries concurrently, you most have multiple connection instances.

  • DB.getConnection isn't expensive while you have multiple instances of connection. The most expensive area for working with DB is running sql query and waiting for its response.

  • To being async with your DB-calls, you should run them in other threads (not in the main thread-pool of Akka or Play); Slick does it for you. It manages a thread-pool and run your DB-calls on them, then your main threads will be free for processing income requests. Then you dont need to wrap your DB-calls in actors to being async.

Reza Same'ei
  • 819
  • 8
  • 22
  • nice points, I have managed it with pattern like this: MysqlConn (singleton) > handling one connection at a time, then return object connection when called; ActorMysql (Actor Class) > instantiated each time another actor or modules want to store or retrieve data, within this actor will call MysqlConn.getConnection; Well, up until now it is still reliable – ans4175 Jan 15 '16 at 08:20
  • It's reliable; But what about concurrency? Your single connection is your bottleneck and don't let you run mutiple queries concurrently. – Reza Same'ei Jan 15 '16 at 08:37
0

I think you should take a connection from pool and return when it is done. If we go by single connection per actor what if that connection gets disconnect, you may need to reinitialize it.

For transactions you may want to try

DB.withTransaction { conn =>  // do whatever you need with the connection}
Gajendra Naidu
  • 395
  • 3
  • 16
0

For more functional way of database access I would recommend to look into slick which has a nice API that can be integrated with plain actors and going further with streams.

d-n-ust
  • 122
  • 1
  • 9