Ativo Programs for Jira 📖 Documentation
  • Getting started
    • Quickstart guide
    • Prerequisites
    • Installation
      • Updates
    • Cheat sheet
  • Configuration
    • Permissions
    • Team
    • Program (release train)
    • Period (program increment)
    • Project and Jira (JQL) filters
  • Preparing a Program Increment (PI)
    • Program cycle
    • Feature backlog
  • Program Increment (PI) planning
    • Plan issues
    • Plan epics (features)
    • Plan dependencies
    • Raise risks and impediments
    • PI Objectives
    • Capacity
    • Team breakout
  • Scrum of scrums / Art Sync
    • Scrum of scrums and art-sync preparation
    • Progress per team
    • Progress per program
  • Cross-pi
    • Cross-PI dependencies
  • Advanced
    • Export issues, PI objectives, milestones and capacity to Excel
    • Ativo Programs custom fields
      • Field sharing
      • Ativo Program fields use PI keys since April 2023
    • Fields added to screens
    • Issue types
    • Limit permissions of suppliers
    • Jira Service Management tickets on the program board
    • Server to cloud migration
  • REST API
    • Authentication
    • Version
    • Cloud REST API Rate Limiting
    • Cloud REST API
    • Data Center REST API
  • Training
    • User training
Powered by GitBook
On this page
  • Implementation
  • Rate limit responses
  • Retry with backoff
  • Other tips
  • Further reading
  1. REST API

Cloud REST API Rate Limiting

Ativo Programs uses rate limiting on its Cloud REST API to ensure service availability and responsiveness among clients.

PreviousVersionNextCloud REST API

Last updated 8 months ago

Implementation

Ativo limits the number of Jira Cloud REST API calls per client. The exact settings are not published, but requests are at risk of being rate limited when doing more than 100 requests over a few minutes.

The rate limits are calculated per client, not per user. E.g., if John and Ann are Jira admins working on the same Jira tenant (same URL), then the API calls of John and Ann both count together against the rate limits.

There is no rate limiting on the Data Center platform. It is at the discretion of the Jira Data Center Admins to review scripts and external integrations.

Rate limit responses

In cases of rate limiting, the HTTP header response contains the status code 429.

Retry with backoff

A response with the status code 429 has not been processed. You can (and probably want to) safely retry it.

The best practice is to retry events with an exponential backoff. We also recommend using Jitter (a random small time added) to avoid the .

For example:

Retry number
Waiting time (ms)

1

1000 - 1300 ms (randomly chosen in this range)

2

2000 - 2500 ms

3

4000 - 5000 ms

4

8000 - 10000 ms

5 and successive

16000 - 20000 ms

Example rate limiting retry code with Jitter and exponential retry (TypeScript):

/**
 * Wrapper around fetch to include rate limiting handling
 * (c) Ativo Programs 2024
 * Shared for illustrative purposes. Review, test & adopt before use. 
 * The ATIVO EULA applies.
 */
export default class RateLimitedRetryFetch {

    protected readonly TTL_MAX = 5; // how many retries (TTL = Time To Live)
    protected readonly RETRY_WAIT_TIME = 1000; // in ms, increases exponential with back-offs
    protected readonly JITTER_MIN_FACTOR = 1; // don't go below the requested wait time
    protected readonly JITTER_MAX_FACTOR = 1.3; // max increase factor for Jitter
    protected readonly MAX_RETRY_TIME = 16000; // in ms, max retry time before applying Jitter

    public fetchWithRetry(url: string, options: RequestInit = {},): Promise<Response> {
        return this.requestStep(url, options, this.TTL_MAX);
    }

    /**
     * Can be called recursively, until time to live (ttl) is 0
     */
    private async requestStep(url: string, options: RequestInit = {}, ttl: number): Promise<Response> {
        if (ttl <= 0) throw new Error('Rate limit exceeded and retries exhausted');
        const response = await fetch(url, options);
        if (response.status === 429) {
            return this.handleBackOffAndRetry(url, options, ttl);
        } else {
            return response; // If response is not 429, return the response. No retries then.
        }
    }

    private async handleBackOffAndRetry(url: string, options: RequestInit = {}, ttl: number) {
        const waitTimeInMs = this.getWaitTimeInMs(ttl);
        const jitterWaitTimeInMs = this.applyJitter(waitTimeInMs);
        await new Promise(resolve => setTimeout(resolve, jitterWaitTimeInMs)); //sleeps
        return this.requestStep(url, options, ttl - 1);
    }

    private getWaitTimeInMs(ttl: number) {
        const iteration = (this.TTL_MAX - ttl); // between 0 and 4
        const backoffFactor = Math.pow(2, iteration); // between 1 (2^0) and 16 (2^4)
        const waitTime =  backoffFactor * this.RETRY_WAIT_TIME; // between 1000 ms and 16000 ms
        return Math.min(waitTime, this.MAX_RETRY_TIME); //keep wait under 16000 ms, even when increasing number of retries 
    }

    private applyJitter(waitTimeInMs: number): number {
        const jitterFactor = RateLimitedRetryFetch.getRandomBetween(this.JITTER_MIN_FACTOR, this.JITTER_MAX_FACTOR);
        return Math.floor(jitterFactor * waitTimeInMs);
    }

    private static getRandomBetween(min: number, max: number): number {
        return Math.random() * (max - min) + min;
    }

}

Other tips

The Ativo configuration typically changes slowly, allowing external apps and scripts to cache REST API responses for a reasonable duration.

Further reading

The Ativo API rate limiting is implemented in a similar way as the . Code that works with Jira rate limiting should also work for Ativo API rate limiting (except we don't use the Retry-After and X-RateLimit-Reset headers, but these are also optional in Jira).

thundering herd problem
Jira Rate Limiting