Skip to content

Defining Delegates

Oleg Karasik edited this page Dec 4, 2019 · 3 revisions

The delegate is defined using DefineDelegate(...) method:

new HostBuilder()
  .DefineStatefulService(
    serviceBuilder => 
    { 
      serviceBuilder.DefineDelegate(delegateBuilder => { ... });
    })
  .Build()
  .Run()

Configuring delegate event

The delegates are executed as reaction on service lifecycle (routine or package) events. The stateful and stateless services define a rich set of events:

[Flags]
public enum StatefulServiceLifecycleEvent
{
  // This service event occurs when replica initialization begins. 
  //
  // All delegates are guaranteed to be executed before any of ICommunicationListeners is created.
  //
  // **WARNING**: All delegate does **not** have **write** access to reliable state.
  OnStartup,

  // This service event occurs when replica is changing roles.
  //
  // **WARNING**: All delegate does **not** have **write** access to reliable state.
  OnChangeRole,

  // This service event occurs when primary replica begins execution.
  //
  // All delegates are guaranteed to be executed after all ICommunicationListeners are opened. 
  // All delegates have **write** access to reliable state.
  //
  // **WARNING**: This event occurs every time replica is promoted to primary replica.
  OnRun,

  // This service event occurs when replica is shutting down.
  //
  // All delegates are guaranteed to be executed after all ICommunicationListeners are closed.
  //
  // **WARNING**: All delegate does **not** have **write** access to reliable state.
  //
  // **WARNING**: If event payload has IsAborting set to true then replica is terminated
  //              abnormaly and there no more guarantees.
  OnShutdown,

  // This service event occurs when replica detected data loss.
  //
  // Please see the official documentation: 
  // https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-reliable-services-backup-restore#restore
  OnDataLoss,

  // This service event occurs when replica state is restored.
  //
  // Please see the official documentation:
  // https://docs.microsoft.com/en-us/dotnet/api/microsoft.servicefabric.services.runtime.statefulservicebase.onrestorecompletedasync?view=azure-dotnet
  OnRestoreCompleted,

  // This service event occurs when code package is added to service package. 
  OnCodePackageAdded,

  // This service event occurs when code package in service package is modified.
  OnCodePackageModified,

  // This service event occurs when code package is removed from service package.
  OnCodePackageRemoved,

  // This service event occurs when config package is added to service package.
  OnConfigPackageAdded,

  // This service event occurs when config package in service package is modified.
  OnConfigPackageModified,

  // This service event occurs when config package is removed from service package.
  OnConfigPackageRemoved,

  // This service event occurs when data package is removed to service package.
  OnDataPackageAdded,

  // This service event occurs when data package in service package is modified.
  OnDataPackageModified,

  // This service event occurs when data package is removed from service package.
  OnDataPackageRemoved
}
[Flags]
public enum StatelessServiceLifecycleEvent
{
  // This service event occurs when instance initialization begins. 
  //
  // All delegates are guaranteed to be executed before any of ICommunicationListeners is created.
  OnStartup,

  // This service event occurs when instance begins execution.
  //
  // All delegates are guaranteed to be executed after all ICommunicationListeners are opened. 
  OnRun,

  // This service event occurs when instance is shutting down.
  //
  // All delegates are guaranteed to be executed after all ICommunicationListeners are closed.
  //
  // **WARNING**: If event payload has IsAborting set to true then instance is terminated
  //              abnormally and there no more guarantees.
  OnShutdown,

  // This service event occurs when code package is added to service package. 
  OnCodePackageAdded,

  // This service event occurs when code package in service package is modified.
  OnCodePackageModified,

  // This service event occurs when code package is removed from service package.
  OnCodePackageRemoved,

  // This service event occurs when config package is added to service package.
  OnConfigPackageAdded,

  // This service event occurs when config package in service package is modified.
  OnConfigPackageModified,

  // This service event occurs when config package is removed from service package.
  OnConfigPackageRemoved,

  // This service event occurs when data package is removed to service package.
  OnDataPackageAdded,

  // This service event occurs when data package in service package is modified.
  OnDataPackageModified,

  // This service event occurs when data package is removed from service package.
  OnDataPackageRemoved
}

The event to react on is configured using UseEvent(...) method:

...

.DefineDelegate(
  delegateBuilder => 
  {
    delegateBuilder.UseEvent(/* event */);
  })

...

By default the delegate is configured to react on StatefulServiceLifecycleEvent.OnRun when configuring stateful service and StatelessServiceLifecycleEvent.OnRun when configuring stateless service.

Information

The background jobs are configured as a reaction on StatefulServiceLifecycleEvent.OnRun and StatelessServiceLifecycleEvent.OnRun events.

You also can configure multiple delegates to react on the same event:

...

.DefineDelegate(
  delegateBuilder => 
  {
    delegateBuilder.UseEvent(StatefulServiceLifecycleEvent.OnRun);
  })
.DefineDelegate(
  delegateBuilder => 
  {
    delegateBuilder.UseEvent(StatefulServiceLifecycleEvent.OnRun);
  })

...

... or you can configure the same delegate to react on multiple events:

...

.DefineDelegate(
  delegateBuilder => 
  {
    delegateBuilder.UseEvent(
      StatefulServiceLifecycleEvent.OnRun | StatefulServiceLifecycleEvent.OnShutdown);
  })

...

Detailed information about the event can be obtained from invocation context. The type of the invocation context depends on the service type: it is IStatefulServiceDelegateInvocationContext for stateful service and IStatelessServiceDelegateInvocationContext for stateless service.

Invocation context has the information about the event and in some cases it can include additional payload about the event.

Stateful Service event invocation contexts and payloads

OnChangeRole

Context

public interface IStatefulServiceDelegateInvocationContextOnChangeRole
  : IStatefulServiceDelegateInvocationContext
{
  IStatefulServiceEventPayloadOnChangeRole Payload { get; }
}

Payload

public interface IStatefulServiceEventPayloadOnChangeRole
{
  ReplicaRole NewRole { get; }
}

OnShutdown

Context

public interface IStatefulServiceDelegateInvocationContextOnShutdown
  : IStatefulServiceDelegateInvocationContext
{
  IStatefulServiceEventPayloadOnShutdown Payload { get; }
}

Payload

public interface IStatefulServiceEventPayloadOnShutdown
{
  // **true** indicates abnormal termination.
  bool IsAborting { get; }
}

OnDataLoss

Context

public interface IStatefulServiceDelegateInvocationContextOnDataLoss
  : IStatefulServiceDelegateInvocationContext
{
  IStatefulServiceEventPayloadOnDataLoss Payload { get; }
}

Payload

public interface IStatefulServiceEventPayloadOnDataLoss
  : IStatefulServiceDelegateInvocationContext
{
  IStatefulServiceRestoreContext RestoreContext { get; }
}

// Allows to perform replica state restore from the backup. 
// 
// Please see the official documentation for more explanation:
// https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-reliable-services-backup-restore
public interface IStatefulServiceRestoreContext
{
  Task RestoreAsync(
    RestoreDescription restoreDescription);

  Task RestoreAsync(
    RestoreDescription restoreDescription,
    CancellationToken cancellationToken);
}

Warning

If RestoreAsync completed successfully then underlying service will automatically report successful state restoration.

On*PackageAdded, On*PackageModified and On*PackageRemoved

Contexts

public interface IStatefulServiceDelegateInvocationContextOnPackageAdded<TPackage>
  : IStatefulServiceDelegateInvocationContext
{
  IServiceEventPayloadOnPackageAdded<TPackage> Payload { get; }
}

public interface IStatefulServiceDelegateInvocationContextOnPackageModified<TPackage>
  : IStatefulServiceDelegateInvocationContext
{
  IServiceEventPayloadOnPackageModified<TPackage> Payload { get; }
}

public interface IStatefulServiceDelegateInvocationContextOnPackageRemoved<TPackage>
  : IStatefulServiceDelegateInvocationContext
{
  IServiceEventPayloadOnPackageRemoved<TPackage> Payload { get; }
}

Payloads

public interface IServiceEventPayloadOnPackageAdded<TPackage>
{
  TPackage Package { get; }
}

public interface IServiceEventPayloadOnPackageModified<TPackage>
{
  TPackage OldPackage { get; }
  TPackage NewPackage { get; }
}

public interface IServiceEventPayloadOnPackageRemoved<TPackage>
{
  TPackage Package { get; }
}

Warning

The TPackage is CodePackage, ConfigurationPackage or DataPackage.

Stateless Service event invocation contexts and payloads

OnShutdown

Context

public interface IStatelessServiceDelegateInvocationContextOnShutdown
  : IStatelessServiceDelegateInvocationContext
{
  IStatelessServiceEventPayloadOnShutdown Payload { get; }
}

Payload

public interface IStatelessServiceEventPayloadOnShutdown
{
  // **true** indicates abnormal termination.
  bool IsAborting { get; }
}

On*PackageAdded, On*PackageModified and On*PackageRemoved

Contexts

public interface IStatelessServiceDelegateInvocationContextOnPackageAdded<TPackage>
  : IStatelessServiceDelegateInvocationContext
{
  IServiceEventPayloadOnPackageAdded<TPackage> Payload { get; }
}

public interface IStatelessServiceDelegateInvocationContextOnPackageModified<TPackage>
  : IStatelessServiceDelegateInvocationContext
{
  IServiceEventPayloadOnPackageModified<TPackage> Payload { get; }
}

public interface IStatelessServiceDelegateInvocationContextOnPackageRemoved<TPackage>
  : IStatelessServiceDelegateInvocationContext
{
  IServiceEventPayloadOnPackageRemoved<TPackage> Payload { get; }
}

Payloads

public interface IServiceEventPayloadOnPackageAdded<TPackage>
{
  TPackage Package { get; }
}

public interface IServiceEventPayloadOnPackageModified<TPackage>
{
  TPackage OldPackage { get; }
  TPackage NewPackage { get; }
}

public interface IServiceEventPayloadOnPackageRemoved<TPackage>
{
  TPackage Package { get; }
}

Warning

The TPackage is CodePackage, ConfigurationPackage or DataPackage.

Configuring action to execute

The action to execute is configured using UseDelegate(...) method:

...

.DefineDelegate(
  delegateBuilder => 
  {
    delegateBuilder.UseDelegate(() => { ... }));
  })

...

This method can accept both synchronous i.e. Action<...> and asynchronous Func<..., Task> actions with up to fifteen parameters.

Information

All actions whose return type is different from Task or Task<> are considered synchronous and their return values are ignored. All Action parameters are automatically populated using dependency injection

Asynchronous actions can require caller's CancellationToken. The CancellationToken can be obtained by adding additional CancellationToken to action's parameter list:

...

.DefineDelegate(
  delegateBuilder =>
  {
    delegateBuilder.UseDelegate(
      (CancellationToken cancellationToken) => Task.Delay(Timeout.Infinite, cancellationToken));
  }
)

...

In addition to CancellationToken, UseDelegate(...) supports direct injection of the event payload.

Warning

If delegate is configured to execute on multiple service event then direct payload injection will result in dependency injection error (because each payload is available only in scope of specific service event)

...

.DefineDelegate(
  delegateBuilder =>
  {
    delegateBuilder.UseDelegate(
      (IStatelessServiceEventPayloadOnShutdown eventPayload) => { ... }));
  }
)

...