<commit_before>=== Quick start examples
==== How to dependency to Bucket4j
The Bucket4j is distributed through https://mvnrepository.com/artifact/com.github.vladimir-bukhtoyarov/bucket4j-core[Maven Central].
You need to add the dependency to your project as described below in order to be able to compile and run examples

.Maven dependency
[source, xml, subs=attributes+]
----
<dependency>
    <groupId>com.github.vladimir-bukhtoyarov</groupId>
    <artifactId>bucket4j-core</artifactId>
    <version>{revnumber}</version>
</dependency>
----

.Gradle dependency
[source, groovy, subs=attributes+]
----
implementation 'com.github.vladimir-bukhtoyarov:bucket4j-core:{revnumber}'
----

==== Create your first Bucket, limiting the rate of heavy work
Imagine that you have a thread-pool executor and you want to know what your threads are doing at the moment when thread-pool throws RejectedExecutionException.
Printing stack traces of all threads in the JVM will be the best way to know where are all threads have stuck and why the thread pool is overflown.
But acquiring stack traces is a very cost operation by itself, and you want to do it not often than 1 time per 10 minutes:
[source, java]
----
// define the limit 1 time per 10 minute
Bandwidth limit = Bandwidth.simple(1, Duration.ofMinutes(10));
// construct the bucket
Bucket bucket = Bucket.builder().addLimit(limit).build();

...

try {
   executor.execute(anyRunnable);
} catch (RejectedExecutionException e) {
    // print stacktraces only if limit is not exceeded
    if (bucket.tryConsume(1)) {
        ThreadInfo[] stackTraces = ManagementFactory.getThreadMXBean().dumpAllThreads(true, true);
        StacktraceUtils.print(stackTraces);
    }
    throw e;
}
----

==== Using bucket as scheduler
Suppose you need to have a fresh exchange rate between dollars and euros.
To get the rate you continuously poll the third-party provider,
and by contract with the provider you should poll not often than 100 times per 1 minute, else provider will block your IP:
[source, java]
----
// define the limit 100 times per 1 minute
Bandwidth limit = Bandwidth.simple(100, Duration.ofMinutes(1));
// construct the bucket
Bucket bucket = Bucket.builder().addLimit(limit).build();

...
volatile double exchangeRate;
...

// do polling in infinite loop
while (true) {
  // Consume a token from the token bucket.
  // If a token is not available this method will block until the refill adds one to the bucket.
  bucket.asScheduler().consume(1);

  exchangeRate = pollExchangeRate();
}
----

==== Limiting the rate of access to REST API
Imagine that you develop yet another social network and you want to provide REST API for third-party developers.
To protect your system from overloading you want to introduce the following limitation:

> The bucket size is 50 calls (which cannot be exceeded at any given time), with a "refill rate" of 10 calls per second that continually increases tokens in the bucket.
In other words. if the client app averages 10 calls per second, it will never be throttled,
and moreover, the client has overdraft equals to 50 calls which can be used if the average is a little bit higher than 10 calls/sec in a short time period.

Constructing the bucket to satisfy the requirements above is a little bit more complicated than for previous examples,
because we have to deal with overdraft, but it is not rocket science:
[source, java]
----
import io.github.bucket4j.Bucket4j;

public class ThrottlingFilter implements javax.servlet.Filter {

    private Bucket createNewBucket() {
         long overdraft = 50;
         Refill refill = Refill.greedy(10, Duration.ofSeconds(1));
         Bandwidth limit = Bandwidth.classic(overdraft, refill);
         return Bucket.builder().addLimit(limit).build();
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
        HttpSession session = httpRequest.getSession(true);

        String appKey = SecurityUtils.getThirdPartyAppKey();
        Bucket bucket = (Bucket) session.getAttribute("throttler-" + appKey);
        if (bucket == null) {
            bucket = createNewBucket();
            session.setAttribute("throttler-" + appKey, bucket);
        }

        // tryConsume returns false immediately if no tokens available with the bucket
        if (bucket.tryConsume(1)) {
            // the limit is not exceeded
            filterChain.doFilter(servletRequest, servletResponse);
        } else {
            // limit is exceeded
            HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
            httpResponse.setContentType("text/plain");
            httpResponse.setStatus(429);
            httpResponse.getWriter().append("Too many requests");
        }
    }

}
----
If you want to provide more information to the end-user about the state of the bucket, then the last fragment of code above can be rewritten in the following way:
[source, java]
----
        HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
        ConsumptionProbe probe = bucket.tryConsumeAndReturnRemaining(1);
        if (probe.isConsumed()) {
            // the limit is not exceeded
            httpResponse.setHeader("X-Rate-Limit-Remaining", "" + probe.getRemainingTokens());
            filterChain.doFilter(servletRequest, servletResponse);
        } else {
            // limit is exceeded
            HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
            httpResponse.setStatus(429);
            httpResponse.setHeader("X-Rate-Limit-Retry-After-Seconds", "" + TimeUnit.NANOSECONDS.toSeconds(probe.getNanosToWaitForRefill()));
            httpResponse.setContentType("text/plain");
            httpResponse.getWriter().append("Too many requests");
        }
----

==== Example of multiple bandwidth
Imagine that you are developing a load testing tool, in order to ensure that a testable system is able to dispatch 1000 requests per 1 minute.
But you do not want to randomly kill the testable system by generating all 1000 events in one second instead of 1 minute.
To solve the problem you can construct the following bucket:
[source, java]
----
static final long MAX_WAIT_NANOS = TimeUnit.HOURS.toNanos(1);
// ...

Bucket bucket = Bucket.builder()
       // allows 1000 tokens per 1 minute
       .addLimit(Bandwidth.simple(1000, Duration.ofMinutes(1)))
       // but not often then 50 tokens per 1 second
       .addLimit(Bandwidth.simple(50, Duration.ofSeconds(1)))
       .build();

// ...
while (true) {
  // Consume a token from the token bucket.  If a token is not available this method will block until the refill adds one to the bucket.
  if (bucket.tryConsume(1, MAX_WAIT_NANOS, BlockingStrategy.PARKING)) {
       workloadExecutor.execute(new LoadTask());
  };
}
----

==== Specifying initial amount of tokens
By default initial size of the bucket equals capacity.
But sometimes, you may want to have a lesser initial size, for example for the case of cold start in order to prevent denial of service:

[source, java]
----
int initialTokens = 42;
Bandwidth limit = Bandwidth
    .simple(1000, Duration.ofHours(1))
    .withInitialTokens(initialTokens);
Bucket bucket = Bucket.builder()
    .addLimit(limit)
    .build();
----

==== Turning-off the refill greediness
When bandwidth is created via ``Bandwidth#simple`` method it does refill in a greedy manner, because bandwidth tries to add the tokens to the bucket as soon as possible.
For example bandwidth with refill "10 tokens per 1 second" will add 1 token per every 100 milliseconds,
in other words, the refill will not wait 1 second to regenerate a whole bunch of 10 tokens.

If greediness is undesired then you should explicitly choose a non-greedy refill.
For example, the bandwidth bellow will refill 10 tokens per 1 second instead of 1 token per 100 milliseconds:
[source, java]
----
// When refill created via "intervally" factory method then greediness is turned-off.
Refill refill = Refill.intervally(10, Duration.ofSeconds(1));
Bandwidth bandwidth = Bandwidth.classic(600, refill);
----

Also, it is possible to specify the time when the first refill should happen.
This option can be used to configure clear interval boundary i.e. start of the second, minute, hour, day.
[source, java]
----
   // imagine that wall clock is 16:20, and we need to schedule the first refill to 17:00
   Instant firstRefillTime = ZonedDateTime.now()
             .truncatedTo(ChronoUnit.HOURS)
             .plus(1, ChronoUnit.HOURS)
             .toInstant();

   // see detailed explanation for useAdaptiveInitialTokens in the javadocs for 'intervallyAligned' method
   boolean useAdaptiveInitialTokens = false;

   Bandwidth.classic(400, Refill.intervallyAligned(400, Duration.ofHours(1), firstRefillTime, useAdaptiveInitialTokens));
----


==== Returning tokens back to bucket
The https://en.wikipedia.org/wiki/Compensating_transaction[compensating transaction] is one of the obvious use cases when you want to return tokens back to the bucket:
[source, java]
----
Bucket wallet;
...
if (wallet.tryConsume(50)) { // get 50 cents from wallet
    try {
        buyCocaCola();
    } catch(NoCocaColaException e) {
        // return money to wallet
        wallet.addTokens(50);
    }
}
----

==== Customizing time measurement - choosing nanotime time resolution
By default Bucket4j uses millisecond time resolution, it is the preferred time measurement strategy.
But rarely(for example benchmarking) do you wish the nanosecond precision:
[source, java]
----
Bucket.builder().withNanosecondPrecision()
----
Be very careful to choose this time measurement strategy, because ``System.nanoTime()`` produces inaccurate results,
use this strategy only if the period of bandwidth is too small that millisecond resolution will be undesired.

==== Customizing time measurement -  Specify custom time measurement strategy
You can specify your custom time meter if existing milliseconds or nanotime time meters are not enough for your purposes.
Imagine that you have a clock, which synchronizes its time with other machines in the current cluster,
if you want to use the time provided by this clock instead of time provided by JVM then you can write something like this:

[source, java]
----
public class ClusteredTimeMeter implements TimeMeter {

    @Override
    public long currentTimeNanos() {
        return ClusteredClock.currentTimeMillis() * 1_000_000;
    }

}

Bandwidth limit = Bandwidth.simple(100, Duration.ofMinutes(1));
Bucket bucket = Bucket.builder()
    .withCustomTimePrecision(new ClusteredTimeMeter())
    .addLimit(limit)
    .build();
----
<commit_msg>#253: fix typos in docs<commit_after>=== Quick start examples
==== How to dependency to Bucket4j
The Bucket4j is distributed through https://mvnrepository.com/artifact/com.github.vladimir-bukhtoyarov/bucket4j-core[Maven Central].
You need to add the dependency to your project as described below in order to be able to compile and run examples

.Maven dependency
[source, xml, subs=attributes+]
----
<dependency>
    <groupId>com.github.vladimir-bukhtoyarov</groupId>
    <artifactId>bucket4j-core</artifactId>
    <version>{revnumber}</version>
</dependency>
----

.Gradle dependency
[source, groovy, subs=attributes+]
----
implementation 'com.github.vladimir-bukhtoyarov:bucket4j-core:{revnumber}'
----

==== Create your first Bucket, limiting the rate of heavy work
Imagine that you have a thread-pool executor and you want to know what your threads are doing at the moment when thread-pool throws RejectedExecutionException.
Printing stack traces of all threads in the JVM will be the best way to know where are all threads have stuck and why the thread pool is overflown.
But acquiring stack traces is a very cost operation by itself, and you want to do it not often than 1 time per 10 minutes:
[source, java]
----
// define the limit 1 time per 10 minute
Bandwidth limit = Bandwidth.simple(1, Duration.ofMinutes(10));
// construct the bucket
Bucket bucket = Bucket.builder().addLimit(limit).build();

...

try {
   executor.execute(anyRunnable);
} catch (RejectedExecutionException e) {
    // print stacktraces only if limit is not exceeded
    if (bucket.tryConsume(1)) {
        ThreadInfo[] stackTraces = ManagementFactory.getThreadMXBean().dumpAllThreads(true, true);
        StacktraceUtils.print(stackTraces);
    }
    throw e;
}
----

==== Using bucket as scheduler
Suppose you need to have a fresh exchange rate between dollars and euros.
To get the rate you continuously poll the third-party provider,
and by contract with the provider you should poll not often than 100 times per 1 minute, else provider will block your IP:
[source, java]
----
// define the limit 100 times per 1 minute
Bandwidth limit = Bandwidth.simple(100, Duration.ofMinutes(1));
// construct the bucket
Bucket bucket = Bucket.builder().addLimit(limit).build();

...
volatile double exchangeRate;
...

// do polling in infinite loop
while (true) {
  // Consume a token from the token bucket.
  // If a token is not available this method will block until the refill adds one to the bucket.
  bucket.asScheduler().consume(1);

  exchangeRate = pollExchangeRate();
}
----

==== Limiting the rate of access to REST API
Imagine that you develop yet another social network and you want to provide REST API for third-party developers.
To protect your system from overloading you want to introduce the following limitation:

> The bucket size is 50 calls (which cannot be exceeded at any given time), with a "refill rate" of 10 calls per second that continually increases tokens in the bucket.
In other words. if the client app averages 10 calls per second, it will never be throttled,
and moreover, the client has overdraft equals to 50 calls which can be used if the average is a little bit higher than 10 calls/sec in a short time period.

Constructing the bucket to satisfy the requirements above is a little bit more complicated than for previous examples,
because we have to deal with overdraft, but it is not rocket science:
[source, java]
----
import io.github.bucket4j.Bucket4j;

public class ThrottlingFilter implements javax.servlet.Filter {

    private Bucket createNewBucket() {
         long overdraft = 50;
         Refill refill = Refill.greedy(10, Duration.ofSeconds(1));
         Bandwidth limit = Bandwidth.classic(overdraft, refill);
         return Bucket.builder().addLimit(limit).build();
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
        HttpSession session = httpRequest.getSession(true);

        String appKey = SecurityUtils.getThirdPartyAppKey();
        Bucket bucket = (Bucket) session.getAttribute("throttler-" + appKey);
        if (bucket == null) {
            bucket = createNewBucket();
            session.setAttribute("throttler-" + appKey, bucket);
        }

        // tryConsume returns false immediately if no tokens available with the bucket
        if (bucket.tryConsume(1)) {
            // the limit is not exceeded
            filterChain.doFilter(servletRequest, servletResponse);
        } else {
            // limit is exceeded
            HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
            httpResponse.setContentType("text/plain");
            httpResponse.setStatus(429);
            httpResponse.getWriter().append("Too many requests");
        }
    }

}
----
If you want to provide more information to the end-user about the state of the bucket, then the last fragment of code above can be rewritten in the following way:
[source, java]
----
        HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
        ConsumptionProbe probe = bucket.tryConsumeAndReturnRemaining(1);
        if (probe.isConsumed()) {
            // the limit is not exceeded
            httpResponse.setHeader("X-Rate-Limit-Remaining", "" + probe.getRemainingTokens());
            filterChain.doFilter(servletRequest, servletResponse);
        } else {
            // limit is exceeded
            HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
            httpResponse.setStatus(429);
            httpResponse.setHeader("X-Rate-Limit-Retry-After-Seconds", "" + TimeUnit.NANOSECONDS.toSeconds(probe.getNanosToWaitForRefill()));
            httpResponse.setContentType("text/plain");
            httpResponse.getWriter().append("Too many requests");
        }
----

==== Example of multiple bandwidth
Imagine that you are developing a load testing tool, in order to ensure that a testable system is able to dispatch 1000 requests per 1 minute.
But you do not want to randomly kill the testable system by generating all 1000 events in one second instead of 1 minute.
To solve the problem you can construct the following bucket:
[source, java]
----
static final long MAX_WAIT_NANOS = TimeUnit.HOURS.toNanos(1);
// ...

Bucket bucket = Bucket.builder()
       // allows 1000 tokens per 1 minute
       .addLimit(Bandwidth.simple(1000, Duration.ofMinutes(1)))
       // but not often then 50 tokens per 1 second
       .addLimit(Bandwidth.simple(50, Duration.ofSeconds(1)))
       .build();

// ...
while (true) {
  // Consume a token from the token bucket.  If a token is not available this method will block until the refill adds one to the bucket.
  if (bucket.asBlocking().tryConsume(1, MAX_WAIT_NANOS, BlockingStrategy.PARKING)) {
       workloadExecutor.execute(new LoadTask());
  };
}
----

==== Specifying initial amount of tokens
By default initial size of the bucket equals capacity.
But sometimes, you may want to have a lesser initial size, for example for the case of cold start in order to prevent denial of service:

[source, java]
----
int initialTokens = 42;
Bandwidth limit = Bandwidth
    .simple(1000, Duration.ofHours(1))
    .withInitialTokens(initialTokens);
Bucket bucket = Bucket.builder()
    .addLimit(limit)
    .build();
----

==== Turning-off the refill greediness
When bandwidth is created via ``Bandwidth#simple`` method it does refill in a greedy manner, because bandwidth tries to add the tokens to the bucket as soon as possible.
For example bandwidth with refill "10 tokens per 1 second" will add 1 token per every 100 milliseconds,
in other words, the refill will not wait 1 second to regenerate a whole bunch of 10 tokens.

If greediness is undesired then you should explicitly choose a non-greedy refill.
For example, the bandwidth bellow will refill 10 tokens per 1 second instead of 1 token per 100 milliseconds:
[source, java]
----
// When refill created via "intervally" factory method then greediness is turned-off.
Refill refill = Refill.intervally(10, Duration.ofSeconds(1));
Bandwidth bandwidth = Bandwidth.classic(600, refill);
----

Also, it is possible to specify the time when the first refill should happen.
This option can be used to configure clear interval boundary i.e. start of the second, minute, hour, day.
[source, java]
----
   // imagine that wall clock is 16:20, and we need to schedule the first refill to 17:00
   Instant firstRefillTime = ZonedDateTime.now()
             .truncatedTo(ChronoUnit.HOURS)
             .plus(1, ChronoUnit.HOURS)
             .toInstant();

   // see detailed explanation for useAdaptiveInitialTokens in the javadocs for 'intervallyAligned' method
   boolean useAdaptiveInitialTokens = false;

   Bandwidth.classic(400, Refill.intervallyAligned(400, Duration.ofHours(1), firstRefillTime, useAdaptiveInitialTokens));
----


==== Returning tokens back to bucket
The https://en.wikipedia.org/wiki/Compensating_transaction[compensating transaction] is one of the obvious use cases when you want to return tokens back to the bucket:
[source, java]
----
Bucket wallet;
...
if (wallet.tryConsume(50)) { // get 50 cents from wallet
    try {
        buyCocaCola();
    } catch(NoCocaColaException e) {
        // return money to wallet
        wallet.addTokens(50);
    }
}
----

==== Customizing time measurement - choosing nanotime time resolution
By default Bucket4j uses millisecond time resolution, it is the preferred time measurement strategy.
But rarely(for example benchmarking) do you wish the nanosecond precision:
[source, java]
----
Bucket.builder().withNanosecondPrecision()
----
Be very careful to choose this time measurement strategy, because ``System.nanoTime()`` produces inaccurate results,
use this strategy only if the period of bandwidth is too small that millisecond resolution will be undesired.

==== Customizing time measurement -  Specify custom time measurement strategy
You can specify your custom time meter if existing milliseconds or nanotime time meters are not enough for your purposes.
Imagine that you have a clock, which synchronizes its time with other machines in the current cluster,
if you want to use the time provided by this clock instead of time provided by JVM then you can write something like this:

[source, java]
----
public class ClusteredTimeMeter implements TimeMeter {

    @Override
    public long currentTimeNanos() {
        return ClusteredClock.currentTimeMillis() * 1_000_000;
    }

}

Bandwidth limit = Bandwidth.simple(100, Duration.ofMinutes(1));
Bucket bucket = Bucket.builder()
    .withCustomTimePrecision(new ClusteredTimeMeter())
    .addLimit(limit)
    .build();
----

{'fraction_non_alphanumeric': 0.08495403520118261, 'fraction_numerical': 0.01353536286783388, 'mean_word_length': 3.6276186404446347, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 21, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4112027', 'n_tokens_mistral': 5993, 'n_tokens_neox': 5613, 'n_words': 2368}