Should AppHost.OnAfterInit start the mq server?

I have the following code using aws sqs as mq service [snippet]:

public class TestAppHost : AppSelfHostBase
{
    public TestAppHost() : base("Testing", typeof(TestAppHost).Assembly)
    {
    }

    public override void Configure(Container container)
    {
        QueueNames.QueuePrefix = "SS-TEST";
        var mqServer = new ServiceStack.Aws.Sqs.SqsMqServer(accessKey, secret, RegionEndpoint.USWest2)
        {
              DisableBuffering= true,
              RetryCount = 1,
        }

       container.Register<IMessageService>(mqServer);
       container.Register<IMessageFactory>(mqServer.MessageFactory);

       mqServer.RegisterHandler<Type1Request>(ServiceController.ExecuteMessage);
       // ...
       mqServer.RegisterHandler<Type30Request>(ServiceController.ExecuteMessage);
    }

    public override void OnAfterInit()
    {
        var mqService = HostContext.TryResolve<IMessageService>();
        mqService.Start();
        
        System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5)); //This gives time for worker threads to actually run

        base.OnAfterInit();
    }       
}

I noticed the following. Because I sleep for 5s in OnAfterInit, it gives the sqs workers time to pull messages. However, service stack has not finished its “Init” which means arrays like RequestConvertersArray are null because PopulateArrayFilters has not had a chance to run [i.e. it is waiting for OnAfterInit to finish].

If there is a message on the queue when this code starts, it will be received before OnAfterInit returns,. However, RequestConvertersArray is null so ApplyRequestConvertersAsync throws an exception which results in Naq being invoked and moving the message to the dlq.

I looked at the sample test code MqServerIntroTests at function Does_dispose_request_scope_dependency_in_PostMessageHandler which does start the mq service in configure but that has the same problem [i.e. workers pulling messages before service stack is initialized].

One way around this is to start the mq service after calling Init on the app host. So my question comes down to: Is my understanding correct. Am I doing this right?

Another way to delay start until after it’s initialized is to register an AfterInitCallbacks, e.g:

AfterInitCallbacks.Add(host => {
    host.TryResolve<IMessageService>().Start();
});

Thanks for the info mythz! I had a “quick” look at when AfterInitCallbacks actions get invoked. It is inside ServiceStackHost.OnAfterInit But when I look at ServiceStackHost.Init I see it is implemented like:

...
    OnAfterInit(); // <-- callbacks invoked under this callstack

    PopulateArrayFilters();

    LogInitComplete();
...

So it appears the array filters are initialized after the callbacks would be invoked which I means I think the same problem would exist. However, I haven’t tried it so I could be wrong. I will try it out later today/tomorrow and get back to you to confirm.

Thanks!

Just to follow up. It does appear that callbacks registered with AfterInitCallbacks are still invoked too early. Maybe PopulateArrayFilters needs to be invoked before OnAfterInit?

Here is what I did when registering the callback:

            appHost.AfterInitCallbacks.Add(host =>
            {
                var mqServer = host.TryResolve<IMessageService>();
                mqServer.Start();

                System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
            });

This results in the worker threads starting to pull messages before the array filters have been initialized. When a message is pulled from a queue, the request filters are needed to filter but they have not yet been initialized resulting in a null reference exception.

I’ve moved executing AfterInitCallbacks to the end of Init() in this commit.

This change is available from v5.4.1 that’s now available on MyGet.

I think that would work. But out of curiosity, would you consider moving OnAfterInit after HttpHandlerFactory.Init? One benefit is that it means OnAfterInit truly means after the init is done. But certainly one down side is the impact on existing code is less certain (well, at least to me).

Thanks again!