protected internal Task <string> GenerateDocumentKeyForStorageAsync(object entity) { if (entity is IDynamicMetaObjectProvider) { string id; if (GenerateEntityIdOnTheClient.TryGetIdFromDynamic(entity, out id)) { return(CompletedTask.With(id)); } else { return(GenerateKeyAsync(entity) .ContinueWith(task => { // If we generated a new id, store it back into the Id field so the client has access to to it if (task.Result != null) { GenerateEntityIdOnTheClient.TrySetIdOnDynamic(entity, task.Result); } return task.Result; })); } } return(GetOrGenerateDocumentKeyAsync(entity) .ContinueWith(task => { GenerateEntityIdOnTheClient.TrySetIdentity(entity, task.Result); return task.Result; })); }
private void NotifySubscribers(RavenJObject curDoc, out long lastReceivedEtag) { T instance; var metadata = curDoc[Constants.Metadata.Key] as RavenJObject; lastReceivedEtag = metadata[Constants.Metadata.Etag].Value <long>(); if (_isStronglyTyped) { instance = curDoc.Deserialize <T>(_conventions); var docId = metadata[Constants.Metadata.Id].Value <string>(); if (string.IsNullOrEmpty(docId) == false) { _generateEntityIdOnTheClient.TrySetIdentity(instance, docId); } } else { instance = (T)(object)curDoc; } foreach (var subscriber in _subscribers) { _proccessingCts.Token.ThrowIfCancellationRequested(); try { subscriber.OnNext(instance); } catch (Exception ex) { Logger.WarnException( string.Format( "Subscription #{0}. Subscriber threw an exception", _options.SubscriptionId), ex); if (_options.IgnoreSubscribersErrors == false) { IsErroredBecauseOfSubscriber = true; LastSubscriberException = ex; try { subscriber.OnError(ex); } catch (Exception) { // can happen if a subscriber doesn't have an onError handler - just ignore it } break; } } } }
private string GetId(object entity) { string id; if (generateEntityIdOnTheClient.TryGetIdFromInstance(entity, out id) == false) { id = generateEntityIdOnTheClient.GenerateDocumentKeyForStorage(entity); generateEntityIdOnTheClient.TrySetIdentity(entity, id); //set Id property if it was null } return(id); }
private void StoreInternal(object entity, Guid?etag, string id, bool forceConcurrencyCheck) { if (null == entity) { throw new ArgumentNullException("entity"); } DocumentMetadata value; if (entitiesAndMetadata.TryGetValue(entity, out value)) { value.ETag = etag ?? value.ETag; value.ForceConcurrencyCheck = forceConcurrencyCheck; return; } if (id == null) { if (GenerateDocumentKeysOnStore) { id = GenerateEntityIdOnTheClient.GenerateDocumentKeyForStorage(entity); } else { RememberEntityForDocumentKeyGeneration(entity); } } else { // Store it back into the Id field so the client has access to to it GenerateEntityIdOnTheClient.TrySetIdentity(entity, id); } // we make the check here even if we just generated the key // users can override the key generation behavior, and we need // to detect if they generate duplicates. AssertNoNonUniqueInstance(entity, id); var metadata = new RavenJObject(); var tag = documentStore.Conventions.GetTypeTagName(entity.GetType()); if (tag != null) { metadata.Add(Constants.RavenEntityName, tag); } if (id != null) { knownMissingIds.Remove(id); } StoreEntityInUnitOfWork(id, entity, etag, metadata, forceConcurrencyCheck); }
private string GetId(object entity) { string id; if (generateEntityIdOnTheClient.TryGetIdFromInstance(entity, out id)) { id = generateEntityIdOnTheClient.GenerateDocumentKeyForStorage(entity); } else { id = generateEntityIdOnTheClient.GenerateDocumentKeyForStorage(entity); generateEntityIdOnTheClient.TrySetIdentity(entity, id); } return(id); }
/// <summary> /// Updates the batch results. /// </summary> protected void UpdateBatchResults(IList <BatchResult> batchResults, SaveChangesData saveChangesData) { for (var i = saveChangesData.DeferredCommandsCount; i < batchResults.Count; i++) { var batchResult = batchResults[i]; if (batchResult.Method != "PUT") { continue; } var entity = saveChangesData.Entities[i - saveChangesData.DeferredCommandsCount]; DocumentMetadata documentMetadata; if (entitiesAndMetadata.TryGetValue(entity, out documentMetadata) == false) { continue; } batchResult.Metadata["@etag"] = new RavenJValue(batchResult.Etag.ToString()); entitiesByKey[batchResult.Key] = entity; documentMetadata.ETag = batchResult.Etag; documentMetadata.Key = batchResult.Key; documentMetadata.OriginalMetadata = (RavenJObject)batchResult.Metadata.CloneToken(); documentMetadata.Metadata = batchResult.Metadata; documentMetadata.OriginalValue = EntityToJson.ConvertEntityToJson(documentMetadata.Key, entity, documentMetadata.Metadata); GenerateEntityIdOnTheClient.TrySetIdentity(entity, batchResult.Key); foreach (var documentStoreListener in listeners.StoreListeners) { documentStoreListener.AfterStore(batchResult.Key, entity, batchResult.Metadata); } } var lastPut = batchResults.LastOrDefault(x => x.Method == "PUT"); if (lastPut == null) { return; } documentStore.LastEtagHolder.UpdateLastWrittenEtag(lastPut.Etag); }
/// <summary> /// Converts the json document to an entity. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="id">The id.</param> /// <param name="documentFound">The document found.</param> /// <param name="metadata">The metadata.</param> /// <returns></returns> protected object ConvertToEntity <T>(string id, RavenJObject documentFound, RavenJObject metadata) { if (typeof(T) == typeof(RavenJObject)) { return((T)(object)documentFound.CloneToken()); } var entity = default(T); EnsureNotReadVetoed(metadata); var documentType = Conventions.GetClrType(id, documentFound, metadata); if (documentType != null) { var type = Type.GetType(documentType); if (type != null) { entity = (T)documentFound.Deserialize(type, Conventions); } } if (Equals(entity, default(T))) { entity = documentFound.Deserialize <T>(Conventions); var document = entity as RavenJObject; if (document != null) { entity = (T)(object)(new DynamicJsonObject(document)); } } GenerateEntityIdOnTheClient.TrySetIdentity(entity, id); foreach (var documentConversionListener in listeners.ConversionListeners) { documentConversionListener.DocumentToEntity(id, entity, documentFound, metadata); } return(entity); }
private Task PullDocuments() { return(Task.Run(async() => { try { Etag lastProcessedEtagOnClient = null; while (true) { anySubscriber.WaitOne(); cts.Token.ThrowIfCancellationRequested(); var pulledDocs = false; Etag lastProcessedEtagOnServer = null; int processedDocs = 0; var queue = new BlockingCollection <T>(); Task processingTask = null; using (var subscriptionRequest = CreatePullingRequest()) using (var response = await subscriptionRequest.ExecuteRawResponseAsync().ConfigureAwait(false)) { await response.AssertNotFailingResponse().ConfigureAwait(false); using (var responseStream = await response.GetResponseStreamWithHttpDecompression().ConfigureAwait(false)) { cts.Token.ThrowIfCancellationRequested(); using (var streamedDocs = new AsyncServerClient.YieldStreamResults(subscriptionRequest, responseStream, customizedEndResult: reader => { if (Equals("LastProcessedEtag", reader.Value) == false) { return false; } lastProcessedEtagOnServer = Etag.Parse(AsyncHelpers.RunSync(reader.ReadAsString)); return true; })) { while (await streamedDocs.MoveNextAsync().ConfigureAwait(false)) { if (pulledDocs == false) // first doc in batch { BeforeBatch(); processingTask = Task.Run(() => { T doc; while (queue.TryTake(out doc, Timeout.Infinite)) { cts.Token.ThrowIfCancellationRequested(); foreach (var subscriber in subscribers) { try { subscriber.OnNext(doc); } catch (Exception ex) { logger.WarnException(string.Format("Subscription #{0}. Subscriber threw an exception", id), ex); if (options.IgnoreSubscribersErrors == false) { IsErroredBecauseOfSubscriber = true; LastSubscriberException = ex; try { subscriber.OnError(ex); } catch (Exception) { // can happen if a subscriber doesn't have an onError handler - just ignore it } break; } } } if (IsErroredBecauseOfSubscriber) { break; } processedDocs++; } }); } pulledDocs = true; cts.Token.ThrowIfCancellationRequested(); var jsonDoc = streamedDocs.Current; if (isStronglyTyped) { var instance = jsonDoc.Deserialize <T>(conventions); var docId = jsonDoc[Constants.Metadata].Value <string>("@id"); if (string.IsNullOrEmpty(docId) == false) { generateEntityIdOnTheClient.TrySetIdentity(instance, docId); } queue.Add(instance); } else { queue.Add((T)(object)jsonDoc); } if (IsErroredBecauseOfSubscriber) { break; } } } } } queue.CompleteAdding(); if (processingTask != null) { await processingTask; } if (IsErroredBecauseOfSubscriber) { break; } if (lastProcessedEtagOnServer != null) { if (pulledDocs) { // This is an acknowledge when the server returns documents to the subscriber. if (BeforeAcknowledgment()) { AcknowledgeBatchToServer(lastProcessedEtagOnServer); AfterAcknowledgment(lastProcessedEtagOnServer); } AfterBatch(processedDocs); continue; // try to pull more documents from subscription } else { if (lastProcessedEtagOnClient != lastProcessedEtagOnServer) { // This is a silent acknowledge, this can happen because there was no documents in range // to be accessible in the time available. This condition can happen when documents must match // a set of conditions to be eligible. AcknowledgeBatchToServer(lastProcessedEtagOnServer); lastProcessedEtagOnClient = lastProcessedEtagOnServer; continue; // try to pull more documents from subscription } } } while (newDocuments.WaitOne(options.ClientAliveNotificationInterval) == false) { using (var clientAliveRequest = CreateClientAliveRequest()) { clientAliveRequest.ExecuteRequest(); } } } } catch (ErrorResponseException e) { SubscriptionException subscriptionException; if (AsyncDocumentSubscriptions.TryGetSubscriptionException(e, out subscriptionException)) { throw subscriptionException; } throw; } })); }
private Task PullDocuments() { return(Task.Run(async() => { while (true) { anySubscriber.WaitOne(); cts.Token.ThrowIfCancellationRequested(); var pulledDocs = false; Etag lastProcessedEtagOnServer = null; using (var subscriptionRequest = CreatePullingRequest()) using (var response = await subscriptionRequest.ExecuteRawResponseAsync().ConfigureAwait(false)) { await response.AssertNotFailingResponse().ConfigureAwait(false); using (var responseStream = await response.GetResponseStreamWithHttpDecompression().ConfigureAwait(false)) { cts.Token.ThrowIfCancellationRequested(); using (var streamedDocs = new AsyncServerClient.YieldStreamResults(subscriptionRequest, responseStream, customizedEndResult: reader => { if (Equals("LastProcessedEtag", reader.Value) == false) { return false; } lastProcessedEtagOnServer = Etag.Parse(reader.ReadAsString().ResultUnwrap()); return true; })) { while (await streamedDocs.MoveNextAsync().ConfigureAwait(false)) { if (pulledDocs == false) // first doc in batch { BeforeBatch(); } pulledDocs = true; cts.Token.ThrowIfCancellationRequested(); var jsonDoc = streamedDocs.Current; T instance = null; foreach (var subscriber in subscribers) { try { if (isStronglyTyped) { if (instance == null) { instance = jsonDoc.Deserialize <T>(conventions); var docId = jsonDoc[Constants.Metadata].Value <string>("@id"); if (string.IsNullOrEmpty(docId) == false) { generateEntityIdOnTheClient.TrySetIdentity(instance, docId); } } subscriber.OnNext(instance); } else { subscriber.OnNext((T)(object)jsonDoc); } } catch (Exception ex) { logger.WarnException("Subscriber threw an exception", ex); if (options.IgnoreSubscribersErrors == false) { IsErrored = true; try { subscriber.OnError(ex); } catch (Exception) { // can happen if a subscriber doesn't have an onError handler - just ignore it } break; } } } if (IsErrored) { break; } } } } } if (IsErrored) { break; } if (pulledDocs) { using (var acknowledgmentRequest = CreateAcknowledgmentRequest(lastProcessedEtagOnServer)) { try { acknowledgmentRequest.ExecuteRequest(); } catch (Exception) { if (acknowledgmentRequest.ResponseStatusCode != HttpStatusCode.RequestTimeout) // ignore acknowledgment timeouts { throw; } } } AfterBatch(); continue; // try to pull more documents from subscription } while (newDocuments.WaitOne(options.ClientAliveNotificationInterval) == false) { using (var clientAliveRequest = CreateClientAliveRequest()) { clientAliveRequest.ExecuteRequest(); } } } })); }