Skip to content

Valkey Glide

Avi Fenesh edited this page Apr 9, 2025 · 1 revision

RateLimiterValkeyGlide

npm version GitHub
Rate limiting with Valkey Glide - for more details about glide see Valkey Glide - Nodejs.

Usage

const { RateLimiterValkeyGlide } = require("rate-limiter-flexible");
const { GlideClient } = require("@valkey/valkey-glide");

// Create a Valkey Glide client
const glideClient = await GlideClient.createClient({
  addresses: [{ host: "127.0.0.1", port: 8080 }],
  useTls: false,
  requestTimeout: 1000,
  clientName: "myApp",
});

const opts = {
  // Basic options
  storeClient: glideClient,
  points: 5, // Number of points
  duration: 5, // Per second(s)
  // Custom options
  // Add any custom options here if needed
};

const rateLimiterValkeyGlide = new RateLimiterValkeyGlide(opts);

rateLimiterValkeyGlide
  .consume(userEmail)
  .then((rateLimiterRes) => {
    // ... Some app logic here ...
  })
  .catch((rejRes) => {
    if (rejRes instanceof Error) {
      // Some Valkey error
      // Never happen if `insuranceLimiter` set up
      // Decide what to do with it in other case
    } else {
      // Can't consume
      // If there is no error, promise rejected with number of ms before next request allowed
      const secs = Math.round(rejRes.msBeforeNext / 1000) || 1;
      res.set("Retry-After", String(secs));
      res.status(429).send("Too Many Requests");
    }
  });

See all options here

Valkey Cluster Support

RateLimiterValkeyGlide works seamlessly with Valkey clusters. Use GlideClusterClient to connect to your Valkey cluster:

const { RateLimiterValkeyGlide } = require("rate-limiter-flexible");
const { GlideClusterClient } = require("@valkey/valkey-glide");

// Connect to Valkey cluster
const glideClusterClient = await GlideClusterClient.createClient({
  addresses: [{ host: "127.0.0.1", port: 8081 }],
  useTLS: false,
  requestTimeout: 1000,
});

const rateLimiter = new RateLimiterValkeyGlide({
  storeClient: glideClusterClient,
  points: 2,
  duration: 5,
});

Insurance Limiter for Resilience

You can set up an insurance limiter to handle cases when the Valkey connection fails:

const {
  RateLimiterValkeyGlide,
  RateLimiterMemory,
} = require("rate-limiter-flexible");

const rateLimiter = new RateLimiterValkeyGlide({
  storeClient: glideClient,
  points: 5,
  duration: 5,
  insuranceLimiter: new RateLimiterMemory({
    points: 5,
    duration: 5,
  }),
});

// If the Valkey connection fails, the in-memory limiter will be used automatically

Custom Lua Functions

RateLimiterValkeyGlide supports custom Lua scripts for rate limiting logic. This allows you to customize the rate limiting behavior:

// Custom Lua script that starts counting from 1 instead of 0
const customScript = `local key = KEYS[1]
local pointsToConsume = tonumber(ARGV[1])
local secDuration = tonumber(ARGV[2])

-- Start counting from 1 (instead of 0) when key doesn't exist
local exists = server.call('exists', key)
if exists == 0 then
  if secDuration > 0 then
    server.call('set', key, "1", 'EX', secDuration)
  else
    server.call('set', key, "1")
  end
  local pttl = server.call('pttl', key)
  return {1, pttl}
end

-- Handle duration case
if secDuration > 0 then
  server.call('set', key, "0", 'EX', secDuration, 'NX')
end

-- Handle increment and return result
local consumed = server.call('incrby', key, pointsToConsume)
local pttl = server.call('pttl', key)
return {consumed, pttl}`;

const rateLimiter = new RateLimiterValkeyGlide({
  storeClient: glideClient,
  points: 2,
  duration: 5,
  customFunction: customScript,
});

When providing a custom Lua script via customFunction, it must:

  1. Accept parameters:

    • KEYS[1]: The key being rate limited
    • ARGV[1]: Points to consume (as string, use tonumber() to convert)
    • ARGV[2]: Duration in seconds (as string, use tonumber() to convert)
  2. Return an array with exactly two elements:

    • [0]: Consumed points (number)
    • [1]: TTL in milliseconds (number)
  3. Handle scenarios:

    • New key creation: Initialize with expiry for fixed windows.
    • Key updates: Increment existing counters.

Specific Options

In addition to the common options, RateLimiterValkeyGlide supports these specific options:

  • storeClient: Required. The Valkey Glide client instance (GlideClient or GlideClusterClient).
  • blockDuration: Duration in seconds to block a key if points are consumed over the limit. Default is 0 (no blocking).
  • rejectIfValkeyNotReady: When set to true, rejects immediately when Valkey connection is not ready. Default is false.
  • execEvenly: Distribute actions evenly over the duration. Default is false.
  • execEvenlyMinDelayMs: Minimum delay in milliseconds between actions when execEvenly is true.
  • customFunction: Custom Lua script for rate limiting logic. Overrides the default script.
  • inMemoryBlockOnConsumed: Points threshold for triggering in-memory blocking.
  • inMemoryBlockDuration: Duration in seconds for in-memory blocking.
  • customFunctionLibName: Custom name for the Lua function library. Defaults to 'ratelimiter'. Use a custom name only if you need different libraries for different rate limiters.
  • insuranceLimiter: A backup RateLimiterAbstract instance to use if the Valkey client fails.

Smooth Rate Limiting

You can distribute actions evenly over the duration to smooth out traffic using the execEvenly option:

const rateLimiter = new RateLimiterValkeyGlide({
  storeClient: glideClient,
  points: 10,
  duration: 1,
  execEvenly: true, // Enable even distribution
  execEvenlyMinDelayMs: 20, // Minimum delay between actions
});

Prevent Spikes with In-Memory Blocking

Avoid extra requests to Valkey with in-memory blocking:

const rateLimiter = new RateLimiterValkeyGlide({
  storeClient: glideClient,
  points: 5,
  duration: 1,
  inMemoryBlockOnConsumed: 10, // Block when consumed 10 points
  inMemoryBlockDuration: 10, // Block for 10 seconds in memory
});

Read more about In-memory Block Strategy

Valkey Connection Requirements

RateLimiterValkeyGlide registers and uses Lua functions in Valkey. It requires permissions to execute Lua functions if acl is in use.

valkey glide also supports Redis OSS, but since it uses functions, it is limited to version 7.0 or higher.

Performance Considerations

For high-traffic scenarios, consider:

  1. Using in-memory blocking to reduce load on Valkey
  2. Configuring an insurance limiter for failover
  3. Setting up proper Valkey connection management with error handling
  4. Using cluster mode for enhanced scalability
Clone this wiki locally