In addition, graphical gcov front-ends such as lcov can be used for coverage
analysis:
- lcov --directory tst/ --directory src --capture --output-file coverage.info
- genhtml coverage.info --output-directory out
+ lcov --directory tst/ --directory src --capture --output-file coverage.info
+ genhtml coverage.info --output-directory out
Open the out/index.html using any web browser.
# Change the numbers just before release.
m4_define([SDL_MAJOR], [1])
-m4_define([SDL_MINOR], [3])
+m4_define([SDL_MINOR], [4])
m4_define([SDL_MICRO], [0])
# SDL ABI version with libtool
#
# Change the numbers just before release.
-m4_define([SDL_CURRENT], [3])
-m4_define([SDL_REVISION], [10])
+m4_define([SDL_CURRENT], [4])
+m4_define([SDL_REVISION], [11])
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])
+sdl (1.4.0-1) UNRELEASED; urgency=low
+
+ * Add synchronous readiness check API
+
+ -- Timo Tietavainen <timo.tietavainen@nokia.com> Wed, 11 Aug 2021 08:02:59 +0300
+
sdl (1.3.0-1) UNRELEASED; urgency=low
* Definable timeout for DB backend readiness in synchronous SDL API
Version history
---------------
+[1.4.0] - 2021-08-11
+
+* Add synchronous readiness check API
+
[1.3.0] - 2021-08-05
* Definable timeout for DB backend readiness in synchronous SDL API
operations. See waitReadyAsync() function
`doxygen documentation <#doxygen-generated-sdl-api-documentation>`_
for corresponding asynchronous API for details.
+* Use waitReady() function before doing first operation via synchronous
+ APIs to ensure that SDL and backend data storage are ready to handle
+ operations. See waitReady() function
+ `doxygen documentation <#doxygen-generated-sdl-api-documentation>`_
+ for corresponding synchronous API for details.
* Avoid using heavy search functions (for example: *AsyncStorage::findKeys()*).
Rather define your keys so that you know which keys should be read.
SyncStorageImpl(std::unique_ptr<AsyncStorage> asyncStorage,
System& system);
+ virtual void waitReady(const Namespace& ns, const std::chrono::steady_clock::duration& timeout) override;
+
virtual void set(const Namespace& ns, const DataMap& dataMap) override;
virtual bool setIf(const Namespace& ns, const Key& key, const Data& oldData, const Data& newData) override;
void pollAndHandleEvents(int timeout_ms);
- void waitForReadinessCheckCallback();
+ void waitForReadinessCheckCallback(const std::chrono::steady_clock::duration& timeout);
void waitForOperationCallback();
void waitSdlToBeReady(const Namespace& ns);
+ void waitSdlToBeReady(const Namespace& ns, const std::chrono::steady_clock::duration& timeout);
+
void waitReadyAck(const std::error_code& error);
void modifyAck(const std::error_code& error);
using DataMap = std::map<Key, Data>;
+ /**
+ * Wait for the service to become ready to serve. There is typically a waiting period
+ * when product cluster is brought up (deploying container orchestrated service,
+ * container restart etc.). The function can be called and used to synchronize the
+ * application startup with the readiness of the underlying data storage.
+ *
+ * This function can also be used if SDL returns an error indicating connection cut
+ * to backend data storage. In that situation client can use this function to get an
+ * indication when SDL is again ready to serve. SDL error code documentation indicates
+ * the error codes for which the usage of this function is applicable.
+ *
+ * Exceptions thrown (excluding standard exceptions such as std::bad_alloc) are all
+ * derived from shareddatalayer::Exception base class. Client can catch only that
+ * exception if separate handling for different shareddatalayer error situations is not
+ * needed.
+ *
+ * @param ns Namespace under which this operation is targeted. As it is possible to
+ * use different DB backend instances for namespaces, it is necessary to
+ * provide namespace for this call to test the DB backend readiness of that
+ * particular namespace. it is recommended to call this always when starting
+ * to use new namespace.
+ *
+ * @param timeout Timeout value after which readiness waiting will be expired in case
+ * the SDL service won't come up. It is recommended to use rather long
+ * timeout value to have enough time for a system to recover for example
+ * from restart scenarios. Suitable timeout value depends greatly from
+ * the environment itself. As an example an application could use 10
+ * seconds timeout value and have a loop what does a re-try every time
+ * when previous waitReady call has failed. On a side note, timeout
+ * value 0 means that readiness is waited interminable.
+ *
+ * @throw NotConnected if shareddatalayer is not connected to the backend data storage.
+ * @throw RejectedBySdl if SDL rejects the request.
+ * @throw BackendError if the backend data storage fails to process the request.
+ */
+ virtual void waitReady(const Namespace& ns, const std::chrono::steady_clock::duration& timeout) = 0;
+
/**
* Write data to shared data layer storage. Writing is done atomically, i.e. either
* all succeeds or all fails.
public:
MockableSyncStorage() { }
+ virtual void waitReady(const Namespace&, const std::chrono::steady_clock::duration&) override { logAndAbort(__PRETTY_FUNCTION__); }
+
virtual void set(const Namespace&, const DataMap&) override { logAndAbort(__PRETTY_FUNCTION__); }
virtual bool setIf(const Namespace&, const Key&, const Data&, const Data&) override { logAndAbort(__PRETTY_FUNCTION__); }
Name: sdl
-Version: 1.3.0
+Version: 1.4.0
Release: 1%{?dist}
Summary: C++ API library for Shared Data Layer clients
%{_includedir}/sdl
%changelog
+* Thu Aug 11 2021 Timo Tietavainen <timo.tietavainen@nokia.com> - 1.4.0-1
+- Add synchronous readiness check API
+
* Thu Aug 05 2021 Timo Tietavainen <timo.tietavainen@nokia.com> - 1.3.0-1
- Definable timeout for DB backend readiness in synchronous SDL API
- Fix SDL configuration file path Valgrind errors
namespace
{
- std::shared_ptr<shareddatalayer::SyncStorage> createSyncStorage(std::ostream& out)
+ std::shared_ptr<shareddatalayer::SyncStorage> createSyncStorage(const std::string& nsStr, std::ostream& out)
{
try
{
auto sdl(shareddatalayer::SyncStorage::create());
+ sdl->waitReady(nsStr, std::chrono::minutes(1));
+ sdl->setOperationTimeout(std::chrono::seconds(5));
return sdl;
}
catch (const shareddatalayer::Exception& error)
const auto timeout(map["timeout"].as<int>());
auto ns(map["ns"].as<std::string>());
setTimeout(timeout);
- auto sdl(createSyncStorage(out));
+ auto sdl(createSyncStorage(ns, out));
if (sdl == nullptr)
return EXIT_FAILURE;
{
}
-void SyncStorageImpl::waitReadyAck(const std::error_code&)
+void SyncStorageImpl::waitReadyAck(const std::error_code& error)
{
isReady = true;
+ localError = error;
}
void SyncStorageImpl::modifyAck(const std::error_code& error)
asyncStorage->handleEvents();
}
-void SyncStorageImpl::waitForReadinessCheckCallback()
+void SyncStorageImpl::waitForReadinessCheckCallback(const std::chrono::steady_clock::duration& timeout)
{
- if (operationTimeout == std::chrono::steady_clock::duration::zero())
+ if (timeout == std::chrono::steady_clock::duration::zero())
{
while (!isReady)
pollAndHandleEvents(NO_TIMEOUT);
}
else
{
- int pollTimeout_ms = std::chrono::duration_cast<std::chrono::milliseconds>(operationTimeout).count() / 10;
+ auto timeout_ms(std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count());
+ auto pollTimeout_ms(timeout_ms / 10);
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
- while(!isReady && (std::chrono::steady_clock::now() - start < operationTimeout))
+ while(!isReady && (std::chrono::steady_clock::now() - start < std::chrono::milliseconds(timeout_ms)))
pollAndHandleEvents(pollTimeout_ms);
}
}
void SyncStorageImpl::waitSdlToBeReady(const Namespace& ns)
+{
+ waitSdlToBeReady(ns, operationTimeout);
+}
+
+void SyncStorageImpl::waitSdlToBeReady(const Namespace& ns, const std::chrono::steady_clock::duration& timeout)
{
isReady = false;
asyncStorage->waitReadyAsync(ns,
std::bind(&shareddatalayer::SyncStorageImpl::waitReadyAck,
this,
- std::error_code()));
- waitForReadinessCheckCallback();
+ std::placeholders::_1));
+ waitForReadinessCheckCallback(timeout);
+}
+
+void SyncStorageImpl::waitReady(const Namespace& ns, const std::chrono::steady_clock::duration& timeout)
+{
+ waitSdlToBeReady(ns, timeout);
+ if(!isReady)
+ throw RejectedBySdl("Timeout, SDL service not ready for the '" + ns + "' namespace");
+ verifyBackendResponse();
}
void SyncStorageImpl::set(const Namespace& ns, const DataMap& dataMap)
#include <sdl/notconnected.hpp>
#include <sdl/operationinterrupted.hpp>
#include <sdl/rejectedbybackend.hpp>
+#include <sdl/rejectedbysdl.hpp>
using namespace shareddatalayer;
using namespace shareddatalayer::redis;
SyncStorage::DataMap dataMap;
SyncStorage::Keys keys;
const SyncStorage::Namespace ns;
+ std::chrono::steady_clock::duration TEST_READY_WAIT_TIMEOUT;
std::chrono::steady_clock::duration TEST_OPERATION_WAIT_TIMEOUT;
+ int TEST_READY_POLL_WAIT_TIMEOUT;
int TEST_OPERATION_POLL_WAIT_TIMEOUT;
SyncStorageImplTest():
asyncStorageMockPassedToImplementation(new StrictMock<AsyncStorageMock>()),
dataMap({{ "key1", { 0x0a, 0x0b, 0x0c } }, { "key2", { 0x0d, 0x0e, 0x0f, 0xff } }}),
keys({ "key1", "key2" }),
ns("someKnownNamespace"),
+ TEST_READY_WAIT_TIMEOUT(std::chrono::minutes(1)),
TEST_OPERATION_WAIT_TIMEOUT(std::chrono::seconds(1)),
+ TEST_READY_POLL_WAIT_TIMEOUT(std::chrono::duration_cast<std::chrono::milliseconds>(TEST_READY_WAIT_TIMEOUT).count() / 10),
TEST_OPERATION_POLL_WAIT_TIMEOUT(std::chrono::duration_cast<std::chrono::milliseconds>(TEST_OPERATION_WAIT_TIMEOUT).count() / 10)
{
expectConstructorCalls();
}));
}
+ void expectHandleEvents_callWaitReadyAckWithError()
+ {
+ EXPECT_CALL(*asyncStorageMockRawPtr, handleEvents())
+ .Times(1)
+ .WillOnce(Invoke([this]()
+ {
+ savedReadyAck(AsyncRedisCommandDispatcherErrorCode::NOT_CONNECTED);
+ }));
+ }
+
void expectHandleEvents_callModifyAck()
{
EXPECT_CALL(*asyncStorageMockRawPtr, handleEvents())
.WillOnce(SaveArg<1>(&savedReadyAck));
}
-
void expectModifyAckWithError()
{
EXPECT_CALL(*asyncStorageMockRawPtr, handleEvents())
syncStorage->set(ns, dataMap);
}
+TEST_F(SyncStorageImplTest, WaitReadySuccessfully)
+{
+ InSequence dummy;
+ expectWaitReadyAsync();
+ expectPollWait(TEST_READY_POLL_WAIT_TIMEOUT);
+ expectHandleEvents_callWaitReadyAck();
+ syncStorage->waitReady(ns, TEST_READY_WAIT_TIMEOUT);
+}
+
+TEST_F(SyncStorageImplTest, WaitReadyCanThrowRejectedBySdl)
+{
+ InSequence dummy;
+ expectWaitReadyAsync();
+ EXPECT_THROW(syncStorage->waitReady(ns, std::chrono::nanoseconds(1)), RejectedBySdl);
+}
+
+TEST_F(SyncStorageImplTest, WaitReadyCanThrowNotConnected)
+{
+ InSequence dummy;
+ expectWaitReadyAsync();
+ expectPollWait(TEST_READY_POLL_WAIT_TIMEOUT);
+ expectHandleEvents_callWaitReadyAckWithError();
+ EXPECT_THROW(syncStorage->waitReady(ns, TEST_READY_WAIT_TIMEOUT), NotConnected);
+}
+
TEST_F(SyncStorageImplTest, SetSuccessfully)
{
InSequence dummy;