Edit

Share via


Grain lifecycle overview

Orleans grains use an observable lifecycle (see Orleans Lifecycle) for ordered activation and deactivation. This allows grain logic, system components, and application logic to start and stop in an ordered manner during grain activation and collection.

Stages

The predefined grain lifecycle stages are as follows:

public static class GrainLifecycleStage
{
    public const int First = int.MinValue;
    public const int SetupState = 1_000;
    public const int Activate = 2_000;
    public const int Last = int.MaxValue;
}

While Orleans uses the grain lifecycle during grain activation, grains aren't always deactivated during some error cases (such as silo crashes). Therefore, applications shouldn't rely on the grain lifecycle always executing during grain deactivations.

Grain lifecycle participation

Application logic can participate in a grain's lifecycle in two ways:

A grain always participates in its lifecycle, so application logic can be introduced by overriding the participate method.

Example participation

public override void Participate(IGrainLifecycle lifecycle)
{
    base.Participate(lifecycle);
    lifecycle.Subscribe(
        this.GetType().FullName,
        GrainLifecycleStage.SetupState,
        OnSetupState);
}

In the preceding example, Grain<TGrainState> overrides the Grain.Participate method to tell the lifecycle to call its OnSetupState method during the GrainLifecycleStage.SetupState stage of the lifecycle.

Components created during a grain's construction can also participate in the lifecycle without adding any special grain logic. Since Orleans creates the grain's context (IGrainContext), including its lifecycle (IGrainContext.ObservableLifecycle), before creating the grain, any component injected into the grain by the container can participate in the grain's lifecycle.

Components created during a grain's construction can also participate in the lifecycle without adding any special grain logic. Since Orleans creates the grain's activation context (IGrainActivationContext), including its lifecycle (IGrainActivationContext.ObservableLifecycle), before creating the grain, any component injected into the grain by the container can participate in the grain's lifecycle.

Example: Component participation

The following component participates in the grain's lifecycle when created using its factory function Create(...). This logic could exist in the component's constructor, but that risks adding the component to the lifecycle before it's fully constructed, which might not be safe.

public class MyComponent : ILifecycleParticipant<IGrainLifecycle>
{
    public static MyComponent Create(IGrainContext context)
    {
        var component = new MyComponent();
        component.Participate(context.ObservableLifecycle);
        return component;
    }

    public void Participate(IGrainLifecycle lifecycle)
    {
        lifecycle.Subscribe<MyComponent>(GrainLifecycleStage.Activate, OnActivate);
    }

    private Task OnActivate(CancellationToken ct)
    {
        // Do stuff
    }
}
public class MyComponent : ILifecycleParticipant<IGrainLifecycle>
{
    public static MyComponent Create(IGrainActivationContext context)
    {
        var component = new MyComponent();
        component.Participate(context.ObservableLifecycle);
        return component;
    }

    public void Participate(IGrainLifecycle lifecycle)
    {
        lifecycle.Subscribe<MyComponent>(GrainLifecycleStage.Activate, OnActivate);
    }

    private Task OnActivate(CancellationToken ct)
    {
        // Do stuff
    }
}

By registering the example component in the service container using its Create(...) factory function, any grain constructed with the component as a dependency has the component participate in its lifecycle without requiring any special logic in the grain itself.

Register component in container

services.AddTransient<MyComponent>(sp =>
    MyComponent.Create(sp.GetRequiredService<IGrainContext>());
services.AddTransient<MyComponent>(sp =>
    MyComponent.Create(sp.GetRequiredService<IGrainActivationContext>());

Grain with component as a dependency

public class MyGrain : Grain, IMyGrain
{
    private readonly MyComponent _component;

    public MyGrain(MyComponent component)
    {
        _component = component;
    }
}