Пример #1
0
        protected override void PerformWork(ref List <ArraySegment <Message> > items, out string nextCursor, ref int totalCount, out object state)
        {
            var cursors = new List <Cursor>();

            lock (_lockObj)
            {
                items = new List <ArraySegment <Message> >(_cursors.Count);
                for (int i = 0; i < _cursors.Count; i++)
                {
                    Cursor cursor = Cursor.Clone(_cursors[i]);
                    cursors.Add(cursor);

                    MessageStoreResult <Message> storeResult = _cursorTopics[i].Store.GetMessages(cursor.Id, MaxMessages);
                    ulong next = storeResult.FirstMessageId + (ulong)storeResult.Messages.Count;

                    cursor.Id = next;

                    if (storeResult.Messages.Count > 0)
                    {
                        items.Add(storeResult.Messages);
                        totalCount += storeResult.Messages.Count;
                    }
                }

                nextCursor = Cursor.MakeCursor(cursors);

                // Return the state as a list of cursors
                state = cursors;
            }
        }
Пример #2
0
 public override string GetCursor()
 {
     return(Cursor.MakeCursor(_cursors));
 }
Пример #3
0
            private void WorkImpl(ConcurrentDictionary <string, Topic> topics, TaskCompletionSource <object> taskCompletionSource)
            {
Process:
                int totalCount = 0;
                string nextCursor = null;
                List <ArraySegment <Message> > items = null;

                lock (_lockObj)
                {
                    items = new List <ArraySegment <Message> >(Cursors.Count);
                    for (int i = 0; i < Cursors.Count; i++)
                    {
                        Cursor cursor = Cursors[i];

                        MessageStoreResult <Message> storeResult = cursor.Topic.Store.GetMessages(cursor.Id, _maxMessages);
                        ulong next = storeResult.FirstMessageId + (ulong)storeResult.Messages.Count;
                        cursor.Id = next;

                        if (storeResult.Messages.Count > 0)
                        {
                            items.Add(storeResult.Messages);
                            totalCount += storeResult.Messages.Count;
                        }
                    }

                    nextCursor = Cursor.MakeCursor(Cursors);
                }

                if (Alive && items.Count > 0)
                {
                    var         messageResult = new MessageResult(items, nextCursor, totalCount);
                    Task <bool> callbackTask  = Invoke(messageResult);

                    if (callbackTask.IsCompleted)
                    {
                        try
                        {
                            // Make sure exceptions propagate
                            callbackTask.Wait();

                            if (callbackTask.Result)
                            {
                                // Sync path
                                goto Process;
                            }
                            else
                            {
                                // If the callback said it's done then stop
                                taskCompletionSource.TrySetResult(null);
                            }
                        }
                        catch (Exception ex)
                        {
                            taskCompletionSource.TrySetException(ex);
                        }
                    }
                    else
                    {
                        WorkImplAsync(callbackTask, topics, taskCompletionSource);
                    }
                }
                else
                {
                    taskCompletionSource.TrySetResult(null);
                }
            }
Пример #4
0
        protected override void PerformWork(ref List <ArraySegment <Message> > items, out string nextCursor, ref int totalCount, out object state)
        {
            // The list of cursors represent (streamid, payloadid)
            var cursors = new List <Cursor>();

            foreach (var stream in _streams)
            {
                // Get the mapping for this stream
                Linktionary <ulong, ScaleoutMapping> mapping = stream.Value;

                // See if we have a cursor for this key
                Cursor cursor = null;

                // REVIEW: We should optimize this
                int index = _cursors.FindIndex(c => c.Key == stream.Key);

                bool consumed = true;

                if (index != -1)
                {
                    cursor = Cursor.Clone(_cursors[index]);

                    // If there's no node for this cursor id it's likely because we've
                    // had an app domain restart and the cursor position is now invalid.
                    if (mapping[cursor.Id] == null)
                    {
                        // Set it to the first id in this mapping
                        cursor.Id = stream.Value.MinKey;

                        // Mark this cursor as unconsumed
                        consumed = false;
                    }
                }
                else
                {
                    // Create a cursor and add it to the list.
                    // Point the Id to the first value
                    cursor = new Cursor
                    {
                        Id  = stream.Value.MinKey,
                        Key = stream.Key
                    };

                    consumed = false;
                }

                cursors.Add(cursor);

                // Try to find a local mapping for this payload
                LinkedListNode <KeyValuePair <ulong, ScaleoutMapping> > node = mapping[cursor.Id];

                // Skip this node only if this isn't a new cursor
                if (node != null && consumed)
                {
                    // Skip this node since we've already consumed it
                    node = node.Next;
                }

                while (node != null)
                {
                    KeyValuePair <ulong, ScaleoutMapping> pair = node.Value;

                    // Stop if we got more than max messages
                    if (totalCount >= MaxMessages)
                    {
                        break;
                    }

                    // It should be ok to lock here since groups aren't modified that often
                    lock (EventKeys)
                    {
                        // For each of the event keys we care about, extract all of the messages
                        // from the payload
                        foreach (var eventKey in EventKeys)
                        {
                            LocalEventKeyInfo info;
                            if (pair.Value.EventKeyMappings.TryGetValue(eventKey, out info))
                            {
                                int maxMessages = Math.Min(info.Count, MaxMessages);
                                MessageStoreResult <Message> storeResult = info.Store.GetMessages(info.MinLocal, maxMessages);

                                if (storeResult.Messages.Count > 0)
                                {
                                    items.Add(storeResult.Messages);
                                    totalCount += storeResult.Messages.Count;
                                }
                            }
                        }
                    }

                    // Update the cursor id
                    cursor.Id = pair.Key;
                    node      = node.Next;
                }
            }

            nextCursor = Cursor.MakeCursor(cursors);

            state = cursors;
        }
Пример #5
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="subscriber"></param>
        /// <param name="cursor"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        public IDisposable Subscribe(ISubscriber subscriber, string cursor, Func <MessageResult, Task <bool> > callback, int messageBufferSize)
        {
            IEnumerable <Cursor> cursors = null;

            if (cursor == null)
            {
                cursors = from key in subscriber.EventKeys
                          select new Cursor
                {
                    Key = key,
                    Id  = GetMessageId(key)
                };
            }
            else
            {
                cursors = Cursor.GetCursors(cursor);
            }

            var subscription = new Subscription(subscriber.Identity, cursors, callback, messageBufferSize);
            var topics       = new HashSet <Topic>();

            foreach (var key in subscriber.EventKeys)
            {
                Topic topic = _topics.GetOrAdd(key, _ => new Topic());

                // Set the subscription for this topic
                subscription.SetCursorTopic(key, topic);

                // Add it to the list of topics
                topics.Add(topic);
            }

            foreach (var topic in topics)
            {
                topic.AddSubscription(subscription);
            }

            if (!String.IsNullOrEmpty(cursor))
            {
                // Update all of the cursors so we're within the range
                foreach (var pair in subscription.Cursors)
                {
                    Topic topic;
                    if (_topics.TryGetValue(pair.Key, out topic) && pair.Id > topic.Store.GetMessageCount())
                    {
                        subscription.UpdateCursor(pair.Key, 0);
                    }
                }
            }

            Action <string, string> eventAdded = (eventKey, eventCursor) =>
            {
                Topic topic = _topics.GetOrAdd(eventKey, _ => new Topic());

                // Get the cursor for this event key
                ulong id = eventCursor == null ? 0 : UInt64.Parse(eventCursor);

                // Add or update the cursor (in case it already exists)
                subscription.AddOrUpdateCursor(eventKey, id, topic);

                // Add it to the list of subs
                topic.AddSubscription(subscription);
            };

            Action <string> eventRemoved = eventKey => RemoveEvent(subscription, eventKey);

            subscriber.EventAdded   += eventAdded;
            subscriber.EventRemoved += eventRemoved;

            // If there's a cursor then schedule work for this subscription
            if (!String.IsNullOrEmpty(cursor))
            {
                _engine.Schedule(subscription);
            }

            return(new DisposableAction(() =>
            {
                // This will stop work from continuting to happen
                subscription.Dispose();

                subscriber.EventAdded -= eventAdded;
                subscriber.EventRemoved -= eventRemoved;

                string currentCursor = Cursor.MakeCursor(subscription.Cursors);

                foreach (var eventKey in subscriber.EventKeys)
                {
                    RemoveEvent(subscription, eventKey);
                }

                subscription.Invoke(new MessageResult(currentCursor));
            }));
        }