Annotation Interface Costly


@Retention(RUNTIME) @Target(METHOD) public @interface Costly
Marks a resource method that costs enough to be worth bounding, and names the budget it draws on.

What this bounds, and what it does not

The budget is a count of requests in flight, not a rate and not a per-client quota. A route carrying @Costly("vectors") runs at most as many times concurrently as the vectors pool has permits, whoever is asking; the surplus is answered 503 rather than queued indefinitely.

The distinction matters because the traffic this exists for does not come from one client. A scraper spread over thousands of addresses trips no per-IP rate limit — every address is well behaved on its own — while the aggregate still puts every connection in the pool on a matrix fetch. Counting concurrency instead of requests-per-client is indifferent to how the load is distributed, which is the property that makes it worth having alongside whatever the front Apache is doing.

It is not a defence against a determined attacker: a full pool is a 503, and a 503 is still a response. What it buys is that the database and heap see a bounded number of expensive queries regardless of the arrival pattern, so the site degrades to "some requests are refused" instead of "nothing responds".

See Also:
  • Required Element Summary

    Required Elements
    Modifier and Type
    Required Element
    Description
    Name of the pool this route draws permits from.
  • Optional Element Summary

    Optional Elements
    Modifier and Type
    Optional Element
    Description
    int
    How long a request waits for a permit before being answered 503, in seconds.
  • Element Details

    • value

      String value
      Name of the pool this route draws permits from.

      Routes naming the same pool share one budget, so group routes that contend for the same resource. Must be a key of CostlyEndpointBudget#POOLS; an unknown name fails fast at startup rather than silently going unbounded.

    • queueSeconds

      int queueSeconds
      How long a request waits for a permit before being answered 503, in seconds.

      A short wait absorbs a burst that clears quickly without making the caller retry. A long one just moves the queue from the client into the servlet container's thread pool, which is the thing the budget exists to protect, so keep this small.

      Default:
      2