1
0
mirror of synced 2024-12-22 09:13:07 +01:00

Added support for delaying feature initialisation

This commit is contained in:
Mark van Renswoude 2024-12-02 09:26:12 +01:00
parent d4ab9f87d1
commit d4cc061955
6 changed files with 151 additions and 32 deletions

9
.readthedocs.yaml Normal file
View File

@ -0,0 +1,9 @@
version 2
build
os ubuntu-22.04
tools
python 3.12
sphinx
configuration docs/conf.py

View File

@ -17,7 +17,10 @@ namespace Tapeti.Config
/// <summary>
/// Various Tapeti features which can be turned on or off.
/// </summary>
ITapetiConfigFeatues Features { get; }
/// <remarks>
/// Calling this method will freeze the feature set if <see cref="ITapetiConfigBuilder.DelayFeatures"/> is used.
/// </remarks>
ITapetiConfigFeatures GetFeatures();
/// <summary>
/// Provides access to the different kinds of registered middleware.
@ -34,7 +37,7 @@ namespace Tapeti.Config
/// <summary>
/// Various Tapeti features which can be turned on or off.
/// </summary>
public interface ITapetiConfigFeatues
public interface ITapetiConfigFeatures
{
/// <summary>
/// Determines whether 'publisher confirms' are used. This RabbitMQ features allows Tapeti to

View File

@ -53,6 +53,40 @@ namespace Tapeti.Config
void RegisterBinding(IBinding binding);
/// <inheritdoc cref="ITapetiConfigFeaturesBuilder.DisablePublisherConfirms"/>
ITapetiConfigBuilder DisablePublisherConfirms();
/// <inheritdoc cref="ITapetiConfigFeaturesBuilder.SetPublisherConfirms"/>
ITapetiConfigBuilder SetPublisherConfirms(bool enabled);
/// <inheritdoc cref="ITapetiConfigFeaturesBuilder.EnableDeclareDurableQueues"/>
ITapetiConfigBuilder EnableDeclareDurableQueues();
/// <inheritdoc cref="ITapetiConfigFeaturesBuilder.SetDeclareDurableQueues"/>
ITapetiConfigBuilder SetDeclareDurableQueues(bool enabled);
/// <inheritdoc cref="ITapetiConfigFeaturesBuilder.DisableVerifyDurableQueues"/>
ITapetiConfigBuilder DisableVerifyDurableQueues();
/// <inheritdoc cref="ITapetiConfigFeaturesBuilder.SetVerifyDurableQueues"/>
ITapetiConfigBuilder SetVerifyDurableQueues(bool enabled);
/// <summary>
/// Allows the core features to be determine on-demand when first required by the connection instead
/// of before <see cref="TapetiConnection"/> is constructed.
/// </summary>
/// <param name="onBuild">Called when the feature set is required. From that moment on the feature set is frozen.</param>
ITapetiConfigBuilder DelayFeatures(Action<ITapetiConfigFeaturesBuilder> onBuild);
}
/// <summary>
/// Configures Tapeti core features. Every method returns the builder instance for method chaining.
/// </summary>
public interface ITapetiConfigFeaturesBuilder
{
/// <summary>
/// Disables 'publisher confirms'. This RabbitMQ features allows Tapeti to be notified if a message
/// has no route, and guarantees delivery for request-response style messages and those marked with
@ -62,7 +96,7 @@ namespace Tapeti.Config
/// and disables Tapeti.Flow from verifying if a request/response can be routed. This may
/// result in never-ending flows. Only disable if you can accept those consequences.
/// </summary>
ITapetiConfigBuilder DisablePublisherConfirms();
ITapetiConfigFeaturesBuilder DisablePublisherConfirms();
/// <summary>
@ -74,7 +108,7 @@ namespace Tapeti.Config
/// and disables Tapeti.Flow from verifying if a request/response can be routed. This may
/// result in never-ending flows. Only disable if you can accept those consequences.
/// </summary>
ITapetiConfigBuilder SetPublisherConfirms(bool enabled);
ITapetiConfigFeaturesBuilder SetPublisherConfirms(bool enabled);
/// <summary>
@ -84,7 +118,7 @@ namespace Tapeti.Config
/// Note that access to the RabbitMQ Management plugin's REST API is required for this
/// feature to work, since AMQP does not provide a way to query existing bindings.
/// </remarks>
ITapetiConfigBuilder EnableDeclareDurableQueues();
ITapetiConfigFeaturesBuilder EnableDeclareDurableQueues();
/// <summary>
/// Configures the automatic creation of durable queues and updating of their bindings.
@ -93,7 +127,7 @@ namespace Tapeti.Config
/// Note that access to the RabbitMQ Management plugin's REST API is required for this
/// feature to work, since AMQP does not provide a way to query existing bindings.
/// </remarks>
ITapetiConfigBuilder SetDeclareDurableQueues(bool enabled);
ITapetiConfigFeaturesBuilder SetDeclareDurableQueues(bool enabled);
/// <summary>
@ -102,7 +136,7 @@ namespace Tapeti.Config
/// exchange, which do not correspond to Tapeti's configuration, as these will cause an error
/// while verifying.
/// </summary>
ITapetiConfigBuilder DisableVerifyDurableQueues();
ITapetiConfigFeaturesBuilder DisableVerifyDurableQueues();
/// <summary>
@ -111,7 +145,7 @@ namespace Tapeti.Config
/// exchange, which do not correspond to Tapeti's configuration, as these will cause an error
/// while verifying.
/// </summary>
ITapetiConfigBuilder SetVerifyDurableQueues(bool enabled);
ITapetiConfigFeaturesBuilder SetVerifyDurableQueues(bool enabled);
}

View File

@ -135,7 +135,7 @@ namespace Tapeti.Connection
DeclareExchange(channel, exchange);
// The delivery tag is lost after a reconnect, register under the new tag
if (config.Features.PublisherConfirms)
if (config.GetFeatures().PublisherConfirms)
{
lastDeliveryTag++;
@ -848,7 +848,7 @@ namespace Tapeti.Connection
}
if (config.Features.PublisherConfirms)
if (config.GetFeatures().PublisherConfirms)
{
lastDeliveryTag = 0;

View File

@ -125,9 +125,9 @@ namespace Tapeti.Connection
CustomBindingTarget bindingTarget;
if (config.Features.DeclareDurableQueues)
if (config.GetFeatures().DeclareDurableQueues)
bindingTarget = new DeclareDurableQueuesBindingTarget(clientFactory, routingKeyStrategy, exchangeStrategy, cancellationToken);
else if (config.Features.VerifyDurableQueues)
else if (config.GetFeatures().VerifyDurableQueues)
bindingTarget = new PassiveDurableQueuesBindingTarget(clientFactory, routingKeyStrategy, exchangeStrategy, cancellationToken);
else
bindingTarget = new NoVerifyBindingTarget(clientFactory, routingKeyStrategy, exchangeStrategy, cancellationToken);

View File

@ -135,7 +135,7 @@ namespace Tapeti
/// <inheritdoc />
public ITapetiConfigBuilder DisablePublisherConfirms()
{
GetConfig().SetPublisherConfirms(false);
GetConfig().GetFeaturesBuilder().DisablePublisherConfirms();
return this;
}
@ -143,7 +143,7 @@ namespace Tapeti
/// <inheritdoc />
public ITapetiConfigBuilder SetPublisherConfirms(bool enabled)
{
GetConfig().SetPublisherConfirms(enabled);
GetConfig().GetFeaturesBuilder().SetPublisherConfirms(enabled);
return this;
}
@ -151,7 +151,7 @@ namespace Tapeti
/// <inheritdoc />
public ITapetiConfigBuilder EnableDeclareDurableQueues()
{
GetConfig().SetDeclareDurableQueues(true);
GetConfig().GetFeaturesBuilder().EnableDeclareDurableQueues();
return this;
}
@ -159,7 +159,7 @@ namespace Tapeti
/// <inheritdoc />
public ITapetiConfigBuilder SetDeclareDurableQueues(bool enabled)
{
GetConfig().SetDeclareDurableQueues(enabled);
GetConfig().GetFeaturesBuilder().SetDeclareDurableQueues(enabled);
return this;
}
@ -167,7 +167,7 @@ namespace Tapeti
/// <inheritdoc />
public ITapetiConfigBuilder DisableVerifyDurableQueues()
{
GetConfig().SetVerifyDurableQueues(false);
GetConfig().GetFeaturesBuilder().DisablePublisherConfirms();
return this;
}
@ -175,7 +175,15 @@ namespace Tapeti
/// <inheritdoc />
public ITapetiConfigBuilder SetVerifyDurableQueues(bool enabled)
{
GetConfig().SetVerifyDurableQueues(enabled);
GetConfig().GetFeaturesBuilder().SetVerifyDurableQueues(enabled);
return this;
}
/// <inheritdoc />
public ITapetiConfigBuilder DelayFeatures(Action<ITapetiConfigFeaturesBuilder> onBuild)
{
GetConfig().GetFeaturesBuilder().DelayFeatures(onBuild);
return this;
}
@ -221,12 +229,13 @@ namespace Tapeti
/// <inheritdoc />
internal class Config : ITapetiConfig
{
private readonly ConfigFeatures features = new();
private ConfigFeaturesBuilder? featuresBuilder = new();
private ITapetiConfigFeatures? features;
private readonly ConfigMiddleware middleware = new();
private readonly ConfigBindings bindings = new();
public IDependencyResolver DependencyResolver { get; }
public ITapetiConfigFeatues Features => features;
public ITapetiConfigMiddleware Middleware => middleware;
public ITapetiConfigBindings Bindings => bindings;
@ -237,6 +246,17 @@ namespace Tapeti
}
public ITapetiConfigFeatures GetFeatures()
{
if (features != null)
return features;
features = featuresBuilder!.Build();
featuresBuilder = null;
return features;
}
public void Lock()
{
bindings.Lock();
@ -260,24 +280,17 @@ namespace Tapeti
}
public void SetPublisherConfirms(bool enabled)
internal ConfigFeaturesBuilder GetFeaturesBuilder()
{
features.PublisherConfirms = enabled;
}
if (featuresBuilder == null)
throw new InvalidOperationException("Tapeti features are already frozen");
public void SetDeclareDurableQueues(bool enabled)
{
features.DeclareDurableQueues = enabled;
}
public void SetVerifyDurableQueues(bool enabled)
{
features.VerifyDurableQueues = enabled;
return featuresBuilder;
}
}
internal class ConfigFeatures : ITapetiConfigFeatues
internal class ConfigFeatures : ITapetiConfigFeatures
{
public bool PublisherConfirms { get; internal set; } = true;
public bool DeclareDurableQueues { get; internal set; }
@ -285,6 +298,66 @@ namespace Tapeti
}
internal class ConfigFeaturesBuilder : ITapetiConfigFeaturesBuilder
{
private bool publisherConfirms = true;
private bool declareDurableQueues;
private bool verifyDurableQueues = true;
private Action<ITapetiConfigFeaturesBuilder>? onBuild;
public ITapetiConfigFeaturesBuilder DisablePublisherConfirms()
{
return SetPublisherConfirms(false);
}
public ITapetiConfigFeaturesBuilder SetPublisherConfirms(bool enabled)
{
publisherConfirms = enabled;
return this;
}
public ITapetiConfigFeaturesBuilder EnableDeclareDurableQueues()
{
return SetDeclareDurableQueues(true);
}
public ITapetiConfigFeaturesBuilder SetDeclareDurableQueues(bool enabled)
{
declareDurableQueues = enabled;
return this;
}
public ITapetiConfigFeaturesBuilder DisableVerifyDurableQueues()
{
return SetVerifyDurableQueues(false);
}
public ITapetiConfigFeaturesBuilder SetVerifyDurableQueues(bool enabled)
{
verifyDurableQueues = enabled;
return this;
}
// ReSharper disable once ParameterHidesMember
public void DelayFeatures(Action<ITapetiConfigFeaturesBuilder> onBuild)
{
this.onBuild = onBuild;
}
public ITapetiConfigFeatures Build()
{
onBuild?.Invoke(this);
return new ConfigFeatures
{
DeclareDurableQueues = declareDurableQueues,
PublisherConfirms = publisherConfirms,
VerifyDurableQueues = verifyDurableQueues
};
}
}
internal class ConfigMiddleware : ITapetiConfigMiddleware
{
private readonly List<IMessageMiddleware> messageMiddleware = new();