Bug: feat(redis-interface): add redis interface command to set multiple the keys in redis and increment if the key already exists



 add redis interface command to set multiple the keys in redis and increment if the key already exists
diff --git a/crates/redis_interface/src/commands.rs b/crates/redis_interface/src/commands.rs
index 746b424abc8..19497d6fbb8 100644
--- a/crates/redis_interface/src/commands.rs
+++ b/crates/redis_interface/src/commands.rs
@@ -14,7 +14,7 @@ use common_utils::{
 use error_stack::{report, ResultExt};
 use fred::{
     interfaces::{HashesInterface, KeysInterface, ListInterface, SetsInterface, StreamsInterface},
-    prelude::RedisErrorKind,
+    prelude::{LuaInterface, RedisErrorKind},
     types::{
         Expiration, FromRedis, MultipleIDs, MultipleKeys, MultipleOrderedPairs, MultipleStrings,
         MultipleValues, RedisKey, RedisMap, RedisValue, ScanType, Scanner, SetOptions, XCap,
@@ -852,12 +852,31 @@ impl super::RedisConnectionPool {
             .await
             .change_context(errors::RedisError::ConsumerGroupClaimFailed)
     }
+
+    #[instrument(level = "DEBUG", skip(self))]
+    pub async fn incr_keys_using_script<V>(
+        &self,
+        lua_script: &'static str,
+        key: Vec<String>,
+        values: V,
+    ) -> CustomResult<(), errors::RedisError>
+    where
+        V: TryInto<MultipleValues> + Debug + Send + Sync,
+        V::Error: Into<fred::error::RedisError> + Send + Sync,
+    {
+        self.pool
+            .eval(lua_script, key, values)
+            .await
+            .change_context(errors::RedisError::IncrementHashFieldFailed)
+    }
 }
 
 #[cfg(test)]
 mod tests {
     #![allow(clippy::expect_used, clippy::unwrap_used)]
 
+    use std::collections::HashMap;
+
     use crate::{errors::RedisError, RedisConnectionPool, RedisEntryId, RedisSettings};
 
     #[tokio::test]
@@ -911,7 +930,6 @@ mod tests {
 
         assert!(is_success);
     }
-
     #[tokio::test]
     async fn test_delete_non_existing_key_success() {
         let is_success = tokio::task::spawn_blocking(move || {
@@ -930,6 +948,44 @@ mod tests {
         })
         .await
         .expect("Spawn block failure");
+        assert!(is_success);
+    }
+
+    #[tokio::test]
+    async fn test_setting_keys_using_scripts() {
+        let is_success = tokio::task::spawn_blocking(move || {
+            futures::executor::block_on(async {
+                // Arrange
+                let pool = RedisConnectionPool::new(&RedisSettings::default())
+                    .await
+                    .expect("failed to create redis connection pool");
+                let lua_script = r#"
+                local results = {}
+                for i = 1, #KEYS do
+                    results[i] = redis.call("INCRBY", KEYS[i], ARGV[i])
+                end
+                return results
+                "#;
+                let mut keys_and_values = HashMap::new();
+                for i in 0..10 {
+                    keys_and_values.insert(format!("key{}", i), i);
+                }
+
+                let key = keys_and_values.keys().cloned().collect::<Vec<_>>();
+                let values = keys_and_values
+                    .values()
+                    .map(|val| val.to_string())
+                    .collect::<Vec<String>>();
+
+                // Act
+                let result = pool.incr_keys_using_script(lua_script, key, values).await;
+
+                // Assert Setup
+                result.is_ok()
+            })
+        })
+        .await
+        .expect("Spawn block failure");
 
         assert!(is_success);
     }

## Type of Change
<!-- Put an `x` in the boxes that apply -->

- [ ] Bugfix
- [ ] New feature
- [ ] Enhancement
- [ ] Refactoring
- [ ] Dependency updates
- [ ] Documentation
- [ ] CI/CD

## Description
add redis interface command to set multiple the keys in redis and increment if the key already exists


### Additional Changes

- [ ] This PR modifies the API contract
- [ ] This PR modifies the database schema
- [ ] This PR modifies application configuration/environment variables

<!--
Provide links to the files with corresponding changes.

Following are the paths where you can find config files:
1. `config`
2. `crates/router/src/configs`
3. `loadtest/config`
-->


## Motivation and Context
<!--
Why is this change required? What problem does it solve?
If it fixes an open issue, please link to the issue here.

If you don't have an issue, we'd recommend starting with one first so the PR
can focus on the implementation (unless it is an obvious bug or documentation fix
that will have little conversation).
-->


## How did you test it?
 
 - Wrote a unit test to insert the keys
<img width="1725" alt="Screenshot 2024-12-13 at 1 05 33 PM" src="https://github.com/user-attachments/assets/5165c747-6e53-4db2-9dce-6aa338c33517" />


## Checklist
<!-- Put an `x` in the boxes that apply -->

- [ ] I formatted the code `cargo +nightly fmt --all`
- [ ] I addressed lints thrown by `cargo clippy`
- [ ] I reviewed the submitted code
- [ ] I added unit tests for my changes where possible
