From 6aecfb56fb8fec535267641f9029c46feb16dd24 Mon Sep 17 00:00:00 2001 From: Rajesh Kalaria Date: Mon, 12 Sep 2022 08:10:41 +0530 Subject: [PATCH 1/2] VE-3573: possible fix for endpoint not found intermittent issue --- .../evernym/verity/actor/agent/AgentCommon.scala | 2 +- .../actor/agent/msgsender/AgentMsgSender.scala | 14 +++++--------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/verity/src/main/scala/com/evernym/verity/actor/agent/AgentCommon.scala b/verity/src/main/scala/com/evernym/verity/actor/agent/AgentCommon.scala index a09badda..b340a0c9 100644 --- a/verity/src/main/scala/com/evernym/verity/actor/agent/AgentCommon.scala +++ b/verity/src/main/scala/com/evernym/verity/actor/agent/AgentCommon.scala @@ -208,7 +208,7 @@ trait AgentCommon } def agencyDidPairFutByCache(agencyDID: DidStr): Future[DidPair] = { - val gadp = GetAgencyIdentityCacheParam(agencyDID, GetAgencyIdentity(agencyDID, getEndpoint = false)) + val gadp = GetAgencyIdentityCacheParam(agencyDID, GetAgencyIdentity(agencyDID)) val gadfcParam = GetCachedObjectParam(KeyDetail(gadp, required = true), AGENCY_IDENTITY_CACHE_FETCHER) generalCache.getByParamAsync(gadfcParam) .map(cqr => DidPair(agencyDID, cqr.getAgencyInfoReq(agencyDID).verKeyReq)) diff --git a/verity/src/main/scala/com/evernym/verity/actor/agent/msgsender/AgentMsgSender.scala b/verity/src/main/scala/com/evernym/verity/actor/agent/msgsender/AgentMsgSender.scala index 05acf1d0..ec491d19 100644 --- a/verity/src/main/scala/com/evernym/verity/actor/agent/msgsender/AgentMsgSender.scala +++ b/verity/src/main/scala/com/evernym/verity/actor/agent/msgsender/AgentMsgSender.scala @@ -55,12 +55,8 @@ trait AgentMsgSender getAgencyIdentityFut(localAgencyDID, gad, mw) } - private def handleRemoteAgencyEndpointNotFound(theirAgencyDID: DidStr): Exception = { - val errorMsg = - "error while getting endpoint from ledger (" + - "possible-causes: ledger pool not reachable/up/responding etc, " + - s"target DID: $theirAgencyDID)" - LedgerSvcException(errorMsg) + private def handleRemoteAgencyEndpointNotFound(theirAgencyDID: DidStr, errorMsg: String): Exception = { + LedgerSvcException(s"error while getting endpoint from ledger (target DID: $theirAgencyDID): $errorMsg") } private def getRemoteAgencyEndpoint(implicit sm: SendMsgParam, mw: MetricsWriter): Future[String] = { @@ -70,10 +66,10 @@ trait AgentMsgSender val theirAgencyInfo = cqr.getAgencyInfoReq(theirAgencyDID) logger.info(s"theirAgencyInfo received for '$theirAgencyDID': " + theirAgencyInfo) theirAgencyInfo.endpointOpt.getOrElse( - throw handleRemoteAgencyEndpointNotFound(theirAgencyDID) + throw handleRemoteAgencyEndpointNotFound(theirAgencyDID, "endpoint found to be empty (via cache)") ) }.recover { - case _: Exception => throw handleRemoteAgencyEndpointNotFound(theirAgencyDID) + case e: Exception => throw handleRemoteAgencyEndpointNotFound(theirAgencyDID, e.getMessage) } case Right(endpoint) => Future.successful(endpoint) } @@ -92,7 +88,7 @@ trait AgentMsgSender r }.recover { case e: Exception => - logger.error("error while sending message to their agency endpoint '${sm.theirRoutingParam.route}': " + Exceptions.getStackTraceAsString(e)) + logger.error(s"error while sending message to their agency endpoint '${sm.theirRoutingParam.route}': " + Exceptions.getStackTraceAsString(e)) handleMsgDeliveryResult(MsgDeliveryResult.failed(sm, MSG_DELIVERY_STATUS_FAILED, Exceptions.getErrorMsg(e))) throw e } From 8b6171bafbd37ca96a5cf6bacb410f9c0b5b19a5 Mon Sep 17 00:00:00 2001 From: Rajesh Kalaria Date: Mon, 12 Sep 2022 17:09:15 +0530 Subject: [PATCH 2/2] VE-3573: cache refactoring to use proper/correct cached object --- .../verity/actor/agent/AgentCommon.scala | 8 +- .../actor/agent/agency/AgencyAgent.scala | 6 +- .../actor/agent/agency/AgencyIdUtil.scala | 4 +- .../agent/msgsender/AgentMsgSender.scala | 8 +- .../actor/agent/state/UsesConfigs.scala | 8 +- .../com/evernym/verity/cache/base/Cache.scala | 117 ++++----- .../evernym/verity/cache/base/package.scala | 3 + .../fetchers/AgencyIdentityCacheFetcher.scala | 18 +- .../fetchers/AgentConfigCacheFetcher.scala | 21 +- .../cache/fetchers/CacheValueFetcher.scala | 30 +-- .../fetchers/KeyValueMapperFetcher.scala | 14 +- .../LedgerGetEndpointCacheFetcher.scala | 16 +- .../LedgerGetVerKeyCacheFetcher.scala | 18 +- .../AgencySetupEndpointHandler.scala | 4 +- .../cache/AgencyIdentityCacheSpec.scala | 227 ++++++++++++++++++ .../evernym/verity/cache/BasicCacheSpec.scala | 28 +-- .../verity/cache/CacheMaxSizeSpec.scala | 20 +- .../verity/cache/CacheMaxWeightSpec.scala | 28 +-- 18 files changed, 401 insertions(+), 177 deletions(-) create mode 100644 verity/src/test/scala/com/evernym/verity/cache/AgencyIdentityCacheSpec.scala diff --git a/verity/src/main/scala/com/evernym/verity/actor/agent/AgentCommon.scala b/verity/src/main/scala/com/evernym/verity/actor/agent/AgentCommon.scala index b340a0c9..68934dad 100644 --- a/verity/src/main/scala/com/evernym/verity/actor/agent/AgentCommon.scala +++ b/verity/src/main/scala/com/evernym/verity/actor/agent/AgentCommon.scala @@ -20,7 +20,7 @@ import com.evernym.verity.actor.msg_tracer.progress_tracker.{HasMsgProgressTrack import com.evernym.verity.actor.resourceusagethrottling.EntityId import com.evernym.verity.agentmsg.msgcodec.UnknownFormatType import com.evernym.verity.cache.{AGENCY_IDENTITY_CACHE_FETCHER, AGENT_ACTOR_CONFIG_CACHE_FETCHER, KEY_VALUE_MAPPER_ACTOR_CACHE_FETCHER} -import com.evernym.verity.cache.base.{Cache, FetcherParam, GetCachedObjectParam, KeyDetail} +import com.evernym.verity.cache.base.{Cache, FetcherParam, GetCachedObjectParam, ReqParam} import com.evernym.verity.cache.fetchers.{AgentConfigCacheFetcher, CacheValueFetcher, GetAgencyIdentityCacheParam} import com.evernym.verity.config.ConfigConstants.{AKKA_SHARDING_REGION_NAME_USER_AGENT, VDR_LEDGER_PREFIX_MAPPINGS, VDR_UNQUALIFIED_LEDGER_PREFIX, VERITY_ENDORSER_DEFAULT_DID} import com.evernym.verity.did.didcomm.v1.messages.{MsgFamily, MsgType, TypedMsgLike} @@ -200,7 +200,7 @@ trait AgentCommon case (Some(adp), Some(ak)) if adp.DID.nonEmpty => Future.successful(adp.copy(verKey = ak.verKey)) case (Some(adp), _) if adp.DID.nonEmpty && adp.verKey.isEmpty => agencyDidPairFutByCache(adp.DID) case _ => - val gcop = GetCachedObjectParam(KeyDetail(AGENCY_DID_KEY, required = false), KEY_VALUE_MAPPER_ACTOR_CACHE_FETCHER) + val gcop = GetCachedObjectParam(ReqParam(AGENCY_DID_KEY, required = false), KEY_VALUE_MAPPER_ACTOR_CACHE_FETCHER) generalCache.getByParamAsync(gcop).flatMap { cqr => agencyDidPairFutByCache(cqr.getAgencyDIDReq) } @@ -208,8 +208,8 @@ trait AgentCommon } def agencyDidPairFutByCache(agencyDID: DidStr): Future[DidPair] = { - val gadp = GetAgencyIdentityCacheParam(agencyDID, GetAgencyIdentity(agencyDID)) - val gadfcParam = GetCachedObjectParam(KeyDetail(gadp, required = true), AGENCY_IDENTITY_CACHE_FETCHER) + val gadp = GetAgencyIdentityCacheParam(agencyDID, GetAgencyIdentity(agencyDID, getEndpoint = false)) + val gadfcParam = GetCachedObjectParam(ReqParam(gadp, required = true), AGENCY_IDENTITY_CACHE_FETCHER) generalCache.getByParamAsync(gadfcParam) .map(cqr => DidPair(agencyDID, cqr.getAgencyInfoReq(agencyDID).verKeyReq)) } diff --git a/verity/src/main/scala/com/evernym/verity/actor/agent/agency/AgencyAgent.scala b/verity/src/main/scala/com/evernym/verity/actor/agent/agency/AgencyAgent.scala index e12132db..391e770e 100644 --- a/verity/src/main/scala/com/evernym/verity/actor/agent/agency/AgencyAgent.scala +++ b/verity/src/main/scala/com/evernym/verity/actor/agent/agency/AgencyAgent.scala @@ -14,7 +14,7 @@ import com.evernym.verity.actor.cluster_singleton.{AddMapping, ForKeyValueMapper import com.evernym.verity.actor.wallet.{CreateNewKey, CreateWallet, NewKeyCreated, WalletCreated} import com.evernym.verity.agentmsg.msgpacker.UnpackParam import com.evernym.verity.cache.{LEDGER_GET_ENDPOINT_CACHE_FETCHER, LEDGER_GET_VER_KEY_CACHE_FETCHER} -import com.evernym.verity.cache.base.{GetCachedObjectParam, KeyDetail} +import com.evernym.verity.cache.base.{GetCachedObjectParam, ReqParam} import com.evernym.verity.cache.fetchers.{GetEndpointParam, GetVerKeyParam} import com.evernym.verity.config.ConfigConstants import com.evernym.verity.constants.ActorNameConstants._ @@ -211,13 +211,13 @@ class AgencyAgent(val agentActorContext: AgentActorContext, //dhh Same note about caching as above. def getCachedEndpointFromLedger(did: DidStr, req: Boolean = false): Future[Option[Either[StatusDetail, String]]] = { val gep = GetEndpointParam(did, ledgerReqSubmitter) - val gcop = GetCachedObjectParam(KeyDetail(gep, required = req), LEDGER_GET_ENDPOINT_CACHE_FETCHER) + val gcop = GetCachedObjectParam(ReqParam(gep, required = req), LEDGER_GET_ENDPOINT_CACHE_FETCHER) getCachedStringValue(did, gcop) } def getCachedVerKeyFromLedger(did: DidStr, req: Boolean = false): Future[Option[Either[StatusDetail, String]]] = { val gvkp = GetVerKeyParam(did, ledgerReqSubmitter) - val gcop = GetCachedObjectParam(KeyDetail(gvkp, required = req), LEDGER_GET_VER_KEY_CACHE_FETCHER) + val gcop = GetCachedObjectParam(ReqParam(gvkp, required = req), LEDGER_GET_VER_KEY_CACHE_FETCHER) getCachedStringValue(did, gcop) } diff --git a/verity/src/main/scala/com/evernym/verity/actor/agent/agency/AgencyIdUtil.scala b/verity/src/main/scala/com/evernym/verity/actor/agent/agency/AgencyIdUtil.scala index 7b7fa242..edb29a89 100644 --- a/verity/src/main/scala/com/evernym/verity/actor/agent/agency/AgencyIdUtil.scala +++ b/verity/src/main/scala/com/evernym/verity/actor/agent/agency/AgencyIdUtil.scala @@ -5,7 +5,7 @@ import com.evernym.verity.constants.Constants.AGENCY_DID_KEY import scala.concurrent.{ExecutionContext, Future} import com.evernym.verity.cache.KEY_VALUE_MAPPER_ACTOR_CACHE_FETCHER -import com.evernym.verity.cache.base.{Cache, GetCachedObjectParam, KeyDetail} +import com.evernym.verity.cache.base.{Cache, GetCachedObjectParam, ReqParam} trait AgencyIdUtil extends HasExecutionContextProvider { @@ -13,7 +13,7 @@ trait AgencyIdUtil private implicit val executionContext: ExecutionContext = futureExecutionContext def getAgencyDID(implicit generalCache: Cache): Future[String] = { - val gcop = GetCachedObjectParam(KeyDetail(AGENCY_DID_KEY, required = false), KEY_VALUE_MAPPER_ACTOR_CACHE_FETCHER) + val gcop = GetCachedObjectParam(ReqParam(AGENCY_DID_KEY, required = false), KEY_VALUE_MAPPER_ACTOR_CACHE_FETCHER) generalCache.getByParamAsync(gcop).map { cqr => cqr.getAgencyDIDReq } diff --git a/verity/src/main/scala/com/evernym/verity/actor/agent/msgsender/AgentMsgSender.scala b/verity/src/main/scala/com/evernym/verity/actor/agent/msgsender/AgentMsgSender.scala index ec491d19..508d6910 100644 --- a/verity/src/main/scala/com/evernym/verity/actor/agent/msgsender/AgentMsgSender.scala +++ b/verity/src/main/scala/com/evernym/verity/actor/agent/msgsender/AgentMsgSender.scala @@ -11,7 +11,7 @@ import com.evernym.verity.ledger.LedgerSvcException import com.evernym.verity.protocol.protocols.connecting.common.TheirRoutingParam import com.evernym.verity.actor.wallet.PackedMsg import com.evernym.verity.cache.AGENCY_IDENTITY_CACHE_FETCHER -import com.evernym.verity.cache.base.{CacheQueryResponse, GetCachedObjectParam, KeyDetail} +import com.evernym.verity.cache.base.{QueryResult, GetCachedObjectParam, ReqParam} import com.evernym.verity.cache.fetchers.GetAgencyIdentityCacheParam import com.evernym.verity.did.DidStr import com.evernym.verity.did.didcomm.v1.messages.MsgId @@ -40,17 +40,17 @@ trait AgentMsgSender def getAgencyIdentityFut(localAgencyDID: String, gad: GetAgencyIdentity, - mw: MetricsWriter): Future[CacheQueryResponse] = { + mw: MetricsWriter): Future[QueryResult] = { mw.runWithSpan("getAgencyIdentityFut", "AgentMsgSender", InternalSpan) { val gadp = GetAgencyIdentityCacheParam(localAgencyDID, gad) - val gadfcParam = GetCachedObjectParam(KeyDetail(gadp, required = true), AGENCY_IDENTITY_CACHE_FETCHER) + val gadfcParam = GetCachedObjectParam(ReqParam(gadp, required = true), AGENCY_IDENTITY_CACHE_FETCHER) generalCache.getByParamAsync(gadfcParam) } } private def theirAgencyEndpointFut(localAgencyDID:DidStr, theirAgencyDID: DidStr, - mw: MetricsWriter): Future[CacheQueryResponse] = { + mw: MetricsWriter): Future[QueryResult] = { val gad = GetAgencyIdentity(theirAgencyDID) getAgencyIdentityFut(localAgencyDID, gad, mw) } diff --git a/verity/src/main/scala/com/evernym/verity/actor/agent/state/UsesConfigs.scala b/verity/src/main/scala/com/evernym/verity/actor/agent/state/UsesConfigs.scala index d224e9a6..19f3914f 100644 --- a/verity/src/main/scala/com/evernym/verity/actor/agent/state/UsesConfigs.scala +++ b/verity/src/main/scala/com/evernym/verity/actor/agent/state/UsesConfigs.scala @@ -9,7 +9,7 @@ import com.evernym.verity.util2.Status.getUnhandledError import com.evernym.verity.actor.agent.user.{AgentConfig, AgentConfigs, GetConfigDetail, GetConfigs} import com.evernym.verity.agentmsg.msgfamily.ConfigDetail import com.evernym.verity.cache.AGENT_ACTOR_CONFIG_CACHE_FETCHER -import com.evernym.verity.cache.base.{Cache, GetCachedObjectParam, KeyDetail} +import com.evernym.verity.cache.base.{Cache, GetCachedObjectParam, ReqParam} import com.evernym.verity.cache.fetchers.GetConfigCacheParam import com.evernym.verity.did.DidStr @@ -59,9 +59,9 @@ trait UsesConfigs extends HasAppConfig with HasExecutionContextProvider { def getConfigsFromUserAgent(configs: Set[GetConfigDetail]): Future[AgentConfigs] = { - def buildKeyDetails(gcd: Set[GetConfigDetail], req: Boolean): Set[KeyDetail] = { - if (gcd.nonEmpty) Set(KeyDetail(GetConfigCacheParam(ownerAgentKeyDIDReq, GetConfigs(gcd.map(_.name))), required = req)) - else Set.empty[KeyDetail] + def buildKeyDetails(gcd: Set[GetConfigDetail], req: Boolean): Set[ReqParam] = { + if (gcd.nonEmpty) Set(ReqParam(GetConfigCacheParam(ownerAgentKeyDIDReq, GetConfigs(gcd.map(_.name))), required = req)) + else Set.empty[ReqParam] } val (reqs, nonReqs) = configs.partition(_.req) diff --git a/verity/src/main/scala/com/evernym/verity/cache/base/Cache.scala b/verity/src/main/scala/com/evernym/verity/cache/base/Cache.scala index a0d0b985..9ae9ee21 100644 --- a/verity/src/main/scala/com/evernym/verity/cache/base/Cache.scala +++ b/verity/src/main/scala/com/evernym/verity/cache/base/Cache.scala @@ -20,9 +20,6 @@ trait CacheBase extends HasExecutionContextProvider { private implicit def executionContext: ExecutionContext = futureExecutionContext - type Key = String - type FetcherId = Int - protected val logger: Logger = getLoggerByClass(classOf[CacheBase]) def name: String @@ -62,12 +59,12 @@ trait CacheBase extends HasExecutionContextProvider { cacheByFetcher.getOrElse(fetcherParam, throw new RuntimeException("cache provider not found for fetcher: " + fetcherParam)) } - def getCachedObjectsByFetcherId(fetcherParam: FetcherParam): Map[Key, Any] = { + def getCachedObjectsByFetcherId(fetcherParam: FetcherParam): Map[CacheKey, Any] = { val cacheProvider = getCacheProvider(fetcherParam) cacheProvider.cachedObjects } - private def addToFetcherCache(fetcherParam: FetcherParam, key: Key, value: AnyRef): Unit = { + private def addToFetcherCache(fetcherParam: FetcherParam, key: CacheKey, value: AnyRef): Unit = { val cacheProvider = getCacheProvider(fetcherParam) cacheProvider.put(key, value) logger.debug("cached object added: " + fetcherParam) @@ -105,25 +102,25 @@ trait CacheBase extends HasExecutionContextProvider { } //this function gets executed when cache has to go to the source and fetch the actual value - private def processFetchResultFromSource(fetchedResult: Map[String, AnyRef]) - (implicit frfc: FetchResultFromCache): Map[String, AnyRef] = { + private def processFetchResultFromSource(fetchedResult: Map[CacheRequest, RespParam]) + (implicit frfc: FetchResultFromCache): Map[CacheRequest, AnyRef] = { logMsg(frfc.gcop.fetcherParam, frfc.reqId, "fetched data from source: " + fetchedResult) - fetchedResult.foreach { case (k, v) => + fetchedResult.foreach { case (cr, rp) => if (frfc.fetcherExpiryTimeInSeconds.forall(_ > 0)) { - addToFetcherCache(frfc.gcop.fetcherParam, k, v) + addToFetcherCache(frfc.gcop.fetcherParam, cr.storageKey, rp.result) } } - val found = frfc.cachedObjectsFound.map(fc => fc._1 -> fc._2) - found ++ fetchedResult + val found = frfc.objectsFoundInCache.map(fc => fc._1 -> fc._2) + found ++ fetchedResult.map(r => r._1 -> r._2.result) } - private def prepareFinalResponse(finalResult: Map[String, AnyRef]) - (implicit frfc: FetchResultFromCache): CacheQueryResponse = { + private def prepareFinalResponse(finalResult: Map[CacheRequest, AnyRef]) + (implicit frfc: FetchResultFromCache): QueryResult = { logStats(frfc.gcop.fetcherParam, frfc.reqId) collectMetrics() val fetcher = getFetcherById(frfc.gcop.fetcherParam) - val requiredKeyNames = frfc.keyMappings.filter(_.keyDetail.required).map(_.loggingKey) - val missingKeys = frfc.keyMappings.map(_.loggingKey).diff(finalResult.keySet) + val requiredKeyNames = frfc.cacheRequests.filter(_.reqParam.required).map(_.storageKey) + val missingKeys = frfc.cacheRequests.map(_.storageKey).diff(finalResult.keySet.map(_.storageKey)) val requiredButMissing = missingKeys.intersect(requiredKeyNames) if (requiredButMissing.nonEmpty) { logMsg(frfc.gcop.fetcherParam, frfc.reqId, "given required keys neither found in cache nor in source: " + @@ -131,7 +128,7 @@ trait CacheBase extends HasExecutionContextProvider { throw fetcher.throwRequiredKeysNotFoundException(requiredButMissing) } else { logMsg(frfc.gcop.fetcherParam, frfc.reqId, "returning requested keys: " + finalResult.keySet.mkString(", ")) - CacheQueryResponse(finalResult) + QueryResult(finalResult.map(r => r._1.respKey -> r._2)) } } @@ -140,62 +137,69 @@ trait CacheBase extends HasExecutionContextProvider { val id = UUID.randomUUID.toString logMsg(gcop.fetcherParam, id, "input param: " + gcop) val fetcher = getFetcherById(gcop.fetcherParam) - val keyMappings = fetcher.toKeyDetailMappings(gcop.kds) - val requestedCacheKeys = keyMappings.map(_.cacheKey) + val cacheRequests = gcop.reqParams.flatMap(fetcher.toCacheRequests) + val requestedCacheKeys = cacheRequests.map(_.storageKey) val cacheProvider = getCacheProvider(gcop.fetcherParam) val cachedObjectsFound = - requestedCacheKeys - .map(k => k -> cacheProvider.get(k)) + cacheRequests + .map(cr => cr -> cacheProvider.get(cr.storageKey)) .filter(_._2.isDefined) .map(r => r._1 -> r._2.get) .toMap - val keysFoundInCache = cachedObjectsFound.keySet + val keysFoundInCache = cachedObjectsFound.keySet.map(_.storageKey) val keysNotFoundInCache = requestedCacheKeys.diff(keysFoundInCache) - val originalKeysNotFound = keyMappings.filter(km => keysNotFoundInCache.contains(km.cacheKey)).map(_.keyDetail) - logMsg(gcop.fetcherParam, id, - "keys found in cache : " + - keyMappings - .filter(km => keysFoundInCache.contains(km.cacheKey)) - .map(k =>k.loggingKey) + val originalKeysNotFound = cacheRequests.filter(cr => keysNotFoundInCache.contains(cr.storageKey)) + logMsg( + gcop.fetcherParam, + id, + msg = "keys found in cache : " + + cacheRequests + .filter(cr => keysFoundInCache.contains(cr.storageKey)) + .map(cr => cr.storageKey) ) if (keysNotFoundInCache.nonEmpty) - logMsg(gcop.fetcherParam, id, "keys NOT found in cache : " + keyMappings.filter(km => - keysNotFoundInCache.contains(km.cacheKey)).map(_.loggingKey)) + logMsg( + gcop.fetcherParam, + id, + msg = "keys NOT found in cache : " + + cacheRequests + .filter(cr => keysNotFoundInCache.contains(cr.storageKey)) + ) - FetchResultFromCache(gcop, id, keyMappings, fetcher.expiryTimeInSeconds, requestedCacheKeys, - cachedObjectsFound, keysFoundInCache, keysNotFoundInCache, originalKeysNotFound) + FetchResultFromCache(id, gcop, cacheRequests, fetcher.expiryTimeInSeconds, requestedCacheKeys, + keysFoundInCache, keysNotFoundInCache, originalKeysNotFound, cachedObjectsFound) } - def getByParamSync(gcop: GetCachedObjectParam): CacheQueryResponse = { + def getByParamSync(gcop: GetCachedObjectParam): QueryResult = { val fetcher = getSyncFetcherById(gcop.fetcherParam) implicit val frfc: FetchResultFromCache = fetchFromCache(gcop) val finalResult = if (frfc.keysNotFoundInCache.nonEmpty) { - processFetchResultFromSource(fetcher.getByKeyDetails(frfc.originalKeysNotFound)) + processFetchResultFromSource(fetcher.getByRequests(frfc.objectsNotFoundInCache)) } else { - frfc.cachedObjectsFound.map(fc => fc._1 -> fc._2) + frfc.objectsFoundInCache.map(fc => fc._1 -> fc._2) } prepareFinalResponse(finalResult) } - def getByParamAsync(gcop: GetCachedObjectParam): Future[CacheQueryResponse] = { + def getByParamAsync(gcop: GetCachedObjectParam): Future[QueryResult] = { val fetcher = getAsyncFetcherById(gcop.fetcherParam) implicit val cpfr: FetchResultFromCache = fetchFromCache(gcop) val finalResult = if (cpfr.keysNotFoundInCache.nonEmpty) { - fetcher.getByKeyDetails(cpfr.originalKeysNotFound).map { r => + fetcher.getByRequests(cpfr.objectsNotFoundInCache).map { r => processFetchResultFromSource(r) }.recover { case e: Exception => throw e } } else { - Future(cpfr.cachedObjectsFound.map(fc => fc._1 -> fc._2)) + Future(cpfr.objectsFoundInCache.map(fc => fc._1 -> fc._2)) } finalResult map { fr => @@ -209,7 +213,7 @@ trait CacheBase extends HasExecutionContextProvider { * * @param data response data as "map of key value pair" */ -case class CacheQueryResponse(data: Map[String, Any]) extends CacheResponseUtil +case class QueryResult(data: Map[RespKey, Any]) extends CacheResponseUtil class Cache(override val name: String, override val fetchers: Map[FetcherParam, CacheValueFetcher], @@ -220,44 +224,45 @@ class Cache(override val name: String, */ override def futureExecutionContext: ExecutionContext = executionContext } + /** * - * @param key key used by code for search/lookup purposes + * @param cmd cache client provides the command to be used by cache fetcher * @param required is it required (either it should exists/provided by source or cache) */ -case class KeyDetail(key: Any, required: Boolean) { - def keyAs[T]: T = key.asInstanceOf[T] +case class ReqParam(cmd: Any, required: Boolean) { + def cmdAs[T]: T = cmd.asInstanceOf[T] } /** * - * @param keyDetail key detail - * @param cacheKey key used for cache value lookup - * @param loggingKey key to be used for logging + * @param result the fetched item from the source */ -case class KeyMapping(keyDetail: KeyDetail, cacheKey: String, loggingKey: String) +case class RespParam(result: AnyRef) + +case class CacheRequest(reqParam: ReqParam, respKey: RespKey, storageKey: CacheKey) object GetCachedObjectParam { - def apply(kd: KeyDetail, fetcherParam: FetcherParam): GetCachedObjectParam = + def apply(kd: ReqParam, fetcherParam: FetcherParam): GetCachedObjectParam = GetCachedObjectParam(Set(kd), fetcherParam) } /** * input parameter to cache, multiple keys can be requested from cache * - * @param kds set of key detail + * @param reqParams set of key detail * @param fetcherParam fetcherParam */ -case class GetCachedObjectParam(kds: Set[KeyDetail], fetcherParam: FetcherParam) +case class GetCachedObjectParam(reqParams: Set[ReqParam], fetcherParam: FetcherParam) -case class FetchResultFromCache(gcop: GetCachedObjectParam, - reqId: String, - keyMappings: Set[KeyMapping], +case class FetchResultFromCache(reqId: String, + gcop: GetCachedObjectParam, + cacheRequests: Set[CacheRequest], fetcherExpiryTimeInSeconds: Option[Int], - requestedCacheKeys: Set[String], - cachedObjectsFound: Map[String, AnyRef], - keysFoundInCache: Set[String], - keysNotFoundInCache: Set[String], - originalKeysNotFound: Set[KeyDetail]) + requestedCacheKeys: Set[CacheKey], + keysFoundInCache: Set[CacheKey], + keysNotFoundInCache: Set[CacheKey], + objectsNotFoundInCache: Set[CacheRequest], + objectsFoundInCache: Map[CacheRequest, AnyRef]) case class FetcherParam(id: Int, name: String) \ No newline at end of file diff --git a/verity/src/main/scala/com/evernym/verity/cache/base/package.scala b/verity/src/main/scala/com/evernym/verity/cache/base/package.scala index 312555a0..0b784eb0 100644 --- a/verity/src/main/scala/com/evernym/verity/cache/base/package.scala +++ b/verity/src/main/scala/com/evernym/verity/cache/base/package.scala @@ -2,4 +2,7 @@ package com.evernym.verity.cache package object base { val DEFAULT_MAX_CACHE_SIZE = 30 + + type CacheKey = String //used by the cache for storage/lookup + type RespKey = String //used to return the response key (to be used by the cache client code) } diff --git a/verity/src/main/scala/com/evernym/verity/cache/fetchers/AgencyIdentityCacheFetcher.scala b/verity/src/main/scala/com/evernym/verity/cache/fetchers/AgencyIdentityCacheFetcher.scala index 917a9f6b..069537f3 100644 --- a/verity/src/main/scala/com/evernym/verity/cache/fetchers/AgencyIdentityCacheFetcher.scala +++ b/verity/src/main/scala/com/evernym/verity/cache/fetchers/AgencyIdentityCacheFetcher.scala @@ -3,7 +3,7 @@ package com.evernym.verity.cache.fetchers import com.evernym.verity.actor.agent.agency.{AgencyInfo, GetAgencyIdentity} import com.evernym.verity.actor.agent.msgrouter._ import com.evernym.verity.cache.AGENCY_IDENTITY_CACHE_FETCHER -import com.evernym.verity.cache.base.{FetcherParam, KeyDetail, KeyMapping} +import com.evernym.verity.cache.base.{CacheRequest, CacheKey, FetcherParam, ReqParam, RespParam} import com.evernym.verity.config.AppConfig import com.evernym.verity.config.ConfigConstants._ import com.evernym.verity.did.DidStr @@ -26,20 +26,18 @@ class AgencyIdentityCacheFetcher(val agentMsgRouter: AgentMsgRouter, //time to live in seconds, afterwards they will be considered as expired and re-fetched from source override lazy val defaultExpiryTimeInSeconds: Option[Int] = Option(1800) - override def toKeyDetailMappings(keyDetails: Set[KeyDetail]): Set[KeyMapping] = { - keyDetails.map { kd => - val gadcp = kd.keyAs[GetAgencyIdentityCacheParam] - KeyMapping(kd, gadcp.gad.did, gadcp.gad.did) - } + override def toCacheRequests(rp: ReqParam): Set[CacheRequest] = { + val gadcp = rp.cmdAs[GetAgencyIdentityCacheParam] + Set(CacheRequest(rp, gadcp.gad.did, buildCacheKey(gadcp.gad))) } - override def getByKeyDetail(kd: KeyDetail): Future[Map[String, AnyRef]] = { - val gadcp = kd.keyAs[GetAgencyIdentityCacheParam] + override def getByRequest(cr: CacheRequest): Future[Option[RespParam]] = { + val gadcp = cr.reqParam.cmdAs[GetAgencyIdentityCacheParam] val gadFutResp = agentMsgRouter.execute(InternalMsgRouteParam(gadcp.localAgencyDID, gadcp.gad)) gadFutResp.map { case ai: AgencyInfo if ! ai.isErrorFetchingAnyData => logger.info(s"agency info received from source for '${gadcp.gad.did}': " + ai) - Map(gadcp.gad.did -> ai) + Option(RespParam(ai)) case ai: AgencyInfo if ai.verKeyErrorOpt.isDefined => throw buildUnexpectedResponse(ai.verKeyErrorOpt.get) case ai: AgencyInfo if ai.endpointErrorOpt.isDefined => @@ -47,6 +45,8 @@ class AgencyIdentityCacheFetcher(val agentMsgRouter: AgentMsgRouter, case x => throw buildUnexpectedResponse(x) } } + + private def buildCacheKey(gad: GetAgencyIdentity): CacheKey = s"${gad.did}:${gad.getVerKey}:${gad.getEndpoint}" } case class GetAgencyIdentityCacheParam(localAgencyDID: DidStr, gad: GetAgencyIdentity) \ No newline at end of file diff --git a/verity/src/main/scala/com/evernym/verity/cache/fetchers/AgentConfigCacheFetcher.scala b/verity/src/main/scala/com/evernym/verity/cache/fetchers/AgentConfigCacheFetcher.scala index 1a9a3c67..16414638 100644 --- a/verity/src/main/scala/com/evernym/verity/cache/fetchers/AgentConfigCacheFetcher.scala +++ b/verity/src/main/scala/com/evernym/verity/cache/fetchers/AgentConfigCacheFetcher.scala @@ -3,7 +3,7 @@ package com.evernym.verity.cache.fetchers import com.evernym.verity.actor.agent.msgrouter.{AgentMsgRouter, InternalMsgRouteParam} import com.evernym.verity.actor.agent.user.{AgentConfigs, GetConfigs} import com.evernym.verity.cache.AGENT_ACTOR_CONFIG_CACHE_FETCHER -import com.evernym.verity.cache.base.{FetcherParam, KeyDetail, KeyMapping} +import com.evernym.verity.cache.base.{CacheRequest, FetcherParam, ReqParam, RespParam} import com.evernym.verity.config.AppConfig import com.evernym.verity.config.ConfigConstants.AGENT_CONFIG_CACHE import com.evernym.verity.did.DidStr @@ -17,7 +17,7 @@ class AgentConfigCacheFetcher(val agentMsgRouter: AgentMsgRouter, extends AsyncCacheValueFetcher{ override def futureExecutionContext: ExecutionContext = executionContext - private implicit val executionContextImplc: ExecutionContext = executionContext + private implicit val executionContextImpl: ExecutionContext = executionContext lazy val fetcherParam: FetcherParam = AGENT_ACTOR_CONFIG_CACHE_FETCHER lazy val cacheConfigPath: Option[String] = Option(AGENT_CONFIG_CACHE) @@ -25,18 +25,19 @@ class AgentConfigCacheFetcher(val agentMsgRouter: AgentMsgRouter, //time to live in seconds, afterwards they will be considered as expired and re-fetched from source override lazy val defaultExpiryTimeInSeconds: Option[Int] = Option(300) - override def toKeyDetailMappings(keyDetails: Set[KeyDetail]): Set[KeyMapping] = { - keyDetails.flatMap { kd => - val gccp = kd.keyAs[GetConfigCacheParam] - gccp.gc.names.map(n => KeyMapping(kd, gccp.agentDID + "-" + n, n)) - } + override def toCacheRequests(rp: ReqParam): Set[CacheRequest] = { + val gccp = rp.cmdAs[GetConfigCacheParam] + gccp.gc.names.map(confName => CacheRequest(rp, confName, gccp.agentDID + "-" + confName)) } - override def getByKeyDetail(kd: KeyDetail): Future[Map[String, AnyRef]] = { - val gcp = kd.keyAs[GetConfigCacheParam] + override def getByRequest(cr: CacheRequest): Future[Option[RespParam]] = { + val gcp = cr.reqParam.cmdAs[GetConfigCacheParam] val confFutResp = agentMsgRouter.execute(InternalMsgRouteParam(gcp.agentDID, gcp.gc)) confFutResp map { - case fc: AgentConfigs => fc.configs.map(c => c.name -> c.value).toMap + case ac: AgentConfigs if ac.configs.nonEmpty => + ac.configs.find(_.name == cr.respKey).map(cd => RespParam(cd.value)) + case _: AgentConfigs => + None case x => throw buildUnexpectedResponse(x) } } diff --git a/verity/src/main/scala/com/evernym/verity/cache/fetchers/CacheValueFetcher.scala b/verity/src/main/scala/com/evernym/verity/cache/fetchers/CacheValueFetcher.scala index e7af163b..37e15eef 100644 --- a/verity/src/main/scala/com/evernym/verity/cache/fetchers/CacheValueFetcher.scala +++ b/verity/src/main/scala/com/evernym/verity/cache/fetchers/CacheValueFetcher.scala @@ -4,7 +4,7 @@ import akka.util.Timeout import com.evernym.verity.util2.Exceptions.{BadRequestErrorException, HandledErrorException, InternalServerErrorException} import com.evernym.verity.util2.HasExecutionContextProvider import com.evernym.verity.util2.Status.{DATA_NOT_FOUND, StatusDetail, getUnhandledError} -import com.evernym.verity.cache.base.{DEFAULT_MAX_CACHE_SIZE, FetcherParam, KeyDetail, KeyMapping} +import com.evernym.verity.cache.base.{CacheRequest, DEFAULT_MAX_CACHE_SIZE, FetcherParam, ReqParam, RespParam} import com.evernym.verity.cache.providers.MaxWeightParam import com.evernym.verity.config.AppConfig import com.evernym.verity.config.ConfigConstants.TIMEOUT_GENERAL_ACTOR_ASK_TIMEOUT_IN_SECONDS @@ -58,11 +58,11 @@ trait CacheValueFetcher extends HasExecutionContextProvider { (keySize + valueSize).toInt } - //NOTE: this provides mapping from key detail to KeyMapping - def toKeyDetailMappings(keyDetails: Set[KeyDetail]): Set[KeyMapping] + //NOTE: this provides mapping from key param to Cache Request + def toCacheRequests(rp: ReqParam): Set[CacheRequest] - def composeMultiKeyDetailResult(result: Set[Map[String, AnyRef]]): Map[String, AnyRef] = - result.flatten.map(e => e._1 -> e._2).toMap + def composeMultiKeyDetailResult(result: Set[(CacheRequest, Option[RespParam])]): Map[CacheRequest, RespParam] = + result.filter(_._2.isDefined).map(r => r._1 -> r._2.get).toMap def throwRequiredKeysNotFoundException(reqKeysNotFound: Set[String]): HandledErrorException = { new BadRequestErrorException(DATA_NOT_FOUND.statusCode, Option("required keys not found: " + reqKeysNotFound.mkString(", "))) @@ -80,12 +80,10 @@ trait CacheValueFetcher extends HasExecutionContextProvider { trait SyncCacheValueFetcher extends CacheValueFetcher { - def getByKeyDetail(kd: KeyDetail): Map[String, AnyRef] + def getByRequest(cr: CacheRequest): Option[RespParam] - def getByKeyDetails(kds: Set[KeyDetail]): Map[String, AnyRef] = { - val result = kds.map { kd => - getByKeyDetail(kd) - } + def getByRequests(crs: Set[CacheRequest]): Map[CacheRequest, RespParam] = { + val result = crs.map { cr => cr -> getByRequest(cr)} composeMultiKeyDetailResult(result) } } @@ -95,15 +93,13 @@ trait AsyncCacheValueFetcher extends CacheValueFetcher { implicit val timeout: Timeout = buildTimeout(appConfig, TIMEOUT_GENERAL_ACTOR_ASK_TIMEOUT_IN_SECONDS, DEFAULT_GENERAL_RESPONSE_TIMEOUT_IN_SECONDS) - def getByKeyDetail(kd: KeyDetail): Future[Map[String, AnyRef]] + def getByRequest(cr: CacheRequest): Future[Option[RespParam]] - def getByKeyDetails(kds: Set[KeyDetail]): Future[Map[String, AnyRef]] = { - Future.traverse(kds) { kd => - getByKeyDetail(kd) + def getByRequests(crs: Set[CacheRequest]): Future[Map[CacheRequest, RespParam]] = { + Future.traverse(crs) { cr => + getByRequest(cr).map(r => cr -> r) } flatMap { result => - Future { - composeMultiKeyDetailResult(result) - } + Future.successful(composeMultiKeyDetailResult(result)) } } } diff --git a/verity/src/main/scala/com/evernym/verity/cache/fetchers/KeyValueMapperFetcher.scala b/verity/src/main/scala/com/evernym/verity/cache/fetchers/KeyValueMapperFetcher.scala index 3a9a9d4a..a32d0a16 100644 --- a/verity/src/main/scala/com/evernym/verity/cache/fetchers/KeyValueMapperFetcher.scala +++ b/verity/src/main/scala/com/evernym/verity/cache/fetchers/KeyValueMapperFetcher.scala @@ -4,7 +4,7 @@ import akka.actor.{ActorRef, ActorSystem} import akka.pattern.ask import com.evernym.verity.actor.cluster_singleton.{ForKeyValueMapper, GetValue} import com.evernym.verity.cache.KEY_VALUE_MAPPER_ACTOR_CACHE_FETCHER -import com.evernym.verity.cache.base.{FetcherParam, KeyDetail, KeyMapping} +import com.evernym.verity.cache.base.{CacheRequest, FetcherParam, ReqParam, RespParam} import com.evernym.verity.config.AppConfig import com.evernym.verity.config.ConfigConstants._ import com.evernym.verity.constants.ActorNameConstants._ @@ -30,15 +30,15 @@ class KeyValueMapperFetcher(val as: ActorSystem, lazy val singletonParentProxyActor: ActorRef = getActorRefFromSelection(SINGLETON_PARENT_PROXY, as)(appConfig) - override def toKeyDetailMappings(keyDetails: Set[KeyDetail]): Set[KeyMapping] = { - keyDetails.map(kd => KeyMapping(kd, kd.key.toString, kd.key.toString)) + override def toCacheRequests(rp: ReqParam): Set[CacheRequest] = { + Set(CacheRequest(rp, rp.cmd.toString, rp.cmd.toString)) } - override def getByKeyDetail(kd: KeyDetail): Future[Map[String, AnyRef]] = { - val gdFut = singletonParentProxyActor ? ForKeyValueMapper(GetValue(kd.key.toString)) + override def getByRequest(cr: CacheRequest): Future[Option[RespParam]] = { + val gdFut = singletonParentProxyActor ? ForKeyValueMapper(GetValue(cr.respKey)) gdFut map { - case Some(v: String) => Map(kd.key.toString -> v) - case None => Map.empty[String, AnyRef] + case Some(v: String) => Option(RespParam(v)) + case None => None case e => throw buildUnexpectedResponse(e) } } diff --git a/verity/src/main/scala/com/evernym/verity/cache/fetchers/LedgerGetEndpointCacheFetcher.scala b/verity/src/main/scala/com/evernym/verity/cache/fetchers/LedgerGetEndpointCacheFetcher.scala index 4d8bd405..4a260c98 100644 --- a/verity/src/main/scala/com/evernym/verity/cache/fetchers/LedgerGetEndpointCacheFetcher.scala +++ b/verity/src/main/scala/com/evernym/verity/cache/fetchers/LedgerGetEndpointCacheFetcher.scala @@ -1,7 +1,7 @@ package com.evernym.verity.cache.fetchers import com.evernym.verity.cache.LEDGER_GET_ENDPOINT_CACHE_FETCHER -import com.evernym.verity.cache.base.{FetcherParam, KeyDetail, KeyMapping} +import com.evernym.verity.cache.base.{CacheRequest, FetcherParam, ReqParam, RespParam} import com.evernym.verity.config.AppConfig import com.evernym.verity.config.ConfigConstants._ import com.evernym.verity.ledger.{AttribResult, LegacyLedgerSvc, Submitter} @@ -25,21 +25,19 @@ class EndpointCacheFetcher (val legacyLedgerSvc: LegacyLedgerSvc, //time to live in seconds, afterwards they will be considered as expired and re-fetched from source override lazy val defaultExpiryTimeInSeconds: Option[Int] = Option(1800) - override def toKeyDetailMappings(keyDetails: Set[KeyDetail]): Set[KeyMapping] = { - keyDetails.map { kd => - val gvp = kd.keyAs[GetEndpointParam] - KeyMapping(kd, gvp.did, gvp.did) - } + override def toCacheRequests(rp: ReqParam): Set[CacheRequest] = { + val gvp = rp.cmdAs[GetEndpointParam] + Set(CacheRequest(rp, gvp.did, gvp.did)) } - override def getByKeyDetail(kd: KeyDetail): Future[Map[String, AnyRef]] = { - val gep = kd.keyAs[GetEndpointParam] + override def getByRequest(cr: CacheRequest): Future[Option[RespParam]] = { + val gep = cr.reqParam.cmdAs[GetEndpointParam] //TODO: at present vdr apis doesn't support getting endpoints (either via did doc or otherwise) // in future when vdr api supports it, we should replace below call accordingly val gepFut = legacyLedgerSvc.getAttrib(gep.submitterDetail, gep.did, VDRAdapterUtil.URL) gepFut.map { case ar: AttribResult if ar.value.isDefined => - Map(gep.did -> ar.value.map(_.toString).orNull) + Option(RespParam(ar.value.map(_.toString).orNull)) case ar: AttribResult if ar.value.isEmpty => throw new BadRequestErrorException(DATA_NOT_FOUND.statusCode, Option("endpoint not found for DID: " + gep.did)) case x => diff --git a/verity/src/main/scala/com/evernym/verity/cache/fetchers/LedgerGetVerKeyCacheFetcher.scala b/verity/src/main/scala/com/evernym/verity/cache/fetchers/LedgerGetVerKeyCacheFetcher.scala index 6c1be4f2..96d13d04 100644 --- a/verity/src/main/scala/com/evernym/verity/cache/fetchers/LedgerGetVerKeyCacheFetcher.scala +++ b/verity/src/main/scala/com/evernym/verity/cache/fetchers/LedgerGetVerKeyCacheFetcher.scala @@ -2,7 +2,7 @@ package com.evernym.verity.cache.fetchers import com.evernym.verity.util2.Status.StatusDetailException import com.evernym.verity.cache.LEDGER_GET_VER_KEY_CACHE_FETCHER -import com.evernym.verity.cache.base.{FetcherParam, KeyDetail, KeyMapping} +import com.evernym.verity.cache.base.{CacheRequest, FetcherParam, ReqParam, RespParam} import com.evernym.verity.config.AppConfig import com.evernym.verity.config.ConfigConstants._ import com.evernym.verity.ledger.Submitter @@ -27,18 +27,16 @@ class LedgerVerKeyCacheFetcher(val vdr: VDRAdapter, //time to live in seconds, afterwards they will be considered as expired and re-fetched from source override lazy val defaultExpiryTimeInSeconds: Option[Int] = Option(1800) - override def toKeyDetailMappings(keyDetails: Set[KeyDetail]): Set[KeyMapping] = { - keyDetails.map { kd => - val gvp = kd.keyAs[GetVerKeyParam] - KeyMapping(kd, gvp.did, gvp.did) - } + override def toCacheRequests(rp: ReqParam): Set[CacheRequest] = { + val gvp = rp.cmdAs[GetVerKeyParam] + Set(CacheRequest(rp, gvp.did, gvp.did)) } - override def getByKeyDetail(kd: KeyDetail): Future[Map[String, AnyRef]] = { - val gvkp = kd.keyAs[GetVerKeyParam] - val didDocFut = vdr.resolveDID(VDRUtil.toFqDID(gvkp.did, isVdrMultiLedgerSupportEnabled, vdrUnqualifiedLedgerPrefix, vdrLedgerPrefixMappings)) + override def getByRequest(cr: CacheRequest): Future[Option[RespParam]] = { + val gvp = cr.reqParam.cmdAs[GetVerKeyParam] + val didDocFut = vdr.resolveDID(VDRUtil.toFqDID(gvp.did, isVdrMultiLedgerSupportEnabled, vdrUnqualifiedLedgerPrefix, vdrLedgerPrefixMappings)) didDocFut - .map { dd =>Map(gvkp.did -> dd.verKey)} + .map { dd => Option(RespParam(dd.verKey))} .recover { case StatusDetailException(sd) => throw buildUnexpectedResponse(sd) } diff --git a/verity/src/main/scala/com/evernym/verity/http/route_handlers/restricted/AgencySetupEndpointHandler.scala b/verity/src/main/scala/com/evernym/verity/http/route_handlers/restricted/AgencySetupEndpointHandler.scala index f98568e0..f2f6be02 100644 --- a/verity/src/main/scala/com/evernym/verity/http/route_handlers/restricted/AgencySetupEndpointHandler.scala +++ b/verity/src/main/scala/com/evernym/verity/http/route_handlers/restricted/AgencySetupEndpointHandler.scala @@ -8,7 +8,7 @@ import com.evernym.verity.actor.agent.agency.{CreateKey, SetEndpoint, UpdateEndp import com.evernym.verity.actor.agent.msgrouter.{ActorAddressDetail, GetRoute} import com.evernym.verity.actor.{AgencyPublicDid, EndpointSet} import com.evernym.verity.cache.KEY_VALUE_MAPPER_ACTOR_CACHE_FETCHER -import com.evernym.verity.cache.base.{GetCachedObjectParam, KeyDetail} +import com.evernym.verity.cache.base.{GetCachedObjectParam, ReqParam} import com.evernym.verity.constants.Constants.AGENCY_DID_KEY import com.evernym.verity.did.DidStr import com.evernym.verity.http.HttpUtil.optionalEntityAs @@ -44,7 +44,7 @@ trait AgencySetupEndpointHandler extends BaseRequestHandler { } protected def getAgencyDIDOptFut: Future[Option[String]] = { - val gcop = GetCachedObjectParam(KeyDetail(AGENCY_DID_KEY, required = false), KEY_VALUE_MAPPER_ACTOR_CACHE_FETCHER) + val gcop = GetCachedObjectParam(ReqParam(AGENCY_DID_KEY, required = false), KEY_VALUE_MAPPER_ACTOR_CACHE_FETCHER) platform.agentActorContext.generalCache.getByParamAsync(gcop).map { cqr => cqr.getAgencyDIDOpt } diff --git a/verity/src/test/scala/com/evernym/verity/cache/AgencyIdentityCacheSpec.scala b/verity/src/test/scala/com/evernym/verity/cache/AgencyIdentityCacheSpec.scala new file mode 100644 index 00000000..b55e0191 --- /dev/null +++ b/verity/src/test/scala/com/evernym/verity/cache/AgencyIdentityCacheSpec.scala @@ -0,0 +1,227 @@ +package com.evernym.verity.cache + +import akka.util.Timeout +import com.evernym.verity.actor.agent.agency.{AgencyInfo, GetAgencyIdentity} +import com.evernym.verity.actor.testkit.{CommonSpecUtil, PersistentActorSpec} +import com.evernym.verity.actor.testkit.actor.MockLegacyLedgerSvc +import com.evernym.verity.cache.base.{Cache, CacheRequest, CacheKey, FetcherParam, GetCachedObjectParam, QueryResult, ReqParam, RespParam} +import com.evernym.verity.cache.fetchers.{AsyncCacheValueFetcher, CacheValueFetcher, GetAgencyIdentityCacheParam} +import com.evernym.verity.config.AppConfig +import com.evernym.verity.config.ConfigConstants.AGENCY_DETAIL_CACHE +import com.evernym.verity.did.{DidStr, VerKeyStr} +import com.evernym.verity.testkit.BasicAsyncSpec +import com.evernym.verity.util2.Status.StatusDetail +import com.evernym.verity.util2.{ExecutionContextProvider, Status} +import com.typesafe.config.{Config, ConfigFactory} +import org.scalatest.concurrent.Eventually + +import java.util.concurrent.TimeUnit +import scala.concurrent.duration.Duration +import scala.concurrent.{ExecutionContext, Future} + +class AgencyIdentityCacheSpec + extends PersistentActorSpec + with BasicAsyncSpec + with Eventually { + + val agencyInfoProvider = new AgencyInfoProvider() + + override def beforeAll(): Unit = { + val _ = platform.singletonParentProxy //to make sure singleton proxy actor gets created before use + updateAgencyInfoProvider(agencyDid1, Option("verkey"), Option("http://test.endpoint.com")) + } + + lazy val ecp = new ExecutionContextProvider(appConfig) + override implicit lazy val executionContext: ExecutionContext = ecp.futureExecutionContext + lazy val cache: Cache = buildCache() + + val localAgencyDid: DidStr = CommonSpecUtil.generateNewDid().did + val agencyDid1: DidStr = CommonSpecUtil.generateNewDid().did + + "Cache" - { + "without any items cached" - { + "should respond with size 0" in { + cache.allCacheSize shouldBe 0 + cache.allCacheMissCount shouldBe 0 + cache.allCacheHitCount shouldBe 0 + } + } + + "when asked for agency identity for agencyDid1 without endpoint" - { + "should get it from source and save it in cache" in { + getFromCache(cache, Set(buildReqParam(agencyDid1, getEndpoint = false, required = true))).map { cqr => + cqr shouldBe QueryResult(Map(agencyDid1 -> AgencyInfo(Option(Right("verkey")), None))) + cache.allCacheSize shouldBe 1 + cache.allCacheMissCount shouldBe 1 + cache.allCacheHitCount shouldBe 0 + } + } + } + + "when asked for agency identity again for agencyDid1 without endpoint" - { + "should get it from cache" in { + getFromCache(cache, Set(buildReqParam(agencyDid1, getEndpoint = false, required = true))).map { cqr => + cqr shouldBe QueryResult(Map(agencyDid1 -> AgencyInfo(Option(Right("verkey")), None))) + cache.allCacheSize shouldBe 1 + cache.allCacheMissCount shouldBe 1 + cache.allCacheHitCount shouldBe 1 + } + } + } + + "when asked for agency identity for agencyDid1 without verkey" - { + "should get it from source and save it in cache" in { + + getFromCache(cache, Set(buildReqParam(agencyDid1, getVerKey = false, required = true))).map { cqr => + cqr shouldBe QueryResult(Map(agencyDid1 -> AgencyInfo(None, Option(Right("http://test.endpoint.com"))))) + cache.allCacheSize shouldBe 2 + cache.allCacheMissCount shouldBe 2 + cache.allCacheHitCount shouldBe 1 + } + } + } + + "when asked for agency identity again for agencyDid1 without verkey" - { + "should get it from cache" in { + + getFromCache(cache, Set(buildReqParam(agencyDid1, getVerKey = false, required = true))).map { cqr => + cqr shouldBe QueryResult(Map(agencyDid1 -> AgencyInfo(None, Option(Right("http://test.endpoint.com"))))) + cache.allCacheSize shouldBe 2 + cache.allCacheMissCount shouldBe 2 + cache.allCacheHitCount shouldBe 2 + } + } + } + + "when asked for agency identity for agencyDid1" - { + "should get it from source and save it in cache" in { + getFromCache(cache, Set(buildReqParam(agencyDid1, required = true))).map { cqr => + cqr shouldBe QueryResult(Map(agencyDid1 -> AgencyInfo(Option(Right("verkey")), Option(Right("http://test.endpoint.com"))))) + cache.allCacheSize shouldBe 3 + cache.allCacheMissCount shouldBe 3 + cache.allCacheHitCount shouldBe 2 + } + } + } + + "when asked for agency identity again for agencyDid1" - { + "should get it from cache" in { + getFromCache(cache, Set(buildReqParam(agencyDid1, required = true))).map { cqr => + cqr shouldBe QueryResult(Map(agencyDid1 -> AgencyInfo(Option(Right("verkey")), Option(Right("http://test.endpoint.com"))))) + cache.allCacheSize shouldBe 3 + cache.allCacheMissCount shouldBe 3 + cache.allCacheHitCount shouldBe 3 + } + } + } + } + + override def overrideConfig: Option[Config] = Option { + ConfigFactory.parseString { + """verity.cache.agency-detail { + expiration-time-in-seconds = 1 + max-size = 10 + }""".stripMargin + } + } + + private def buildReqParam(did: DidStr, + getVerKey: Boolean=true, + getEndpoint: Boolean=true, + required: Boolean=false): ReqParam = { + ReqParam(GetAgencyIdentityCacheParam(localAgencyDid, GetAgencyIdentity(did, getVerKey, getEndpoint)), required) + } + + val agencyIdentityCacheFetcherParam: FetcherParam = AGENCY_IDENTITY_CACHE_FETCHER + val agencyIdentityCacheFetcher = new MockAgencyIdentityCacheFetcher(agencyInfoProvider, appConfig, executionContext) + val fetchers: Map[FetcherParam, AsyncCacheValueFetcher] = Map( + agencyIdentityCacheFetcherParam -> agencyIdentityCacheFetcher) + + def buildCache(name: String = "TestCache", fetchers: Map[FetcherParam, CacheValueFetcher] = fetchers): Cache = { + new Cache(name, fetchers, metricsWriter, executionContext) + } + + implicit val timeout: Timeout = Timeout(Duration.create(5, TimeUnit.SECONDS)) + + lazy val mockLegacyLedger: MockLegacyLedgerSvc = platform.agentActorContext.legacyLedgerSvc.asInstanceOf[MockLegacyLedgerSvc] + + def updateAgencyInfoProvider(agencyDid: DidStr, + verKey: Option[VerKeyStr], + endpoint: Option[String]): Unit = { + agencyInfoProvider.updateAgencyInfo( + agencyDid, + AgencyInfo( + verKey.map(vk => Right(vk)), + endpoint.map(ep => Right(ep)) + ) + ) + } + + def getFromCache(cache: Cache, rps: Set[ReqParam]): Future[QueryResult] = { + cache.getByParamAsync(GetCachedObjectParam(rps, agencyIdentityCacheFetcherParam)) + } + + override def executionContextProvider: ExecutionContextProvider = ecp +} + +class MockAgencyIdentityCacheFetcher(val agencyInfoProvider: AgencyInfoProvider, + val appConfig: AppConfig, + executionContext: ExecutionContext) + extends AsyncCacheValueFetcher{ + + override def futureExecutionContext: ExecutionContext = executionContext + private implicit val executionContextImpl: ExecutionContext = executionContext + + lazy val fetcherParam: FetcherParam = AGENCY_IDENTITY_CACHE_FETCHER + + lazy val cacheConfigPath: Option[String] = Option(AGENCY_DETAIL_CACHE) + + //time to live in seconds, afterwards they will be considered as expired and re-fetched from source + override lazy val defaultExpiryTimeInSeconds: Option[Int] = Option(1800) + + override def toCacheRequests(rp: ReqParam): Set[CacheRequest] = { + val gadcp = rp.cmdAs[GetAgencyIdentityCacheParam] + Set(CacheRequest(rp, gadcp.gad.did, buildLookupKey(gadcp.gad))) + } + + override def getByRequest(cr: CacheRequest): Future[Option[RespParam]] = { + val gadcp = cr.reqParam.cmdAs[GetAgencyIdentityCacheParam] + val verKeyResult = if (gadcp.gad.getVerKey) agencyInfoProvider.getVerKey(gadcp.gad.did) else None + val endpointResult = if (gadcp.gad.getEndpoint) agencyInfoProvider.getEndpoint(gadcp.gad.did) else None + val gadFutResp = Future.successful(AgencyInfo(verKeyResult, endpointResult)) + gadFutResp.map { + case ai: AgencyInfo if ! ai.isErrorFetchingAnyData => + logger.info(s"agency info received from source for '${gadcp.gad.did}': " + ai) + Option(RespParam(ai)) + case ai: AgencyInfo if ai.verKeyErrorOpt.isDefined => + throw buildUnexpectedResponse(ai.verKeyErrorOpt.get) + case ai: AgencyInfo if ai.endpointErrorOpt.isDefined => + throw buildUnexpectedResponse(ai.endpointErrorOpt.get) + case x => throw buildUnexpectedResponse(x) + } + } + + private def buildLookupKey(gad: GetAgencyIdentity): CacheKey = s"${gad.did}:${gad.getVerKey}:${gad.getEndpoint}" +} + +class AgencyInfoProvider { + private var agencyAgents: Map[DidStr, AgencyInfo] = Map.empty + + def updateAgencyInfo(did: DidStr, agencyInfo: AgencyInfo): Unit = { + agencyAgents = agencyAgents ++ Map(did -> agencyInfo) + } + + def getVerKey(did: DidStr): Option[Either[StatusDetail, VerKeyStr]] = { + agencyAgents + .get(did) + .map(_.verKey) + .getOrElse(Option(Left(Status.DATA_NOT_FOUND))) + } + + def getEndpoint(did: DidStr): Option[Either[StatusDetail, String]] = { + agencyAgents + .get(did) + .map(_.endpoint) + .getOrElse(Option(Left(Status.DATA_NOT_FOUND))) + } +} \ No newline at end of file diff --git a/verity/src/test/scala/com/evernym/verity/cache/BasicCacheSpec.scala b/verity/src/test/scala/com/evernym/verity/cache/BasicCacheSpec.scala index 2318c531..e3eeec5b 100644 --- a/verity/src/test/scala/com/evernym/verity/cache/BasicCacheSpec.scala +++ b/verity/src/test/scala/com/evernym/verity/cache/BasicCacheSpec.scala @@ -7,8 +7,8 @@ import akka.util.Timeout import com.evernym.verity.util2.ExecutionContextProvider import com.evernym.verity.actor._ import com.evernym.verity.actor.cluster_singleton.{AddMapping, ForKeyValueMapper} -import com.evernym.verity.actor.testkit.{PersistentActorSpec, TestAppConfig} -import com.evernym.verity.cache.base.{Cache, CacheQueryResponse, FetcherParam, GetCachedObjectParam, KeyDetail} +import com.evernym.verity.actor.testkit.PersistentActorSpec +import com.evernym.verity.cache.base.{Cache, QueryResult, FetcherParam, GetCachedObjectParam, ReqParam} import com.evernym.verity.cache.fetchers.{AsyncCacheValueFetcher, CacheValueFetcher, KeyValueMapperFetcher} import com.evernym.verity.testkit.{BasicAsyncSpec, CancelGloballyAfterFailure} import com.typesafe.config.{Config, ConfigFactory} @@ -44,7 +44,7 @@ class BasicCacheSpec "when asked for non existing key" - { "should get empty map" in { getFromCache(cache, "a", required = false).map { cqr => - cqr shouldBe CacheQueryResponse(Map.empty) + cqr shouldBe QueryResult(Map.empty) cache.allCacheSize shouldBe 0 cache.allCacheMissCount shouldBe 1 cache.allCacheHitCount shouldBe 0 @@ -57,7 +57,7 @@ class BasicCacheSpec addKeyValueMapping(AddMapping("a", "apple")) getFromCache(cache, "a", required = true).map { cqr => - cqr shouldBe CacheQueryResponse(Map("a" -> "apple")) + cqr shouldBe QueryResult(Map("a" -> "apple")) cache.allCacheSize shouldBe 1 cache.allCacheMissCount shouldBe 2 cache.allCacheHitCount shouldBe 0 @@ -68,7 +68,7 @@ class BasicCacheSpec "when asked key value mapper object with key 'a'" - { "should get it from cache" in { getFromCache(cache, "a", required = true).map { cqr => - cqr shouldBe CacheQueryResponse(Map("a" -> "apple")) + cqr shouldBe QueryResult(Map("a" -> "apple")) cache.allCacheSize shouldBe 1 cache.allCacheMissCount shouldBe 2 cache.allCacheHitCount shouldBe 1 @@ -80,7 +80,7 @@ class BasicCacheSpec "should get it from source as the cache would have been expired" in { Thread.sleep(1500) getFromCache(cache, "a", required = true).map { cqr => - cqr shouldBe CacheQueryResponse(Map("a" -> "apple")) + cqr shouldBe QueryResult(Map("a" -> "apple")) cache.allCacheSize shouldBe 1 cache.allCacheMissCount shouldBe 3 cache.allCacheHitCount shouldBe 1 @@ -91,7 +91,7 @@ class BasicCacheSpec "when asked for key value mapper object with key 'b'" - { "should respond with empty map" in { getFromCache(cache, "b", required = false).map { cqr => - cqr shouldBe CacheQueryResponse(Map.empty) + cqr shouldBe QueryResult(Map.empty) cache.allCacheSize shouldBe 1 cache.allCacheMissCount shouldBe 4 cache.allCacheHitCount shouldBe 1 @@ -103,8 +103,8 @@ class BasicCacheSpec "when asked for multiple values from cache" - { "should get it from from cache" in { addKeyValueMapping(AddMapping("b", "berry")) - getFromCache(cache, Set(KeyDetail("a", required = true), KeyDetail("b", required = true))).map { cqr => - cqr shouldBe CacheQueryResponse(Map("a" -> "apple", "b" -> "berry")) + getFromCache(cache, Set(ReqParam("a", required = true), ReqParam("b", required = true))).map { cqr => + cqr shouldBe QueryResult(Map("a" -> "apple", "b" -> "berry")) cache.allCacheSize shouldBe 2 cache.allCacheMissCount shouldBe 5 cache.allCacheHitCount shouldBe 2 @@ -116,8 +116,8 @@ class BasicCacheSpec "should respond with updated value after expiry time" in { addKeyValueMapping(AddMapping("b", "blackberry")) Thread.sleep(1100) - getFromCache(cache, Set(KeyDetail("a", required = true), KeyDetail("b", required = true))).map { cqr => - cqr shouldBe CacheQueryResponse(Map("a" -> "apple", "b" -> "blackberry")) + getFromCache(cache, Set(ReqParam("a", required = true), ReqParam("b", required = true))).map { cqr => + cqr shouldBe QueryResult(Map("a" -> "apple", "b" -> "blackberry")) cache.allCacheSize shouldBe 2 cache.allCacheMissCount shouldBe 7 cache.allCacheHitCount shouldBe 2 @@ -153,11 +153,11 @@ class BasicCacheSpec ma.value shouldBe am.value } - def getFromCache(cache: Cache, key: String, required: Boolean): Future[CacheQueryResponse] = { - getFromCache(cache, Set(KeyDetail(key, required))) + def getFromCache(cache: Cache, key: String, required: Boolean): Future[QueryResult] = { + getFromCache(cache, Set(ReqParam(key, required))) } - def getFromCache(cache: Cache, keyDetails: Set[KeyDetail]): Future[CacheQueryResponse] = { + def getFromCache(cache: Cache, keyDetails: Set[ReqParam]): Future[QueryResult] = { cache.getByParamAsync(GetCachedObjectParam(keyDetails, keyValueMapperFetcher)) } diff --git a/verity/src/test/scala/com/evernym/verity/cache/CacheMaxSizeSpec.scala b/verity/src/test/scala/com/evernym/verity/cache/CacheMaxSizeSpec.scala index 40c9e6d6..3d903096 100644 --- a/verity/src/test/scala/com/evernym/verity/cache/CacheMaxSizeSpec.scala +++ b/verity/src/test/scala/com/evernym/verity/cache/CacheMaxSizeSpec.scala @@ -1,7 +1,7 @@ package com.evernym.verity.cache import com.evernym.verity.actor.testkit.ActorSpec -import com.evernym.verity.cache.base.{Cache, CacheQueryResponse, FetcherParam, GetCachedObjectParam, KeyDetail, KeyMapping} +import com.evernym.verity.cache.base.{Cache, CacheRequest, FetcherParam, GetCachedObjectParam, QueryResult, ReqParam, RespParam} import com.evernym.verity.util2.Status.StatusDetail import com.evernym.verity.cache.fetchers.{AsyncCacheValueFetcher, CacheValueFetcher} import com.evernym.verity.config.AppConfig @@ -27,10 +27,10 @@ class CacheMaxSizeSpec "when kept adding objects more than max size" - { "should respect the max size cap" in { (1 to 100).foreach { i => - val gcop = GetCachedObjectParam(KeyDetail(GetMaxSizeCacheReq(i.toString, Right(i.toString)), required = true), mockFetcher) + val gcop = GetCachedObjectParam(ReqParam(GetMaxSizeCacheReq(i.toString, Right(i.toString)), required = true), mockFetcher) val fut = cache.getByParamAsync(gcop) val cqr = Await.result(fut, 2.second) - cqr shouldBe CacheQueryResponse(Map(s"$i" -> s"$i")) + cqr shouldBe QueryResult(Map(s"$i" -> s"$i")) } eventually(timeout(Span(5, Seconds)), interval(Span(100, Millis))) { mockMaxSizeFetcher.maxSize shouldBe Option(20) @@ -61,18 +61,16 @@ class MockMaxSizeCacheFetcher(val appConfig: AppConfig, ec: ExecutionContext) override lazy val defaultExpiryTimeInSeconds: Option[Int] = Option(300) override lazy val defaultMaxSize: Option[Int] = Option(20) - override def toKeyDetailMappings(keyDetails: Set[KeyDetail]): Set[KeyMapping] = { - keyDetails.map { kd => - val gvp = kd.keyAs[GetMaxSizeCacheReq] - KeyMapping(kd, gvp.id, gvp.id) - } + override def toCacheRequests(rp: ReqParam): Set[CacheRequest] = { + val gvp = rp.cmdAs[GetMaxSizeCacheReq] + Set(CacheRequest(rp, gvp.id, gvp.id)) } - override def getByKeyDetail(kd: KeyDetail): Future[Map[String, AnyRef]] = { - val getReq = kd.keyAs[GetMaxSizeCacheReq] + override def getByRequest(cr: CacheRequest): Future[Option[RespParam]] = { + val getReq = cr.reqParam.cmdAs[GetMaxSizeCacheReq] val respFut = Future(getReq.resp) respFut.map { - case Right(resp) => Map(getReq.id -> resp) + case Right(resp) => Option(RespParam(resp)) case Left(d) => throw buildUnexpectedResponse(d) } } diff --git a/verity/src/test/scala/com/evernym/verity/cache/CacheMaxWeightSpec.scala b/verity/src/test/scala/com/evernym/verity/cache/CacheMaxWeightSpec.scala index 642b4311..e42856cf 100644 --- a/verity/src/test/scala/com/evernym/verity/cache/CacheMaxWeightSpec.scala +++ b/verity/src/test/scala/com/evernym/verity/cache/CacheMaxWeightSpec.scala @@ -1,7 +1,7 @@ package com.evernym.verity.cache -import com.evernym.verity.actor.testkit.{ActorSpec, TestAppConfig} -import com.evernym.verity.cache.base.{Cache, FetcherParam, GetCachedObjectParam, KeyDetail, KeyMapping} +import com.evernym.verity.actor.testkit.ActorSpec +import com.evernym.verity.cache.base.{Cache, CacheRequest, FetcherParam, GetCachedObjectParam, ReqParam, RespParam} import com.evernym.verity.util2.Status.StatusDetail import com.evernym.verity.cache.fetchers.{AsyncCacheValueFetcher, CacheValueFetcher} import com.evernym.verity.config.AppConfig @@ -23,7 +23,7 @@ class CacheMaxWeightSpec "when asked for object 1" - { "should respond with appropriate value" in { val value = Array.range(0, 270).map(_.toByte) - val gcop = GetCachedObjectParam(KeyDetail(GetMaxWeightCacheReq("1", Right(value)), required = true), mockFetcher) + val gcop = GetCachedObjectParam(ReqParam(GetMaxWeightCacheReq("1", Right(value)), required = true), mockFetcher) cache.getByParamAsync(gcop).map { _ => cache.allKeys shouldBe Set("1") cache.allCacheHitCount shouldBe 0 @@ -35,7 +35,7 @@ class CacheMaxWeightSpec "when asked for object 2" - { "should respond with appropriate value" in { val value = Array.range(0, 270).map(_.toByte) - val gcop = GetCachedObjectParam(KeyDetail(GetMaxWeightCacheReq("2", Right(value)), required = true), mockFetcher) + val gcop = GetCachedObjectParam(ReqParam(GetMaxWeightCacheReq("2", Right(value)), required = true), mockFetcher) cache.getByParamAsync(gcop).map { _ => cache.allKeys shouldBe Set("1", "2") cache.allCacheHitCount shouldBe 0 @@ -47,7 +47,7 @@ class CacheMaxWeightSpec "when asked for object 3" - { "should respond with appropriate value" in { val value = Array.range(0, 270).map(_.toByte) - val gcop = GetCachedObjectParam(KeyDetail(GetMaxWeightCacheReq("3", Right(value)), required = true), mockFetcher) + val gcop = GetCachedObjectParam(ReqParam(GetMaxWeightCacheReq("3", Right(value)), required = true), mockFetcher) cache.getByParamAsync(gcop).map { _ => cache.allKeys shouldBe Set("1", "2", "3") cache.allCacheHitCount shouldBe 0 @@ -61,7 +61,7 @@ class CacheMaxWeightSpec //1MB is the total weight for the cache, so anything which makes // total cache size to cross ~1MB should result in eviction val largerObject = Array.range(0, 270).map(_.toByte) - val gcop = GetCachedObjectParam(KeyDetail(GetMaxWeightCacheReq("larger", Right(largerObject)), required = true), mockFetcher) + val gcop = GetCachedObjectParam(ReqParam(GetMaxWeightCacheReq("larger", Right(largerObject)), required = true), mockFetcher) cache.getByParamAsync(gcop).map { _ => eventually(timeout(Span(10, Seconds)), interval(Span(300, Millis))) { cache.allKeys shouldBe Set("2", "3", "larger") @@ -74,7 +74,7 @@ class CacheMaxWeightSpec "when asked for a larger object again" - { "should be responded from the cache itself" in { - val gcop = GetCachedObjectParam(KeyDetail(GetMaxWeightCacheReq("larger", Right("test")), required = true), mockFetcher) + val gcop = GetCachedObjectParam(ReqParam(GetMaxWeightCacheReq("larger", Right("test")), required = true), mockFetcher) cache.getByParamAsync(gcop).map { _ => cache.allKeys shouldBe Set("2", "3", "larger") cache.allCacheHitCount shouldBe 1 @@ -109,18 +109,16 @@ class MockMaxWeightCacheFetcher(ec: ExecutionContext, val _appConfig: AppConfig) override lazy val defaultMaxWeightInBytes: Option[Long] = Option(1000) override lazy val defaultMaxSize: Option[Int] = Option(100) //this won't matter as the max weight will take precedence - override def toKeyDetailMappings(keyDetails: Set[KeyDetail]): Set[KeyMapping] = { - keyDetails.map { kd => - val gvp = kd.keyAs[GetMaxWeightCacheReq] - KeyMapping(kd, gvp.id, gvp.id) - } + override def toCacheRequests(rp: ReqParam): Set[CacheRequest] = { + val gvp = rp.cmdAs[GetMaxWeightCacheReq] + Set(CacheRequest(rp, gvp.id, gvp.id)) } - override def getByKeyDetail(kd: KeyDetail): Future[Map[String, AnyRef]] = { - val getReq = kd.keyAs[GetMaxWeightCacheReq] + override def getByRequest(cr: CacheRequest): Future[Option[RespParam]] = { + val getReq = cr.reqParam.cmdAs[GetMaxWeightCacheReq] val respFut = Future(getReq.resp) respFut.map { - case Right(resp) => Map(getReq.id -> resp) + case Right(resp) => Option(RespParam(resp)) case Left(d) => throw buildUnexpectedResponse(d) } }