dotnet/orleans

How do I correctly call the DeactivateOnIdle() method within the Grain? Getting error!

mstfcck opened this issue · 3 comments

I'm trying to make a simple application that will run time-based processes with Orleans.

All the codes of the application I am working on are as follows.

I create 1 Grain (ExecutorGrain) on 1 Silo. This will be a logic that will run a time-based business within Grain.

PS: ExecutorGrainV2 is a different example, I just added it to the code, ExecutorGrain and ExecutorGrainV2 are not active at the same time at run time.

For example; process data from a specific Event for 10 minutes and completely Dispose of the Grain when the time is up (like Kafka/RabbitMQ consumer).

After this Grain is Disposed, I want it to be cleared from the system until it is called again, and when it is created again, I want it to run a completely new process (with the same business code).

NOTE: Ideally, of course, my goal will be more than 1 Silo and more than 1 Grain.

DeactivateOnIdle(); No matter where I call the method, I get an error. You can see the entire log of the error I received after running the code sample I gave below.

  • I reviewed the documentation and could not find sufficient information.
  • I reviewed other issues opened on the subject and tried similar things.
  • I couldn't find a method on StackOverflow.
  • I also checked the sample application codes, but I could not find a suitable reference for such a scenario.

The Dispose() operation seems to cause an error at runtime, but I couldn't understand why and how.

I request your support regarding the code.

Packages

Packages
    <ItemGroup>
        <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0"/>
        <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0"/>
        <PackageReference Include="Microsoft.Orleans.Server" Version="8.0.0"/>
        <PackageReference Include="OrleansDashboard" Version="7.2.2"/>
    </ItemGroup>

Code

using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Orleans.Configuration;

class Program
{
    static async Task Main(string[] args)
    {
        var builder = Host.CreateDefaultBuilder(args)
            .UseOrleans(silo =>
            {
                silo.UseLocalhostClustering()
                    .UseDashboard(x => x.HostSelf = true)
                    .Configure<ClusterOptions>(options =>
                    {
                        options.ClusterId = "dev";
                        options.ServiceId = "Orleans";
                    })
                    .ConfigureLogging(logging => logging.AddConsole());
            })
            .UseConsoleLifetime()
            .ConfigureServices((hostContext, services) => { });

        using var host = builder.Build();
        await host.StartAsync();

        var client = host.Services.GetRequiredService<IClusterClient>();
        
        var executorGrain = client.GetGrain<IExecutorGrain>(new Random().Next());
        await executorGrain.Execute();
        
    }
}

public interface IExecutorGrain : IGrainWithIntegerKey
{
    Task Execute(); 
}

public class ExecutorGrain : Grain, IExecutorGrain
{
    public async Task Execute()
    {
        Console.WriteLine($">>> Executing");

        var counter = 0;

        while (counter < 5)
        {
            // Here; there will be time-based processes

            await Task.Delay(1000);
            Console.WriteLine($">>> Execute Counter: {counter++}");
        }
        
        Console.WriteLine($">>> Executed");

        DeactivateOnIdle(); 
    }
}


public class ExecutorGrainV2 : Grain, IExecutorGrain
{
    private IDisposable? _timer;
    
    public override Task OnActivateAsync(CancellationToken cancellationToken)
    {
        _timer = RegisterTimer(TimerAsync, null, TimeSpan.FromMilliseconds(10), TimeSpan.FromMilliseconds(10));
        return base.OnActivateAsync(cancellationToken);
    }

    public async Task Execute()
    {
        Console.WriteLine($">>> Executing");

        var counter = 0;

        while (counter < 5)
        {
            // Here; there will be time-based processes
            
            await Task.Delay(1000);
            Console.WriteLine($">>> Execute Counter: {counter++}");
        }
        
        Console.WriteLine($">>> Executed");
    }

    private Task TimerAsync(object state = null)
    {
        Console.WriteLine($">>> Timer Executed");
    
        _timer?.Dispose();
        _timer = null;
        DeactivateOnIdle();
        return Task.CompletedTask;
    }
}

Console Logs after Run

Logs

info: Orleans.Runtime.Silo[100404]
      Silo starting with GC settings: ServerGC=False GCLatencyMode=Interactive
warn: Orleans.Runtime.Silo[100405]
      Note: Silo not running with ServerGC turned on - recommend checking app config : <configuration>-<runtime>-<gcServer enabled="true">
warn: Orleans.Runtime.Silo[100405]
      Note: ServerGC only kicks in on multi-core systems (settings enabling ServerGC have no effect on single-core machines).
info: Orleans.Runtime.Silo[100403]
      -------------- Initializing silo on host MSTFCCK.local MachineName MSTFCCK at 127.0.0.1:11111, gen 68851927 --------------
info: Orleans.Runtime.Silo[100415]
      Starting silo Silo_61487
warn: Orleans.Runtime.NoOpHostEnvironmentStatistics[100708]
      No implementation of IHostEnvironmentStatistics was found. Load shedding will not work yet
info: Orleans.Runtime.Silo[100422]
      -------------- Started silo S127.0.0.1:11111:68851927, ConsistentHashCode 9DED6DA9 --------------
info: Orleans.Hosting.SiloHostedService[0]
      Starting Orleans Silo.
info: Orleans.Runtime.SiloOptionsLogger[0]
      Configuration Orleans.Configuration.ActivationCountBasedPlacementOptions: 
      ChooseOutOf: 2
      
info: Orleans.Runtime.SiloOptionsLogger[0]
      Configuration Orleans.Configuration.ClusterMembershipOptions: 
      NumMissedTableIAmAliveLimit: 2
      LivenessEnabled: True
      ProbeTimeout: 00:00:05
      TableRefreshTimeout: 00:01:00
      DeathVoteExpirationTimeout: 00:02:00
      IAmAliveTablePublishTimeout: 00:05:00
      MaxJoinAttemptTime: 00:05:00
      UseLivenessGossip: True
      NumProbedSilos: 3
      NumMissedProbesLimit: 3
      NumVotesForDeathDeclaration: 2
      DefunctSiloExpiration: 7.00:00:00
      DefunctSiloCleanupPeriod: 01:00:00
      LocalHealthDegradationMonitoringPeriod: 00:00:10
      ExtendProbeTimeoutDuringDegradation: True
      EnableIndirectProbes: True
      
info: Orleans.Runtime.SiloOptionsLogger[0]
      Configuration Orleans.Configuration.ClusterOptions: 
      ClusterId: dev
      ServiceId: Orleans
      
info: Orleans.Runtime.SiloOptionsLogger[0]
      Configuration Orleans.Configuration.ConnectionOptions: 
      ProtocolVersion: Version1
      ConnectionsPerEndpoint: 1
      ConnectionRetryDelay: 00:00:01
      OpenConnectionTimeout: 00:00:05
      
info: Orleans.Runtime.SiloOptionsLogger[0]
      Configuration Orleans.Configuration.ConsistentRingOptions: 
      NumVirtualBucketsConsistentRing: 30
      UseVirtualBucketsConsistentRing: True
      
info: Orleans.Runtime.SiloOptionsLogger[0]
      Configuration Orleans.Configuration.DevelopmentClusterMembershipOptions: 
      PrimarySiloEndpoint: 127.0.0.1:11111
      
info: Orleans.Runtime.SiloOptionsLogger[0]
      Configuration Orleans.Configuration.EndpointOptions: 
      AdvertisedIPAddress: 127.0.0.1
      SiloPort: 11111
      GatewayPort: 30000
      SiloListeningEndpoint: 
      GatewayListeningEndpoint: 
      
info: Orleans.Runtime.SiloOptionsLogger[0]
      Configuration Orleans.Configuration.GrainCollectionOptions: 
      CollectionQuantum: 00:01:00
      CollectionAge: 00:15:00
      ActivationTimeout: 00:00:30
      DeactivationTimeout: 00:00:30
      
info: Orleans.Runtime.SiloOptionsLogger[0]
      Configuration Orleans.Configuration.GrainDirectoryOptions: 
      CachingStrategy: Adaptive
      CacheSize: 1000000
      InitialCacheTTL: 00:00:30
      MaximumCacheTTL: 00:04:00
      CacheTTLExtensionFactor: 2
      LazyDeregistrationDelay: 00:01:00
      
info: Orleans.Runtime.SiloOptionsLogger[0]
      Configuration Orleans.Configuration.GrainVersioningOptions: 
      DefaultCompatibilityStrategy: BackwardCompatible
      DefaultVersionSelectorStrategy: AllCompatibleVersions
      
info: Orleans.Runtime.SiloOptionsLogger[0]
      Configuration Orleans.Configuration.LoadSheddingOptions: 
      LoadSheddingEnabled: False
      LoadSheddingLimit: 95
      
info: Orleans.Runtime.SiloOptionsLogger[0]
      Configuration Orleans.Configuration.SchedulingOptions: 
      DelayWarningThreshold: 00:00:10
      ActivationSchedulingQuantum: 00:00:00.1000000
      TurnWarningLengthThreshold: 00:00:01
      MaxPendingWorkItemsSoftLimit: 0
      StoppedActivationWarningInterval: 00:01:00
      
info: Orleans.Runtime.SiloOptionsLogger[0]
      Configuration Orleans.Configuration.SiloMessagingOptions: 
      SiloSenderQueues: 0
      GatewaySenderQueues: 0
      MaxForwardCount: 2
      ClientDropTimeout: 00:01:00
      ClientRegistrationRefresh: 00:05:00
      ClientGatewayShutdownNotificationTimeout: 00:00:05
      MaxEnqueuedRequestsSoftLimit: 0
      MaxEnqueuedRequestsHardLimit: 0
      MaxEnqueuedRequestsSoftLimit_StatelessWorker: 0
      MaxEnqueuedRequestsHardLimit_StatelessWorker: 0
      MaxRequestProcessingTime: 02:00:00
      AssumeHomogenousSilosForTesting: False
      ShutdownRerouteTimeout: 00:00:10
      SystemResponseTimeout: 00:30:00
      GrainWorkloadAnalysisPeriod: 00:00:05
      RequestProcessingWarningTime: 00:00:05
      RequestQueueDelayWarningTime: 00:00:10
      WaitForMessageToBeQueuedForOutboundTime: 00:00:02
      ResponseTimeout: 00:30:00
      ResponseTimeoutWithDebugger: 00:30:00
      DropExpiredMessages: True
      MaxMessageHeaderSize: 10485760
      MaxMessageBodySize: 104857600
      
info: Orleans.Runtime.SiloOptionsLogger[0]
      Configuration Orleans.Configuration.SiloOptions: 
      SiloName: Silo_61487
      
info: Orleans.Runtime.Silo[100401]
      Silo Start()
info: Orleans.Runtime.Silo[100452]
      Start local grain directory took 0 milliseconds to finish
info: Orleans.Runtime.MembershipService.MembershipTableManager[100603]
      MembershipOracle starting on host MSTFCCK.local with SiloAddress S127.0.0.1:11111:68851927 at 2024-03-07 21:32:07.987 GMT
info: Orleans.Runtime.MembershipService.SystemTargetBasedMembershipTable[100635]
      Creating in-memory membership table
info: Orleans.Runtime.MembershipService.MembershipTableSystemTarget[100637]
      GrainBasedMembershipTable Activated.
info: Orleans.Runtime.Silo[0]
      Grain Service OrleansDashboard.SiloGrainService registered successfully.
info: Orleans.Runtime.Silo[100452]
      Init grain services took 9 milliseconds to finish
info: Orleans.Runtime.Silo[100452]
      Start deployment load collector took 5 milliseconds to finish
info: Orleans.Runtime.MembershipService.SystemTargetBasedMembershipTable[100631]
      Connected to membership table provider.
info: Orleans.Runtime.MembershipService.MembershipAgent[100663]
      Joining
info: Orleans.Runtime.MembershipService.MembershipAgent[100604]
      -BecomeActive
info: Orleans.Runtime.MembershipService.MembershipAgent[100605]
      -Finished BecomeActive.
info: OrleansDashboard.Dashboard[0]
      Dashboard listening on 8080
info: OrleansDashboard.SiloGrainService[102925]
      Starting OrleansDashboard.SiloGrainService grain service on: S127.0.0.1:11111:68851927 x9DED6DA9, with range <(0 0], Size=x100000000, %Ring=100%>
info: Orleans.Runtime.Silo[0]
      Grain Service OrleansDashboard.SiloGrainService started successfully.
info: OrleansDashboard.SiloGrainService[102934]
      My range changed from <(0 0], Size=x100000000, %Ring=100%> to <(0 0], Size=x100000000, %Ring=100%> increased = True
info: Orleans.Hosting.SiloHostedService[0]
      Orleans Silo started.
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /Users/mustafacicek/Workplace/SWDev/DynamicEventManager/DynamicConsumerGenerator/bin/Debug/net8.0
>>> Executing
info: Orleans.Runtime.Management.ManagementGrain[0]
      GetDetailedHosts OnlyActive=True
>>> Execute Counter: 0
>>> Execute Counter: 1
info: Orleans.Runtime.Management.ManagementGrain[0]
      GetDetailedHosts OnlyActive=True
info: Orleans.Runtime.SiloControl[0]
      GetSimpleGrainStatistics
>>> Execute Counter: 2
>>> Execute Counter: 3
info: Orleans.Runtime.Management.ManagementGrain[0]
      GetDetailedHosts OnlyActive=True
info: Orleans.Runtime.SiloControl[0]
      GetSimpleGrainStatistics
>>> Execute Counter: 4
>>> Executed
warn: Orleans.Grain[0]
      Exception disposing activation [Activation: S127.0.0.1:11111:68851927/executor/562D707F@f18269b47f5340dda15ecd34342c0049#GrainType=ExecutorGrain,DynamicConsumerGenerator Placement=RandomPlacement State=Invalid]
      System.ObjectDisposedException: Cannot access a disposed object.
      Object name: 'IServiceProvider'.
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.ThrowHelper.ThrowObjectDisposedException()
         at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(ServiceIdentifier serviceIdentifier, ServiceProviderEngineScope serviceProviderEngineScope)
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
         at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IServiceProvider provider)
         at Orleans.Runtime.ActivationData.GetComponent[TComponent]() in /_/src/Orleans.Runtime/Catalog/ActivationData.cs:line 257
         at Orleans.Runtime.ActivationData.SetGrainInstance(Object grainInstance) in /_/src/Orleans.Runtime/Catalog/ActivationData.cs:line 305
         at Orleans.Runtime.ActivationData.UnregisterMessageTarget() in /_/src/Orleans.Runtime/Catalog/ActivationData.cs:line 1829
         at Orleans.Runtime.ActivationData.FinishDeactivating(CancellationToken cancellationToken) in /_/src/Orleans.Runtime/Catalog/ActivationData.cs:line 1731
fail: Orleans.Runtime.GrainTimer[101413]
      Caught and ignored exception thrown from timer callback for timer GrainTimer.sys.svc.user.95B6CAE6/127.0.0.1:11111@68851927Timer TimerCallbackHandler:[SystemTarget: S127.0.0.1:11111:68851927/sys.svc.user.95B6CAE6/127.0.0.1:11111@68851927@0d73e76f00000000311eb42200000000]->System.Threading.Tasks.Task <Start>b__11_0(System.Object)
      System.ObjectDisposedException: Cannot access a disposed object.
      Object name: 'IServiceProvider'.
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.ThrowHelper.ThrowObjectDisposedException()
         at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
         at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetServiceOrCreateInstance[T](IServiceProvider provider)
         at Orleans.Serialization.GeneratedCodeHelpers.OrleansGeneratedCodeHelper.GetService[TService](Object caller, ICodecProvider codecProvider) in /_/src/Orleans.Serialization/GeneratedCodeHelpers/OrleansGeneratedCodeHelper.cs:line 72
         at OrleansCodeGen.Orleans.Runtime.Proxy_IManagementGrain..ctor(GrainReferenceShared arg0, IdSpan arg1) in /_/src/Orleans.Core/Orleans.CodeGenerator/Orleans.CodeGenerator.OrleansSerializationSourceGenerator/Orleans.Core.orleans.g.cs:line 1115
         at Proxy_IManagementGrain(Object, GrainReferenceShared, IdSpan)
         at Orleans.GrainReferences.GrainReferenceActivatorProvider.GrainReferenceActivator.CreateReference(GrainId grainId) in /_/src/Orleans.Core/GrainReferences/GrainReferenceActivator.cs:line 380
         at Orleans.GrainReferences.GrainReferenceActivator.CreateReference(GrainId grainId, GrainInterfaceType interfaceType) in /_/src/Orleans.Core/GrainReferences/GrainReferenceActivator.cs:line 57
         at Orleans.GrainFactory.GetGrain(Type interfaceType, IdSpan grainKey, String grainClassNamePrefix) in /_/src/Orleans.Core/Core/GrainFactory.cs:line 217
         at Orleans.GrainFactory.GetGrain[TGrainInterface](Int64 primaryKey, String grainClassNamePrefix) in /_/src/Orleans.Core/Core/GrainFactory.cs:line 51
         at OrleansDashboard.SiloGrainService.CollectStatistics(Boolean canDeactivate)
         at Orleans.Runtime.GrainTimer.ForwardToAsyncCallback(Object state) in /_/src/Orleans.Runtime/Timers/GrainTimer.cs:line 116

In your sample, the silo will stop after await executorGrain.Execute(); returns, no?

In your sample, the silo will stop after await executorGrain.Execute(); returns, no?

@benjaminpetit for this implementation, yes. But normally, it will keep running, and I plan to consume events.

The problem in your example code is that you are missing the following line at the end of your Main(string[] args) method:

await host.WaitForShutdownAsync();

EDIT: I've opened #9001 and #9000 with defensive fixes to prevent the errors, but the above line of code still needs to be there.