solo-io / gloo

The Feature-rich, Kubernetes-native, Next-Generation API Gateway Built on Envoy

Home Page:https://docs.solo.io/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

HTTP Local RateLimit filter not honoring the tokens bucket refills

sadieleob opened this issue · comments

Gloo Edge Product

Enterprise

Gloo Edge Version

1.16.13

Kubernetes Version

1.25.13

Describe the bug

The customer is configuring HTTP Local RateLimit Filter to fill the buckets each second with 600 tokens up to a maximum of 1000 tokens, however, in their load tests they are getting requests with a response code of RL/429 even with ~600 rps.

Expected Behavior

The Token Bucket Algorithm should refill the buckets each second with 600 tokens up to a maximum of 1000 tokens, and with ~600 requests per second, it should not kick in the RL and return a response code of RL/429.

Steps to reproduce the bug

  1. Define the following in the Gateway object:
spec:
  bindAddress: '::'
  bindPort: 8080
  httpGateway:
    options:
      httpLocalRatelimit:
        defaultLimit:
          fillInterval: 1s
          maxTokens: 1000
          tokensPerFill: 600
        enableXRatelimitHeaders: true
  1. Perform load testing sending 600 rps and confirm failed HTTP requests with 429 errors.
  2. K6 script example (you might need to adjust the number of VUs):

k6 run --out web-dashboard k6_load_test.sh

cat k6_load_test.sh

import http from 'k6/http';
import { check, sleep } from 'k6';

// Define the options for the test

export let options = {
    vus: 100, // 1 virtual user
    duration: '30m', // 1 minute
    rps: 600 // 5 requests per second
};

export default function () {
    // Perform a basic HTTP GET request
    let getResponse = http.get('URL');
    check(getResponse, {
        'GET request: is status 200': (r) => r.status === 200,
        'GET request: response time < 500ms': (r) => r.timings.duration < 500,
    });

    // Perform a basic HTTP POST request
    let postPayload = JSON.stringify({ title: 'foo', body: 'bar', userId: 1 });
    let postParams = { headers: { 'Content-Type': 'application/json' } };
    let postResponse = http.post('URL', postPayload, postParams);
    check(postResponse, {
        'POST request: is status 201': (r) => r.status === 201,
        'POST request: response time < 500ms': (r) => r.timings.duration < 500,
    });

    // Sleep for 1 second between iterations
    sleep(1);
}

Additional Environment Detail

No response

Additional Context

Customer is testing this feature as part of a POC.

Zendesk ticket #3835 has been linked to this issue.