orleans
orleans copied to clipboard
NullReferenceException when activating stateless grain after upgrade to 3.6.2
We recently upgraded from 3.5.1
to 3.6.2
.
Since then we get from time to time this exception (note that it works sometimes!):
System.NullReferenceException: Object reference not set to an instance of an object.
at ActivationData Orleans.Runtime.Catalog.GetOrCreateActivation(ActivationAddress address, bool newPlacement, string grainType, string genericArguments, Dictionary<string, object> requestContextData, out Task activatedPromise) in /_/src/Orleans.Runtime/Catalog/Catalog.cs:line 585
at void Orleans.Runtime.Dispatcher.ReceiveMessage(Message message) in /_/src/Orleans.Runtime/Core/Dispatcher.cs:line 210
This grain is stateless
and activated a lot of times.
It looks like this:
[StatelessWorker(maxLocalWorkers: 3)]
[StorageProvider(ProviderName = "datastore")]
internal class ReadOnlyProductGrain : Grain<ProductState>, IReadOnlyProductGrain
{
private Interfaces.ProductData.Product product;
private long? brandId;
private long? productTypeId;
private readonly ILogger<ReadOnlyProductGrain> logger;
public ReadOnlyProductGrain(ILogger<ReadOnlyProductGrain> logger)
{
this.logger = logger;
}
public override async Task OnActivateAsync()
{
try
{
if (State != null && !State.NeedsInitialization())
{
product = new Interfaces.ProductData.Product
{
Availability = State.Availability,
Price = State.Price,
IsPublic = State.IsPublic,
IsOrderingAllowed = State.IsOrderingAllowed
};
brandId = State.Brand?.Id;
productTypeId = State.ProductType.Id;
}
else
{
var productGrain = GrainFactory.GetGrain<IProductGrain>(this.GetPrimaryKeyString());
product = await productGrain.GetProduct();
brandId = await productGrain.GetBrandId();
productTypeId = await productGrain.GetProductTypeId();
}
}
catch (Exception e)
{
logger.LogError(e, "Exception during grain activation");
throw;
}
}
public Task<long?> GetBrandId()
{
DeactivateOnIdle();
return Task.FromResult(brandId);
}
public Task<long?> GetProductTypeId()
{
DeactivateOnIdle();
return Task.FromResult(productTypeId);
}
public Task<Interfaces.ProductData.Price> GetPrice()
{
DeactivateOnIdle();
return Task.FromResult(product.Price);
}
public Task<Interfaces.ProductData.Availability> GetAvailability()
{
DeactivateOnIdle();
return Task.FromResult(product.Availability);
}
public Task<bool?> IsOrderingAllowed()
{
DeactivateOnIdle();
return Task.FromResult(product.IsOrderingAllowed);
}
public Task<bool?> IsPublic()
{
DeactivateOnIdle();
return Task.FromResult(product.IsPublic);
}
public Task<Interfaces.ProductData.Product> GetProduct()
{
DeactivateOnIdle();
return Task.FromResult(product);
}
}
}
You may notice the many DeactivateOnIdle
calls, because we don't want the Grain to stay activated / in memory.
So as a workaround we removed all this DeactivateOnIdle
calls and instead added a very short time period when the graind should be deactivated:
.Configure<GrainCollectionOptions>(options =>
{
options.ClassSpecificCollectionAge[typeof(ReadOnlyProductGrain).FullName!] =
TimeSpan.FromSeconds(61);
})
So bascially the question is:
Is this a bug or are we doing something wrong? I.e. should we not call DeactivateOnIdle
that often for a grain which is activated a lot of times?
Triage: @Nasicus can you provide a GitHub repro project?
Hi @Nasicus We have added the "Needs: Author Feedback" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.
Hi @Nasicus We have added the "Needs: Author Feedback" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.
@rafikiassumani-msft unfortunately not, it's deeply integrated into one of our real projects.... if I have time I can try to reproduce it, but I'm not sure when that will be.
I was really mainly interested in knowing if the dev / you immediately could say "huh... yeah you're not supposed to call DeactivateOnIdle
that often / in that manner" or something like that.
Calling DeactivateOnIdle()
ought to be fine. Is there any more to the stack trace than the 3 lines you posted?
I just checked and found another stack trace, a lot of it is from our own code though, so not sure if it's any help:
Pythia.Orleans.Grains.ProductList.ProductListImprovementException: Call to Product Grain 6332927 failed.
Exception:
System.NullReferenceException: Object reference not set to an instance of an object.
at ActivationData Orleans.Runtime.Catalog.GetOrCreateActivation(ActivationAddress address, bool newPlacement, string grainType, string genericArguments, Dictionary<string, object> requestContextData, out Task activatedPromise) in /_/src/Orleans.Runtime/Catalog/Catalog.cs:line 585
at void Orleans.Runtime.Dispatcher.ReceiveMessage(Message message) in /_/src/Orleans.Runtime/Core/Dispatcher.cs:line 210
at async Task<T> Orleans.Internal.OrleansTaskExtentions.ToTypedTask<T>(Task<object> task)+ConvertAsync(?) in /_/src/Orleans.Core/Async/TaskExtensions.cs:line 114
at async Task<Product> Pythia.Orleans.Grains.ProductList.ProductListImprovementGrain.GetProduct(long productId, int portalId) in /app/Pythia.Orleans.Grains/ProductList/ProductListImprovementGrain.cs:line 148
at async Task<bool> Pythia.Orleans.Grains.ProductList.ProductListImprovementGrain.PriceDropCheck(ProductListItem entry, int portalId) in /app/Pythia.Orleans.Grains/ProductList/ProductListImprovementGrain.cs:line 133
at void Pythia.Orleans.Grains.ProductList.ProductListImprovementGrain+<>c__DisplayClass12_0+<<SelectProductListItemForNotification>b__7>d.MoveNext() in /app/Pythia.Orleans.Grains/ProductList/ProductListImprovementGrain.cs:line 158
at void Pythia.Shared.AsyncUtils+<>c__DisplayClass0_0<TInput, TOutput>+<<RestrictedWhenAll>g__Apply|3>d.MoveNext() in /app/Pythia.Shared/AsyncUtils.cs:line 47;Call to Product Grain 6881946 failed.
Exception:
System.NullReferenceException: Object reference not set to an instance of an object.
at ActivationData Orleans.Runtime.Catalog.GetOrCreateActivation(ActivationAddress address, bool newPlacement, string grainType, string genericArguments, Dictionary<string, object> requestContextData, out Task activatedPromise) in /_/src/Orleans.Runtime/Catalog/Catalog.cs:line 585
at void Orleans.Runtime.Dispatcher.ReceiveMessage(Message message) in /_/src/Orleans.Runtime/Core/Dispatcher.cs:line 210
at async Task<T> Orleans.Internal.OrleansTaskExtentions.ToTypedTask<T>(Task<object> task)+ConvertAsync(?) in /_/src/Orleans.Core/Async/TaskExtensions.cs:line 114
at async Task<Product> Pythia.Orleans.Grains.ProductList.ProductListImprovementGrain.GetProduct(long productId, int portalId) in /app/Pythia.Orleans.Grains/ProductList/ProductListImprovementGrain.cs:line 148
at async Task<bool> Pythia.Orleans.Grains.ProductList.ProductListImprovementGrain.PriceDropCheck(ProductListItem entry, int portalId) in /app/Pythia.Orleans.Grains/ProductList/ProductListImprovementGrain.cs:line 133
at void Pythia.Orleans.Grains.ProductList.ProductListImprovementGrain+<>c__DisplayClass12_0+<<SelectProductListItemForNotification>b__7>d.MoveNext() in /app/Pythia.Orleans.Grains/ProductList/ProductListImprovementGrain.cs:line 158
at void Pythia.Shared.AsyncUtils+<>c__DisplayClass0_0<TInput, TOutput>+<<RestrictedWhenAll>g__Apply|3>d.MoveNext() in /app/Pythia.Shared/AsyncUtils.cs:line 47
at async Task<ProductListItem> Pythia.Orleans.Grains.ProductList.ProductListImprovementGrain.SelectProductListItemForNotification(int portalId, IReadOnlyList<ProductListItem> productListEntries, Func<ProductListItem, int, Task<bool>> validityCheckFn, ISet<long> exclusions, Func<ProductListItem, int> orderingByFn) in /app/Pythia.Orleans.Grains/ProductList/ProductListImprovementGrain.cs:line 198
at async Task<ProductListItem> Pythia.Orleans.Grains.ProductList.ProductListImprovementGrain.GetGreatestPriceDrop(int portalId, IReadOnlyList<ProductListItem> productListEntries, IProductListImprovementNotificationGrain productListImprovementNotificationGrain) in /app/Pythia.Orleans.Grains/ProductList/ProductListImprovementGrain.cs:line 110
at async Task Pythia.Orleans.Grains.ProductList.ProductListImprovementGrain.ReceiveReminder(string reminderName, TickStatus status) in /app/Pythia.Orleans.Grains/ProductList/ProductListImprovementGrain.cs:line 97
at async Task<object> Orleans.OrleansCodeGenRemindableMethodInvoker.Invoke(IAddressable grain, InvokeMethodRequest request) in /_/src/Orleans.Core/obj/Release/net6.0/Orleans.Core.orleans.g.cs:line 5215
at async Task Orleans.Runtime.GrainMethodInvoker.Invoke() in /_/src/Orleans.Runtime/Core/GrainMethodInvoker.cs:line 104
at async Task OrleansDashboard.Metrics.GrainProfilerFilter.Invoke(IIncomingGrainCallContext context)
at async Task Orleans.Runtime.GrainMethodInvoker.Invoke() in /_/src/Orleans.Runtime/Core/GrainMethodInvoker.cs:line 104
at async Task Orleans.Runtime.InsideRuntimeClient.Invoke(IAddressable target, IInvokable invokable, Message message) in /_/src/Orleans.Runtime/Core/InsideRuntimeClient.cs:line 469
at async Task Orleans.Runtime.ReminderService.LocalReminderService+LocalReminderData.OnTimerTick() in /_/src/Orleans.Runtime/ReminderService/LocalReminderService.cs:line 631
@Nasicus apologies for the slow response. Are you able to provide a memory dump? If you are running on Windows, you can use procdump to capture a dump as the NullReferenceException
is thrown: procdump -e 1 -f "NullReferenceException" YourApp.exe
Fix PR: #7918 Apologies for the hassle, @Nasicus!