Aerospike as a gateway rate limiter just like redis-reactor

Hi, I am adding spring cloud gateway as a gateway application for my system. There are features that I need to include in this gateway and throttling ( rate-limiting ) is one of them. Currently, the spring cloud gateway provides default Redis-reactive implementation (https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.0.2.RELEASE/single/spring-cloud-gateway.html#_redis_ratelimiter) for throttling which uses LUA script in the Redis server.

I am already using aerospike for storing data in one of my products and want to use aerospike as a rate-limiter (throttling) for APIs.

Requirement:

An API call comes to the gateway server. Gateway server invokes aerospike to get the counter against a key (this key can be a String like user-id). Aerospike returns the counter after incrementing it by one. This counter is cleared every minute/hour.

A user is allowed to make a request say 1000 times to the gateway application and once this limit exceeded gateway will send back the ‘Too Many Requests’ error. After one minute this counter is reset to 0. The user can start making a request since the counter is reset for that minute.

Aerospike supports atomic operate commands which can udpate a record and return the new value (see the following topic for an example and I am sure there are more on this forum: Command like redis incr :atomic increment operations).

You could then set a ttl on the record (only when resetting its value) with a ttl of 60 seconds, and making sure the record’s ttl is not updated when doing the operate command (pass -2 in the policy for the operate command). You could of course also have another bin with a timestamp in it, etc…

Thanks, @meher for your reply. Looked at the below code and I understand your point where passing -2 in policy so that ttl is not updated for that minute. e.g. Key coounterKey = new Key(this.namespace, this.set, “my-counter”);

counterBin = new Bin("some-counter", 1);
Record record = this.client.operate(null, coounterKey, Operation.add(counterBin), Operation.get("some-counter"));
System.out.println("New value: " + record.getLong("some-counter"));

But here setting the value to bin “some-counter” is left to the caller which may fail in a multi-threaded environment. Reading a value in different operation is not thread-safe (the other operations like add and get in a single operation are safe). I was looking more of an atomic function that takes care of atomic add and returns the incremented value. Some function or UDF like

  • Function checks the counter value.

  • Increment counter by one.

  • return the incremented value.

All the above operations in one go. So every time we invoke this function it will always return one incremented value to any caller (multi-threaded).

I am not sure I understand the multi-threaded environment impact. The operate command is atomic and will either succeed or roll back. The caller can specify the value to increment by (not the value to set it to).

Regarding the initial check, I think it can be done with an Expression that would evaluate based on the value in the counter.

You should be able to do this with a UDF as well of course, but I think an operate command and an Expression should be enough and would be much more efficient (lightweight).

© 2021 Copyright Aerospike, Inc. | All rights reserved. Creators of the Aerospike Database.