91 lines
3.7 KiB
Scala
91 lines
3.7 KiB
Scala
package com.twitter.tweetypie.storage
|
|
|
|
import com.twitter.storage.client.manhattan.kv.ManhattanValue
|
|
import com.twitter.util.Time
|
|
|
|
/**
|
|
* A [[TweetStateRecord]] represents an action taken on a tweet and can be used to determine a tweet's state.
|
|
*
|
|
* The state is determined by the record with the most recent timestamp. In the absence of any
|
|
* record a tweet is considered found, which is to say the tweet has not been through the
|
|
* deletion process.
|
|
*
|
|
* The [[TweetStateRecord]] type is determined by the lkey of a tweet manhattan record:
|
|
* metadata/delete_state -> HardDeleted
|
|
* metadata/soft_delete_state -> SoftDeleted
|
|
* metadata/undelete_state -> Undeleted
|
|
* metadata/force_added_state -> ForceAdded
|
|
*
|
|
* See the README in this directory for more details about the state of a tweet.
|
|
*/
|
|
sealed trait TweetStateRecord {
|
|
def tweetId: TweetId
|
|
def createdAt: Long
|
|
def stateKey: TweetKey.LKey.StateKey
|
|
def values: Map[String, Long] = Map("timestamp" -> createdAt)
|
|
def name: String
|
|
|
|
def toTweetMhRecord: TweetManhattanRecord = {
|
|
val valByteBuffer = ByteArrayCodec.toByteBuffer(Json.encode(values))
|
|
val value = ManhattanValue(valByteBuffer, Some(Time.fromMilliseconds(createdAt)))
|
|
TweetManhattanRecord(TweetKey(tweetId, stateKey), value)
|
|
}
|
|
}
|
|
|
|
object TweetStateRecord {
|
|
|
|
/** When a soft-deleted or bounce deleted tweet is ultimately hard-deleted by an offline job. */
|
|
case class HardDeleted(tweetId: TweetId, createdAt: Long, deletedAt: Long)
|
|
extends TweetStateRecord {
|
|
// timestamp in the mh backend is the hard deletion timestamp
|
|
override def values = Map("timestamp" -> createdAt, "softdelete_timestamp" -> deletedAt)
|
|
def stateKey = TweetKey.LKey.HardDeletionStateKey
|
|
def name = "hard_deleted"
|
|
}
|
|
|
|
/** When a tweet is deleted by the user. It can still be undeleted while in the soft deleted state. */
|
|
case class SoftDeleted(tweetId: TweetId, createdAt: Long) extends TweetStateRecord {
|
|
def stateKey = TweetKey.LKey.SoftDeletionStateKey
|
|
def name = "soft_deleted"
|
|
}
|
|
|
|
/** When a tweet is deleted by go/bouncer for violating Twitter Rules. It MAY NOT be undeleted. */
|
|
case class BounceDeleted(tweetId: TweetId, createdAt: Long) extends TweetStateRecord {
|
|
def stateKey = TweetKey.LKey.BounceDeletionStateKey
|
|
def name = "bounce_deleted"
|
|
}
|
|
|
|
/** When a tweet is undeleted by an internal system. */
|
|
case class Undeleted(tweetId: TweetId, createdAt: Long) extends TweetStateRecord {
|
|
def stateKey = TweetKey.LKey.UnDeletionStateKey
|
|
def name = "undeleted"
|
|
}
|
|
|
|
/** When a tweet is created using the forceAdd endpoint. */
|
|
case class ForceAdded(tweetId: TweetId, createdAt: Long) extends TweetStateRecord {
|
|
def stateKey = TweetKey.LKey.ForceAddedStateKey
|
|
def name = "force_added"
|
|
}
|
|
|
|
def fromTweetMhRecord(record: TweetManhattanRecord): Option[TweetStateRecord] = {
|
|
def ts = TimestampDecoder.decode(record, TimestampType.Default).getOrElse(0L)
|
|
def sdts = TimestampDecoder.decode(record, TimestampType.SoftDelete).getOrElse(0L)
|
|
def tweetId = record.pkey
|
|
|
|
record.lkey match {
|
|
case TweetKey.LKey.HardDeletionStateKey => Some(HardDeleted(tweetId, ts, sdts))
|
|
case TweetKey.LKey.SoftDeletionStateKey => Some(SoftDeleted(tweetId, ts))
|
|
case TweetKey.LKey.BounceDeletionStateKey => Some(BounceDeleted(tweetId, ts))
|
|
case TweetKey.LKey.UnDeletionStateKey => Some(Undeleted(tweetId, ts))
|
|
case TweetKey.LKey.ForceAddedStateKey => Some(ForceAdded(tweetId, ts))
|
|
case _ => None
|
|
}
|
|
}
|
|
|
|
def fromTweetMhRecords(records: Seq[TweetManhattanRecord]): Seq[TweetStateRecord] =
|
|
records.flatMap(fromTweetMhRecord)
|
|
|
|
def mostRecent(records: Seq[TweetManhattanRecord]): Option[TweetStateRecord] =
|
|
fromTweetMhRecords(records).sortBy(_.createdAt).lastOption
|
|
}
|