public static Cursor Clone(Cursor cursor) { return new Cursor { Id = cursor.Id, Key = cursor.Key }; }
public static Cursor[] GetCursors(string cursor) { var cursors = new List<Cursor>(); var current = new Cursor(); bool escape = false; var sb = new StringBuilder(); foreach (var ch in cursor) { if (escape) { sb.Append(ch); escape = false; } else { if (ch == '\\') { escape = true; } else if (ch == ',') { current.Key = sb.ToString(); sb.Clear(); } else if (ch == '|') { current.Id = UInt64.Parse(sb.ToString(), NumberStyles.HexNumber); cursors.Add(current); current = new Cursor(); sb.Clear(); } else { sb.Append(ch); } } } if (sb.Length > 0) { current.Id = UInt64.Parse(sb.ToString(), NumberStyles.HexNumber); cursors.Add(current); } return cursors.ToArray(); }
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; }