0

So I'm trying to work with both Squeryl and Akka Actors. I've done a lot of searching and all I've been able to find is the following Google Group post:

https://groups.google.com/forum/#!topic/squeryl/M0iftMlYfpQ

I think I might have shot myself in the foot as I originally created this factory pattern so I could toss around Database objects.

object DatabaseType extends Enumeration {
  type DatabaseType = Value
  val Postgres = Value(1,"Postgres")
  val H2 = Value(2,"H2")
}

object Database {

  def getInstance(dbType : DatabaseType, jdbcUrl : String, username : String, password : String) : Database = {

    Class.forName(jdbcDriver(dbType))
    new Database(Session.create(
      _root_.java.sql.DriverManager.getConnection(jdbcUrl,username,password),
      squerylAdapter(dbType)))

  }

  private def jdbcDriver(db : DatabaseType) = {
    db match {
      case DatabaseType.Postgres => "org.postgresql.Driver"
      case DatabaseType.H2 => "org.h2.Driver"
    }
  }

  private def squerylAdapter(db : DatabaseType) = {
    db match {
      case DatabaseType.Postgres => new PostgreSqlAdapter
      case DatabaseType.H2 => new H2Adapter
    }
  }      
}

Originally in my implementation, I tried surrounding all my statements in using(session), but I'd keep getting the dreaded "No session is bound to the current thread" error, so I added the session.bindToCuirrentThread to the constructor.

class Database(session: Session) {

  session.bindToCurrent

  def failedBatch(filename : String, message : String, start : Timestamp = now, end : Timestamp = now) =
batch.insert(new Batch(0,filename,Some(start),Some(end),ProvisioningStatus.Fail,Some(message)))

  def startBatch(batch_id : Long, start : Timestamp = now) =
batch update (b => where (b.id === batch_id) set (b.start := Some(start)))

...more functions

This worked reasonably well, until I got to Scala Actors.

class TransferActor() extends Actor {

  def databaseInstance() = {
    val dbConfig = config.getConfig("provisioning.database")
    Database.getInstance(DatabaseType.Postgres,
      dbConfig.getString("jdbcUrl"),
      dbConfig.getString("username"),
      dbConfig.getString("password"))
  }

  lazy val config = ConfigManager.current

  override def receive: Actor.Receive = { /* .. do some work */

I constantly get the following:

[ERROR] [03/11/2014 17:02:57.720] [provisioning-system-akka.actor.default-dispatcher-4] [akka://provisioning-system/user/$c] No session is bound to current thread, a session must be created via Session.create 
and bound to the thread via 'work' or 'bindToCurrentThread'
Usually this error occurs when a statement is executed outside of a transaction/inTrasaction block
java.lang.RuntimeException: No session is bound to current thread, a session must be created via Session.create 
and bound to the thread via 'work' or 'bindToCurrentThread'

I'm getting a fresh Database object each time, not caching it with a lazy val, so shouldn't that constructor always get called and attach to my current thread? Does Akka attach different threads to different actors and swap them around? Should I just add a function to call session.bindToCurrentThread each time I'm in an actor? Seems kinda hacky.

djsumdog
  • 2,560
  • 1
  • 29
  • 55
  • One thing that worked was moving instantiating the Database into the receive: Actor.Receive ={} block. This seems to work, but not sure if it's the best long term solution. – djsumdog Mar 11 '14 at 04:46

1 Answers1

1

Does Akka attach different threads to different actors and swap them around?

That's exactly how the actor model works. The idea is that you can have a small thread pool servicing a very large number of threads because actors only need to use a thread when they have a message waiting to be processed.

Some general tips for Squeryl.... A session is a one to one association with a JDBC connection. The main advantage of keeping Sessions open is that you can have a transaction open that gives you a consistent view of the database as you perform multiple operations. If you don't need that, make your session/transaction code granular to avoid these types of issues. If you do need it, don't rely on Sessions being available in a thread local context. Use the transaction(session){} or transaction(sessionFactory){} methods to explicitly tell Squeryl where you want your Session to come from.

Dave Whittaker
  • 3,102
  • 13
  • 14