
im.actor.server.social.SocialManager.scala Maven / Gradle / Ivy
The newest version!
package im.actor.server.social
import scala.concurrent.Future
import scala.concurrent._
import scala.concurrent.duration._
import scala.util.{ Success, Failure }
import akka.actor._
import akka.cluster.sharding.{ ClusterShardingSettings, ClusterSharding, ShardRegion }
import akka.pattern.{ ask, pipe }
import akka.util.Timeout
import slick.driver.PostgresDriver.api._
import im.actor.server.db.DbExtension
import im.actor.server.persist
sealed trait SocialExtension extends Extension {
val region: SocialManagerRegion
}
final class SocialExtensionImpl(system: ActorSystem, db: Database) extends SocialExtension {
lazy val region: SocialManagerRegion = SocialManager.startRegion()(system, db)
}
object SocialExtension extends ExtensionId[SocialExtension] with ExtensionIdProvider {
override def lookup = SocialExtension
override def createExtension(system: ExtendedActorSystem) = new SocialExtensionImpl(system, DbExtension(system).db)
}
@SerialVersionUID(1L)
case class SocialManagerRegion(ref: ActorRef)
object SocialManager {
private case class Envelope(userId: Int, payload: Message)
private sealed trait Message
@SerialVersionUID(1L)
private case class RelationsNoted(userIds: Set[Int]) extends Message
@SerialVersionUID(1L)
private case class RelationNoted(userId: Int) extends Message
@SerialVersionUID(1L)
private case object GetRelations extends Message
@SerialVersionUID(1L)
private case class Relations(userIds: Set[Int])
@SerialVersionUID(1L)
private case class Initiated(userIds: Set[Int])
private val extractEntityId: ShardRegion.ExtractEntityId = {
case env @ Envelope(userId, payload) ⇒ (userId.toString, env)
}
private val extractShardId: ShardRegion.ExtractShardId = msg ⇒ msg match {
case Envelope(userId, _) ⇒ (userId % 100).toString // TODO: configurable
}
private val typeName = "SocialManager"
private def startRegion(props: Props)(implicit system: ActorSystem): SocialManagerRegion =
SocialManagerRegion(ClusterSharding(system).start(
typeName = typeName,
entityProps = props,
settings = ClusterShardingSettings(system),
extractEntityId = extractEntityId,
extractShardId = extractShardId
))
def startRegion()(implicit system: ActorSystem, db: Database): SocialManagerRegion =
startRegion(props)
def startRegionProxy()(implicit system: ActorSystem): SocialManagerRegion =
SocialManagerRegion(ClusterSharding(system).startProxy(
typeName = typeName,
role = None,
extractEntityId = extractEntityId,
extractShardId = extractShardId
))
def props(implicit db: Database) = Props(classOf[SocialManager], db)
def recordRelations(userId: Int, relatedTo: Set[Int])(implicit region: SocialManagerRegion): Unit = {
region.ref ! Envelope(userId, RelationsNoted(relatedTo))
}
def recordRelation(userId: Int, relatedTo: Int)(implicit region: SocialManagerRegion): Unit = {
region.ref ! Envelope(userId, RelationNoted(relatedTo))
}
def getRelations(userId: Int)(
implicit
region: SocialManagerRegion,
timeout: Timeout,
ec: ExecutionContext
): Future[Set[Int]] = {
region.ref.ask(Envelope(userId, GetRelations)).mapTo[Relations] map (_.userIds)
}
}
class SocialManager(implicit db: Database) extends Actor with ActorLogging with Stash {
import SocialManager._
implicit val ec: ExecutionContext = context.dispatcher
context.setReceiveTimeout(15.minutes) // TODO: configurable
def receive = {
case env @ Envelope(userId, _) ⇒
stash()
db.run(persist.social.RelationRepo.find(userId)) onComplete {
case Success(userIds) ⇒
self ! Initiated(userIds.toSet)
case Failure(e) ⇒
log.error(e, "Failed to load realations")
context.stop(self)
}
context.become(stashing)
case msg ⇒
stash()
}
def stashing: Receive = {
case Initiated(userIds: Set[Int]) ⇒
unstashAll()
context.become(working(userIds))
case msg ⇒
stash()
}
def working(userIds: Set[Int]): Receive = {
case env @ Envelope(userId, RelationsNoted(notedUserIds)) ⇒
val uniqUserIds = notedUserIds.diff(userIds).filterNot(_ == userId)
if (uniqUserIds.nonEmpty) {
context.become(working(userIds ++ uniqUserIds))
db.run(persist.social.RelationRepo.create(userId, uniqUserIds))
}
case env @ Envelope(userId, RelationNoted(notedUserId)) ⇒
if (!userIds.contains(notedUserId) && userId != notedUserId) {
context.become(working(userIds + notedUserId))
db.run(persist.social.RelationRepo.create(userId, notedUserId))
}
case env @ Envelope(userId, GetRelations) ⇒
sender() ! Relations(userIds)
case ReceiveTimeout ⇒
context.parent ! ShardRegion.Passivate(stopMessage = PoisonPill)
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy