private async void WaitForReplicationFromServerAsync(string url, AsyncCountdownEvent countDown, Etag etag, BlockingCollection<Exception> errors) { try { while (countDown.Active) { var etags = await GetReplicatedEtagsFor(url); var replicated = etag.CompareTo(etags.DocumentEtag) <= 0 || etag.CompareTo(etags.AttachmentEtag) <= 0; if (!replicated) { if (countDown.Active) { #if NET45 await Task.Delay(100); #else await TaskEx.Delay(100); #endif } continue; } countDown.Signal(); return; } } catch (Exception ex) { errors.Add(ex); countDown.Signal(); } }
/// <summary> /// Represents an replication operation to all destination servers of an item specified by ETag /// </summary> /// <param name="etag">ETag of an replicated item</param> /// <param name="timeout">Optional timeout</param> /// <param name="database">The database from which to check, if null, the default database for the document store connection string</param> /// <param name="replicas">The min number of replicas that must have the value before we can return (or the number of destinations, if higher)</param> /// <returns>Task which will have the number of nodes that the caught up to the specified etag</returns> public async Task<int> WaitAsync(Etag etag = null, TimeSpan? timeout = null, string database = null, int replicas = 2) { etag = etag ?? documentStore.LastEtagHolder.GetLastWrittenEtag(); if (etag == Etag.Empty) return replicas; // if the etag is empty, nothing to do var asyncDatabaseCommands = documentStore.AsyncDatabaseCommands; if (database != null) asyncDatabaseCommands = asyncDatabaseCommands.ForDatabase(database); asyncDatabaseCommands.ForceReadFromMaster(); var doc = await asyncDatabaseCommands.GetAsync("Raven/Replication/Destinations"); if (doc == null) return -1; var replicationDocument = doc.DataAsJson.JsonDeserialization<ReplicationDocument>(); if (replicationDocument == null) return -1; var destinationsToCheck = replicationDocument.Destinations .Where( x => x.Disabled == false && x.IgnoredClient == false) .Select(x => x.ClientVisibleUrl ?? x.Url) .ToList(); if (destinationsToCheck.Count == 0) return 0; int toCheck = Math.Min(replicas, destinationsToCheck.Count); var countDown = new AsyncCountdownEvent(toCheck); var errors = new BlockingCollection<Exception>(); foreach (var url in destinationsToCheck) { WaitForReplicationFromServerAsync(url, countDown, etag, errors); } if (await countDown.WaitAsync().WaitWithTimeout(timeout) == false) { throw new TimeoutException( string.Format("Confirmed that the specified etag {0} was replicated to {1} of {2} servers, during {3}", etag, (toCheck - countDown.Count), toCheck, timeout)); } if (errors.Count > 0 && countDown.Count > 0) throw new AggregateException(errors); return countDown.Count; }