11using Microsoft . Extensions . DependencyInjection ;
2- using System . Diagnostics ;
32
43namespace DurableStateMachines . Tests ;
54
@@ -8,11 +7,18 @@ public class DurableRingBufferCollectionTests(TestFixture fixture)
87{
98 public interface IDurableRingBufferCollectionGrain : IGrainWithStringKey
109 {
10+ // Workaround methods to survive deactivation
11+ Task < Dictionary < string , int > > GetAllCapacities ( ) ;
12+ Task SetAllCapacities ( Dictionary < string , int > capacities ) ;
13+
14+ // Collection methods
1115 Task < int > GetBuffersCount ( ) ;
1216 Task < List < string > > GetKeys ( ) ;
1317 Task < bool > ContainsBuffer ( string key ) ;
1418 Task < bool > RemoveBuffer ( string key ) ;
1519 Task ClearAll ( ) ;
20+
21+ // Buffer methods
1622 Task Enqueue ( string key , string value ) ;
1723 Task < TryValue < string ? > > TryDequeue ( string key ) ;
1824 Task SetBufferCapacity ( string key , int capacity ) ;
@@ -27,6 +33,25 @@ public class DurableRingBufferCollectionGrain(
2733 : DurableGrain , IDurableRingBufferCollectionGrain
2834 {
2935 private const int DefaultCapacity = 1 ;
36+ private readonly Dictionary < string , int > _capacities = [ ] ;
37+
38+ private IDurableRingBuffer < string > GetBuffer ( string key )
39+ {
40+ var capacity = _capacities . GetValueOrDefault ( key , DefaultCapacity ) ;
41+ return state . EnsureBuffer ( key , capacity ) ;
42+ }
43+
44+ public Task < Dictionary < string , int > > GetAllCapacities ( ) => Task . FromResult ( new Dictionary < string , int > ( _capacities ) ) ;
45+
46+ public Task SetAllCapacities ( Dictionary < string , int > capacities )
47+ {
48+ _capacities . Clear ( ) ;
49+ foreach ( var ( key , value ) in capacities )
50+ {
51+ _capacities [ key ] = value ;
52+ }
53+ return Task . CompletedTask ;
54+ }
3055
3156 public Task < int > GetBuffersCount ( ) => Task . FromResult ( state . Count ) ;
3257 public Task < List < string > > GetKeys ( ) => Task . FromResult ( state . Keys . ToList ( ) ) ;
@@ -37,26 +62,29 @@ public async Task<bool> RemoveBuffer(string key)
3762 var removed = state . Remove ( key ) ;
3863 if ( removed )
3964 {
65+ _capacities . Remove ( key ) ;
4066 await WriteStateAsync ( ) ;
4167 }
4268 return removed ;
4369 }
4470
4571 public async Task ClearAll ( )
4672 {
73+ _capacities . Clear ( ) ;
4774 state . Clear ( ) ;
75+
4876 await WriteStateAsync ( ) ;
4977 }
5078
5179 public async Task Enqueue ( string key , string value )
5280 {
53- state . GetOrCreate ( key , DefaultCapacity ) . Enqueue ( value ) ;
81+ GetBuffer ( key ) . Enqueue ( value ) ;
5482 await WriteStateAsync ( ) ;
5583 }
5684
5785 public async Task < TryValue < string ? > > TryDequeue ( string key )
5886 {
59- var success = state . GetOrCreate ( key , DefaultCapacity ) . TryDequeue ( out var item ) ;
87+ var success = GetBuffer ( key ) . TryDequeue ( out var item ) ;
6088 if ( success )
6189 {
6290 await WriteStateAsync ( ) ;
@@ -66,19 +94,20 @@ public async Task Enqueue(string key, string value)
6694
6795 public async Task SetBufferCapacity ( string key , int capacity )
6896 {
69- state . GetOrCreate ( key , DefaultCapacity ) . SetCapacity ( capacity ) ;
97+ _capacities [ key ] = capacity ;
98+ GetBuffer ( key ) ; // This will use the now just set '_capacities[key]', as it does EnsureBuffer(key, capacity).
7099 await WriteStateAsync ( ) ;
71100 }
72101
73102 public async Task ClearBuffer ( string key )
74103 {
75- state . GetOrCreate ( key , DefaultCapacity ) . Clear ( ) ;
104+ GetBuffer ( key ) . Clear ( ) ;
76105 await WriteStateAsync ( ) ;
77106 }
78107
79- public Task < int > GetBufferCapacity ( string key ) => Task . FromResult ( state . GetOrCreate ( key , DefaultCapacity ) . Capacity ) ;
80- public Task < int > GetBufferItemsCount ( string key ) => Task . FromResult ( state . GetOrCreate ( key , DefaultCapacity ) . Count ) ;
81- public Task < List < string > > GetAllBufferItems ( string key ) => Task . FromResult ( state . GetOrCreate ( key , DefaultCapacity ) . ToList ( ) ) ;
108+ public Task < int > GetBufferCapacity ( string key ) => Task . FromResult ( GetBuffer ( key ) . Capacity ) ;
109+ public Task < int > GetBufferItemsCount ( string key ) => Task . FromResult ( GetBuffer ( key ) . Count ) ;
110+ public Task < List < string > > GetAllBufferItems ( string key ) => Task . FromResult ( GetBuffer ( key ) . ToList ( ) ) ;
82111 }
83112
84113 private IDurableRingBufferCollectionGrain GetGrain ( string key ) => fixture . Cluster . Client . GetGrain < IDurableRingBufferCollectionGrain > ( key ) ;
@@ -119,15 +148,15 @@ public async Task GetOrCreate()
119148 const string keyA = "BufferA" ;
120149
121150 // We create a buffer implicitly by setting its capacity.
122- // This calls GetOrCreate(keyA, DefaultCapacity) which creates the buffer and sets capacity to 10.
123151 await grain . SetBufferCapacity ( keyA , 10 ) ;
124152 Assert . Equal ( 10 , await grain . GetBufferCapacity ( keyA ) ) ;
125153
126- // Than we enqueue an item. This calls GetOrCreate(keyA, DefaultCapacity).
127- // Because the buffer already exists, the capacity parameter should be ignored.
154+ // Than we enqueue an item. This calls GetBuffer(keyA).
155+ // Because the grain now remembers the capacity is 10, it will be used,
156+ // and the durable state's capacity will not be changed.
128157 await grain . Enqueue ( keyA , "item1" ) ;
129158
130- // The capacity should have NOT changed to the default .
159+ // The capacity should have NOT changed.
131160 Assert . Equal ( 10 , await grain . GetBufferCapacity ( keyA ) ) ;
132161 Assert . Equal ( 1 , await grain . GetBufferItemsCount ( keyA ) ) ;
133162
@@ -188,7 +217,9 @@ public async Task Persistence()
188217 await grain . Enqueue ( KeyB , "b1" ) ;
189218 await grain . RemoveBuffer ( KeyC ) ;
190219
220+ var capacities1 = await grain . GetAllCapacities ( ) ;
191221 await DeactivateGrain ( grain ) ;
222+ await grain . SetAllCapacities ( capacities1 ) ;
192223
193224 Assert . Equal ( 2 , await grain . GetBuffersCount ( ) ) ;
194225 Assert . True ( await grain . ContainsBuffer ( KeyA ) ) ;
@@ -203,7 +234,9 @@ public async Task Persistence()
203234 await grain . TryDequeue ( KeyA ) ; // Should only remove item "a1" not "BufferA"
204235 await grain . RemoveBuffer ( KeyB ) ;
205236
237+ var capacities2 = await grain . GetAllCapacities ( ) ;
206238 await DeactivateGrain ( grain ) ;
239+ await grain . SetAllCapacities ( capacities2 ) ;
207240
208241 Assert . Equal ( 1 , await grain . GetBuffersCount ( ) ) ;
209242 Assert . True ( await grain . ContainsBuffer ( KeyA ) ) ;
@@ -228,7 +261,9 @@ public async Task Restore()
228261 await grain . Enqueue ( key , $ "item-{ i } -2") ;
229262 }
230263
264+ var capacities = await grain . GetAllCapacities ( ) ;
231265 await DeactivateGrain ( grain ) ; // To trigger a restore from the snapshot
266+ await grain . SetAllCapacities ( capacities ) ;
232267
233268 Assert . Equal ( numBuffers , await grain . GetBuffersCount ( ) ) ;
234269
0 commit comments