From 25baa5ffc1d5a0c28cd9150c88ceffa3fd81899e Mon Sep 17 00:00:00 2001
From: jano3 <jano@bob.co.za>
Date: Tue, 4 Jun 2024 16:11:35 +0200
Subject: [PATCH] Add redis function to set a lock on a specific key

---
 redis/redis.go | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 58 insertions(+)

diff --git a/redis/redis.go b/redis/redis.go
index 86958ed..e4f6ccc 100644
--- a/redis/redis.go
+++ b/redis/redis.go
@@ -27,6 +27,13 @@ type ClientWithHelpers struct {
 	Available bool
 }
 
+type SetLockKeyOptions struct {
+	Value          *string
+	MaxRetries     *int
+	RetryInterval  *time.Duration
+	FallbackResult *bool
+}
+
 func GetRedisClient(isDebug bool) *ClientWithHelpers {
 	if redisClient != nil && redisClient.IsConnected() {
 		return redisClient
@@ -232,3 +239,54 @@ func (r ClientWithHelpers) RateLimit(key string, limitFn func(int) redis_rate.Li
 		return res.Allowed == 1, nil
 	}
 }
+
+// SetLockKey attempts to set a lock on the specified key.
+func (r ClientWithHelpers) SetLockKey(key string, expiration time.Duration, lockOptions ...SetLockKeyOptions) (success bool, err error) {
+	fallbackResult := false
+	if len(lockOptions) > 0 && lockOptions[0].FallbackResult != nil {
+		fallbackResult = *lockOptions[0].FallbackResult
+	}
+
+	if !r.IsConnected() {
+		return fallbackResult, errors.Error("Redis is not connected")
+	}
+
+	value := "1"
+	retries := 0
+	retryInterval := 100 * time.Millisecond
+	if len(lockOptions) > 0 {
+		// Only use the values that were set
+		if lockOptions[0].Value != nil {
+			value = *lockOptions[0].Value
+		}
+		if lockOptions[0].MaxRetries != nil {
+			retries = *lockOptions[0].MaxRetries
+		}
+		if lockOptions[0].RetryInterval != nil {
+			retryInterval = *lockOptions[0].RetryInterval
+		}
+	}
+
+	for retries >= 0 {
+		success, err = r.Client.SetNX(ctx, key, value, expiration).Result()
+		if err != nil {
+			logs.ErrorWithMsg(fmt.Sprintf("Error setting lock key %s", key), err)
+
+			// Prevent further calls in this execution from trying to connect and also timeout
+			if strings.HasSuffix(err.Error(), "i/o timeout") {
+				r.Available = false
+			}
+
+			return fallbackResult, err
+		}
+
+		if success || retries == 0 {
+			break
+		}
+
+		retries--
+		time.Sleep(retryInterval)
+	}
+
+	return success, nil
+}
-- 
GitLab