Ignite-3: .NET Native Compute and future support for other platforms.

IDIEP-136
Author
Sponsor
Created

  

Status


Motivation

As a user, I want to implement Compute jobs in C#/.NET (or another language of my choice).

Use Cases

Case 1: “.NET Shop”

Case 2: “Cross-Team Interop”

Description

Requirements

Public API

There are two parts - job execution on client (ICompute.SubmitAsync) and job implementation on server.

Job Execution

Job execution API does not need to change. 

Ignite needs to understand the job platform to invoke a correct executor (Java, .NET, Python, etc).

Add JobExecutionOptions.ExecutorType property

Pros:

Notes

JobExecutionOptions options = JobExecutionOptions.builder().executorType(JobExecutorType.DotNet).build();

JobDescriptor<Object, Object> jobDesc = JobDescriptor
       .builder("MyNamespace.MyJob, MyAssembly")
       .options(options)
       .build();

.NET New APIs: IComputeJob, IJobExecutionContext

public interface IComputeJob<TArg, TResult>
{
   IMarshaller<TArg>? InputMarshaller => null;
   IMarshaller<TResult>? ResultMarshaller => null;

   ValueTask<TResult> ExecuteAsync(IJobExecutionContext context, TArg arg, CancellationToken cancellationToken);
}

public interface IJobExecutionContext
{
   IIgnite Ignite { get; }
}

Example job implementation:

public class ToStringJob : IComputeJob<object?, string?>
{
   public async ValueTask<string?> ExecuteAsync(IJobExecutionContext context, object? arg, CancellationToken cancellationToken)
   {
       await context.Ignite.Tables.GetTablesAsync();

       return arg?.ToString();
   }
}

Interop: Client Protocol (TCP Socket)

Execution Flow

Later: More Efficient Transports

Client protocol reuse is the quickest way to add platform compute to Ignite, and the most universal (easily implemented in any language). However, it is not the most performant (~40us overhead per API call - see Performance section below).

We expect to add an alternative, more performant mechanism in the future (such as embedded CLR with JNI interop, similar to Ignite 2.x).

Deployment Unit Handling

Error Handling

Exceptions in .NET jobs should be captured together with the full stack trace:


Refer to Ignite 2.x which successfully intermixed .NET and Java stack traces in a way that is displayed nicely in most IDEs.

Packaging

.NET Runtime Dependency

Implementing a .NET Compute Job

As a user, you follow the steps:


.NET Job Execution Implementation Details

Assembly Loading and Versioning

Use AssemblyLoadContext to implement loading/unloading and support multiple versions of the same assembly. 

Every deployment unit should have a separate load context.

Expose Ignite API to Compute Jobs

Use the same “special” client instance that handles job execution and expose it in JobExecutionContext

Client Protocol Changes

Limitations

No limitations. We expect to be able to run any user code as part of a Compute Job with this approach.

Performance


Benchmark            Mode  Cnt      Score      Error   Units
execJavaLocal        avgt    3     24.401 ±   11.221   us/op
execDotNetLocal      avgt    3     67.523 ±   61.175   us/op
execJavaLocalClient  avgt    3     70.879 ±   52.371   us/op
execJavaRemote       avgt    3    100.285 ±   45.470   us/op
execDotNetRemote     avgt    3    128.691 ±   73.251   us/op

~40 us overhead for a local socket roundtrip

Risks and Assumptions

None. Client protocol change will be handled via feature flag, preserving backwards and forwards compatibility.

Discussion Links

Reference Links

Tickets