Skip to content

Commit cc04c6f

Browse files
devin-ai-integration[bot]ohhmm
authored andcommitted
Implement Redis cache with unit tests
1 parent 3ccba6f commit cc04c6f

File tree

5 files changed

+261
-0
lines changed

5 files changed

+261
-0
lines changed

omnn/rt/storage/CMakeLists.txt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
cmake_minimum_required(VERSION 3.15)
2+
3+
# Find required packages
4+
find_package(hiredis REQUIRED)
5+
find_package(Boost REQUIRED COMPONENTS unit_test_framework)
6+
7+
# Add Redis cache library
8+
add_library(redis_cache
9+
RedisCache.cpp
10+
RedisCache.h
11+
)
12+
13+
target_link_libraries(redis_cache
14+
PUBLIC
15+
hiredis::hiredis
16+
cache_base
17+
)
18+
19+
target_include_directories(redis_cache
20+
PUBLIC
21+
${CMAKE_CURRENT_SOURCE_DIR}
22+
)
23+
24+
# Add tests subdirectory
25+
add_subdirectory(tests)

omnn/rt/storage/RedisCache.cpp

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
#include "RedisCache.h"
2+
#include <chrono>
3+
#include <thread>
4+
#include <stdexcept>
5+
6+
namespace omnn {
7+
namespace rt {
8+
namespace storage {
9+
10+
RedisCache::RedisCache(const std::string& host, int port, int timeout_ms,
11+
int retry_count, int retry_delay_ms)
12+
: host_(host), port_(port), timeout_ms_(timeout_ms),
13+
retry_count_(retry_count), retry_delay_ms_(retry_delay_ms),
14+
context_(nullptr) {
15+
if (!Connect()) {
16+
throw std::runtime_error("Failed to connect to Redis server");
17+
}
18+
}
19+
20+
RedisCache::~RedisCache() {
21+
Disconnect();
22+
}
23+
24+
bool RedisCache::Connect() {
25+
Disconnect(); // Ensure any existing connection is closed
26+
27+
struct timeval timeout = {
28+
.tv_sec = timeout_ms_ / 1000,
29+
.tv_usec = (timeout_ms_ % 1000) * 1000
30+
};
31+
32+
context_ = redisConnectWithTimeout(host_.c_str(), port_, timeout);
33+
34+
if (context_ == nullptr || context_->err) {
35+
if (context_) {
36+
redisFree(context_);
37+
context_ = nullptr;
38+
}
39+
return false;
40+
}
41+
return true;
42+
}
43+
44+
void RedisCache::Disconnect() {
45+
if (context_) {
46+
redisFree(context_);
47+
context_ = nullptr;
48+
}
49+
}
50+
51+
bool RedisCache::RetryOperation(const std::function<bool()>& operation) {
52+
for (int i = 0; i < retry_count_; ++i) {
53+
if (operation()) {
54+
return true;
55+
}
56+
57+
if (!IsConnected() && !Connect()) {
58+
std::this_thread::sleep_for(std::chrono::milliseconds(retry_delay_ms_));
59+
continue;
60+
}
61+
62+
std::this_thread::sleep_for(std::chrono::milliseconds(retry_delay_ms_));
63+
}
64+
return false;
65+
}
66+
67+
bool RedisCache::Set(const std::string& key, const std::string& value) {
68+
return RetryOperation([this, &key, &value]() {
69+
if (!IsConnected()) return false;
70+
71+
redisReply* reply = (redisReply*)redisCommand(context_, "SET %s %s",
72+
key.c_str(), value.c_str());
73+
if (!reply) return false;
74+
75+
bool success = (reply->type == REDIS_REPLY_STATUS &&
76+
strcasecmp(reply->str, "OK") == 0);
77+
freeReplyObject(reply);
78+
return success;
79+
});
80+
}
81+
82+
bool RedisCache::Get(const std::string& key, std::string& value) {
83+
return RetryOperation([this, &key, &value]() {
84+
if (!IsConnected()) return false;
85+
86+
redisReply* reply = (redisReply*)redisCommand(context_, "GET %s",
87+
key.c_str());
88+
if (!reply) return false;
89+
90+
bool success = false;
91+
if (reply->type == REDIS_REPLY_STRING) {
92+
value = std::string(reply->str, reply->len);
93+
success = true;
94+
}
95+
96+
freeReplyObject(reply);
97+
return success;
98+
});
99+
}
100+
101+
bool RedisCache::Clear() {
102+
return RetryOperation([this]() {
103+
if (!IsConnected()) return false;
104+
105+
redisReply* reply = (redisReply*)redisCommand(context_, "FLUSHDB");
106+
if (!reply) return false;
107+
108+
bool success = (reply->type == REDIS_REPLY_STATUS &&
109+
strcasecmp(reply->str, "OK") == 0);
110+
freeReplyObject(reply);
111+
return success;
112+
});
113+
}
114+
115+
bool RedisCache::IsConnected() const {
116+
return context_ && !context_->err;
117+
}
118+
119+
} // namespace storage
120+
} // namespace rt
121+
} // namespace omnn

omnn/rt/storage/RedisCache.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#pragma once
2+
3+
#include <string>
4+
#include <memory>
5+
#include <hiredis/hiredis.h>
6+
#include "CacheBase.h"
7+
8+
namespace omnn {
9+
namespace rt {
10+
namespace storage {
11+
12+
class RedisCache : public CacheBase {
13+
public:
14+
RedisCache(const std::string& host = "localhost", int port = 6379,
15+
int timeout_ms = 1000, int retry_count = 5, int retry_delay_ms = 1000);
16+
~RedisCache();
17+
18+
bool Set(const std::string& key, const std::string& value) override;
19+
bool Get(const std::string& key, std::string& value) override;
20+
bool Clear() override;
21+
bool IsConnected() const;
22+
23+
private:
24+
bool Connect();
25+
void Disconnect();
26+
bool RetryOperation(const std::function<bool()>& operation);
27+
28+
std::string host_;
29+
int port_;
30+
int timeout_ms_;
31+
int retry_count_;
32+
int retry_delay_ms_;
33+
redisContext* context_;
34+
};
35+
36+
} // namespace storage
37+
} // namespace rt
38+
} // namespace omnn

omnn/rt/storage/tests/CMakeLists.txt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
cmake_minimum_required(VERSION 3.15)
2+
3+
# Redis cache test
4+
add_executable(redis_cache_test redis_cache_test.cpp)
5+
6+
target_link_libraries(redis_cache_test
7+
PRIVATE
8+
redis_cache
9+
Boost::unit_test_framework
10+
)
11+
12+
add_test(NAME redis_cache_test COMMAND redis_cache_test)
13+
14+
# Set test properties
15+
set_tests_properties(redis_cache_test PROPERTIES
16+
ENVIRONMENT "OPENMIND_TEST_REDIS_RETRY_COUNT=5;OPENMIND_TEST_REDIS_RETRY_DELAY=1000"
17+
TIMEOUT 60
18+
)
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#define BOOST_TEST_MODULE redis_cache_test
2+
#include <boost/test/unit_test.hpp>
3+
#include "../RedisCache.h"
4+
#include <cstdlib>
5+
#include <string>
6+
7+
using namespace omnn::rt::storage;
8+
9+
struct RedisTestConfig {
10+
RedisTestConfig() {
11+
// Get retry configuration from environment
12+
const char* retry_count = std::getenv("OPENMIND_TEST_REDIS_RETRY_COUNT");
13+
const char* retry_delay = std::getenv("OPENMIND_TEST_REDIS_RETRY_DELAY");
14+
15+
retry_count_ = retry_count ? std::stoi(retry_count) : 5;
16+
retry_delay_ms_ = retry_delay ? std::stoi(retry_delay) : 1000;
17+
}
18+
19+
int retry_count_;
20+
int retry_delay_ms_;
21+
};
22+
23+
BOOST_FIXTURE_TEST_SUITE(redis_cache_tests, RedisTestConfig)
24+
25+
BOOST_AUTO_TEST_CASE(test_basic_operations) {
26+
RedisCache cache("localhost", 6379, 1000, retry_count_, retry_delay_ms_);
27+
28+
// Test Set operation
29+
BOOST_CHECK(cache.Set("test_key", "test_value"));
30+
31+
// Test Get operation
32+
std::string value;
33+
BOOST_CHECK(cache.Get("test_key", value));
34+
BOOST_CHECK_EQUAL(value, "test_value");
35+
36+
// Test Clear operation
37+
BOOST_CHECK(cache.Clear());
38+
BOOST_CHECK(!cache.Get("test_key", value));
39+
}
40+
41+
BOOST_AUTO_TEST_CASE(test_nonexistent_key) {
42+
RedisCache cache("localhost", 6379, 1000, retry_count_, retry_delay_ms_);
43+
44+
std::string value;
45+
BOOST_CHECK(!cache.Get("nonexistent_key", value));
46+
}
47+
48+
BOOST_AUTO_TEST_CASE(test_connection_retry) {
49+
// Test with invalid host to trigger retry mechanism
50+
RedisCache cache("invalid_host", 6379, 100, 2, 100);
51+
52+
// Operations should fail after retries
53+
BOOST_CHECK(!cache.Set("test_key", "test_value"));
54+
55+
std::string value;
56+
BOOST_CHECK(!cache.Get("test_key", value));
57+
}
58+
59+
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)