the-algorithm/tweetypie/common/src/scala/com/twitter/tweetypie/storage/GetDeletedTweetsHandler.scala

151 lines
4.7 KiB
Scala

package com.twitter.tweetypie.storage
import com.twitter.finagle.stats.StatsReceiver
import com.twitter.stitch.Stitch
import com.twitter.storage.client.manhattan.kv.DeniedManhattanException
import com.twitter.tweetypie.storage.Response.TweetResponseCode
import com.twitter.tweetypie.storage.TweetUtils._
import com.twitter.tweetypie.storage_internal.thriftscala.StoredTweet
import com.twitter.tweetypie.thriftscala.DeletedTweet
import scala.util.control.NonFatal
sealed trait DeleteState
object DeleteState {
/**
* This tweet is deleted but has not been permanently deleted from Manhattan. Tweets in this state
* may be undeleted.
*/
case object SoftDeleted extends DeleteState
/**
* This tweet is deleted after being bounced for violating the Twitter Rules but has not been
* permanently deleted from Manhattan. Tweets in this state may NOT be undeleted.
*/
case object BounceDeleted extends DeleteState
/**
* This tweet has been permanently deleted from Manhattan.
*/
case object HardDeleted extends DeleteState
/**
* There is no data in Manhattan to distinguish this tweet id from one that never existed.
*/
case object NotFound extends DeleteState
/**
* This tweet exists and is not in a deleted state.
*/
case object NotDeleted extends DeleteState
}
case class DeletedTweetResponse(
tweetId: TweetId,
overallResponse: TweetResponseCode,
deleteState: DeleteState,
tweet: Option[DeletedTweet])
object GetDeletedTweetsHandler {
def apply(
read: ManhattanOperations.Read,
stats: StatsReceiver
): TweetStorageClient.GetDeletedTweets =
(unfilteredTweetIds: Seq[TweetId]) => {
val tweetIds = unfilteredTweetIds.filter(_ > 0)
Stats.addWidthStat("getDeletedTweets", "tweetIds", tweetIds.size, stats)
val stitches = tweetIds.map { tweetId =>
read(tweetId)
.map { mhRecords =>
val storedTweet = buildStoredTweet(tweetId, mhRecords)
TweetStateRecord.mostRecent(mhRecords) match {
case Some(m: TweetStateRecord.SoftDeleted) => softDeleted(m, storedTweet)
case Some(m: TweetStateRecord.BounceDeleted) => bounceDeleted(m, storedTweet)
case Some(m: TweetStateRecord.HardDeleted) => hardDeleted(m, storedTweet)
case _ if storedTweet.getFieldBlobs(expectedFields).isEmpty => notFound(tweetId)
case _ => notDeleted(tweetId, storedTweet)
}
}
.handle {
case _: DeniedManhattanException =>
DeletedTweetResponse(
tweetId,
TweetResponseCode.OverCapacity,
DeleteState.NotFound,
None
)
case NonFatal(ex) =>
TweetUtils.log.warning(
ex,
s"Unhandled exception in GetDeletedTweetsHandler for tweetId: $tweetId"
)
DeletedTweetResponse(tweetId, TweetResponseCode.Failure, DeleteState.NotFound, None)
}
}
Stitch.collect(stitches)
}
private def notFound(tweetId: TweetId) =
DeletedTweetResponse(
tweetId = tweetId,
overallResponse = TweetResponseCode.Success,
deleteState = DeleteState.NotFound,
tweet = None
)
private def softDeleted(record: TweetStateRecord.SoftDeleted, storedTweet: StoredTweet) =
DeletedTweetResponse(
record.tweetId,
TweetResponseCode.Success,
DeleteState.SoftDeleted,
Some(
StorageConversions
.toDeletedTweet(storedTweet)
.copy(deletedAtMsec = Some(record.createdAt))
)
)
private def bounceDeleted(record: TweetStateRecord.BounceDeleted, storedTweet: StoredTweet) =
DeletedTweetResponse(
record.tweetId,
TweetResponseCode.Success,
DeleteState.BounceDeleted,
Some(
StorageConversions
.toDeletedTweet(storedTweet)
.copy(deletedAtMsec = Some(record.createdAt))
)
)
private def hardDeleted(record: TweetStateRecord.HardDeleted, storedTweet: StoredTweet) =
DeletedTweetResponse(
record.tweetId,
TweetResponseCode.Success,
DeleteState.HardDeleted,
Some(
StorageConversions
.toDeletedTweet(storedTweet)
.copy(
hardDeletedAtMsec = Some(record.createdAt),
deletedAtMsec = Some(record.deletedAt)
)
)
)
/**
* notDeleted returns a tweet to simplify tweetypie.handler.UndeleteTweetHandler
*/
private def notDeleted(tweetId: TweetId, storedTweet: StoredTweet) =
DeletedTweetResponse(
tweetId = tweetId,
overallResponse = TweetResponseCode.Success,
deleteState = DeleteState.NotDeleted,
tweet = Some(StorageConversions.toDeletedTweet(storedTweet))
)
}