-
Notifications
You must be signed in to change notification settings - Fork 2.1k
[FIX] Dispose StatelessWorkerGrainContext when it contains no more workers
#9636
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -39,6 +39,7 @@ internal partial class StatelessWorkerGrainContext : IGrainContext, IAsyncDispos | |
| private double _integralTerm = 0d; | ||
| private int _detectedIdleCyclesCount = 0; | ||
| private readonly int _minIdleCyclesBeforeRemoval; | ||
| private readonly bool _isIdleWorkerRemovalStrategy; | ||
|
|
||
| public StatelessWorkerGrainContext( | ||
| GrainAddress address, | ||
|
|
@@ -52,7 +53,8 @@ public StatelessWorkerGrainContext( | |
| var strategy = (StatelessWorkerPlacement)_shared.PlacementStrategy; | ||
| var options = _shared.StatelessWorkerOptions; | ||
|
|
||
| if (strategy.RemoveIdleWorkers && options.RemoveIdleWorkers) | ||
| _isIdleWorkerRemovalStrategy = strategy.RemoveIdleWorkers && options.RemoveIdleWorkers; | ||
| if (_isIdleWorkerRemovalStrategy) | ||
| { | ||
| _minIdleCyclesBeforeRemoval = options.MinIdleCyclesBeforeRemoval > 0 ? options.MinIdleCyclesBeforeRemoval : 1; | ||
| _inspectionTimer = new Timer( | ||
|
|
@@ -172,9 +174,23 @@ private async Task RunMessageLoop() | |
|
|
||
| if (_workers.Count == 0) | ||
| { | ||
| // When the last worker is destroyed, we can consider the stateless worker grain | ||
| // activation to be destroyed as well | ||
| // When the last worker is destroyed, we can consider the stateless worker grain activation to be destroyed as well | ||
| _shared.InternalRuntime.Catalog.UnregisterMessageTarget(this); | ||
|
|
||
| if (_isIdleWorkerRemovalStrategy) | ||
| { | ||
| var completion = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); | ||
| EnqueueWorkItem(WorkItemType.DisposeAsync, new DisposeAsyncWorkItemState(completion)); | ||
|
|
||
| // DO NOT await this as it would deadlock the work loop! | ||
| _ = completion.Task.ContinueWith(t => | ||
| { | ||
| if (t.Exception is { } ex) | ||
| { | ||
| LogErrorInMessageLoop(_shared.Logger, ex); | ||
| } | ||
| }, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Current); | ||
| } | ||
| } | ||
| break; | ||
| } | ||
|
|
@@ -343,16 +359,12 @@ private async Task DeactivatedTaskInternal(TaskCompletionSource completion) | |
|
|
||
| private async Task DisposeAsyncInternal(TaskCompletionSource completion) | ||
| { | ||
| try | ||
| { | ||
| if (_inspectionTimer != null) | ||
| { | ||
| await _inspectionTimer.DisposeAsync(); | ||
| _inspectionTimer = null; | ||
| } | ||
| } | ||
| catch | ||
| if (_inspectionTimer != null) | ||
| { | ||
| await _inspectionTimer.DisposeAsync().AsTask() | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why is the change here needed? what is the benefit of
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, this is an "just in case" thing so the rest of the method continues |
||
| .ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing | ConfigureAwaitOptions.ContinueOnCapturedContext); | ||
|
|
||
| _inspectionTimer = null; | ||
| } | ||
|
|
||
| try | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can there be a scenario where
_workers.Countreached 0, but in the queue there is an item to processWorkItemType.Messagewhich will then create a new worker? In that case a new worker will be created right after.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
very unlikely since the destruction of the workers comes after the fact of a grain disposing (which means it wont receive more messages), but possible! If that happens, the dispose command will be processed after that message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The alternative would be to stop the disposal if a worker exists within the SW context, we should not do that, we should respect the call for disposal from outside!