55using System ;
66using System . Collections . Generic ;
77using System . Linq ;
8+ using System . Runtime . CompilerServices ;
89using System . Runtime . Serialization ;
910using Microsoft . StreamProcessing . Internal ;
1011using 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