question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

New Annotation to combine different Annotations

See original GitHub issue

Problems with current annotations:

  • you cannot specify more then one rate limiter
  • you cannot specify the order of decorators (for example specify if rate limiter should take precedent before retries to not count them or should it be after the retryier to count the retries.

Proposal for new annotations:

@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD})
@Documented
public @interface ResilienceDecorators {

  public Resilience[] value();
}

@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD})
@Repeatable(ResilienceDecorators.class)
@Documented
public @interface Resilience {

  Retry[] retry() default {};

  RateLimiter[] rateLimiter() default {};

  @interface Retry {

    public static final String DEFAULT_CONFIG = "default";

    public String name();

    public String baseConfig() default DEFAULT_CONFIG;
  }

  @interface RateLimiter {

    static String FIXED_WEIGHT = "__FIXED_WEIGHT__";

    String name();

    int weight() default 1;

    String weightCalculator() default FIXED_WEIGHT;
  }
}

Usage examples on a JAX-RS interface for a REST Proxy Client framework like retrofit or si.mazi.rescu:

@Path("")
@Produces(MediaType.APPLICATION_JSON)
public interface Binance {

  @GET
  @Path("api/v1/exchangeInfo")
  @Resilience(retry = @Retry(name = "exchangeInfo"))
  @Resilience(rateLimiter = @RateLimiter(name = REQUEST_WEIGHT_RATE_LIMITER))
  BinanceExchangeInfo exchangeInfo() throws IOException;

  @GET
  @Path("api/v1/depth")
  @Resilience(retry = @Retry(name = "depth"))
  @Resilience(
      rateLimiter =
          @RateLimiter(name = REQUEST_WEIGHT_RATE_LIMITER, weightCalculator = "depthWeight"))
  BinanceOrderbook depth(@QueryParam("symbol") String symbol, @QueryParam("limit") Integer limit)
      throws IOException, BinanceException;

  public static int depthWeight(String symbol, Integer limit) {
    if (limit <= 100) {
      return 1;
    } else if (limit <= 500) {
      return 5;
    } else if (limit <= 1000) {
      return 10;
    }
    return 50;
  }

  @GET
  @Path("api/v1/aggTrades")
  @Resilience(retry = @Retry(name = "aggTrades"))
  @Resilience(rateLimiter = @RateLimiter(name = REQUEST_WEIGHT_RATE_LIMITER))
  List<BinanceAggTrades> aggTrades(
      @QueryParam("symbol") String symbol,
      @QueryParam("fromId") Long fromId,
      @QueryParam("startTime") Long startTime,
      @QueryParam("endTime") Long endTime,
      @QueryParam("limit") Integer limit)
      throws IOException, BinanceException;

  @GET
  @Path("api/v1/ticker/24hr")
  @Resilience(retry = @Retry(name = "ticker24h"))
  @Resilience(rateLimiter = @RateLimiter(name = REQUEST_WEIGHT_RATE_LIMITER, weight = 5))
  List<BinanceTicker24h> ticker24h() throws IOException, BinanceException;

  @GET
  @Path("api/v1/ticker/24hr")
  @Resilience(retry = @Retry(name = "ticker24h"))
  @Resilience(rateLimiter = @RateLimiter(name = REQUEST_WEIGHT_RATE_LIMITER))
  BinanceTicker24h ticker24h(@QueryParam("symbol") String symbol)
      throws IOException, BinanceException;

  @GET
  @Path("api/v1/ticker/allPrices")
  @Resilience(retry = @Retry(name = "tickerAllPrices"))
  @Resilience(rateLimiter = @RateLimiter(name = REQUEST_WEIGHT_RATE_LIMITER, weight = 2))
  List<BinancePrice> tickerAllPrices() throws IOException, BinanceException;

  @POST
  @Path("api/v3/order")
  @Resilience(
      retry =
          @Retry(
              name = "newOrder",
              baseConfig = ResilienceRegistries.NON_IDEMPOTENTE_CALLS_RETRY_CONFIG_NAME))
  @Resilience(rateLimiter = @RateLimiter(name = ORDERS_PER_SECOND_RATE_LIMITER))
  @Resilience(rateLimiter = @RateLimiter(name = ORDERS_PER_DAY_RATE_LIMITER))
  @Resilience(rateLimiter = @RateLimiter(name = REQUEST_WEIGHT_RATE_LIMITER))
  BinanceNewOrder newOrder(
      @FormParam("symbol") String symbol,
      @FormParam("side") OrderSide side,
      @FormParam("type") OrderType type,
      @FormParam("timeInForce") TimeInForce timeInForce,
      @FormParam("quantity") BigDecimal quantity,
      @FormParam("price") BigDecimal price,
      @FormParam("newClientOrderId") String newClientOrderId,
      @FormParam("stopPrice") BigDecimal stopPrice,
      @FormParam("icebergQty") BigDecimal icebergQty,
      @FormParam("recvWindow") Long recvWindow,
      @FormParam("timestamp") SynchronizedValueFactory<Long> timestamp,
      @HeaderParam(X_MBX_APIKEY) String apiKey,
      @QueryParam(SIGNATURE) ParamsDigest signature)
      throws IOException, BinanceException;
}

This also showcases the “weight” feature for RateLimiter I’m proposing at: https://github.com/resilience4j/resilience4j/issues/642

If you would like to see a working implementation of these annotations then take a look at: https://github.com/knowm/XChange/pull/3231

Issue Analytics

  • State:open
  • Created 4 years ago
  • Reactions:1
  • Comments:22 (20 by maintainers)

github_iconTop GitHub Comments

5reactions
walec51commented, Sep 24, 2019

ok, give me some time to prepare it

1reaction
walec51commented, Nov 21, 2019

@RobWin could you have a look at my PR, it was a lot of work and I would like to know if there is anything controversial there

Read more comments on GitHub >

github_iconTop Results From Across the Web

merge multiple annotations with parameters - java
The answer is: probably no, this is not possible (using "standard" java). You see, there is no inheritance for annotations, ...
Read more >
Composing custom annotations with Spring - mscharhag
Spring supports the composition of custom annotations based of one or more Spring annotations. In this post we will learn how we can...
Read more >
How to Merge Multiple Annotations in JAVA
In order to do this, you need to create a custom annotation class. Let's name it BaseTestAnnotation and lets add @RepeatedIfExceptionsTest( ...
Read more >
What Are Meta-Annotations in Java? - DZone
This allows users to combine multiple annotations into a single, composed annotation, reducing duplication and saving a great deal of manual ...
Read more >
Combining annotation feature classes—Help
When you combine annotation feature classes, the annotation from each feature class becomes an annotation class in the new annotation feature class.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found