Skip to content

Latest commit

 

History

History
109 lines (97 loc) · 7.77 KB

configuration-replacement.adoc

File metadata and controls

109 lines (97 loc) · 7.77 KB

On-the-fly configuration replacement

As previously mentioned in the definition for [bucket-bonfiguration] it is an immutable object. It is not possible to add, remove or change the limits for already created configuration, however, you can replace the configuration of the bucket via creating a new configuration instance and calling bucket.replaceConfiguration(newConfiguration, tokensInheritanceStrategy).

Why configuration replacement is not trivial?

  1. The first problem of configuration replacement is deciding on how to propagate available tokens from a bucket with a previous configuration to the bucket with a new configuration. If you don’t care about previous the bucket state then use TokensInheritanceStrategy.RESET. But it becomes a tricky problem when we expect that previous consumption(that has not been compensated by refill yet) should take effect on the bucket with a new configuration. In this case, you need to choose between:

  2. There is another problem when you are choosing PROPORTIONALLY, AS_IS or ADDITIVE or AS_IS and a bucket has more than one bandwidth. For example, how does replaceConfiguration implementation bind bandwidths to each other in the following example?

Bucket bucket = Bucket.builder()
    .addLimit(Bandwidth.simple(10, Duration.ofSeconds(1)))
    .addLimit(Bandwidth.simple(10000, Duration.ofHours(1)))
    .build();
    ...
BucketConfiguration newConfiguration = BucketConfiguration.configurationBuilder()
    .addLimit(Bandwidth.simple(5000, Duration.ofHours(1)))
    .addLimit(Bandwidth.simple(100, Duration.ofSeconds(10)))
    .build();
bucket.replaceConfiguration(newConfiguration, TokensInheritanceStrategy.AS_IS);

It is obvious that a simple strategy - copying tokens by bandwidth index will not work well in this case, because it highly depends on the order in which bandwidths were mentioned in the new and previous configuration.

Taking control over replacement process via bandwidth identifiers

Instead of inventing the backward magic Bucket4j provides you the ability to keep control this process by specifying identifiers for bandwidth, so in case of multiple bandwidth configuration replacement codes can copy available tokens by bandwidth ID. So it is better to rewrite the code above as follows:

Bucket bucket = Bucket.builder()
     .addLimit(Bandwidth.simple(10, Duration.ofSeconds(1)).withId("technical-limit"))
     .addLimit(Bandwidth.simple(10000, Duration.ofHours(1)).withId("business-limit"))
     .build();
     ...
     BucketConfiguration newConfiguration = BucketConfiguration.builder()
        .addLimit(Bandwidth.simple(5000, Duration.ofHours(1)).withId("business-limit"))
        .addLimit(Bandwidth.simple(100, Duration.ofSeconds(10)).withId("technical-limit"))
        .build();
     bucket.replaceConfiguration(newConfiguration, TokensInheritanceStrategy.PROPORTIONALLY);
There are the following rules for bandwidth identifiers:
  • By default bandwidth has <b>null</b> identifier.

  • null value of identifier equals to another null value if and only if there is only one bandwidth with a null identifier.

  • If an identifier for bandwidth is specified then it must be unique in the bucket. Bucket does not allow to create of several bandwidths with the same ID.

TokensInheritanceStrategy explanation

TokensInheritanceStrategy specifies the rules for inheritance of available tokens during configuration replacement process.

There are four strategies:
PROPORTIONALLY

Makes to copy available tokens proportional to bandwidth capacity by following formula: newAvailableTokens = availableTokensBeforeReplacement * (newBandwidthCapacity / capacityBeforeReplacement)

PROPORTIONALLY strategy examples:
  • Example 1: imagine bandwidth that was created by Bandwidth.classic(100, Refill.gready(10, Duration.ofMinutes(1))).

    At the moment of config replacement, there were 40 available tokens.

    After replacing this bandwidth by following Bandwidth.classic(200, Refill.gready(10, Duration.ofMinutes(1))) 40 available tokens will be multiplied by 2(200/100), and after replacement, we will have 80 available tokens.

  • Example 2: imagine bandwidth that was created by Bandwidth.classic(100, Refill.gready(10, Duration.ofMinutes(1))). At the moment of config replacement, there were 40 available tokens. After replacing this bandwidth by following Bandwidth.classic(20, Refill.gready(10, Duration.ofMinutes(1))) 40 available tokens will be multiplied by 0.2(20/100), and after replacement, we will have 8 available tokens.

AS_IS

Instructs to copy available tokens as is, but with one exclusion: if available tokens are greater than new capacity, available tokens will be decreased to new capacity.

AS_IS strategy examples:
  • Example 1: imagine bandwidth that was created by Bandwidth.classic(100, Refill.gready(10, Duration.ofMinutes(1))).

    At the moment of config replacement, it was 40 available tokens.

    After replacing this bandwidth by following Bandwidth.classic(200, Refill.gready(10, Duration.ofMinutes(1)))} 40 available tokens will be just copied, and after replacement, we will have 40 available tokens.

  • Example 2: imagine bandwidth that was created by Bandwidth.classic(100, Refill.gready(10, Duration.ofMinutes(1))).

    At the moment of config replacement, it was 40 available tokens.

    After replacing this bandwidth by following Bandwidth.classic(20, Refill.gready(10, Duration.ofMinutes(1))) 40 available tokens can not be copied as is because it is greater than new capacity, so available tokens will be reduced to 20.

RESET

Use this mode when you want just to forget about the previous bucket state. RESET just instructs to erase all previous states. Using this strategy equals removing a bucket and creating again with a new configuration.

ADDITIVE

Instructs to copy available tokens as is, but with one exclusion: if new bandwidth capacity is greater than old capacity, available tokens will be increased by the difference between the old and the new configuration.

The formula is following:
newAvailableTokens = Math.min(availableTokensBeforeReplacement, newBandwidthCapacity) + Math.max(0, newBandwidthCapacity - capacityBeforeReplacement)

ADDITIVE strategy examples:
  • Example 1: imagine bandwidth that was created by Bandwidth.classic(100, Refill.gready(10, Duration.ofMinutes(1))).

    At the moment of configuration replacement, it was 40 available tokens.

    After replacing this bandwidth by following Bandwidth.classic(200, Refill.gready(10, Duration.ofMinutes(1))) 40 available tokens will be copied and added to the difference between old and new configurations, and after replacement, we will have 140 available tokens.

  • Example 2: imagine bandwidth that was created by Bandwidth.classic(100, Refill.gready(10, Duration.ofMinutes(1))).

    At the moment of config replacement, it was 40 available tokens.

    After replacing this bandwidth by following Bandwidth.classic(20, Refill.gready(10, Duration.ofMinutes(1)))), and after replacement we will have 20 available tokens.

  • Example 3: imagine bandwidth that was created by Bandwidth.classic(100, Refill.gready(10, Duration.ofMinutes(1))).

    At the moment of config replacement, it was 10 available tokens.

    After replacing this bandwidth by following Bandwidth.classic(20, Refill.gready(10, Duration.ofMinutes(1)))), and after replacement, we will have 10 available tokens.