Multiple DBAAS Redis Sentinel groups 28/6128/4
authorPetri Ovaska <petri.ovaska@nokia.com>
Thu, 15 Apr 2021 08:08:13 +0000 (11:08 +0300)
committerPetri Ovaska <petri.ovaska@nokia.com>
Wed, 26 May 2021 14:36:15 +0000 (17:36 +0300)
Added support to have one or more Redis Sentinel DB groups. The DB groups can
be used to spread out SDL DB operations to different DB instances. The new
DBAAS_CLUSTER_ADDR_LIST environment variable is used for Sentinel DB service
addresses configuration. The selection of DB instance is done based on the
calculation of crc32 value from the namespace string and return modulo hash
value of number of addresses in the list.

Issue-ID: RIC-699

Change-Id: I8bb3a78680cedfba4a39f06f7e2f8cdd60d26949
Signed-off-by: Petri Ovaska <petri.ovaska@nokia.com>
22 files changed:
configure.ac
debian/changelog.in
docs/release-notes.rst
docs/user-guide.rst
include/private/asyncstorageimpl.hpp
include/private/configurationreader.hpp
include/private/databaseconfiguration.hpp
include/private/databaseconfigurationimpl.hpp
include/private/redis/asyncdatabasediscovery.hpp
include/private/tst/databaseconfigurationmock.hpp
rpm/sdl.spec.in
src/asyncstorageimpl.cpp
src/configurationreader.cpp
src/databaseconfiguration.cpp
src/databaseconfigurationimpl.cpp
src/redis/asyncdatabasediscovery.cpp
tst/asyncstorage_test.cpp
tst/asyncstorageimpl_test.cpp
tst/configurationreader_test.cpp
tst/databaseconfiguration_test.cpp
tst/databaseconfigurationimpl_test.cpp
tst/syncstorage_test.cpp

index 349e84a..5ad03f7 100644 (file)
@@ -10,8 +10,8 @@
 # Change the numbers just before release.
 
 m4_define([SDL_MAJOR], [1])
-m4_define([SDL_MINOR], [1])
-m4_define([SDL_MICRO], [3])
+m4_define([SDL_MINOR], [2])
+m4_define([SDL_MICRO], [0])
 
 # SDL ABI version with libtool
 #
@@ -28,8 +28,8 @@ m4_define([SDL_MICRO], [3])
 # Change the numbers just before release.
 
 m4_define([SDL_CURRENT], [2])
-m4_define([SDL_REVISION], [7])
-m4_define([SDL_AGE], [1])
+m4_define([SDL_REVISION], [8])
+m4_define([SDL_AGE], [0])
 
 AC_INIT([shareddatalayer], [SDL_MAJOR.SDL_MINOR.SDL_MICRO], [], [], [https://gerrit.o-ran-sc.org/r/admin/repos/ric-plt/sdl])
 AC_CONFIG_HEADERS([include/config.h])
index 9963428..0291e29 100644 (file)
@@ -1,3 +1,10 @@
+sdl (1.2.0-1) UNRELEASED; urgency=low
+
+  * Multiple DBAAS Redis Sentinel groups
+  * New namespace (--ns) option in sdltool test-get-set -command
+
+ -- Petri Ovaska <petri.ovaska@nokia.com>  Wed May 26 17:27:08 2021 +0300
+
 sdl (1.1.3-1) UNRELEASED; urgency=low
 
   * Rename rpm and Debian makefile targets to rpm-pkg and deb-pkg.
index cc07534..ca53e2b 100644 (file)
@@ -29,6 +29,11 @@ This document provides the release notes of the sdl library.
 Version history
 ---------------
 
+[1.2.0] - 2021-05-26
+
+* Multiple DBAAS Redis Sentinel groups
+* New namespace (--ns) option in sdltool test-get-set -command
+
 [1.1.3] - 2020-05-16
 
 * Rename rpm and Debian makefile targets to rpm-pkg and deb-pkg.
index a601b15..9b5d6a8 100644 (file)
@@ -238,6 +238,7 @@ configure database backend:
 * DBAAS_SERVICE_PORT
 * DBAAS_SERVICE_SENTINEL_PORT
 * DBAAS_MASTER_NAME
+* DBAAS_CLUSTER_ADDR_LIST
 
 After DBaaS service is installed, environment variables are exposed to
 application containers. SDL library will automatically use these environment
@@ -419,9 +420,11 @@ storage:
 * Standalone (single DB node without redundancy)
 * Redundant (DB node pair working in master/slave redundancy model)
 
-SDL does not currently have any intelligent logic (e.g. dynamic scaling) on
-which storage node each namespace data is stored. This area might be developed
-further in the future.
+SDL supports also Redis sentinel based DB cluster where deployment has one or
+more DBAAS Redis sentinel group. Different DBAAS Redis sentinel groups
+can be used to distribute SDL DB operations to different SDL DB instances. When
+more than one DBAAS Redis sentinel group exits the selection of SDL DB instance
+is based on the namespace string hash calculation.
 
 SDL does not prevent backend data storage to be deployed in the same node with
 the SDL client. Such deployments are, however, typically used only in
index 34548f0..5999833 100644 (file)
@@ -30,6 +30,7 @@
 #include "private/logger.hpp"
 #include "private/namespaceconfigurationsimpl.hpp"
 #include "private/redis/asyncdatabasediscovery.hpp"
+#include "private/redis/asyncredisstorage.hpp"
 
 namespace shareddatalayer
 {
@@ -39,7 +40,9 @@ namespace shareddatalayer
     {
     public:
         using AsyncDatabaseDiscoveryCreator = std::function<std::shared_ptr<redis::AsyncDatabaseDiscovery>(std::shared_ptr<Engine> engine,
+                                                                                                           const std::string& ns,
                                                                                                            const DatabaseConfiguration& databaseConfiguration,
+                                                                                                           const boost::optional<std::size_t>& addressIndex,
                                                                                                            std::shared_ptr<Logger> logger)>;
 
         AsyncStorageImpl(const AsyncStorageImpl&) = delete;
@@ -94,8 +97,13 @@ namespace shareddatalayer
         std::shared_ptr<Logger> logger;
         AsyncDatabaseDiscoveryCreator asyncDatabaseDiscoveryCreator;
 
-        AsyncStorage& getRedisHandler();
+        std::vector<std::shared_ptr<AsyncRedisStorage>> asyncStorages;
+
+        AsyncStorage& getRedisHandler(const std::string& ns);
         AsyncStorage& getDummyHandler();
+
+        void setAsyncRedisStorageHandlers(const std::string& ns);
+        AsyncStorage& getAsyncRedisStorageHandler(const std::string& ns);
     };
 }
 
index dd151c6..9454ba8 100644 (file)
@@ -26,6 +26,7 @@
 #define DB_PORT_ENV_VAR_NAME "DBAAS_SERVICE_PORT"
 #define SENTINEL_PORT_ENV_VAR_NAME "DBAAS_SERVICE_SENTINEL_PORT"
 #define SENTINEL_MASTER_NAME_ENV_VAR_NAME "DBAAS_MASTER_NAME"
+#define DB_CLUSTER_ADDR_LIST_ENV_VAR_NAME "DBAAS_CLUSTER_ADDR_LIST"
 
 #include <iosfwd>
 #include <string>
@@ -72,6 +73,8 @@ namespace shareddatalayer
         std::string sentinelPortEnvVariableValue;
         const std::string sentinelMasterNameEnvVariableName;
         std::string sentinelMasterNameEnvVariableValue;
+        const std::string dbClusterAddrListEnvVariableName;
+        std::string dbClusterAddrListEnvVariableValue;
         boost::optional<boost::property_tree::ptree> jsonDatabaseConfiguration;
         std::string sourceForDatabaseConfiguration;
         std::unordered_map<std::string, std::pair<boost::property_tree::ptree, std::string>> jsonNamespaceConfigurations;
index 7f4e7bd..56274cb 100644 (file)
@@ -39,7 +39,8 @@ namespace shareddatalayer
             UNKNOWN = 0,
             REDIS_STANDALONE,
             REDIS_CLUSTER,
-            REDIS_SENTINEL
+            REDIS_SENTINEL,
+            SDL_CLUSTER
         };
 
         virtual ~DatabaseConfiguration() = default;
@@ -51,6 +52,7 @@ namespace shareddatalayer
         virtual DatabaseConfiguration::Addresses getServerAddresses() const = 0;
         virtual DatabaseConfiguration::Addresses getDefaultServerAddresses() const = 0;
         virtual boost::optional<HostAndPort> getSentinelAddress() const = 0; // Optional return value, because empty HostAndPort can't be created.
+        virtual boost::optional<HostAndPort> getSentinelAddress(const boost::optional<std::size_t>& addressIndex) const = 0;
         virtual std::string getSentinelMasterName() const = 0;
         virtual bool isEmpty() const = 0;
 
index 8ae4f9a..c499d5a 100644 (file)
@@ -49,6 +49,8 @@ namespace shareddatalayer
 
         boost::optional<HostAndPort> getSentinelAddress() const override;
 
+        boost::optional<HostAndPort> getSentinelAddress(const boost::optional<std::size_t>& addressIndex) const override;
+
         std::string getSentinelMasterName() const override;
 
         bool isEmpty() const override;
index 37a45d4..813d658 100644 (file)
@@ -71,6 +71,7 @@ namespace shareddatalayer
             static std::shared_ptr<AsyncDatabaseDiscovery> create(std::shared_ptr<Engine> engine,
                                                                   const boost::optional<Namespace>& ns,
                                                                   const DatabaseConfiguration& staticDatabaseConfiguration,
+                                                                  const boost::optional<std::size_t>& addressIndex,
                                                                   std::shared_ptr<Logger> logger);
 
         protected:
index 502e6a6..e4fe0af 100644 (file)
@@ -40,6 +40,7 @@ namespace shareddatalayer
             MOCK_CONST_METHOD0(getDefaultServerAddresses, DatabaseConfiguration::Addresses());
             MOCK_CONST_METHOD0(isEmpty, bool());
             MOCK_CONST_METHOD0(getSentinelAddress, boost::optional<HostAndPort>());
+            MOCK_CONST_METHOD1(getSentinelAddress, boost::optional<HostAndPort>(const boost::optional<std::size_t>& addressIndex));
             MOCK_CONST_METHOD0(getSentinelMasterName, std::string());
         };
     }
index f7973ec..ed49ad7 100755 (executable)
@@ -1,5 +1,5 @@
 Name:     sdl
-Version:  1.1.3
+Version:  1.2.0
 Release:  1%{?dist}
 Summary:  C++ API library for Shared Data Layer clients
 
@@ -50,6 +50,10 @@ rm -f %{buildroot}%{_libdir}/lib*.*a
 %{_includedir}/sdl
 
 %changelog
+* Wed May 26 2021 Petri Ovaska <petri.ovaska@nokia.com> - 1.2.0-1
+- Multiple DBAAS Redis Sentinel groups
+- New namespace (--ns) option in sdltool test-get-set -command
+
 * Sat May 16 2020 Timo Tietavainen <timo.tietavainen@nokia.com> - 1.1.3-1
 - Rename rpm and Debian makefile targets to rpm-pkg and deb-pkg.
 - Update CI Dockerfile to utilize rpm-pkg and deb-pkg makefile targets.
index 31ac713..d9478c2 100644 (file)
 #include "private/redis/asyncredisstorage.hpp"
 #endif
 
+#include <boost/optional/optional_io.hpp>
+#include <boost/crc.hpp>
+
 using namespace shareddatalayer;
 using namespace shareddatalayer::redis;
 
 namespace
 {
         std::shared_ptr<AsyncDatabaseDiscovery> asyncDatabaseDiscoveryCreator(std::shared_ptr<Engine> engine,
+                                                                              const std::string& ns,
                                                                               const DatabaseConfiguration& databaseConfiguration,
+                                                                              const boost::optional<std::size_t>& addressIndex,
                                                                               std::shared_ptr<Logger> logger)
         {
             return AsyncDatabaseDiscovery::create(engine,
-                                                  boost::none,
+                                                  ns,
                                                   databaseConfiguration,
+                                                  addressIndex,
                                                   logger);
         }
+
+        std::uint32_t crc32(const std::string& s)
+        {
+           boost::crc_32_type result;
+           result.process_bytes(s.data(), s.size());
+           return result.checksum();
+        }
+
+        std::uint32_t getClusterHashIndex(const std::string& s, const size_t count)
+        {
+            return crc32(s)%count;
+        }
 }
 
 AsyncStorageImpl::AsyncStorageImpl(std::shared_ptr<Engine> engine,
@@ -78,19 +96,40 @@ AsyncStorageImpl::AsyncStorageImpl(std::shared_ptr<Engine> engine,
 {
 }
 
-AsyncStorage& AsyncStorageImpl::getRedisHandler()
+void AsyncStorageImpl::setAsyncRedisStorageHandlers(const std::string& ns)
+{
+    for (std::size_t i = 0; i < databaseConfiguration->getServerAddresses().size(); i++)
+    {
+        auto redisHandler = std::make_shared<AsyncRedisStorage>(engine,
+                                                                asyncDatabaseDiscoveryCreator(
+                                                                        engine,
+                                                                        ns,
+                                                                        std::ref(*databaseConfiguration),
+                                                                        i,
+                                                                        logger),
+                                                                publisherId,
+                                                                namespaceConfigurations,
+                                                                logger);
+        asyncStorages.push_back(redisHandler);
+    }
+}
+
+AsyncStorage& AsyncStorageImpl::getAsyncRedisStorageHandler(const std::string& ns)
+{
+    std::size_t handlerIndex{0};
+    if (DatabaseConfiguration::DbType::SDL_CLUSTER == databaseConfiguration->getDbType())
+        handlerIndex = getClusterHashIndex(ns, databaseConfiguration->getServerAddresses().size());
+    return *asyncStorages.at(handlerIndex);
+}
+
+AsyncStorage& AsyncStorageImpl::getRedisHandler(const std::string& ns)
 {
 #if HAVE_REDIS
-    static AsyncRedisStorage redisHandler{engine,
-                                          asyncDatabaseDiscoveryCreator(
-                                              engine,
-                                              std::ref(*databaseConfiguration),
-                                              logger),
-                                          publisherId,
-                                          namespaceConfigurations,
-                                          logger};
-
-    return redisHandler;
+    auto serverAddresses(databaseConfiguration->getServerAddresses());
+    if (asyncStorages.empty())
+            setAsyncRedisStorageHandlers(ns);
+
+    return getAsyncRedisStorageHandler(ns);
 #else
     logger->error() << "Redis operations cannot be performed, Redis not enabled";
     SHAREDDATALAYER_ABORT("Invalid configuration.");
@@ -106,7 +145,7 @@ AsyncStorage& AsyncStorageImpl::getDummyHandler()
 AsyncStorage& AsyncStorageImpl::getOperationHandler(const std::string& ns)
 {
     if (namespaceConfigurations->isDbBackendUseEnabled(ns))
-        return getRedisHandler();
+        return getRedisHandler(ns);
 
     return getDummyHandler();
 }
index cc5e33e..18945b6 100644 (file)
@@ -213,6 +213,8 @@ ConfigurationReader::ConfigurationReader(const Directories& directories,
     sentinelPortEnvVariableValue({}),
     sentinelMasterNameEnvVariableName(SENTINEL_MASTER_NAME_ENV_VAR_NAME),
     sentinelMasterNameEnvVariableValue({}),
+    dbClusterAddrListEnvVariableName(DB_CLUSTER_ADDR_LIST_ENV_VAR_NAME),
+    dbClusterAddrListEnvVariableValue({}),
     jsonDatabaseConfiguration(boost::none),
     logger(logger)
 {
@@ -230,6 +232,9 @@ ConfigurationReader::ConfigurationReader(const Directories& directories,
         envStr = system.getenv(sentinelMasterNameEnvVariableName.c_str());
         if (envStr)
             sentinelMasterNameEnvVariableValue = envStr;
+        envStr = system.getenv(dbClusterAddrListEnvVariableName.c_str());
+        if (envStr)
+            dbClusterAddrListEnvVariableValue = envStr;
     }
 
     readConfigurationFromDirectories(directories);
@@ -311,7 +316,14 @@ void ConfigurationReader::readDatabaseConfiguration(DatabaseConfiguration& datab
             }
             else
             {
-                validateAndSetDbType("redis-sentinel", databaseConfiguration, sourceForDatabaseConfiguration);
+                if (dbClusterAddrListEnvVariableValue.empty())
+                    validateAndSetDbType("redis-sentinel", databaseConfiguration, sourceForDatabaseConfiguration);
+                else {
+                    validateAndSetDbType("sdl-cluster", databaseConfiguration, sourceForDatabaseConfiguration);
+                    parseDatabaseServersConfigurationFromString(databaseConfiguration,
+                                                                dbClusterAddrListEnvVariableValue,
+                                                                dbClusterAddrListEnvVariableName);
+                }
                 databaseConfiguration.checkAndApplySentinelAddress(dbHostEnvVariableValue + ":" + sentinelPortEnvVariableValue);
                 databaseConfiguration.checkAndApplySentinelMasterName(sentinelMasterNameEnvVariableValue);
             }
index a33f5bc..41dfb09 100644 (file)
@@ -30,7 +30,7 @@ namespace
     {
         std::ostringstream os;
         os << "invalid database type: '" << type << "'. ";
-        os << "Allowed types are: 'redis-standalone' or 'redis-cluster'";
+        os << "Allowed types are: 'redis-standalone', 'redis-cluster', 'redis-sentinel' or 'sdl-cluster'";
         return os.str();
     }
 }
index a8f64e6..211c537 100644 (file)
@@ -21,6 +21,7 @@
 
 #include "private/databaseconfigurationimpl.hpp"
 #include <arpa/inet.h>
+#include <boost/crc.hpp>
 
 using namespace shareddatalayer;
 
@@ -53,6 +54,8 @@ void DatabaseConfigurationImpl::checkAndApplyDbType(const std::string& type)
         dbType = DatabaseConfiguration::DbType::REDIS_CLUSTER;
     else if (type == "redis-sentinel")
         dbType = DatabaseConfiguration::DbType::REDIS_SENTINEL;
+    else if (type == "sdl-cluster")
+        dbType = DatabaseConfiguration::DbType::SDL_CLUSTER;
     else
         throw DatabaseConfiguration::InvalidDbType(type);
 }
@@ -92,6 +95,14 @@ boost::optional<HostAndPort> DatabaseConfigurationImpl::getSentinelAddress() con
     return sentinelAddress;
 }
 
+boost::optional<HostAndPort> DatabaseConfigurationImpl::getSentinelAddress(const boost::optional<std::size_t>& addressIndex) const
+{
+    if (addressIndex)
+        return { HostAndPort(serverAddresses.at(*addressIndex).getHost(), sentinelAddress->getPort()) };
+
+    return getSentinelAddress();
+}
+
 void DatabaseConfigurationImpl::checkAndApplySentinelMasterName(const std::string& name)
 {
     sentinelMasterName = name;
index 28bbf79..8723def 100644 (file)
@@ -35,6 +35,7 @@ using namespace shareddatalayer::redis;
 std::shared_ptr<AsyncDatabaseDiscovery> AsyncDatabaseDiscovery::create(std::shared_ptr<Engine> engine,
                                                                        const boost::optional<Namespace>& ns,
                                                                        const DatabaseConfiguration& staticDatabaseConfiguration,
+                                                                       const boost::optional<std::size_t>& addressIndex,
                                                                        std::shared_ptr<Logger> logger)
 {
     auto staticAddresses(staticDatabaseConfiguration.getServerAddresses());
@@ -57,10 +58,10 @@ std::shared_ptr<AsyncDatabaseDiscovery> AsyncDatabaseDiscovery::create(std::shar
     else
     {
 #if HAVE_HIREDIS
-        if (staticDbType == DatabaseConfiguration::DbType::REDIS_SENTINEL)
+        if (staticDbType == DatabaseConfiguration::DbType::REDIS_SENTINEL ||
+            staticDbType == DatabaseConfiguration::DbType::SDL_CLUSTER)
         {
-            static_cast<void>(ns);
-            auto sentinelAddress(staticDatabaseConfiguration.getSentinelAddress());
+            auto sentinelAddress(staticDatabaseConfiguration.getSentinelAddress(addressIndex));
             if (sentinelAddress)
                 return std::make_shared<AsyncSentinelDatabaseDiscovery>(engine,
                                                                         logger,
index 5e0fe16..3ee1d5f 100644 (file)
@@ -53,3 +53,9 @@ TEST(AsyncStorageTest, CanThrowWhenDisallowedSeparatorCharacterIsUsedInNamespace
     const std::string notValidNamespace(std::string("someNamespace") + AsyncStorage::SEPARATOR);
     EXPECT_THROW(validateNamespace(notValidNamespace), InvalidNamespace);
 }
+
+TEST(AsyncStorageTest, AsyncStorageCreateInstanceHasCorrectType)
+{
+    auto asyncStorageInstance(shareddatalayer::AsyncStorage::create());
+    EXPECT_EQ(typeid(std::unique_ptr<AsyncStorage>), typeid(asyncStorageInstance));
+}
index 84955f5..8fd2147 100644 (file)
@@ -71,12 +71,16 @@ namespace
                                                                   this,
                                                                   std::placeholders::_1,
                                                                   std::placeholders::_2,
-                                                                  std::placeholders::_3)));
+                                                                  std::placeholders::_3,
+                                                                  std::placeholders::_4,
+                                                                  std::placeholders::_5)));
         }
 
         std::shared_ptr<redis::AsyncDatabaseDiscovery> asyncDatabaseDiscoveryCreator(std::shared_ptr<Engine>,
-                                                                              const DatabaseConfiguration&,
-                                                                              std::shared_ptr<Logger>)
+                                                                                     const std::string&,
+                                                                                     const DatabaseConfiguration&,
+                                                                                     const boost::optional<std::size_t>&,
+                                                                                     std::shared_ptr<Logger>)
         {
             return discoveryMock;
         }
@@ -140,3 +144,11 @@ TEST_F(AsyncStorageImplTest, CorrectHandlerIsUsedBasedOnConfiguration)
     AsyncStorage& returnedHandler2 = asyncStorageImpl->getOperationHandler(ns);
     EXPECT_EQ(typeid(AsyncDummyStorage&), typeid(returnedHandler2));
 }
+
+TEST_F(AsyncStorageImplTest, CorrectSdlClusterHandlerIsUsedBasedOnConfiguration)
+{
+    expectNamespaceConfigurationIsDbBackendUseEnabled_returnTrue();
+    dummyDatabaseConfiguration->checkAndApplyDbType("sdl-cluster");
+    AsyncStorage& returnedHandler = asyncStorageImpl->getOperationHandler(ns);
+    EXPECT_EQ(typeid(AsyncRedisStorage&), typeid(returnedHandler));
+}
index b1ed9f1..4e730f0 100644 (file)
@@ -740,6 +740,7 @@ public:
     std::string dbPortEnvVariableValue;
     std::string sentinelPortEnvVariableValue;
     std::string sentinelMasterNameEnvVariableValue;
+    std::string dbClusterAddrListEnvVariableValue;
     std::istringstream is{R"JSON(
         {
             "database":
@@ -774,8 +775,11 @@ public:
             try
             {
                 EXPECT_CALL(systemMock, getenv(_))
-                    .Times(4)
+                    .Times(5)
                     .WillOnce(Return(dbHostEnvVariableValue.c_str()))
+                    .WillOnce(Return(nullptr))
+                    .WillOnce(Return(nullptr))
+                    .WillOnce(Return(nullptr))
                     .WillOnce(Return(nullptr));
                 initializeReaderWithoutDirectories();
                 configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
@@ -798,6 +802,7 @@ TEST_F(ConfigurationReaderEnvironmentVariableTest, EnvironmentConfigurationCanOv
     expectGetEnvironmentString(dbPortEnvVariableValue.c_str());
     expectGetEnvironmentString(nullptr); //SENTINEL_PORT_ENV_VAR_NAME
     expectGetEnvironmentString(nullptr); //SENTINEL_MASTER_NAME_ENV_VAR_NAME
+    expectGetEnvironmentString(nullptr); //DB_CLUSTER_ENV_VAR_NAME
 
     expectDbTypeConfigurationCheckAndApply("redis-standalone");
     expectDBServerAddressConfigurationCheckAndApply("unknownAddress.local:12345");
@@ -814,6 +819,7 @@ TEST_F(ConfigurationReaderEnvironmentVariableTest, EnvironmentConfigurationWitho
     expectGetEnvironmentString(nullptr); //DB_PORT_ENV_VAR_NAME
     expectGetEnvironmentString(nullptr); //SENTINEL_PORT_ENV_VAR_NAME
     expectGetEnvironmentString(nullptr); //SENTINEL_MASTER_NAME_ENV_VAR_NAME
+    expectGetEnvironmentString(nullptr); //DB_CLUSTER_ENV_VAR_NAME
 
     expectDbTypeConfigurationCheckAndApply("redis-standalone");
     expectDBServerAddressConfigurationCheckAndApply("server.local");
@@ -841,6 +847,7 @@ TEST_F(ConfigurationReaderEnvironmentVariableTest, EnvironmentConfigurationAccep
     expectGetEnvironmentString(nullptr); //DB_PORT_ENV_VAR_NAME
     expectGetEnvironmentString(nullptr); //SENTINEL_PORT_ENV_VAR_NAME
     expectGetEnvironmentString(nullptr); //SENTINEL_MASTER_NAME_ENV_VAR_NAME
+    expectGetEnvironmentString(nullptr); //DB_CLUSTER_ENV_VAR_NAME
 
     expectDbTypeConfigurationCheckAndApply("redis-standalone");
     expectDBServerAddressConfigurationCheckAndApply("[2001::123]:12345");
@@ -859,6 +866,7 @@ TEST_F(ConfigurationReaderEnvironmentVariableTest, EnvironmentConfigurationWithS
     expectGetEnvironmentString(sentinelPortEnvVariableValue.c_str());
     sentinelMasterNameEnvVariableValue = "mymaster";
     expectGetEnvironmentString(sentinelMasterNameEnvVariableValue.c_str());
+    expectGetEnvironmentString(nullptr); //DB_CLUSTER_ENV_VAR_NAME
 
     expectDbTypeConfigurationCheckAndApply("redis-sentinel");
     expectSentinelAddressConfigurationCheckAndApply("sentinelAddress.local:2222");
@@ -866,3 +874,27 @@ TEST_F(ConfigurationReaderEnvironmentVariableTest, EnvironmentConfigurationWithS
     initializeReaderWithoutDirectories();
     configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
 }
+
+TEST_F(ConfigurationReaderEnvironmentVariableTest, EnvironmentConfigurationWithSentinelAndClusterConfiguration)
+{
+    InSequence dummy;
+    dbHostEnvVariableValue = "address-0.local";
+    expectGetEnvironmentString(dbHostEnvVariableValue.c_str());
+    dbPortEnvVariableValue = "1111";
+    expectGetEnvironmentString(dbPortEnvVariableValue.c_str());
+    sentinelPortEnvVariableValue = "2222";
+    expectGetEnvironmentString(sentinelPortEnvVariableValue.c_str());
+    sentinelMasterNameEnvVariableValue = "mymaster";
+    expectGetEnvironmentString(sentinelMasterNameEnvVariableValue.c_str());
+    dbClusterAddrListEnvVariableValue = "address-0.local,address-1.local,address-2.local";
+    expectGetEnvironmentString(dbClusterAddrListEnvVariableValue.c_str());
+
+    expectDbTypeConfigurationCheckAndApply("sdl-cluster");
+    expectDBServerAddressConfigurationCheckAndApply("address-0.local");
+    expectDBServerAddressConfigurationCheckAndApply("address-1.local");
+    expectDBServerAddressConfigurationCheckAndApply("address-2.local");
+    expectSentinelAddressConfigurationCheckAndApply("address-0.local:2222");
+    expectSentinelMasterNameConfigurationCheckAndApply(sentinelMasterNameEnvVariableValue);
+    initializeReaderWithoutDirectories();
+    configurationReader->readDatabaseConfiguration(databaseConfigurationMock);
+}
index 331deb7..9fc1a50 100644 (file)
@@ -52,7 +52,7 @@ TEST(DatabaseConfigurationTest, CanThrowAndCatchInvalidDbType)
     }
     catch (const std::exception& e)
     {
-        EXPECT_STREQ("invalid database type: 'someBadDbType'. Allowed types are: 'redis-standalone' or 'redis-cluster'", e.what());
+        EXPECT_STREQ("invalid database type: 'someBadDbType'. Allowed types are: 'redis-standalone', 'redis-cluster', 'redis-sentinel' or 'sdl-cluster'", e.what());
     }
 }
 
index 3b3083b..5a5356c 100644 (file)
@@ -83,6 +83,13 @@ TEST_F(DatabaseConfigurationImplTest, CanApplyRedisSentinelDbTypeStringAndReturn
     EXPECT_EQ(DatabaseConfiguration::DbType::REDIS_SENTINEL, retDbType);
 }
 
+TEST_F(DatabaseConfigurationImplTest, CanApplySdlClusterDbTypeStringAndReturnType)
+{
+    databaseConfigurationImpl->checkAndApplyDbType("sdl-cluster");
+    const auto retDbType(databaseConfigurationImpl->getDbType());
+    EXPECT_EQ(DatabaseConfiguration::DbType::SDL_CLUSTER, retDbType);
+}
+
 TEST_F(DatabaseConfigurationImplTest, CanApplyNewAddressesOneByOneAndReturnAllAddresses)
 {
     databaseConfigurationImpl->checkAndApplyServerAddress("dummydatabaseaddress.local");
@@ -118,14 +125,14 @@ TEST_F(DatabaseConfigurationImplTest, IsEmptyReturnsCorrectInformation)
 
 TEST_F(DatabaseConfigurationImplTest, DefaultSentinelAddressIsNone)
 {
-    EXPECT_EQ(boost::none, databaseConfigurationImpl->getSentinelAddress());
+    EXPECT_EQ(boost::none, databaseConfigurationImpl->getSentinelAddress(boost::none));
 }
 
 TEST_F(DatabaseConfigurationImplTest, CanApplyAndReturnSentinelAddress)
 {
     databaseConfigurationImpl->checkAndApplySentinelAddress("dummydatabaseaddress.local:1234");
-    auto address = databaseConfigurationImpl->getSentinelAddress();
-    EXPECT_NE(boost::none, databaseConfigurationImpl->getSentinelAddress());
+    auto address = databaseConfigurationImpl->getSentinelAddress(boost::none);
+    EXPECT_NE(boost::none, databaseConfigurationImpl->getSentinelAddress(boost::none));
     EXPECT_EQ("dummydatabaseaddress.local", address->getHost());
     EXPECT_EQ(1234, ntohs(address->getPort()));
 }
@@ -140,3 +147,32 @@ TEST_F(DatabaseConfigurationImplTest, CanApplyAndReturnSentinelMasterName)
     databaseConfigurationImpl->checkAndApplySentinelMasterName("mymaster");
     EXPECT_EQ("mymaster", databaseConfigurationImpl->getSentinelMasterName());
 }
+
+TEST_F(DatabaseConfigurationImplTest, CanReturnSDLClusterAddress)
+{
+    databaseConfigurationImpl->checkAndApplyDbType("sdl-cluster");
+    databaseConfigurationImpl->checkAndApplyServerAddress("cluster-0.local");
+    databaseConfigurationImpl->checkAndApplyServerAddress("cluster-1.local");
+    databaseConfigurationImpl->checkAndApplyServerAddress("cluster-2.local");
+    databaseConfigurationImpl->checkAndApplySentinelAddress("cluster-0.local:54321");
+    auto address0 = databaseConfigurationImpl->getSentinelAddress(0);
+    auto address1 = databaseConfigurationImpl->getSentinelAddress(1);
+    auto address2 = databaseConfigurationImpl->getSentinelAddress(2);
+    EXPECT_NE(boost::none, databaseConfigurationImpl->getSentinelAddress(0));
+    EXPECT_NE(boost::none, databaseConfigurationImpl->getSentinelAddress(1));
+    EXPECT_NE(boost::none, databaseConfigurationImpl->getSentinelAddress(2));
+    EXPECT_EQ("cluster-0.local", address0->getHost());
+    EXPECT_EQ("cluster-1.local", address1->getHost());
+    EXPECT_EQ("cluster-2.local", address2->getHost());
+    EXPECT_EQ(54321, ntohs(address0->getPort()));
+}
+
+TEST_F(DatabaseConfigurationImplTest, CanReturnDefaultPortForSDLClusterAddress)
+{
+    databaseConfigurationImpl->checkAndApplyServerAddress("cluster-0.local");
+    databaseConfigurationImpl->checkAndApplySentinelAddress("cluster-0.local");
+    auto address0 = databaseConfigurationImpl->getSentinelAddress(0);
+    EXPECT_NE(boost::none, databaseConfigurationImpl->getSentinelAddress(0));
+    EXPECT_EQ("cluster-0.local", address0->getHost());
+    EXPECT_EQ(26379, ntohs(address0->getPort()));
+}
index fdc4253..9467ef9 100644 (file)
@@ -42,3 +42,9 @@ TEST(SyncStorageTest, IsAbstract)
 {
     EXPECT_TRUE(std::is_abstract<SyncStorage>::value);
 }
+
+TEST(SyncStorageTest, SyncStorageCreateInstanceHasCorrectType)
+{
+    auto syncStorageInstance(shareddatalayer::SyncStorage::create());
+    EXPECT_EQ(typeid(std::unique_ptr<SyncStorage>), typeid(syncStorageInstance));
+}