Skip to content

Commit bc9338f

Browse files
committed
Refactor SessionWindowPipe to use Queue instead of LinkedList
1 parent b3d6b9b commit bc9338f

1 file changed

Lines changed: 41 additions & 18 deletions

File tree

Sources/Core/Microsoft.StreamProcessing/Operators/SessionWindow/SessionWindowPipe.cs

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System;
66
using System.Collections.Generic;
77
using System.Linq;
8+
using System.Runtime.CompilerServices;
89
using System.Runtime.Serialization;
910
using Microsoft.StreamProcessing.Internal;
1011
using Microsoft.StreamProcessing.Internal.Collections;
@@ -25,7 +26,7 @@ internal sealed class SessionWindowPipe<TKey, TPayload> : UnaryPipe<TKey, TPaylo
2526
[DataMember]
2627
private StreamMessage<TKey, TPayload> output;
2728

28-
private LinkedList<TKey> orderedKeys = new LinkedList<TKey>();
29+
private Queue<SessionThreshold> orderedKeys = new Queue<SessionThreshold>();
2930
[DataMember]
3031
private FastDictionary2<TKey, long> windowEndTimeDictionary = new FastDictionary2<TKey, long>();
3132
[DataMember]
@@ -68,13 +69,21 @@ private void ReachTime(int pIndex, long timestamp)
6869
}
6970
}
7071

71-
var current = this.orderedKeys.First;
72-
while (current != null)
72+
while (this.orderedKeys.Count > 0)
7373
{
74-
this.lastDataTimeDictionary.Lookup(current.Value, out int cIndex);
74+
var current = this.orderedKeys.Peek();
75+
this.lastDataTimeDictionary.Lookup(current.Key, out int cIndex);
7576
var threshold = this.lastDataTimeDictionary.entries[cIndex].value == long.MinValue
7677
? this.windowEndTimeDictionary.entries[cIndex].value
77-
: Math.Min(this.lastDataTimeDictionary.entries[cIndex].value + this.sessionTimeout, this.windowEndTimeDictionary.entries[cIndex].value);
78+
: CalculateThreshold(this.lastDataTimeDictionary.entries[cIndex].value, cIndex);
79+
80+
// Skip stale threshold entries (can happen when threshold is updated and new entry is enqueued)
81+
if (current.Threshold != threshold)
82+
{
83+
this.orderedKeys.Dequeue();
84+
continue;
85+
}
86+
7887
if (timestamp >= threshold)
7988
{
8089
var queue = this.stateDictionary.entries[cIndex].value;
@@ -95,13 +104,12 @@ private void ReachTime(int pIndex, long timestamp)
95104
this.windowEndTimeDictionary.entries[cIndex].value = StreamEvent.MaxSyncTime;
96105
else
97106
{
98-
this.windowEndTimeDictionary.Remove(current.Value);
99-
this.lastDataTimeDictionary.Remove(current.Value);
100-
this.stateDictionary.Remove(current.Value);
107+
this.windowEndTimeDictionary.Remove(current.Key);
108+
this.lastDataTimeDictionary.Remove(current.Key);
109+
this.stateDictionary.Remove(current.Key);
101110
}
102111

103-
this.orderedKeys.RemoveFirst();
104-
current = this.orderedKeys.First;
112+
this.orderedKeys.Dequeue();
105113
}
106114
else break;
107115
}
@@ -142,17 +150,18 @@ public override unsafe void OnNext(StreamMessage<TKey, TPayload> batch)
142150
if (!this.lastDataTimeDictionary.Lookup(batch.key.col[i], out keyIndex))
143151
keyIndex = AllocatePartition(batch.key.col[i]);
144152

153+
var newThreshold = CalculateThreshold(vsync[i], keyIndex);
154+
145155
if (!this.stateDictionary.entries[keyIndex].value.Any())
146-
this.orderedKeys.AddLast(new LinkedListNode<TKey>(batch.key.col[i]));
156+
{
157+
this.orderedKeys.Enqueue(new SessionThreshold { Key = batch.key.col[i], Threshold = newThreshold });
158+
}
147159
else
148160
{
149-
var oldThreshold = Math.Min(this.lastDataTimeDictionary.entries[keyIndex].value + this.sessionTimeout, this.windowEndTimeDictionary.entries[keyIndex].value);
150-
var newThreshold = Math.Min(vsync[i] + this.sessionTimeout, this.windowEndTimeDictionary.entries[keyIndex].value);
161+
var oldThreshold = CalculateThreshold(this.lastDataTimeDictionary.entries[keyIndex].value, keyIndex);
151162
if (newThreshold > oldThreshold)
152163
{
153-
var node = this.orderedKeys.Find(batch.key.col[i]);
154-
this.orderedKeys.Remove(node);
155-
this.orderedKeys.AddLast(node);
164+
this.orderedKeys.Enqueue(new SessionThreshold { Key = batch.key.col[i], Threshold = newThreshold });
156165
}
157166
}
158167

@@ -227,10 +236,10 @@ protected override void UpdatePointers()
227236
{
228237
temp.Add(Tuple.Create(
229238
this.windowEndTimeDictionary.entries[iter].key,
230-
Math.Min(this.lastDataTimeDictionary.entries[iter].value + this.sessionTimeout, this.windowEndTimeDictionary.entries[iter].value)));
239+
CalculateThreshold(this.lastDataTimeDictionary.entries[iter].value, iter)));
231240
}
232241
}
233-
foreach (var item in temp.OrderBy(o => o.Item2)) this.orderedKeys.AddLast(new LinkedListNode<TKey>(item.Item1));
242+
foreach (var item in temp.OrderBy(o => o.Item2)) this.orderedKeys.Enqueue(new SessionThreshold { Key = item.Item1, Threshold = item.Item2 });
234243
base.UpdatePointers();
235244
}
236245

@@ -242,6 +251,10 @@ protected override void DisposeState()
242251
this.stateDictionary.Clear();
243252
}
244253

254+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
255+
private long CalculateThreshold(long lastDataTime, int keyIndex) =>
256+
Math.Min(lastDataTime + this.sessionTimeout, this.windowEndTimeDictionary.entries[keyIndex].value);
257+
245258
[DataContract]
246259
private struct ActiveEvent
247260
{
@@ -256,5 +269,15 @@ private struct ActiveEvent
256269

257270
public override string ToString() => "Key='" + this.Key + "', Payload='" + this.Payload;
258271
}
272+
273+
[DataContract]
274+
private struct SessionThreshold
275+
{
276+
[DataMember]
277+
public long Threshold;
278+
279+
[DataMember]
280+
public TKey Key;
281+
}
259282
}
260283
}

0 commit comments

Comments
 (0)