the-algorithm/home-mixer/server/src/main/scala/com/twitter/home_mixer/functional_component/side_effect/TruncateTimelinesPersistenceStoreSideEffect.scala
twitter-team ef4c5eb65e Twitter Recommendation Algorithm
Please note we have force-pushed a new initial commit in order to remove some publicly-available Twitter user information. Note that this process may be required in the future.
2023-03-31 17:36:31 -05:00

69 lines
3.1 KiB
Scala

package com.twitter.home_mixer.functional_component.side_effect
import com.twitter.home_mixer.model.HomeFeatures.PersistenceEntriesFeature
import com.twitter.home_mixer.model.request.FollowingProduct
import com.twitter.home_mixer.model.request.ForYouProduct
import com.twitter.home_mixer.param.HomeGlobalParams.TimelinesPersistenceStoreMaxEntriesPerClient
import com.twitter.home_mixer.service.HomeMixerAlertConfig
import com.twitter.product_mixer.core.functional_component.side_effect.PipelineResultSideEffect
import com.twitter.product_mixer.core.model.common.identifier.SideEffectIdentifier
import com.twitter.product_mixer.core.model.marshalling.response.urt.Timeline
import com.twitter.product_mixer.core.pipeline.PipelineQuery
import com.twitter.stitch.Stitch
import com.twitter.timelinemixer.clients.persistence.TimelineResponseBatchesClient
import com.twitter.timelinemixer.clients.persistence.TimelineResponseV3
import com.twitter.timelineservice.model.TimelineQuery
import com.twitter.timelineservice.model.core.TimelineKind
import javax.inject.Inject
import javax.inject.Singleton
/**
* Side effect that truncates entries in the Timelines Persistence store
* based on the number of entries per client.
*/
@Singleton
class TruncateTimelinesPersistenceStoreSideEffect @Inject() (
timelineResponseBatchesClient: TimelineResponseBatchesClient[TimelineResponseV3])
extends PipelineResultSideEffect[PipelineQuery, Timeline] {
override val identifier: SideEffectIdentifier =
SideEffectIdentifier("TruncateTimelinesPersistenceStore")
def getResponsesToDelete(query: PipelineQuery): Seq[TimelineResponseV3] = {
val responses =
query.features.map(_.getOrElse(PersistenceEntriesFeature, Seq.empty)).toSeq.flatten
val responsesByClient = responses.groupBy(_.clientPlatform).values.toSeq
val maxEntriesPerClient = query.params(TimelinesPersistenceStoreMaxEntriesPerClient)
responsesByClient.flatMap {
_.sortBy(_.servedTime.inMilliseconds)
.foldRight((Seq.empty[TimelineResponseV3], maxEntriesPerClient)) {
case (response, (responsesToDelete, remainingCap)) =>
if (remainingCap > 0) (responsesToDelete, remainingCap - response.entries.size)
else (response +: responsesToDelete, remainingCap)
} match { case (responsesToDelete, _) => responsesToDelete }
}
}
final override def apply(
inputs: PipelineResultSideEffect.Inputs[PipelineQuery, Timeline]
): Stitch[Unit] = {
val timelineKind = inputs.query.product match {
case FollowingProduct => TimelineKind.homeLatest
case ForYouProduct => TimelineKind.home
case other => throw new UnsupportedOperationException(s"Unknown product: $other")
}
val timelineQuery = TimelineQuery(id = inputs.query.getRequiredUserId, kind = timelineKind)
val responsesToDelete = getResponsesToDelete(inputs.query)
if (responsesToDelete.nonEmpty)
Stitch.callFuture(timelineResponseBatchesClient.delete(timelineQuery, responsesToDelete))
else Stitch.Unit
}
override val alerts = Seq(
HomeMixerAlertConfig.BusinessHours.defaultSuccessRateAlert(99.8)
)
}