Skip to content

Commit eccfa03

Browse files
ReubenBondCopilot
andcommitted
fix(messaging): preserve pooled callback lifecycle
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent 370f95d commit eccfa03

7 files changed

Lines changed: 51 additions & 11 deletions

File tree

src/Orleans.Core/Messaging/Message.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,10 @@ internal void AddToCacheInvalidationHeader(GrainAddress invalidAddress, GrainAdd
296296
// Another thread initialized it, add to the existing list
297297
lock (_cacheInvalidationHeader)
298298
{
299-
_cacheInvalidationHeader.Add(grainAddressCacheUpdate);
299+
if (_cacheInvalidationHeader.Count < MaxCacheInvalidationHeaderEntries)
300+
{
301+
_cacheInvalidationHeader.Add(grainAddressCacheUpdate);
302+
}
300303
}
301304
}
302305
else
@@ -308,7 +311,10 @@ internal void AddToCacheInvalidationHeader(GrainAddress invalidAddress, GrainAdd
308311
{
309312
lock (_cacheInvalidationHeader)
310313
{
311-
_cacheInvalidationHeader.Add(grainAddressCacheUpdate);
314+
if (_cacheInvalidationHeader.Count < MaxCacheInvalidationHeaderEntries)
315+
{
316+
_cacheInvalidationHeader.Add(grainAddressCacheUpdate);
317+
}
312318
}
313319
}
314320
}

src/Orleans.Core/Networking/Connection.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,7 @@ private bool HandleReceiveMessageFailure(Message message, Exception exception)
482482

483483
// Send the error response and continue processing the next message.
484484
this.Send(response);
485+
message.ReleaseDropped("ReceiveMessageDeserializationFailure");
485486
}
486487
else if (message.Direction == Message.Directions.Response)
487488
{

src/Orleans.Core/Runtime/CallbackData.cs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ internal sealed partial class CallbackData
1414
private int completed;
1515
private StatusResponse? lastKnownStatus;
1616
private ValueStopwatch stopwatch;
17+
private CancellationToken _cancellationToken;
1718
private CancellationTokenRegistration _cancellationTokenRegistration;
1819

1920
public CallbackData(
@@ -33,7 +34,7 @@ public CallbackData(
3334

3435
public Message Message { get; } // might hold metadata used by response pipeline
3536

36-
public bool IsCompleted => this.completed == 1;
37+
public bool IsCompleted => Volatile.Read(ref completed) != 0;
3738

3839
public void SubscribeForCancellation(CancellationToken cancellationToken)
3940
{
@@ -42,12 +43,26 @@ public void SubscribeForCancellation(CancellationToken cancellationToken)
4243
return;
4344
}
4445

45-
cancellationToken.ThrowIfCancellationRequested();
46-
_cancellationTokenRegistration = cancellationToken.UnsafeRegister(static arg =>
46+
if (IsCompleted)
47+
{
48+
return;
49+
}
50+
51+
_cancellationToken = cancellationToken;
52+
var registration = cancellationToken.UnsafeRegister(static arg =>
4753
{
4854
var callbackData = (CallbackData)arg!;
4955
callbackData.OnCancellation();
5056
}, this);
57+
58+
if (IsCompleted)
59+
{
60+
registration.Dispose();
61+
}
62+
else
63+
{
64+
_cancellationTokenRegistration = registration;
65+
}
5166
}
5267

5368
private void SignalCancellation()
@@ -112,9 +127,9 @@ private void OnCancellation()
112127
SignalCancellation();
113128
shared.Unregister(Message);
114129
_applicationRequestInstruments.OnAppRequestsEnd((long)stopwatch.Elapsed.TotalMilliseconds);
115-
_applicationRequestInstruments.OnAppRequestsTimedOut(GetTargetGrainType());
130+
_applicationRequestInstruments.OnAppRequestsCanceled(GetTargetGrainType());
116131
OrleansCallBackDataEvent.Instance.OnCanceled(Message);
117-
context.Complete(Response.FromException(new OperationCanceledException(_cancellationTokenRegistration.Token)));
132+
context.Complete(Response.FromException(new OperationCanceledException(_cancellationToken)));
118133
_cancellationTokenRegistration.Dispose();
119134
ReleaseRequest("CallbackData.OnCancellation");
120135
}

src/Orleans.Core/Runtime/OutsideRuntimeClient.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,12 @@ public async Task StartAsync(CancellationToken cancellationToken)
155155

156156
public async Task StopAsync(CancellationToken cancellationToken)
157157
{
158+
disposing = true;
159+
foreach (var callback in callbacks.Values)
160+
{
161+
callback.OnHostShutdown();
162+
}
163+
158164
this.callbackTimer.Dispose();
159165

160166
if (this.callbackTimerTask is { } task)
@@ -505,7 +511,7 @@ private async Task MonitorCallbackExpiry()
505511

506512
private void ThrowIfDisposed()
507513
{
508-
if (disposed)
514+
if (disposing || disposed)
509515
{
510516
ThrowObjectDisposedException();
511517
}

src/Orleans.Runtime/Core/InsideRuntimeClient.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,10 @@ public InsideRuntimeClient(
113113
public GrainFactory ConcreteGrainFactory { get; }
114114

115115
private GrainLocator GrainLocator
116-
=> this.grainLocator ?? (this.grainLocator = this.ServiceProvider.GetRequiredService<GrainLocator>());
116+
=> this.grainLocator;
117117

118118
private List<IIncomingGrainCallFilter> GrainCallFilters
119-
=> this.grainCallFilters ??= new List<IIncomingGrainCallFilter>(this.ServiceProvider.GetServices<IIncomingGrainCallFilter>());
119+
=> this.grainCallFilters;
120120

121121
private MessageCenter MessageCenter => this.messageCenter ?? (this.messageCenter = this.ServiceProvider.GetRequiredService<MessageCenter>());
122122

@@ -510,6 +510,11 @@ public void DeleteObjectReference(IAddressable obj)
510510

511511
private async Task OnRuntimeInitializeStop(CancellationToken tc)
512512
{
513+
foreach (var callback in callbacks.Values)
514+
{
515+
callback.OnHostShutdown();
516+
}
517+
513518
this.callbackTimer.Dispose();
514519
if (this.callbackTimerTask is { } task)
515520
{
@@ -549,6 +554,8 @@ public void BreakOutstandingMessagesToSilo(SiloAddress deadSilo)
549554

550555
public void Participate(ISiloLifecycle lifecycle)
551556
{
557+
grainLocator = ServiceProvider.GetRequiredService<GrainLocator>();
558+
grainCallFilters = new List<IIncomingGrainCallFilter>(ServiceProvider.GetServices<IIncomingGrainCallFilter>());
552559
_cancellationManager = this.ServiceProvider.GetRequiredService<IGrainCallCancellationManager>();
553560
sharedCallbackData.CancellationManager = _cancellationManager;
554561
systemSharedCallbackData.CancellationManager = _cancellationManager;

test/Orleans.Runtime.Tests/CallbackDataTests.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,11 @@ private static CallbackData CreateCallback(
7878
cancelOnTimeout: false,
7979
waitForCancellationAcknowledgement: false,
8080
cancellationManager: null);
81-
return new CallbackData(shared, completion, new Message(), instruments);
81+
var message = new Message();
82+
message.InitializeRefCount();
83+
var callback = new CallbackData(shared, completion, message, instruments);
84+
message.Release();
85+
return callback;
8286
}
8387

8488
private static ServiceProvider CreateServiceProvider()

test/Orleans.Runtime.Tests/InsideRuntimeClientTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public async Task LateRejectionAfterSiloDisposalDoesNotResolveServices()
3636
RejectionInfo = "The outbound queue is stopped",
3737
},
3838
};
39+
rejection.InitializeRefCount();
3940

4041
await silo.StopSiloAsync(stopGracefully: false);
4142
await silo.DisposeAsync();

0 commit comments

Comments
 (0)