Quantcast
Channel: Active questions tagged redis+java - Stack Overflow
Viewing all articles
Browse latest Browse all 2204

Bucket4j tryConsume method blocking when Redis is unavailable

$
0
0

I'm using bucket4j to limit access to distributed service, where there are several pods, with the bucket distributed with Redis (using Lettuce). My goal now is to ensure that each pod can continue working with a local bucket even if Redis eventually goes offline.

So far I have this:

public class ResilientRateLimiter {    private final Bucket redisBucket;    private final Bucket localBucket;    private Bucket currentBucket;    private boolean redisAvailable = true;    private static final String REDIS_URI = "redis://localhost:6379";    public ResilientRateLimiter() {        RedisClient redisClient = RedisClient.create(REDIS_URI);        LettuceBasedProxyManager proxyManager = buildProxyManager(redisClient);        // Redis bucket with max 50 TPS        Refill refill = Refill.greedy(50, Duration.ofSeconds(1));        Bandwidth limit = Bandwidth.classic(50, refill);        BucketConfiguration configuration = BucketConfiguration.builder().addLimit(limit).build();        this.redisBucket = proxyManager.builder().build("123".getBytes(), configuration);        // Local bucket with max 5 TPS        Bandwidth localLimit = Bandwidth.simple(5, Duration.ofSeconds(1));        this.localBucket = Bucket4j.builder().addLimit(localLimit).build();        this.currentBucket = redisBucket;        startMonitoring(redisClient);    }    private static LettuceBasedProxyManager buildProxyManager(RedisClient redisClient) {        return LettuceBasedProxyManager.builderFor(redisClient)                .withExpirationStrategy(ExpirationAfterWriteStrategy.                        basedOnTimeForRefillingBucketUpToMax(Duration.ofSeconds(2)))                .build();    }    private void switchToLocalBucket() {        System.out.println("Switching to local bucket with 5 TPS");        currentBucket = localBucket;    }    private void switchToRedisBucket() {        System.out.println("Switching back to Redis bucket with 50 TPS");        currentBucket = redisBucket;    }    private void startMonitoring(RedisClient redisClient) {        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);        scheduler.scheduleAtFixedRate(() -> {            try (StatefulRedisConnection<String, String> connection = redisClient.connect()) {                connection.sync().ping(); // Test redis connection                if (!redisAvailable) {                    redisAvailable = true;                    switchToRedisBucket();                }            } catch (RedisConnectionException e) {                System.err.println("Redis is unavailable: " + e.getMessage());                if (redisAvailable) {                    redisAvailable = false;                    switchToLocalBucket();                }            }        }, 0, 3, TimeUnit.SECONDS); // wait 3 seconds until next verification    }    public boolean consume() {        return currentBucket.tryConsume(1);    }}

The problem with this solution is that when I force redis to stop, the currentBucket.tryConsume(1) method blocks the call, leaving subsequent calls waiting until Redis returns, and consequently not using the local bucket.

Does anyone have any ideas on how to make this work correctly? Or a better approach?


Viewing all articles
Browse latest Browse all 2204

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>