Closed jjanuszkiewicz closed 8 years ago
@jjanuszkiewicz Thanks for raising the issue. We will be looking into this. It seems this is coming from the following PR
https://github.com/Particular/NServiceBus/pull/3109
// cc @justabitofcode @ramonsmits
Probably the new interface that don't get registered https://github.com/Particular/NServiceBus/pull/3109/files#diff-6649827d4e8a8f8298fdee5cb143cd4d
Ok, I think I see the problem. The new interface is not properly registered
https://github.com/Particular/NServiceBus/pull/3109/files#diff-e5926d2503e6f8c4074305c7992cb378R36
Assigning to 5.2.15
since this needs patching
Talked to @ramonsmits, he is going to fix it. Assigned him
That was quick - thanks for looking into this!
I realize it might be a bit early, but can you give any ETA for 5.2.15
?
@jjanuszkiewicz I'm trying to reproduce this issue.
The reason of this error is the validation of the registrations:
container.AssertConfigurationIsValid();
You are not actually using any of the features where these entries are related to. I enabled all corresponding features and there was no exception.
busConfiguration.RijndaelEncryptionService();
busConfiguration.EnableSLAPerformanceCounter(TimeSpan.FromMinutes(1));
busConfiguration.EnableCriticalTimePerformanceCounter();
Probably locations that cause the issue:
The issue for these 3 entries is that we have incomplete registrations. Biggest question is, why are we enabling by default features that do not register all required dependancies and should be care? I think we do. I'm not familiar with how RegisterStep
works as it seems that these behavior are registered somewhere in the object builder.
@Scooletz helped to find how these behaviors are registered. We could not find (yet) how that happens with the register step logic. An existing contributor probably can answer this in a few minutes.
@danielmarbach Yes, we saw those lines but that is when building the behavior pipeline. That should not contain any of these behaviors in the first place I think.
By explicitly disabling the features is seems the corresponding behaviors are still (partially) registered in the container.
busConfiguration.DisableFeature<NServiceBus.Features.Encryptor>();
busConfiguration.DisableFeature<NServiceBus.Features.SLAMonitoring>();
busConfiguration.DisableFeature<NServiceBus.Features.CriticalTimeMonitoring>();
Seems like the following is called even though the feature is not enabled although not entirely sure yet:
Something sounds off, that line shouldn't be called if the feature is disabled?
Is there any estimate when this will be fixed?
@jjanuszkiewicz I'm continuing it now.
As a side-note, leaving out container.AssertConfigurationIsValid();
results in everything working but we are treating this as a bug as we should not have incomplete registrations.
Yes, there are incomplete registrations but they will not be resolved at all in this configurations as the dependent features are not activated (Encryptor, SLAMonitoring, CriticalTimeMonitoring).
I assume you want this to make sure all the dependencies for your handlers can be build by the container and that this is part of an acceptance test or similar?
@ramonsmits Yes, this is part of a test procedure that we run on our components.
@jjanuszkiewicz @andreasohlund Found the issue and resolved it in 9d4be09 and is part of PR #3518 which is now ready for review.
Needs verification on V6 too.
Guys, any update on this? And I can see that this is the single item in scope of 5.2.15, so will this actually get released?
@jjanuszkiewicz Thanks for the ping. Please see the following statement from @timbussmann
- we decided to not change the way behaviors are registered as a hotfix in order to avoid breaking changes to the changed behavior.
- we don't see enough value in fixing it in a 5.3 release right now. Because it would still require us to provide a backward compatible mechanisms due to our release policy. While there are incomplete dependency hierarchies, these types will never be resolved and will not cause any harm or undesired behavior.
https://github.com/Particular/NServiceBus/pull/3518#issuecomment-192254707
From: https://github.com/Particular/NServiceBus/pull/3518#issuecomment-191852473
It seems that calling container.AssertConfigurationIsValid()
has the side-effect of actually creating the types as commenting this statement started the bus as expected.
Inspecting the code doco:
Use with caution! Does a full environment test of the configuration of this container. Will try to create every configured instance and afterward calls any methods marked with the [ValidationMethod] attribute
The only working solution is:
Enable all features:
busConfiguration.RijndaelEncryptionService();
busConfiguration.EnableSLAPerformanceCounter(TimeSpan.FromMinutes(1));
busConfiguration.EnableCriticalTimePerformanceCounter();
Make sure you have atleast a dummy message with an encrypted property as not having this does not complete the encryption service setup. This also requires a valid encryption service configuration to satisfy the core IEncryptionService
implementation but can we circumvented by passing a fake implementation.
busConfiguration.RegisterEncryptionService(b=>new FakeEncryptionService());
// or
// http://nsubstitute.github.io/
busConfiguration.RegisterEncryptionService(b=>Substitute.For<ICalculator>());
@ramonsmits Thanks for the detailed follow-up.
While I understand the technical difficulties with this issue and I agree this is not critical (it only affects component "self-test" in our case), I'll really appreciate it if a fix is released in 5.3 (as I understand 5.2.x is not an option, right?)
We've just updated from 4.6 to 5.2 and given that 6 is still a work in progress, and already has a ton of changes, I don't see us adopting v6 anytime soon. Therefore I really hope this issue will be fixed in a 5.x minor update, as this is a regression against v4 and causes us to have a part of our self-test code disabled.
The proposed workaround is not acceptable - I'm not going to enable some random features and fiddle with configuration in production code just to make the selftests pass.
Hi @jjanuszkiewicz
We guarantee backward compatibility between minor versions. Changing the way we register behaviors would introduce a breaking change. This means we can only solve this as a new feature which allows to change the binding mechanics. While I won't say this won't happen in v5, our backlog is already quite full. There is no risk of data or message loss. It's not even affecting runtime behavior of the endpoint therefore this isn't classified as a critical bug.
AssertConfigurationIsValid()
has side effects which we're not taking into account. So even if we fix the issue you're experiencing right now, there is no guarantee it will work afterwards or not break with future changes. It's not part of the NServiceBus Framework and we're not actively verifying compatibility with AssertConfigurationIsValid()
or it's side effects.
We agree that the current behavior isn't optimal (regardless of AssertConfigurationIsValid()
) and we're taking this into account for the v6 release (which allows us to introduce breaking changes). I can understand that the provided workaround is ugly and not very satisfying for you but this will most probably not change anytime soon.
@jjanuszkiewicz You are not using this in production right? As mentioned, the side effects is that it actually creates all registrations meaning it will also start timers etc. and run tasks. You really don't want to just do that when an endpoint starts in production.
Yes, I realize that, thanks. We run it rutinely as part of integration tests (so not on prod env), but also have a possibility to trigger it on demand on a running component on any env, including production - although this is not done automatically.
I'm wondering if we could maybe change the approach a bit, as the actual reason why we run AssertConfigurationIsValid()
is to verify correct registration of our classes. So I'm thinking, maybe we should have a separate StructureMap container for NSB internals (or even let it go with whatever the default container is) and use the "main" SM container for our registrations only? But in that case, how can we register IBus
and ISendOnlyBus
in our container while leaving everything else in NSB-internal one? Would something like this be correct from NSB point of view (not yet tested if it works or even compiles):
var nsbContainer = new Container();
var busConfig = new BusConfiguration();
busConfig.UseContainer<StructureMapBuilder>(c => c.ExistingContainer(nsbContainer));
busConfig.UsePersistence<InMemoryPersistence>();
Bus.Create(busConfig);
var mainContainer = new Container(x =>
For<IBus>().Use(ctx => nsbContainer.GetInstance<IBus>());
For<ISendOnlyBus>().Use(ctx => nsbContainer.GetInstance<ISendOnlyBus>());
);
mainContainer.AssertConfigurationIsValid();
This should leave NSB internals in a separate container and just forward resolving of IBus
and ISendOnlyBus
to that container. AssertConfigurationIsValid()
on the main container would not try to instantiate NSB internals. Would that be a recommended approach?
you could probably have a dedicated test which just creates your container and calls AssertConfigurationIsValid
on it without including NServiceBus at all. In that case you only need to provide a fake binding for IBus
before you validate the bindings. Would that work?
That might work, yes. Thanks for the tip, I'll see what works best for our case.
@Particular/nservicebus-maintainers as long as we don't explicitly see AssertConfigurationIsValid
as supported (and included in the testing) I don't think there is a way to actually "fix" this. As long we don't verify that behavior, there's a risk of introducing a registration on every hotfix version which can break AssertConfigurationIsValid
again. So I'd say we either:
Given the ideas of reducing dependency on containers in general, option 2 seems to contradict this vision.
We know that we should not register our own types on the user container and we definitely want to move away from that. It would probably take a long time to achieve this vision but eventually we will be getting there. I think this issue contains enough valuable and good design choices as "workarounds" to this problem like:
IBus
and ISendOnlyBus
to the dedicated container from the main containerSo let's Close this bug without fixing it as we can't guarantee it
seems that we agree. Closing this then for the reasons in the comments before.
This fails on NServiceBus 5.2.11 (with StructureMap 3.1.6.186):
The last line crashes with:
The equivalent on NServiceBus 4.7.6 with StructureMap 2.6.4.1 succeeds: