Skip to content
Roman edited this page Mar 13, 2025 · 9 revisions

RateLimiterMySQL

It supports mysql2, mysql, sequilize and knex.

Note: It takes 50-150 ms per request on more than 1000 concurrent requests per second. Be careful, when using in applications with high traffic.

Database and table name

By default, RateLimiterMySQL creates rtlmtrflx database and a new table by keyPrefix for every limiter. All limits are stored in one table if tableName option is set. To change the database name set option dbName.

Limiters creation error handling

RateLimiterMySQL throws error on limiter creation, if database or table can NOT be created.

MySQL connections typically take milliseconds to establish. During this connection period, any rate limiter method calls will be rejected with an Error until the connection is fully established. For proper error handling, we recommend providing a ready callback as the second parameter when creating a new instance: new RateLimiterMySQL(opts, ready). This callback allows you to handle any errors that occur during database or table creation for rate limiters (see example below). The ready callback is optional if your process automatically terminates on unhandled errors.

Cleaning up expired data

Rows that expired more than an hour ago are removed every 5 minutes by setTimeout. Note, call rateLimiter.clearExpired(Date.now() - 3600000) manually to remove expired rows in AWS Lambda or GCP function.

Usage

See detailed options description here

const mysql = require('mysql2');
const {RateLimiterMySQL} = require('rate-limiter-flexible');

const pool = mysql.createPool({
  connectionLimit : 100,
  host: 'localhost',
  user: 'root',
  password: 'secret',
});

const opts = {
  storeClient: pool,
  dbName: 'mydb',
  tableName: 'mytable', // all limiters store data in one table
  points: 5, // Number of points
  duration: 1, // Per second(s)
};

const ready = (err) => {
  if (err) {
    // some error happened during database or table creation 
  } else {
    // db and table checked or created. limiter can be used now
  }
};

// if second parameter is not a function or not provided, it may throw unhandled error on database or table creation
const rateLimiter = new RateLimiterMySQL(opts, ready);
rateLimiter.consume(key)
  .then((rateLimiterRes) => {
    // 1 points consumed
  })
  .catch((rej) => {
    // there are no more points
  });

Sequelize and Knex support

It gets internal connection from Sequelize or Knex to make raw queries. After making query, the connection is released.

const rateLimiter = new RateLimiterMySQL({
      storeClient: sequelizeInstance,
}, ready);

const rateLimiter = new RateLimiterMySQL({
      storeClient: knexInstance,
      storeType: `knex`, // knex requires this option 
}, ready);

See detailed options description here

Benchmark

Endpoint is pure NodeJS endpoint launched in node:10.5.0-jessie and mysql:5.7 Docker containers with 4 workers

Endpoint is limited by RateLimiterMySQL with config for 500 random keys:

new RateLimiterMySQL({
  storeClient: mysql,
  points: 4, // Number of points
  duration: 1, // Per second(s)
});

By bombardier -c 500 -l -d 30s -r 1000 -t 5s http://127.0.0.1:3000

Test with 500 concurrent requests with maximum 1000 requests per sec during 30 seconds

Single connection

Statistics        Avg      Stdev        Max
  Reqs/sec      1000.96     250.22    2171.97
  Latency       20.88ms    17.01ms   141.73ms
  Latency Distribution
     50%    12.94ms
     75%    28.33ms
     90%    48.01ms
     95%    59.89ms
     99%    85.00ms
  HTTP codes:
    1xx - 0, 2xx - 24684, 3xx - 0, 4xx - 5322, 5xx - 0

Connection pool 100

Statistics        Avg      Stdev        Max
  Reqs/sec      1002.28     299.86    2669.58
  Latency       14.59ms     6.13ms   102.96ms
  Latency Distribution
     50%    12.91ms
     75%    16.84ms
     90%    20.58ms
     95%    25.60ms
     99%    38.66ms
  HTTP codes:
    1xx - 0, 2xx - 24647, 3xx - 0, 4xx - 5357, 5xx - 0

Heap snapshot statistic on high traffic

Heap snapshot MySQL

Clone this wiki locally