protected override async Task <bool> TimeoutCorrectCreateUpdate(PersistenceRequestHolder <K, E> holder) { if (holder.Rq.Entity == null || mTransform == null) { return(false); } var jsonHolder = mTransform.JsonMaker(holder.Rq.Entity); var alternateHolder = new PersistenceRequestHolder <K, E>(holder.ProfileId, holder.Prq, holder.Prs) { Rq = new PersistenceRepositoryHolder <K, E> { Key = jsonHolder.Key, Timeout = holder.Rq.Timeout }, Rs = new PersistenceRepositoryHolder <K, E>() }; if (!(await RetrieveEntity(alternateHolder, ProcessRead))) { return(false); } holder.Rs.Entity = alternateHolder.Rs.Entity; holder.Rs.Key = alternateHolder.Rs.Key; holder.Rs.KeyReference = alternateHolder.Rs.KeyReference; holder.Rs.ResponseCode = mTransform.Version.SupportsVersioning && jsonHolder.Version.Equals(mTransform.Version.EntityVersionAsString(holder.Rs.Entity)) ? alternateHolder.Rs.ResponseCode : holder.Rs.ResponseCode; return(holder.Rs.IsSuccess); }
protected virtual async Task ProcessVersion(PersistenceRequestHolder <K, Tuple <K, string> > holder) { IResponseHolder result = null; if (mCacheManager.IsActive) { result = await mCacheManager.VersionRead(mTransform, holder.Rq.Key); } if (result == null || !result.IsSuccess) { if (mTransform.Version == null) { //If we don't set a version maker then how can we return the version. result = new PersistenceResponseHolder(PersistenceResponse.NotImplemented501) { IsSuccess = false } } ; else { result = await InternalVersion(holder.Rq.Key, holder); } if (mCacheManager.IsActive && !mCacheManager.IsReadOnly && result.IsSuccess) { mCacheManager.WriteVersion(mTransform, holder.Rq.Key, result.VersionId); } } ProcessOutputKey(holder.Rq, holder.Rs, result); }
protected virtual async Task ProcessRead(PersistenceRequestHolder <K, E> holder) { IResponseHolder <E> result = null; if (mCacheManager.IsActive && holder.Rq.Settings.UseCache) { result = await mCacheManager.Read(mTransform, holder.Rq.Key); } if (result == null || !result.IsSuccess) { result = await InternalRead(holder.Rq.Key, holder); if (mCacheManager.IsActive && !mCacheManager.IsReadOnly && result.IsSuccess) { mCacheManager.Write(mTransform, result.Entity); } } else { result.IsCacheHit = true; } ProcessOutputEntity(holder.Rq.Key, holder.Rq, holder.Rs, result); }
protected override async Task <bool> TimeoutCorrectDelete(PersistenceRequestHolder <K, Tuple <K, string> > holder) { var alternateHolder = new PersistenceRequestHolder <K, E>(holder.ProfileId, holder.Prq, holder.Prs); alternateHolder.Rq = new PersistenceRepositoryHolder <K, E> { Key = holder.Rq.Key, KeyReference = holder.Rq.KeyReference, Timeout = holder.Rq.Timeout }; alternateHolder.Rs = new PersistenceRepositoryHolder <K, E>(); bool byref = alternateHolder.Rq.KeyReference != null && !string.IsNullOrEmpty(alternateHolder.Rq.KeyReference.Item2); bool result; if (byref) { result = await RetrieveEntity(alternateHolder, ProcessReadByRef); } else { result = await RetrieveEntity(alternateHolder, ProcessRead); } if (result) { return(false); } // We should have a 404 response code here. If not send back the one we got otherwise send back 200 OK holder.Rs.Key = alternateHolder.Rs.Key; holder.Rs.KeyReference = alternateHolder.Rs.KeyReference; holder.Rs.ResponseCode = alternateHolder.Rs.ResponseCode != 404 ? alternateHolder.Rs.ResponseCode : 200; return(holder.Rs.IsSuccess); }
/// <summary> /// This method logs entities to the event source. This method will try indefinitely /// as we do not want the Event Source to fail. /// </summary> /// <typeparam Name="KT">The key type.</typeparam> /// <typeparam Name="ET">The entity type.</typeparam> /// <param name="actionType"></param> /// <param name="prq"></param> /// <param name="rq"></param> /// <param name="rs"></param> protected async virtual Task LogEventSource <KT, ET>(string actionType, PersistenceRequestHolder <KT, ET> holder) { // Only pass through the entity if is of the correct entity type. ET might be an entity or it might be a Tuple<K, string>> in which case pass a null E entity = typeof(ET) == typeof(E) ? (E)Convert.ChangeType(holder.Rs.Entity, typeof(E)) : default(E); await LogEventSource(actionType, holder.Prq.Message.OriginatorKey, holder.Rs.Key, entity, holder.Rq.Settings); }
/// <summary> /// We don't want to pass the exception details back to the calling party as this may /// leak sensitive information aboout the application and persistence agent. /// This method logs the error and assigns it a trackable id and sends that instead to the /// front end. /// </summary> /// <typeparam Name="KT">The key type.</typeparam> /// <typeparam Name="ET">The entity type.</typeparam> /// <param Name="action">the entity action.</param> /// <param Name="prq">The payload request.</param> /// <param Name="ex">The exception.</param> /// <param name="rq">The request.</param> /// <param Name="rs">The response.</param> protected void LogException <KT, ET>(string action, PersistenceRequestHolder <KT, ET> holder, Exception ex) { try { var logEvent = new PersistencePayloadLogEvent(holder.Prq, holder.Rq, holder.Rs, LoggingLevel.Info, ex); Logger.Log(logEvent); Guid errorId = Guid.NewGuid(); string errMessage = string.Format("Exception tracker {0}/{1}/{2}", action, (holder.Prq != null && holder.Prq.Message != null ? holder.Prq.Message.OriginatorKey : string.Empty), errorId); holder.Rs.ResponseMessage = errMessage; if (holder.Rq != null) { errMessage += string.Format("/{0}-{1}", holder.Rq.Key, (holder.Rq.Entity == null) ? string.Empty : holder.Rq.Entity.ToString()); } Logger.LogException(errMessage, ex); } catch (Exception) { // Do not fail due to an issue logging } holder.Rs.ResponseCode = 500; holder.Rs.ResponseMessage = ex.Message; }
protected virtual async Task <IResponseHolder> InternalVersion(K key, PersistenceRequestHolder <K, Tuple <K, string> > holder) { return(new PersistenceResponseHolder(PersistenceResponse.NotImplemented501) { IsSuccess = false }); }
/// <summary> /// Retrieves an entity using the supplied read action (read or read by ref) using the persistence retry policy /// </summary> /// <param name="rq">Read request</param> /// <param name="rs">Read response</param> /// <param name="m">Transmission request message</param> /// <param name="l">Tranmission response messages</param> /// <param name="readAction">Read action</param> /// <returns></returns> protected virtual async Task <bool> RetrieveEntity(PersistenceRequestHolder <K, E> holder, Func <PersistenceRequestHolder <K, E>, Task> readAction) { int numberOfRetries = 0; int maximumRetries = mPolicy.PersistenceRetryPolicy.GetMaximumRetries(holder.Prq); while (numberOfRetries < maximumRetries && !holder.Prq.Cancel.IsCancellationRequested) { await readAction(holder); if (holder.Rs.Entity != null || holder.Rs.ResponseCode == 404) { return(holder.Rs.Entity != null); } await Task.Delay(mPolicy.PersistenceRetryPolicy.GetDelayBetweenRetries(holder.Prq, numberOfRetries)); numberOfRetries++; } Collector?.LogMessage(LoggingLevel.Error , string.Format( "Unable to retrieve entity after {0} retries, message cancelled({1}) on channel({2}) for request: {3} - response: {4}" , numberOfRetries , holder.Prq.Cancel.IsCancellationRequested , holder.Prq.Message != null ? holder.Prq.Message.ChannelPriority.ToString() : "null" , holder.Rq , holder.Rs) , "DocDb" ); return(false); }
/// <summary> /// Records the specified holder response. /// </summary> /// <typeparam name="KT">The key type.</typeparam> /// <typeparam name="ET">The entity type.</typeparam> /// <param name="holder">The request holder.</param> public virtual void Record <KT, ET>(PersistenceRequestHolder <KT, ET> holder) { int responseCode = holder.Rs?.ResponseCode ?? 0; var stats = mResponses.GetOrAdd(responseCode, new ResponseCodeStatistics(responseCode)); stats.Record(holder.Extent, holder.Rs); }
/// <summary> /// This is called when the request is retried due to an underlying storage issue. /// </summary> /// <typeparam name="KT">The key type.</typeparam> /// <typeparam name="ET">The entity type.</typeparam> /// <param name="holder">The request holder.</param> /// <param name="retryStart">The tick count of the retry point.</param> protected virtual void ProfileRetry <KT, ET>(PersistenceRequestHolder <KT, ET> holder, int retryStart) { mPolicy.ResourceConsumer?.Retry(holder.ProfileId, retryStart, holder.Rs.ShouldRetry ? ResourceRetryReason.Other : ResourceRetryReason.Timeout); holder.Retry(retryStart); StatisticsInternal.RetryIncrement(); }
/// <summary> /// The internal delete command. /// </summary> /// <param name="key">The key.</param> /// <param name="holder">The request holder.</param> /// <returns></returns> protected override async Task <IResponseHolder> InternalDelete(K key, PersistenceRequestHolder <K, Tuple <K, string> > holder) { if (!Container.Remove(key)) { return(new PersistenceResponseHolder(PersistenceResponse.NotFound404)); } return(new PersistenceResponseHolder(PersistenceResponse.Ok200)); }
//Logging #region Log<KT, ET>... /// <summary> /// /// </summary> /// <typeparam Name="KT">The key type.</typeparam> /// <typeparam Name="ET">The entity type.</typeparam> /// <param name="action"></param> /// <param name="prq"></param> /// <param name="rq"></param> /// <param name="rs"></param> /// <param name="loggingLevel">Logging level</param> /// <param name="message"></param> protected void Log <KT, ET>(string action, PersistenceRequestHolder <KT, ET> holder , LoggingLevel loggingLevel = LoggingLevel.Info, string message = null, string category = null) { var logEvent = new PersistencePayloadLogEvent(holder.Prq, holder.Rq, holder.Rs, loggingLevel) { Message = message ?? string.Empty, Category = category }; Logger.Log(logEvent); }
protected virtual async Task ProcessDeleteByRef(PersistenceRequestHolder <K, Tuple <K, string> > holder) { var result = await InternalDeleteByRef(holder.Rq.KeyReference, holder); if (mCacheManager.IsActive && result.IsSuccess) { await mCacheManager.Delete(mTransform, mTransform.KeyDeserializer(result.Id)); } ProcessOutputKey(holder.Rq, holder.Rs, result); }
/// <summary> /// Update /// </summary> /// <param name="rq">The request.</param> /// <param name="rs">The response.</param> /// <param name="prq">The incoming payload.</param> /// <param name="prs">The outgoing payload.</param> protected override async Task <IResponseHolder <E> > InternalUpdate(K key, PersistenceRequestHolder <K, E> holder) { var jsonHolder = mTransform.JsonMaker(holder.Rq.Entity); var result = await mStorage.Update(mStorageIdMaker(jsonHolder.Key) , jsonHolder.ToBlob() , contentType : "application/json; charset=utf-8" , version : jsonHolder.Version, directory : mDirectory); return(PersistenceResponseFormat(result)); }
protected virtual async Task ProcessDelete(PersistenceRequestHolder <K, Tuple <K, string> > holder) { //We presume that the delete will succeed and remove it from the cache before it is processed. //Worse case this will result in a cache miss. if (mCacheManager.IsActive) { await mCacheManager.Delete(mTransform, holder.Rq.Key); } var result = await InternalDelete(holder.Rq.Key, holder); ProcessOutputKey(holder.Rq, holder.Rs, result); }
//Requests #region Create protected virtual async Task ProcessCreate(PersistenceRequestHolder <K, E> holder) { K key = mTransform.KeyMaker(holder.Rq.Entity); var result = await InternalCreate(key, holder); if (mCacheManager.IsActive && !mCacheManager.IsReadOnly && result.IsSuccess) { mCacheManager.Write(mTransform, result.Entity); } ProcessOutputEntity(key, holder.Rq, holder.Rs, result); }
protected async override Task <IResponseHolder <E> > InternalUpdate(K key, PersistenceRequestHolder <K, E> holder) { if (await mCacheManager.Write(mTransform, holder.Rq.Entity)) { return new PersistenceResponseHolder <E> { IsSuccess = true, StatusCode = 200, Entity = holder.Rq.Entity } } ; return(new PersistenceResponseHolder <E> { IsSuccess = false, StatusCode = 409 }); }
protected async override Task <IResponseHolder> InternalDelete(K key, PersistenceRequestHolder <K, Tuple <K, string> > holder) { if (await mCacheManager.Delete(mTransform, key)) { return new PersistenceResponseHolder <E> { IsSuccess = true, StatusCode = 200 } } ; return(new PersistenceResponseHolder <E> { IsSuccess = false, StatusCode = 404 }); }
/// <summary> /// This method marks the incoming request as complete. /// </summary> /// <typeparam name="KT">The key type.</typeparam> /// <typeparam name="ET">The entity type.</typeparam> /// <param name="holder">The request holder.</param> protected virtual void ProfileEnd <KT, ET>(PersistenceRequestHolder <KT, ET> holder) { mPolicy.ResourceConsumer?.End(holder.ProfileId, holder.Start, holder.result ?? ResourceRequestResult.Unknown); IPersistenceRequestHolder ok; mRequestsCurrent.TryRemove(holder.ProfileId, out ok); PersistenceHandler handler; if (SupportedResolve(holder.Prq.Message.ToServiceMessageHeader(), out handler)) { handler.Record(holder); } }
/// <summary> /// The internal version command. /// </summary> /// <param name="key">The key.</param> /// <param name="holder">The request holder.</param> /// <returns></returns> protected override async Task <IResponseHolder> InternalVersion(K key, PersistenceRequestHolder <K, Tuple <K, string> > holder) { E entity; bool success = Container.TryGetValue(key, out entity); if (success) { JsonHolder <K> jsonHolder = mTransform.JsonMaker(entity); return(new PersistenceResponseHolder(PersistenceResponse.Ok200, jsonHolder.Json)); } else { return(new PersistenceResponseHolder(PersistenceResponse.NotFound404)); } }
/// <summary> /// This is the read implementation. /// </summary> /// <param name="key">The key.</param> /// <param name="holder">The request holder.</param> /// <returns></returns> protected override async Task <IResponseHolder <E> > InternalRead(K key, PersistenceRequestHolder <K, E> holder) { E entity; bool success = Container.TryGetValue(key, out entity); if (success) { JsonHolder <K> jsonHolder = mTransform.JsonMaker(entity); return(new PersistenceResponseHolder <E>(PersistenceResponse.Ok200, jsonHolder.Json, mTransform.PersistenceEntitySerializer.Deserializer(jsonHolder.Json))); } else { return(new PersistenceResponseHolder <E>(PersistenceResponse.NotFound404)); } }
/// <summary> /// This is the create override for the command. /// </summary> /// <param name="key">The key.</param> /// <param name="holder">The entity holder.</param> /// <returns>Returns the response encapsulated in a PersistenceResponseHolder object.</returns> protected override async Task <IResponseHolder <E> > InternalCreate(K key, PersistenceRequestHolder <K, E> holder) { E entity = holder.Rq.Entity; int response = EntityPopulate(key, entity); if (response == 201) { JsonHolder <K> jsonHolder = mTransform.JsonMaker(entity); return(new PersistenceResponseHolder <E>(PersistenceResponse.Created201, jsonHolder.Json, mTransform.PersistenceEntitySerializer.Deserializer(jsonHolder.Json))); } else { return(new PersistenceResponseHolder <E>(PersistenceResponse.Conflict409)); } }
/// <summary> /// This is the entity search. /// </summary> /// <param name="holder">The is the entity search data.</param> /// <returns>This is an async task.</returns> protected virtual async Task ProcessSearch(PersistenceRequestHolder <SearchRequest, SearchResponse> holder) { IResponseHolder <SearchResponse> result = null; result = await InternalSearch(holder.Rq.Key, holder); holder.Rs.Entity = result.Entity; holder.Rs.Key = holder.Rq.Key; //rs.Settings.VersionId = mTransform.Version?.EntityVersionAsString(rs.Entity); //rs.KeyReference = new Tuple<string, string>(rs.Key.ToString(), rs.Settings.VersionId); //holder.Rs.ResponseCode = (int)PersistenceResponse.NotImplemented501; //holder.Rs.ResponseMessage = "Search is not implemented."; }
//Profiling #region ProfileStart/ProfileEnd/ProfileRetry /// <summary> /// This method starts the request profile and creates the request holder with the profile id. /// </summary> /// <typeparam name="KT">The key type.</typeparam> /// <typeparam name="ET">The entity type.</typeparam> /// <param name="prq">The incoming request.</param> /// <param name="prs">The outgoing response.</param> /// <returns>Returns the new request holder.</returns> protected virtual PersistenceRequestHolder <KT, ET> ProfileStart <KT, ET>(TransmissionPayload prq, List <TransmissionPayload> prs) { Guid profileId; if (mPolicy.ResourceConsumer == null) { profileId = Guid.NewGuid(); } else { profileId = mPolicy.ResourceConsumer.Start(prq.Message.ToKey(), prq.Id); } var holder = new PersistenceRequestHolder <KT, ET>(profileId, prq, prs); mRequestsCurrent.TryAdd(holder.ProfileId, holder); return(holder); }
protected virtual async Task ProcessUpdate(PersistenceRequestHolder <K, E> holder) { K key = mTransform.KeyMaker(holder.Rq.Entity); // Remove from the cache first to ensure no change of ending up with a stale cached item // if the write to cache fails for any reason if (mCacheManager.IsActive) { mCacheManager.Delete(mTransform, key); } var result = await InternalUpdate(key, holder); if (mCacheManager.IsActive && result.IsSuccess) { mCacheManager.Write(mTransform, result.Entity); } ProcessOutputEntity(key, holder.Rq, holder.Rs, result); }
/// <summary> /// The internal update command. /// </summary> /// <param name="key">The key.</param> /// <param name="holder">The request holder.</param> /// <returns></returns> protected override async Task <IResponseHolder <E> > InternalUpdate(K key, PersistenceRequestHolder <K, E> holder) { E oldEntity; bool successExisting = Container.TryGetValue(key, out oldEntity); if (!successExisting) { return(new PersistenceResponseHolder <E>(PersistenceResponse.NotFound404)); } E newEntity = holder.Rq.Entity; var ver = mTransform.Version; if (ver.SupportsOptimisticLocking) { if (ver.EntityVersionAsString(oldEntity) != ver.EntityVersionAsString(newEntity)) { return(new PersistenceResponseHolder <E>(PersistenceResponse.PreconditionFailed412)); } } if (ver.SupportsVersioning) { ver.EntityVersionUpdate(newEntity); } Container.Update(key, newEntity, mTransform.ReferenceMaker(newEntity)); var jsonHolder = mTransform.JsonMaker(newEntity); return(new PersistenceResponseHolder <E>(PersistenceResponse.Ok200) { Content = jsonHolder.Json , IsSuccess = true , Entity = newEntity }); }
protected virtual async Task ProcessVersionByRef(PersistenceRequestHolder <K, Tuple <K, string> > holder) { IResponseHolder result = null; if (mCacheManager.IsActive) { result = await mCacheManager.VersionRead(mTransform, holder.Rq.KeyReference); } if (result == null || !result.IsSuccess) { result = await InternalVersionByRef(holder.Rq.KeyReference, holder); if (mCacheManager.IsActive && !mCacheManager.IsReadOnly && result.IsSuccess) { mCacheManager.WriteReference(mTransform, holder.Rq.KeyReference, holder.Rq.Key, result.VersionId); } } else { holder.Rq.Key = mTransform.KeyDeserializer(result.Id); // Pass back the entities actual id in the key field } ProcessOutputKey(holder.Rq, holder.Rs, result); }
/// <summary> /// The internal search command. /// </summary> /// <param name="key">The search request.</param> /// <param name="holder">The request holder.</param> /// <returns></returns> protected async override Task <IResponseHolder <SearchResponse> > InternalSearch(SearchRequest key, PersistenceRequestHolder <SearchRequest, SearchResponse> holder) { var query = Container.Values.AsQueryable <E>(); Expression expression = mTransform.SearchTranslator.Build(key); //Execute Expression on Query bool success = true; //for the time being since we are not executing any queries holder.Rs.Entity = new SearchResponse(); List <JObject> jObjects = new List <JObject>(); foreach (var source in query.ToList()) { jObjects.Add(JObject.FromObject(source)); } if (success) { var resultEntities = query.ToList(); holder.Rs.Entity.Data = jObjects; return(new PersistenceResponseHolder <SearchResponse>(PersistenceResponse.Ok200, null, holder.Rs.Entity)); } //holder.Rs. //key.Select ////Create the expression parameters //ParameterExpression num1 = Expression.Parameter(typeof(E), "num1"); //ParameterExpression num2 = Expression.Parameter(typeof(E), "num2"); ////Create the expression parameters //ParameterExpression[] parameters = new ParameterExpression[] { num1, num2 }; ////Create the expression body //BinaryExpression body = Expression.Add(num1, num2); ////Expression predicateBody = Expression.OrElse(e1, e2); ////Create the expression //Expression<Func<int, int, int>> expression = Expression.Lambda<Func<int, int, int>>(body, parameters); //// Compile the expression //Func<int, int, int> compiledExpression = expression.Compile(); //// Execute the expression. //int result = compiledExpression(3, 4); return(await base.InternalSearch(key, holder)); }
protected async override Task <IResponseHolder <E> > InternalRead(K key, PersistenceRequestHolder <K, E> holder) { return(await mCacheManager.Read(mTransform, key)); }
protected async override Task <IResponseHolder> InternalVersionByRef(Tuple <string, string> reference, PersistenceRequestHolder <K, Tuple <K, string> > holder) { return(await mCacheManager.VersionRead(mTransform, reference)); }