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

Error using CustomLogConsistencyProvider standalone #8157

Closed
dvlprx opened this issue Nov 16, 2022 · 5 comments · Fixed by #8677
Closed

Error using CustomLogConsistencyProvider standalone #8157

dvlprx opened this issue Nov 16, 2022 · 5 comments · Fixed by #8677

Comments

@dvlprx
Copy link

dvlprx commented Nov 16, 2022

After upgrading from Orleans 3.5 to 7.0, the CustomLogConsistencyProvider failes with an exception. Maybe I am missing something, but to me it seems like a bug.

I tracked down the issue in the code and here is what I found out:

Reproduce:

  • Use AddCustomStorageBasedLogConsistencyProvider, via Orleans.Hosting.CustomStorageSiloBuilderExtensions
  • Don't configure any other LogConsistencyProvider

Result: An exception is thrown
'No service for type 'Orleans.Factory`2[Orleans.Runtime.IGrainContext,Orleans.EventSourcing.ILogConsistencyProtocolServices]' has been registered.'

Suspected reason:
The ILogConsistencyProtocolServices are not registered to the di-container inside AddCustomStorageBasedLogConsistencyProvider. When looking at the other providers, it IS registered inside AddLogStorageBasedLogConsistencyProvider.

I found 1 test, using that methods via Tests.GeoClusterTests.SiloBuilderConfigurator. In there, multiple providers are configured, including the storage-based one, that adds the ILogConsistencyProtocolServices to the di-container. So if we add both providers it works, but with CustomLogConsistencyProvider alone it doesn't.

Suggested solution:
Add the following lines to Orleans.Hosting.CustomStorageSiloBuilderExtensions, line 36:

services.TryAddSingleton<Factory<IGrainContext, ILogConsistencyProtocolServices>>(serviceProvider =>
{
    var factory = ActivatorUtilities.CreateFactory(typeof(ProtocolServices), new[] { typeof(IGrainContext) });
    return arg1 => (ILogConsistencyProtocolServices)factory(serviceProvider, new object[] { arg1 });
});

Workaround:

  1. Add the missing registration to your siloBuilder manually
siloBuilder.Services.TryAddSingleton<Factory<IGrainContext, ILogConsistencyProtocolServices>>(serviceProvider =>
{
    var factory = ActivatorUtilities.CreateFactory(typeof(ProtocolServices), new[] { typeof(IGrainContext) });
    return arg1 => (ILogConsistencyProtocolServices)factory(serviceProvider, new object[] { arg1 });
});
  1. Since ProtocolServices is internal, you have to copy the class to your code and use that version, inside (1)
@ghost ghost added the Needs: triage 🔍 label Nov 16, 2022
@dvlprx dvlprx changed the title #Bug: CustomLogConsistencyProvider not working standalone Error using CustomLogConsistencyProvider standalone Nov 16, 2022
@SilentBlueD666
Copy link

Snap! I get same bug too.

P.S. Thanks for the workaround.

@davidelettieri
Copy link
Contributor

Same here

@dvlprx
Copy link
Author

dvlprx commented Dec 23, 2022

The AddStateStorageBasedLogConsistencyProvider may have the same problem too. Also I'm not sure if it is related to 7.0 or would happen also in 3.5, because I did a pretty big refactoring during the migration.

@antomys
Copy link

antomys commented Feb 27, 2023

The issue is still relevant in the 7.1.0 version of the package.
For convenience, here is an actual (as of 27 Feb 2023) code for ProtocolServices

namespace Orleans.Runtime.LogConsistency
{
    /// <summary>
    /// Functionality for use by log view adaptors that run distributed protocols.
    /// This class allows access to these services to providers that cannot see runtime-internals.
    /// It also stores grain-specific information like the grain reference, and caches
    /// </summary>
    internal class ProtocolServices : ILogConsistencyProtocolServices
    {
        private readonly ILogger log;
        private readonly DeepCopier deepCopier;
        private readonly IGrainContext grainContext;   // links to the grain that owns this service object

        public ProtocolServices(
            IGrainContext grainContext,
            ILoggerFactory loggerFactory,
            DeepCopier deepCopier,
            ILocalSiloDetails siloDetails)
        {
            this.grainContext = grainContext;
            this.log = loggerFactory.CreateLogger<ProtocolServices>();
            this.deepCopier = deepCopier;
            this.MyClusterId = siloDetails.ClusterId;
        }

        public GrainId GrainId => grainContext.GrainId;

        public string MyClusterId { get; }

        public T DeepCopy<T>(T value) => this.deepCopier.Copy(value);

        public void ProtocolError(string msg, bool throwexception)
        {
            log.LogError(
                (int)(throwexception ? ErrorCode.LogConsistency_ProtocolFatalError : ErrorCode.LogConsistency_ProtocolError),
                "{GrainId} Protocol Error: {Message}",
                grainContext.GrainId,
                msg);

            if (!throwexception)
                return;

            throw new OrleansException(string.Format("{0} (grain={1}, cluster={2})", msg, grainContext.GrainId, this.MyClusterId));
        }

        public void CaughtException(string where, Exception e)
        {
            log.LogError(
                (int)ErrorCode.LogConsistency_CaughtException,
                e,
               "{GrainId} exception caught at {Location}",
               grainContext.GrainId,
               where);
        }

        public void CaughtUserCodeException(string callback, string where, Exception e)
        {
            log.LogWarning(
                (int)ErrorCode.LogConsistency_UserCodeException,
                e,
                "{GrainId} exception caught in user code for {Callback}, called from {Location}",
                grainContext.GrainId,
                callback,
                where);
        }

        public void Log(LogLevel level, string format, params object[] args)
        {
            if (log != null && log.IsEnabled(level))
            {
                var msg = $"{grainContext.GrainId} {string.Format(format, args)}";
                log.Log(level, 0, msg, null, (m, exc) => $"{m}");
            }
        }
    }
}

@BarnabyDove
Copy link

Can confirm the issue is still affecting version 7.2.1, and that the workaround solution outlined above by @dvlprx and @antomys still works.

Thank you both for providing this solution!

ikkentim pushed a commit to ikkentim/orleans that referenced this issue Oct 19, 2023
ReubenBond added a commit that referenced this issue Oct 26, 2023
…roviders (fixes #8157) (#8677)

Co-authored-by: Tim Potze <[email protected]>
Co-authored-by: Reuben Bond <[email protected]>
@ghost ghost added the Status: Fixed label Oct 26, 2023
@ghost ghost locked as resolved and limited conversation to collaborators Nov 25, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
5 participants