Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve user experience in heterogeneous silos #8776

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions src/Orleans.Core.Abstractions/IDs/GrainTypePrefix.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,16 @@ public static class GrainTypePrefix
/// </summary>
public static readonly ReadOnlyMemory<byte> LegacyGrainPrefixBytes = Encoding.UTF8.GetBytes(LegacyGrainPrefix);

/// <summary>
/// The prefix for stub grain (grain without definitive type).
/// </summary>
public const string StubGrainPrefix = SystemPrefix + "grain.stub.";

/// <summary>
/// A span representation of <see cref="StubGrainPrefix"/>
/// </summary>
public static readonly ReadOnlyMemory<byte> StubGrainPrefixBytes = Encoding.UTF8.GetBytes(StubGrainPrefix);

/// <summary>
/// Returns <see langword="true"/> if the type is a client, <see langword="false"/> if not.
/// </summary>
Expand Down Expand Up @@ -99,5 +109,36 @@ public static class GrainTypePrefix
/// <param name="id">The grain id.</param>
/// <returns><see langword="true"/> if the type is a system target, <see langword="false"/> if not.</returns>
public static bool IsSystemTarget(this in GrainId id) => id.Type.IsSystemTarget();

/// <summary>
/// Returns <see langword="true"/> if the grain is a stub grain.
/// </summary>
/// <param name="type">The grain type.</param>
/// <returns><see langword="true"/> if the type is a stub type , <see langword="false"/> if not.</returns>
public static bool IsStubGrain(this in GrainType type) => type.AsSpan().StartsWith(StubGrainPrefixBytes.Span);

/// <summary>
/// Create a stub grain type.
/// </summary>
/// <param name="grainClassPrefix">The prefix of the class to be used.</param>
/// <returns>The grain type.</returns>
public static GrainType CreateStubGrainType(string grainClassPrefix)
{
return string.IsNullOrWhiteSpace(grainClassPrefix)
? new GrainType(StubGrainPrefixBytes.ToArray())
: GrainType.Create(string.Concat(StubGrainPrefix, grainClassPrefix));
}

public static bool TryGetCrainClassPrefix(GrainType grainType, out string grainClassPrefix)
{
if (!IsStubGrain(grainType))
{
grainClassPrefix = default;
return false;
}

grainClassPrefix = Encoding.UTF8.GetString(grainType.AsSpan().Slice(StubGrainPrefixBytes.Length));
return true;
}
}
}
8 changes: 6 additions & 2 deletions src/Orleans.Core.Abstractions/Runtime/GrainReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ public class GrainReference : IAddressable, IEquatable<GrainReference>, ISpanFor
/// The grain reference functionality which is shared by all grain references of a given type.
/// </summary>
[NonSerialized]
private readonly GrainReferenceShared _shared;
private GrainReferenceShared _shared;

/// <summary>
/// The underlying grain id key.
Expand All @@ -267,7 +267,11 @@ public class GrainReference : IAddressable, IEquatable<GrainReference>, ISpanFor
/// <summary>
/// Gets the grain reference functionality which is shared by all grain references of a given type.
/// </summary>
internal GrainReferenceShared Shared => _shared ?? throw new GrainReferenceNotBoundException(this);
internal GrainReferenceShared Shared
{
get => _shared ?? throw new GrainReferenceNotBoundException(this);
set => _shared = value;
}

/// <summary>
/// Gets the grain reference runtime.
Expand Down
4 changes: 4 additions & 0 deletions src/Orleans.Core/Core/DefaultClientServices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
using Orleans.Serialization.Cloning;
using Microsoft.Extensions.Hosting;
using System.Runtime.InteropServices;
using System;

namespace Orleans
{
Expand All @@ -42,6 +43,8 @@ public static void AddDefaultServices(IServiceCollection services)

services.Add(ServiceDescriptor);

services.TryAddSingleton<TimeProvider>(TimeProvider.System);

// Options logging
services.TryAddSingleton(typeof(IOptionFormatter<>), typeof(DefaultOptionsFormatter<>));
services.TryAddSingleton(typeof(IOptionFormatterResolver<>), typeof(DefaultOptionsFormatterResolver<>));
Expand Down Expand Up @@ -69,6 +72,7 @@ public static void AddDefaultServices(IServiceCollection services)
services.TryAddSingleton<GrainFactory>();
services.TryAddSingleton<GrainInterfaceTypeToGrainTypeResolver>();
services.TryAddSingleton<GrainReferenceActivator>();
services.AddSingleton<IGrainReferenceActivatorProvider, StubGrainReferenceActivatorProvider>();
services.AddSingleton<IGrainReferenceActivatorProvider, GrainReferenceActivatorProvider>();
services.AddSingleton<IGrainReferenceActivatorProvider, UntypedGrainReferenceActivatorProvider>();
services.TryAddSingleton<RpcProvider>();
Expand Down
21 changes: 16 additions & 5 deletions src/Orleans.Core/Core/GrainFactory.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Orleans.GrainReferences;
using Orleans.Metadata;
using Orleans.Runtime;
Expand Down Expand Up @@ -203,16 +206,24 @@ private IAddressable GetGrain(Type interfaceType, IdSpan grainKey, string grainC
{
var grainInterfaceType = this.interfaceTypeResolver.GetGrainInterfaceType(interfaceType);

GrainType grainType;
if (!string.IsNullOrWhiteSpace(grainClassNamePrefix))
bool success;
GrainType grainType = default;
try
{
grainType = this.interfaceTypeToGrainTypeResolver.GetGrainType(grainInterfaceType, grainClassNamePrefix);
success = interfaceTypeToGrainTypeResolver.TryGetGrainType(grainInterfaceType, grainClassNamePrefix, out grainType);
}
else
catch (ArgumentException)
{
grainType = this.interfaceTypeToGrainTypeResolver.GetGrainType(grainInterfaceType);
success = false;
}

if (!success)
{
// Not found in the type map. Maybe it's not available yet ? (heterogeneous case)
grainType = GrainTypePrefix.CreateStubGrainType(grainClassNamePrefix);
}

Debug.Assert(!grainType.IsDefault);
var grainId = GrainId.Create(grainType, grainKey);
var grain = this.referenceActivator.CreateReference(grainId, grainInterfaceType);
return grain;
Expand Down
79 changes: 53 additions & 26 deletions src/Orleans.Core/Core/GrainInterfaceTypeToGrainTypeResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,34 @@ public GrainInterfaceTypeToGrainTypeResolver(IClusterManifestProvider clusterMan
/// </summary>
public GrainType GetGrainType(GrainInterfaceType interfaceType, string prefix)
{
if (string.IsNullOrWhiteSpace(prefix))
if (string.IsNullOrEmpty(prefix))
{
return GetGrainType(interfaceType);
}

GrainType result = default;
if (!TryGetGrainType(interfaceType, prefix, out var grainType))
{
throw new ArgumentException($"Could not find an implementation matching prefix \"{prefix}\" for interface {interfaceType}");
}

return grainType;
}

/// <summary>
/// Resolves a <see cref="GrainType"/> which implements the provided <see cref="GrainInterfaceType"/>, returning <see langword="true"/> if an implementation was found; otherwise <see langword="false"/>.
/// </summary>
/// <param name="interfaceType">The grain interface type.</param>
/// <param name="prefix">A prefix of the grain implementation class name to search for.</param>
/// <param name="result">The resolved grain type.</param>
/// <returns><see langword="true"/> if an implementation was found; otherwise <see langword="false"/>.</returns>
public bool TryGetGrainType(GrainInterfaceType interfaceType, string prefix, out GrainType result)
{
if (string.IsNullOrEmpty(prefix))
{
return TryGetGrainType(interfaceType, out result);
}

result = default;
GrainInterfaceType lookupType;
if (GenericGrainInterfaceType.TryParse(interfaceType, out var genericInterface))
{
Expand Down Expand Up @@ -79,17 +100,12 @@ public GrainType GetGrainType(GrainInterfaceType interfaceType, string prefix)
}
}

if (result.IsDefault)
{
throw new ArgumentException($"Could not find an implementation matching prefix \"{prefix}\" for interface {interfaceType}");
}

if (GenericGrainType.TryParse(result, out var genericGrainType) && !genericGrainType.IsConstructed)
if (!result.IsDefault && GenericGrainType.TryParse(result, out var genericGrainType) && !genericGrainType.IsConstructed)
{
result = genericGrainType.GetConstructed(genericInterface);
}

return result;
return !result.IsDefault;
}

/// <summary>
Expand All @@ -108,33 +124,20 @@ public GrainType GetGrainType(GrainInterfaceType interfaceType)
/// <summary>
/// Resolves a <see cref="GrainType"/> which implements the provided <see cref="GrainInterfaceType"/>, returning <see langword="true"/> if an implementation was found; otherwise <see langword="false"/>.
/// </summary>
/// <param name="interfaceType">The grain interface type.</param>
/// <param name="result">The resolved grain type.</param>
/// <returns><see langword="true"/> if an implementation was found; otherwise <see langword="false"/>.</returns>
public bool TryGetGrainType(GrainInterfaceType interfaceType, out GrainType result)
{
result = default;
var cache = GetCache();
if (cache.Map.TryGetValue(interfaceType, out var entry))
{
if (!entry.PrimaryImplementation.IsDefault)
{
result = entry.PrimaryImplementation;
}
else if (entry.Implementations.Count == 1)
{
result = entry.Implementations[0].GrainType;
}
else if (entry.Implementations.Count > 1)
{
var candidates = string.Join(", ", entry.Implementations.Select(i => $"{i.GrainType} ({i.Prefix})"));
throw new ArgumentException($"Unable to identify a single appropriate grain type for interface {interfaceType}. Candidates: {candidates}");
}
else
{
// No implementations
}
TryFind(interfaceType, entry, out result);
}
else if (_genericMapping.TryGetValue(interfaceType, out result))
{
// Nothing needed here.
}
else if (GenericGrainInterfaceType.TryParse(interfaceType, out var genericInterface) && genericInterface.IsConstructed)
{
Expand Down Expand Up @@ -164,6 +167,30 @@ public bool TryGetGrainType(GrainInterfaceType interfaceType, out GrainType resu
return !result.IsDefault;
}

private bool TryFind(GrainInterfaceType interfaceType, CacheEntry entry, out GrainType result)
{
if (!entry.PrimaryImplementation.IsDefault)
{
result = entry.PrimaryImplementation;
}
else if (entry.Implementations.Count == 1)
{
result = entry.Implementations[0].GrainType;
}
else if (entry.Implementations.Count > 1)
{
var candidates = string.Join(", ", entry.Implementations.Select(i => $"{i.GrainType} ({i.Prefix})"));
throw new ArgumentException($"Unable to identify a single appropriate grain type for interface {interfaceType}. Candidates: {candidates}");
}
else
{
// No implementations
result = default;
}

return !result.IsDefault;
}

/// <summary>
/// Returns the cache, rebuilding it if it is out of date.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public Entry(MethodInfo implementationMethod, MethodInfo interfaceMethod)
}

/// <summary>
/// Gets the grain implmentation <see cref="MethodInfo"/>.
/// Gets the grain implementation <see cref="MethodInfo"/>.
/// </summary>
public MethodInfo ImplementationMethod { get; }

Expand Down
Loading
Loading