# 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
#
# 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])
+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.
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.
* 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
* 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
#include "private/logger.hpp"
#include "private/namespaceconfigurationsimpl.hpp"
#include "private/redis/asyncdatabasediscovery.hpp"
+#include "private/redis/asyncredisstorage.hpp"
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;
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);
};
}
#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>
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;
UNKNOWN = 0,
REDIS_STANDALONE,
REDIS_CLUSTER,
- REDIS_SENTINEL
+ REDIS_SENTINEL,
+ SDL_CLUSTER
};
virtual ~DatabaseConfiguration() = default;
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;
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;
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:
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());
};
}
Name: sdl
-Version: 1.1.3
+Version: 1.2.0
Release: 1%{?dist}
Summary: C++ API library for Shared Data Layer clients
%{_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.
#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,
{
}
-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.");
AsyncStorage& AsyncStorageImpl::getOperationHandler(const std::string& ns)
{
if (namespaceConfigurations->isDbBackendUseEnabled(ns))
- return getRedisHandler();
+ return getRedisHandler(ns);
return getDummyHandler();
}
sentinelPortEnvVariableValue({}),
sentinelMasterNameEnvVariableName(SENTINEL_MASTER_NAME_ENV_VAR_NAME),
sentinelMasterNameEnvVariableValue({}),
+ dbClusterAddrListEnvVariableName(DB_CLUSTER_ADDR_LIST_ENV_VAR_NAME),
+ dbClusterAddrListEnvVariableValue({}),
jsonDatabaseConfiguration(boost::none),
logger(logger)
{
envStr = system.getenv(sentinelMasterNameEnvVariableName.c_str());
if (envStr)
sentinelMasterNameEnvVariableValue = envStr;
+ envStr = system.getenv(dbClusterAddrListEnvVariableName.c_str());
+ if (envStr)
+ dbClusterAddrListEnvVariableValue = envStr;
}
readConfigurationFromDirectories(directories);
}
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);
}
{
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();
}
}
#include "private/databaseconfigurationimpl.hpp"
#include <arpa/inet.h>
+#include <boost/crc.hpp>
using namespace shareddatalayer;
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);
}
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;
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());
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,
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));
+}
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;
}
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));
+}
std::string dbPortEnvVariableValue;
std::string sentinelPortEnvVariableValue;
std::string sentinelMasterNameEnvVariableValue;
+ std::string dbClusterAddrListEnvVariableValue;
std::istringstream is{R"JSON(
{
"database":
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);
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");
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");
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");
expectGetEnvironmentString(sentinelPortEnvVariableValue.c_str());
sentinelMasterNameEnvVariableValue = "mymaster";
expectGetEnvironmentString(sentinelMasterNameEnvVariableValue.c_str());
+ expectGetEnvironmentString(nullptr); //DB_CLUSTER_ENV_VAR_NAME
expectDbTypeConfigurationCheckAndApply("redis-sentinel");
expectSentinelAddressConfigurationCheckAndApply("sentinelAddress.local:2222");
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);
+}
}
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());
}
}
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");
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()));
}
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()));
+}
{
EXPECT_TRUE(std::is_abstract<SyncStorage>::value);
}
+
+TEST(SyncStorageTest, SyncStorageCreateInstanceHasCorrectType)
+{
+ auto syncStorageInstance(shareddatalayer::SyncStorage::create());
+ EXPECT_EQ(typeid(std::unique_ptr<SyncStorage>), typeid(syncStorageInstance));
+}