the-algorithm/representation-scorer/server/src/main/scala/com/twitter/representationscorer/twistlyfeatures/UserSignalServiceRecentEnga...

58 lines
2.4 KiB
Scala

package com.twitter.representationscorer.twistlyfeatures
import com.github.benmanes.caffeine.cache.Caffeine
import com.twitter.stitch.cache.EvictingCache
import com.google.inject.Provides
import com.twitter.finagle.stats.StatsReceiver
import com.twitter.inject.TwitterModule
import com.twitter.representationscorer.common.RepresentationScorerDecider
import com.twitter.stitch.Stitch
import com.twitter.stitch.cache.ConcurrentMapCache
import com.twitter.stitch.cache.MemoizeQuery
import com.twitter.strato.client.Client
import com.twitter.strato.generated.client.recommendations.user_signal_service.SignalsClientColumn
import java.util.concurrent.ConcurrentMap
import java.util.concurrent.TimeUnit
import javax.inject.Singleton
object UserSignalServiceRecentEngagementsClientModule extends TwitterModule {
@Singleton
@Provides
def provide(
client: Client,
decider: RepresentationScorerDecider,
statsReceiver: StatsReceiver
): Long => Stitch[Engagements] = {
val stratoClient = new SignalsClientColumn(client)
/*
This cache holds a users recent engagements for a short period of time, such that batched requests
for multiple (userid, tweetid) pairs don't all need to fetch them.
[1] Caffeine cache keys/values must be objects, so we cannot use the `Long` primitive directly.
The boxed java.lang.Long works as a key, since it is an object. In most situations the compiler
can see where auto(un)boxing can occur. However, here we seem to need some wrapper functions
with explicit types to allow the boxing to happen.
*/
val mapCache: ConcurrentMap[java.lang.Long, Stitch[Engagements]] =
Caffeine
.newBuilder()
.expireAfterWrite(5, TimeUnit.SECONDS)
.maximumSize(
1000 // We estimate 5M unique users in a 5m period - with 2k RSX instances, assume that one will see < 1k in a 5s period
)
.build[java.lang.Long, Stitch[Engagements]]
.asMap
statsReceiver.provideGauge("ussRecentEngagementsClient", "cache_size") { mapCache.size.toFloat }
val engagementsClient =
new UserSignalServiceRecentEngagementsClient(stratoClient, decider, statsReceiver)
val f = (l: java.lang.Long) => engagementsClient.get(l) // See note [1] above
val cachedCall = MemoizeQuery(f, EvictingCache.lazily(new ConcurrentMapCache(mapCache)))
(l: Long) => cachedCall(l) // see note [1] above
}
}