public IList <IQueuedEvent> BatchEvents(IList <IQueuedEvent> eventItems) { List <IQueuedEvent> outputEvents; if (eventItems.Count == 0) { outputEvents = new List <IQueuedEvent>(0); return(outputEvents); } if (eventItems.Count == 1) { outputEvents = new List <IQueuedEvent>(1); IQueuedEvent soleEvent = eventItems[0]; outputEvents.Add(soleEvent); return(outputEvents); } outputEvents = new List <IQueuedEvent>(); IList <IQueuedEvent> currentBatchableEvents = new List <IQueuedEvent>(); IList <IQueuedEvent> batchedEvents; IEventBatcher currentEventBatcher = null; for (int i = 0, size = eventItems.Count; i < size; i++) { IQueuedEvent queuedEvent = eventItems[i]; Object eventObject = queuedEvent.EventObject; IEventBatcher eventBatcher = typeToBatchersDict.GetExtension(eventObject.GetType()); if (eventBatcher != null) { if (currentEventBatcher != null && !eventBatcher.Equals(currentEventBatcher)) { batchedEvents = BatchEventsIntern(currentBatchableEvents, currentEventBatcher); if (batchedEvents != null) { outputEvents.AddRange(batchedEvents); } currentBatchableEvents.Clear(); } currentEventBatcher = eventBatcher; currentBatchableEvents.Add(queuedEvent); continue; } batchedEvents = BatchEventsIntern(currentBatchableEvents, currentEventBatcher); if (batchedEvents != null) { outputEvents.AddRange(batchedEvents); } currentBatchableEvents.Clear(); currentEventBatcher = null; outputEvents.Add(queuedEvent); } batchedEvents = BatchEventsIntern(currentBatchableEvents, currentEventBatcher); if (batchedEvents != null) { outputEvents.AddRange(batchedEvents); } return(outputEvents); }
public void FlushEventQueue() { IList <IList <IQueuedEvent> > eventQueueList = eventQueueTL.Value; if (eventQueueList == null) { return; } IList <IQueuedEvent> eventQueue = eventQueueList[eventQueueList.Count - 1]; eventQueueList.RemoveAt(eventQueueList.Count - 1); if (eventQueueList.Count == 0) { eventQueueTL.Value = null; eventQueueList = null; } if (eventQueueList != null) { // Current flush is not the top-most flush. So we have to re-insert the events // One level out of our current level. We maintain the order at which the events have been queued IList <IQueuedEvent> outerEventQueue = eventQueueList[eventQueueList.Count - 1]; for (int a = 0, size = eventQueue.Count; a < size; a++) { IQueuedEvent queuedEvent = eventQueue[a]; outerEventQueue.Add(queuedEvent); } return; } IList <IQueuedEvent> batchedEvents = BatchEvents(eventQueue); for (int a = 0, size = batchedEvents.Count; a < size; a++) { IQueuedEvent batchedEvent = batchedEvents[a]; HandleEvent(batchedEvent.EventObject, batchedEvent.DispatchTime, batchedEvent.SequenceNumber); } }
protected void BatchEventsIntern(IList <IQueuedEvent> batchableEvents, IList <IQueuedEvent> targetBatchedEvents) { if (batchableEvents.Count == 1) { targetBatchedEvents.Add(batchableEvents[0]); return; } if (batchableEvents.Count == 0) { return; } // Check if all datachanges are in the same localsource state so that we can group them IDictionary <IObjRef, IObjRef> touchedObjRefSet = new Dictionary <IObjRef, IObjRef>(); ISet <IObjRef> touchedAsInsertObjRefDict = new HashSet <IObjRef>(); ISet <IObjRef> touchedAsUpdateObjRefDict = new HashSet <IObjRef>(); ISet <IObjRef> touchedAsDeleteObjRefDict = new HashSet <IObjRef>(); int splitIndex = -1; DateTime?lastDCETime = null, lastQueuedEventTime = null; long lastSequenceNumber = -1; bool? isLocalSource = null; for (int a = 0, size = batchableEvents.Count; a < size; a++) { IQueuedEvent batchableEvent = batchableEvents[a]; IDataChange dataChange = (IDataChange)batchableEvent.EventObject; if (isLocalSource.HasValue && isLocalSource.Value != dataChange.IsLocalSource) { // IsLocalSource differs, we can not batch the events here splitIndex = a; break; } isLocalSource = dataChange.IsLocalSource; lastQueuedEventTime = batchableEvent.DispatchTime; lastDCETime = dataChange.ChangeTime; lastSequenceNumber = batchableEvent.SequenceNumber; IList <IDataChangeEntry> insertsOfItem = dataChange.Inserts; IList <IDataChangeEntry> updatesOfItem = dataChange.Updates; IList <IDataChangeEntry> deletesOfItem = dataChange.Deletes; foreach (IDataChangeEntry insertOfItem in insertsOfItem) { if (insertOfItem.EntityType == null) { continue; } IObjRef objRef = ExtractAndMergeObjRef(insertOfItem, touchedObjRefSet); touchedAsInsertObjRefDict.Add(objRef); } foreach (IDataChangeEntry updateOfItem in updatesOfItem) { if (updateOfItem.EntityType == null) { continue; } IObjRef objRef = ExtractAndMergeObjRef(updateOfItem, touchedObjRefSet); if (touchedAsInsertObjRefDict.Contains(objRef)) { // Object is still seen as new in this batch sequence // So we ignore the update event for this item. The ObjRef already has the updated version continue; } touchedAsUpdateObjRefDict.Add(objRef); } foreach (IDataChangeEntry deleteOfItem in deletesOfItem) { if (deleteOfItem.EntityType == null) { continue; } IObjRef objRef = ExtractAndMergeObjRef(deleteOfItem, touchedObjRefSet); // Object can be removed from the queue because it has been updated AND deleted within the same batched sequence // From the entities point of view there is nothing we are interested in touchedAsUpdateObjRefDict.Remove(objRef); if (!touchedAsInsertObjRefDict.Remove(objRef)) { // Object will only be stored as deleted if it existed BEFORE the batched sequence touchedAsDeleteObjRefDict.Add(objRef); } } } if (splitIndex != -1 && splitIndex < batchableEvents.Count - 1) { // Cleanup garbage touchedAsInsertObjRefDict.Clear(); touchedAsUpdateObjRefDict.Clear(); touchedAsDeleteObjRefDict.Clear(); touchedObjRefSet.Clear(); SplitDataChangeBatch(batchableEvents, splitIndex, targetBatchedEvents); } else { IList <IDataChangeEntry> inserts = touchedAsInsertObjRefDict.Count > 0 ? new List <IDataChangeEntry>(touchedAsInsertObjRefDict.Count) : EmptyList.Empty <IDataChangeEntry>(); IList <IDataChangeEntry> updates = touchedAsUpdateObjRefDict.Count > 0 ? new List <IDataChangeEntry>(touchedAsUpdateObjRefDict.Count) : EmptyList.Empty <IDataChangeEntry>(); IList <IDataChangeEntry> deletes = touchedAsDeleteObjRefDict.Count > 0 ? new List <IDataChangeEntry>(touchedAsDeleteObjRefDict.Count) : EmptyList.Empty <IDataChangeEntry>(); foreach (IObjRef objRef in touchedAsInsertObjRefDict) { inserts.Add(new DataChangeEntry(objRef.RealType, objRef.IdNameIndex, objRef.Id, objRef.Version)); } foreach (IObjRef objRef in touchedAsUpdateObjRefDict) { updates.Add(new DataChangeEntry(objRef.RealType, objRef.IdNameIndex, objRef.Id, objRef.Version)); } foreach (IObjRef objRef in touchedAsDeleteObjRefDict) { deletes.Add(new DataChangeEntry(objRef.RealType, objRef.IdNameIndex, objRef.Id, objRef.Version)); } DataChangeEvent compositeDataChange = new DataChangeEvent(inserts, updates, deletes, lastDCETime.Value, isLocalSource.Value); targetBatchedEvents.Add(new QueuedEvent(compositeDataChange, lastQueuedEventTime.Value, lastSequenceNumber)); } }