From c979c0db16f873c0f8ea6fe5d1b98c15f79d18de Mon Sep 17 00:00:00 2001 From: Timo Tietavainen Date: Tue, 21 Jan 2020 21:57:17 +0200 Subject: [PATCH] Add a new SDL storage API function 'is_active()' Implement a new SDL storage API function 'is_active()' to check healthiness of an SDL instance. For now function only validates SDL DB backend (Redis) connection. This function can be integrated into SDL client (such as A1 Mediator) health checks and runtime initializations. Signed-off-by: Timo Tietavainen Change-Id: I8bf457b9528e39223ea65a20d422d2fab49602bb --- docs/release-notes.rst | 4 ++++ ricsdl-package/examples/sync.py | 5 +++++ ricsdl-package/ricsdl/__init__.py | 2 +- ricsdl-package/ricsdl/backend/dbbackend_abc.py | 5 +++++ ricsdl-package/ricsdl/backend/fake_dict_db.py | 3 +++ ricsdl-package/ricsdl/backend/redis.py | 4 ++++ ricsdl-package/ricsdl/syncstorage.py | 8 +++++++- ricsdl-package/ricsdl/syncstorage_abc.py | 18 ++++++++++++++++++ ricsdl-package/tests/backend/test_fake_dict_db.py | 4 ++++ ricsdl-package/tests/backend/test_redis.py | 17 +++++++++++++++++ ricsdl-package/tests/test_syncstorage.py | 14 +++++++++++++- 11 files changed, 81 insertions(+), 3 deletions(-) diff --git a/docs/release-notes.rst b/docs/release-notes.rst index 007bf35..257c772 100644 --- a/docs/release-notes.rst +++ b/docs/release-notes.rst @@ -33,6 +33,10 @@ This document provides the release notes of the ricsdl library. Version history --------------- +[2.0.3] - 2020-01-22 + +* Add a new SDL storage API function `is_active()` to check healthiness of SDL instance. + [2.0.2] - 2020-01-14 * Version bump. diff --git a/ricsdl-package/examples/sync.py b/ricsdl-package/examples/sync.py index f01369a..6ea9ef9 100644 --- a/ricsdl-package/examples/sync.py +++ b/ricsdl-package/examples/sync.py @@ -78,6 +78,11 @@ mysdl = _try_func_return(SyncStorage) # database services. # mysdl = _try_func_return(lambda: SyncStorage(fake_db_backend='dict')) +# Checks if SDL is operational. Note that it is not necessary to call `is_active()` after each +# SDL instance creation. Below example is here just to show how to call it spontaneously +# when SDL healthiness is needed to check. +is_active = mysdl.is_active() +assert is_active is True # Sets a value 'my_value' for a key 'my_key' under given namespace. Note that value # type must be bytes and multiple key values can be set in one set function call. diff --git a/ricsdl-package/ricsdl/__init__.py b/ricsdl-package/ricsdl/__init__.py index 726caf6..8d4b484 100644 --- a/ricsdl-package/ricsdl/__init__.py +++ b/ricsdl-package/ricsdl/__init__.py @@ -31,7 +31,7 @@ from .exceptions import ( ) -__version__ = '2.0.2' +__version__ = '2.0.3' __all__ = [ diff --git a/ricsdl-package/ricsdl/backend/dbbackend_abc.py b/ricsdl-package/ricsdl/backend/dbbackend_abc.py index 08f4600..4f31554 100644 --- a/ricsdl-package/ricsdl/backend/dbbackend_abc.py +++ b/ricsdl-package/ricsdl/backend/dbbackend_abc.py @@ -28,6 +28,11 @@ from abc import ABC, abstractmethod class DbBackendAbc(ABC): """An abstract Shared Data Layer (SDL) class providing database backend interface.""" + @abstractmethod + def is_connected(self): + """Test database backend connection.""" + pass + @abstractmethod def close(self): """Close database backend connection.""" diff --git a/ricsdl-package/ricsdl/backend/fake_dict_db.py b/ricsdl-package/ricsdl/backend/fake_dict_db.py index 51e6c18..b6c84a2 100644 --- a/ricsdl-package/ricsdl/backend/fake_dict_db.py +++ b/ricsdl-package/ricsdl/backend/fake_dict_db.py @@ -51,6 +51,9 @@ class FakeDictBackend(DbBackendAbc): } ) + def is_connected(self): + return True + def close(self): pass diff --git a/ricsdl-package/ricsdl/backend/redis.py b/ricsdl-package/ricsdl/backend/redis.py index 63972e3..3364497 100644 --- a/ricsdl-package/ricsdl/backend/redis.py +++ b/ricsdl-package/ricsdl/backend/redis.py @@ -90,6 +90,10 @@ class RedisBackend(DbBackendAbc): } ) + def is_connected(self): + with _map_to_sdl_exception(): + return self.__redis.ping() + def close(self): self.__redis.close() diff --git a/ricsdl-package/ricsdl/syncstorage.py b/ricsdl-package/ricsdl/syncstorage.py index 41deeba..29adb17 100644 --- a/ricsdl-package/ricsdl/syncstorage.py +++ b/ricsdl-package/ricsdl/syncstorage.py @@ -25,7 +25,7 @@ from ricsdl.configuration import _Configuration from ricsdl.syncstorage_abc import (SyncStorageAbc, SyncLockAbc) import ricsdl.backend from ricsdl.backend.dbbackend_abc import DbBackendAbc -from ricsdl.exceptions import SdlTypeError +from ricsdl.exceptions import (SdlException, SdlTypeError) def func_arg_checker(exception, start_arg_idx, **types): @@ -132,6 +132,12 @@ class SyncStorage(SyncStorageAbc): } ) + def is_active(self): + try: + return self.__dbbackend.is_connected() + except SdlException: + return False + def close(self): self.__dbbackend.close() diff --git a/ricsdl-package/ricsdl/syncstorage_abc.py b/ricsdl-package/ricsdl/syncstorage_abc.py index 9ccd99d..c5b15b3 100644 --- a/ricsdl-package/ricsdl/syncstorage_abc.py +++ b/ricsdl-package/ricsdl/syncstorage_abc.py @@ -170,6 +170,24 @@ class SyncStorageAbc(ABC): A concrete implementation subclass 'SyncStorage' derives from this abstract class. """ + @abstractmethod + def is_active(self): + """ + Verify SDL storage healthiness. + + Verify SDL connection to the backend data storage. + + Args: + None + + Returns: + bool: True if SDL is operational, false otherwise. + + Raises: + None + """ + pass + @abstractmethod def close(self): """ diff --git a/ricsdl-package/tests/backend/test_fake_dict_db.py b/ricsdl-package/tests/backend/test_fake_dict_db.py index 45b0f0f..3b072e5 100644 --- a/ricsdl-package/tests/backend/test_fake_dict_db.py +++ b/ricsdl-package/tests/backend/test_fake_dict_db.py @@ -57,6 +57,10 @@ def fake_dict_backend_fixture(request): @pytest.mark.usefixtures('fake_dict_backend_fixture') class TestFakeDictBackend: + def test_is_connected_function_success(self): + ret = self.db.is_connected() + assert ret is True + def test_set_function_success(self): self.db.set(self.ns, self.dm) self.db.set(self.ns, self.dm2) diff --git a/ricsdl-package/tests/backend/test_redis.py b/ricsdl-package/tests/backend/test_redis.py index 61b56ff..4f46205 100644 --- a/ricsdl-package/tests/backend/test_redis.py +++ b/ricsdl-package/tests/backend/test_redis.py @@ -72,6 +72,23 @@ def redis_backend_fixture(request): @pytest.mark.usefixtures('redis_backend_fixture') class TestRedisBackend: + def test_is_connected_function_success(self): + self.mock_redis.ping.return_value = True + ret = self.db.is_connected() + self.mock_redis.ping.assert_called_once() + assert ret is True + + def test_is_connected_function_returns_false_if_ping_fails(self): + self.mock_redis.ping.return_value = False + ret = self.db.is_connected() + self.mock_redis.ping.assert_called_once() + assert ret is False + + def test_is_connected_function_can_map_redis_exception_to_sdl_exception(self): + self.mock_redis.ping.side_effect = redis_exceptions.ResponseError('redis error!') + with pytest.raises(ricsdl.exceptions.RejectedByBackend): + self.db.is_connected() + def test_set_function_success(self): self.db.set(self.ns, self.dm) self.mock_redis.mset.assert_called_once_with(self.dm_redis) diff --git a/ricsdl-package/tests/test_syncstorage.py b/ricsdl-package/tests/test_syncstorage.py index 00d3ed8..29fd5d4 100644 --- a/ricsdl-package/tests/test_syncstorage.py +++ b/ricsdl-package/tests/test_syncstorage.py @@ -24,7 +24,7 @@ import pytest from ricsdl.syncstorage import SyncStorage from ricsdl.syncstorage import SyncLock from ricsdl.syncstorage import func_arg_checker -from ricsdl.exceptions import SdlTypeError +from ricsdl.exceptions import (SdlTypeError, NotConnected) @pytest.fixture() @@ -53,6 +53,18 @@ def sync_storage_fixture(request): @pytest.mark.usefixtures('sync_storage_fixture') class TestSyncStorage: + def test_is_active_function_success(self): + self.mock_db_backend.is_connected.return_value = True + ret = self.storage.is_active() + self.mock_db_backend.is_connected.assert_called_once() + assert ret is True + + def test_is_active_function_can_catch_backend_exception_and_return_false(self): + self.mock_db_backend.is_connected.side_effect = NotConnected + ret = self.storage.is_active() + self.mock_db_backend.is_connected.assert_called_once() + assert ret is False + def test_set_function_success(self): self.storage.set(self.ns, self.dm) self.mock_db_backend.set.assert_called_once_with(self.ns, self.dm) -- 2.16.6