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> /// <summary>
/// Various Tapeti features which can be turned on or off. /// Various Tapeti features which can be turned on or off.
/// </summary> /// </summary>
ITapetiConfigFeatues Features { get; } /// <remarks>
/// Calling this method will freeze the feature set if <see cref="ITapetiConfigBuilder.DelayFeatures"/> is used.
/// </remarks>
ITapetiConfigFeatures GetFeatures();
/// <summary> /// <summary>
/// Provides access to the different kinds of registered middleware. /// Provides access to the different kinds of registered middleware.
@ -34,7 +37,7 @@ namespace Tapeti.Config
/// <summary> /// <summary>
/// Various Tapeti features which can be turned on or off. /// Various Tapeti features which can be turned on or off.
/// </summary> /// </summary>
public interface ITapetiConfigFeatues public interface ITapetiConfigFeatures
{ {
/// <summary> /// <summary>
/// Determines whether 'publisher confirms' are used. This RabbitMQ features allows Tapeti to /// 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); 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> /// <summary>
/// Disables 'publisher confirms'. This RabbitMQ features allows Tapeti to be notified if a message /// 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 /// 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 /// 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. /// result in never-ending flows. Only disable if you can accept those consequences.
/// </summary> /// </summary>
ITapetiConfigBuilder DisablePublisherConfirms(); ITapetiConfigFeaturesBuilder DisablePublisherConfirms();
/// <summary> /// <summary>
@ -74,7 +108,7 @@ namespace Tapeti.Config
/// and disables Tapeti.Flow from verifying if a request/response can be routed. This may /// 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. /// result in never-ending flows. Only disable if you can accept those consequences.
/// </summary> /// </summary>
ITapetiConfigBuilder SetPublisherConfirms(bool enabled); ITapetiConfigFeaturesBuilder SetPublisherConfirms(bool enabled);
/// <summary> /// <summary>
@ -84,7 +118,7 @@ namespace Tapeti.Config
/// Note that access to the RabbitMQ Management plugin's REST API is required for this /// 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. /// feature to work, since AMQP does not provide a way to query existing bindings.
/// </remarks> /// </remarks>
ITapetiConfigBuilder EnableDeclareDurableQueues(); ITapetiConfigFeaturesBuilder EnableDeclareDurableQueues();
/// <summary> /// <summary>
/// Configures the automatic creation of durable queues and updating of their bindings. /// 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 /// 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. /// feature to work, since AMQP does not provide a way to query existing bindings.
/// </remarks> /// </remarks>
ITapetiConfigBuilder SetDeclareDurableQueues(bool enabled); ITapetiConfigFeaturesBuilder SetDeclareDurableQueues(bool enabled);
/// <summary> /// <summary>
@ -102,7 +136,7 @@ namespace Tapeti.Config
/// exchange, which do not correspond to Tapeti's configuration, as these will cause an error /// exchange, which do not correspond to Tapeti's configuration, as these will cause an error
/// while verifying. /// while verifying.
/// </summary> /// </summary>
ITapetiConfigBuilder DisableVerifyDurableQueues(); ITapetiConfigFeaturesBuilder DisableVerifyDurableQueues();
/// <summary> /// <summary>
@ -111,7 +145,7 @@ namespace Tapeti.Config
/// exchange, which do not correspond to Tapeti's configuration, as these will cause an error /// exchange, which do not correspond to Tapeti's configuration, as these will cause an error
/// while verifying. /// while verifying.
/// </summary> /// </summary>
ITapetiConfigBuilder SetVerifyDurableQueues(bool enabled); ITapetiConfigFeaturesBuilder SetVerifyDurableQueues(bool enabled);
} }

View File

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

View File

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

View File

@ -135,7 +135,7 @@ namespace Tapeti
/// <inheritdoc /> /// <inheritdoc />
public ITapetiConfigBuilder DisablePublisherConfirms() public ITapetiConfigBuilder DisablePublisherConfirms()
{ {
GetConfig().SetPublisherConfirms(false); GetConfig().GetFeaturesBuilder().DisablePublisherConfirms();
return this; return this;
} }
@ -143,7 +143,7 @@ namespace Tapeti
/// <inheritdoc /> /// <inheritdoc />
public ITapetiConfigBuilder SetPublisherConfirms(bool enabled) public ITapetiConfigBuilder SetPublisherConfirms(bool enabled)
{ {
GetConfig().SetPublisherConfirms(enabled); GetConfig().GetFeaturesBuilder().SetPublisherConfirms(enabled);
return this; return this;
} }
@ -151,7 +151,7 @@ namespace Tapeti
/// <inheritdoc /> /// <inheritdoc />
public ITapetiConfigBuilder EnableDeclareDurableQueues() public ITapetiConfigBuilder EnableDeclareDurableQueues()
{ {
GetConfig().SetDeclareDurableQueues(true); GetConfig().GetFeaturesBuilder().EnableDeclareDurableQueues();
return this; return this;
} }
@ -159,7 +159,7 @@ namespace Tapeti
/// <inheritdoc /> /// <inheritdoc />
public ITapetiConfigBuilder SetDeclareDurableQueues(bool enabled) public ITapetiConfigBuilder SetDeclareDurableQueues(bool enabled)
{ {
GetConfig().SetDeclareDurableQueues(enabled); GetConfig().GetFeaturesBuilder().SetDeclareDurableQueues(enabled);
return this; return this;
} }
@ -167,7 +167,7 @@ namespace Tapeti
/// <inheritdoc /> /// <inheritdoc />
public ITapetiConfigBuilder DisableVerifyDurableQueues() public ITapetiConfigBuilder DisableVerifyDurableQueues()
{ {
GetConfig().SetVerifyDurableQueues(false); GetConfig().GetFeaturesBuilder().DisablePublisherConfirms();
return this; return this;
} }
@ -175,7 +175,15 @@ namespace Tapeti
/// <inheritdoc /> /// <inheritdoc />
public ITapetiConfigBuilder SetVerifyDurableQueues(bool enabled) 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; return this;
} }
@ -221,12 +229,13 @@ namespace Tapeti
/// <inheritdoc /> /// <inheritdoc />
internal class Config : ITapetiConfig internal class Config : ITapetiConfig
{ {
private readonly ConfigFeatures features = new(); private ConfigFeaturesBuilder? featuresBuilder = new();
private ITapetiConfigFeatures? features;
private readonly ConfigMiddleware middleware = new(); private readonly ConfigMiddleware middleware = new();
private readonly ConfigBindings bindings = new(); private readonly ConfigBindings bindings = new();
public IDependencyResolver DependencyResolver { get; } public IDependencyResolver DependencyResolver { get; }
public ITapetiConfigFeatues Features => features;
public ITapetiConfigMiddleware Middleware => middleware; public ITapetiConfigMiddleware Middleware => middleware;
public ITapetiConfigBindings Bindings => bindings; 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() public void Lock()
{ {
bindings.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) return featuresBuilder;
{
features.DeclareDurableQueues = enabled;
}
public void SetVerifyDurableQueues(bool enabled)
{
features.VerifyDurableQueues = enabled;
} }
} }
internal class ConfigFeatures : ITapetiConfigFeatues internal class ConfigFeatures : ITapetiConfigFeatures
{ {
public bool PublisherConfirms { get; internal set; } = true; public bool PublisherConfirms { get; internal set; } = true;
public bool DeclareDurableQueues { get; internal set; } 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 internal class ConfigMiddleware : ITapetiConfigMiddleware
{ {
private readonly List<IMessageMiddleware> messageMiddleware = new(); private readonly List<IMessageMiddleware> messageMiddleware = new();