dotnet / dotnet/orleans

Null reference when trying to get a grain from GrainFactory

Open
#8,928 0 comments 2 reactions 0 assignees View on GitHub
Dominant language
C#
Stars
10.9k
Forks
2.1k
Avg merge
14h 42m
Merged PRs (30d)
354

Description

Hello there,

We have a KafkaConsumer and it is singleton.

```csharp
using KafkaFlow;

namespace ***.API.Infrastructure.Kafka.Consumers.Orders;

public class OrderConsumer(IClusterClient clusterClient, ILogger logger) : IMessageHandler
{
public async Task Handle(IMessageContext context, OrderIntegrationEvent message)
{
if (message is
{
OrderState: OrderState.Rejected or
OrderStateEnum.CanceledByAdmin or
OrderStateEnum.CanceledByCustomer or
OrderStateEnum.CanceledByNfc or
OrderStateEnum.CanceledBySystem
})
{
try
{
var customerGrain = clusterClient.GetGrain(message.Buyer.CustomerId);
var referenceId = message.Id.ToString();
await customerGrain.RequestToRefundAsync(new()
{
Amount = null,
CustomerId = message.Buyer.CustomerId,
ReferenceId = referenceId,
RefundReferenceId = referenceId,
Description = "From Kafka consumer"
});
}
catch (Exception ex)
{
logger.LogError(ex, "An exception has occurred in order consumer");
}
}
}
}
```

this is the code initial code, we get a reference to ICustomerGrain through clusterClient, and then the RequestToRefundAsync method in ICustomerGrain needs to resolve another grain.

The below method is in a Grain class.
```csharp
public async Task RequestToRefundAsync(RequestToRefund request)
{
try
{
await using var scope = this.ServiceProvider.CreateAsyncScope();
var paymentRequestService = scope.ServiceProvider.GetRequiredService();
var detail = await paymentRequestService.GetPaymentRequestDetails(request.ReferenceId);
var referenceIdGrain = this.GrainFactory.GetGrain(request.ReferenceId);

if (detail!.RefundedAmount < 1 && request.Amount.HasValue is false)
{
await referenceIdGrain.RequestToFullRefundAsync(new()
{
ReferenceId = request.ReferenceId,
RefundReferenceId = request.RefundReferenceId,
CustomerId = request.CustomerId,
Description = request.Description
});
}
else
{
await referenceIdGrain.RequestToPartialRefundAsync(new()
{
ReferenceId = request.ReferenceId,
RefundReferenceId = request.RefundReferenceId,
CustomerId = request.CustomerId,
Amount = request.Amount ?? (detail.TotalAmount - detail.RefundedAmount),
Description = request.Description
});
}
}
catch (Exception ex)
{
// metrics
throw;
}
}
```

but this line is the source of the problem, and we get null reference exception after calling ```referenceIdGrain```, the rate of this problem is high in this consumer, and we don't know why is that!

```csharp
var referenceIdGrain = this.GrainFactory.GetGrain(request.ReferenceId);
```

```
System.NullReferenceException: Object reference not set to an instance of an object.
at OPay.API.Grains.CustomerGrain.RequestToRefundAsync(RequestToRefund request) in /opay/src/OPay.API/Grains/CustomerGrain.cs:line 83
at OPay.API.Grains.CustomerGrain.RequestToRefundAsync(RequestToRefund request) in /opay/src/OPay.API/Grains/CustomerGrain.cs:line 107
at Orleans.Runtime.TaskRequest.CompleteInvokeAsync(Task resultTask) in /_/src/Orleans.Core.Abstractions/Runtime/GrainReference.cs:line 750
--- End of stack trace from previous location ---
at Orleans.Serialization.Invocation.ResponseCompletionSource.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token) in /_/src/Orleans.Serialization/Invocation/ResponseCompletionSource.cs:line 98
at System.Threading.Tasks.ValueTask.ValueTaskSourceAsTask.<>c.<.cctor>b__4_0(Object state)
--- End of stack trace from previous location ---
```

maybe this issue can help and be relatable:
https://github.com/dotnet/orleans/issues/5275

Contributor guide

Open the contributing guide

Research direction

Start with the CustomerGrain.RequestToRefundAsync stack trace and CustomerGrain.cs lines 83 and 107, then compare the reported behavior with issue #5275. Reproduce the call path from the Kafka consumer through ICustomerGrain and record the Orleans version and complete exception details. Done means identifying the null object and documenting or fixing a reproducible cause.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
distributed-systems
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.