string RemoveFromLookup(Subscriber subscriber, MessageType typeName) { try { // note: ReaderWriterLockSlim has a thread affinity and cannot be used with await! rwLock.EnterWriteLock(); Dictionary <MessageType, string> subscriptions; if (lookup.TryGetValue(subscriber, out subscriptions)) { string messageId; if (subscriptions.TryGetValue(typeName, out messageId)) { subscriptions.Remove(typeName); if (subscriptions.Count == 0) { lookup.Remove(subscriber); } return(messageId); } } } finally { rwLock.ExitWriteLock(); } return(null); }
void AddToLookup(Subscriber subscriber, MessageType typeName, string messageId) { try { // note: ReaderWriterLockSlim has a thread affinity and cannot be used with await! rwLock.EnterWriteLock(); Dictionary <MessageType, string> dictionary; if (!lookup.TryGetValue(subscriber, out dictionary)) { dictionary = new Dictionary <MessageType, string>(); } else { // replace existing subscriber lookup.Remove(subscriber); } dictionary[typeName] = messageId; lookup[subscriber] = dictionary; } finally { rwLock.ExitWriteLock(); } }
/// <summary> /// Initializes a new instance of the <see cref="Subscription"/> class. /// </summary> /// <param name="messageType"> /// The message type. /// </param> /// <param name="clients"> /// The clients. /// </param> public Subscription(MessageType messageType, IEnumerable<Address> clients) { Contract.Requires<ArgumentNullException>(messageType != null, "messageType != null"); Contract.Requires<ArgumentNullException>(clients != null, "clients != null"); this.Id = FormatId(messageType); this.MessageType = messageType; this.Clients = clients.ToList(); }
public async Task Subscribe(Subscriber subscriber, MessageType messageType, ContextBag context) { //When the subscriber is running V6 and UseLegacyMessageDrivenSubscriptionMode is enabled at the subscriber the 'subcriber.Endpoint' value is null var endpoint = subscriber.Endpoint ?? subscriber.TransportAddress.Split('@').First(); var subscriptionClient = new SubscriptionClient { TransportAddress = subscriber.TransportAddress, Endpoint = endpoint }; var attempts = 0; //note: since we have a design that can run into concurrency exceptions we perform a few retries // we should redesign this in the future to use a separate doc per subscriber and message type do { try { using (var session = OpenAsyncSession()) { var subscriptionDocId = Subscription.FormatId(messageType); var subscription = await session.LoadAsync<Subscription>(subscriptionDocId).ConfigureAwait(false); if (subscription == null) { subscription = new Subscription { Id = subscriptionDocId, MessageType = messageType, Subscribers = new List<SubscriptionClient>() }; await session.StoreAsync(subscription).ConfigureAwait(false); } if (!subscription.Subscribers.Contains(subscriptionClient)) { subscription.Subscribers.Add(subscriptionClient); } else { var savedSubscription = subscription.Subscribers.Single(s => s.Equals(subscriptionClient)); if (savedSubscription.Endpoint != subscriber.Endpoint) { savedSubscription.Endpoint = subscriber.Endpoint; } } await session.SaveChangesAsync().ConfigureAwait(false); } return; } catch (ConcurrencyException) { attempts++; } } while (attempts < 5); }
public static IEnumerable<string> GetPipelineInfoFor(this AuditMessageReceived envelope,MessageType messageType) { var key = "NServiceBus.PipelineInfo." + messageType.TypeName; var result = new List<string>(); if(!envelope.HasHeader(key)) return result; return envelope.Headers[key].Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); }
public Task Subscribe(Subscriber subscriber, MessageType messageType, ContextBag context) { var body = $"{messageType.TypeName}, Version={messageType.Version}"; var label = Serialize(subscriber); var messageId = storageQueue.Send(body, label); AddToLookup(subscriber, messageType, messageId); log.DebugFormat($"Subscriber {subscriber.TransportAddress} added for message {messageType}."); return(TaskEx.CompletedTask); }
public static string FormatId(MessageType messageType) { // use MD5 hash to get a 16-byte hash of the string using (var provider = new MD5CryptoServiceProvider()) { var inputBytes = Encoding.Default.GetBytes(messageType.TypeName + "/" + messageType.Version.Major); var hashBytes = provider.ComputeHash(inputBytes); // generate a guid from the hash: var id = new Guid(hashBytes); return $"Subscriptions/{id}"; } }
public Task Unsubscribe(Subscriber subscriber, MessageType messageType, ContextBag context) { var messageId = RemoveFromLookup(subscriber, messageType); if (messageId != null) { storageQueue.TryReceiveById(messageId); } log.Debug($"Subscriber {subscriber.TransportAddress} removed for message {messageType}."); return(TaskEx.CompletedTask); }
public void Init() { var messages = storageQueue.GetAllMessages() .OrderByDescending(m => m.ArrivedTime) .ThenBy(x => x.Id) // ensure same order of messages with same timestamp accross all endpoints .ToArray(); try { rwLock.EnterWriteLock(); foreach (var m in messages) { var messageTypeString = m.Body as string; var messageType = new MessageType(messageTypeString); //this will parse both 2.6 and 3.0 type strings var subscriber = Deserialize(m.Label); Dictionary <MessageType, string> endpointSubscriptions; if (!lookup.TryGetValue(subscriber, out endpointSubscriptions)) { lookup[subscriber] = endpointSubscriptions = new Dictionary <MessageType, string>(); } if (endpointSubscriptions.ContainsKey(messageType)) { // this message is stale and can be removed storageQueue.TryReceiveById(m.Id); } else { endpointSubscriptions[messageType] = m.Id; } } } finally { rwLock.ExitWriteLock(); } }
public async Task Unsubscribe(Subscriber subscriber, MessageType messageType, ContextBag context) { var subscriptionClient = new SubscriptionClient { TransportAddress = subscriber.TransportAddress, Endpoint = subscriber.Endpoint }; using (var session = OpenAsyncSession()) { var subscriptionDocId = Subscription.FormatId(messageType); var subscription = await session.LoadAsync<Subscription>(subscriptionDocId).ConfigureAwait(false); if (subscription == null) { return; } if (subscription.Subscribers.Contains(subscriptionClient)) { subscription.Subscribers.Remove(subscriptionClient); } await session.SaveChangesAsync().ConfigureAwait(false); } }
/// <summary> /// Equality, only major version is used /// </summary> /// <param name="other"></param> /// <returns></returns> public bool Equals(MessageType other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; return Equals(other.TypeName, TypeName) && other.Version.Major == Version.Major; }
/// <summary> /// The format id. /// </summary> /// <param name="messageType"> /// The message type. /// </param> /// <returns> /// The <see cref="string"/>. /// </returns> public static string FormatId(MessageType messageType) { Contract.Requires<ArgumentNullException>(messageType != null, "messageType != null"); Contract.Ensures(Contract.Result<string>() != null); var id = DeterministicGuid.Create(messageType.TypeName, "/", messageType.Version.Major); return string.Format("Subscriptions/{0}", id); }
/// <summary> /// Equality, only major version is used. /// </summary> public bool Equals(MessageType other) { if (ReferenceEquals(null, other)) { return false; } if (ReferenceEquals(this, other)) { return true; } return Equals(other.TypeName, TypeName); }