Take DBAAS multi-channel publishing Redis modules into use 44/5744/3
authorTimo Tietavainen <timo.tietavainen@nokia.com>
Tue, 9 Mar 2021 12:37:14 +0000 (14:37 +0200)
committerTimo Tietavainen <timo.tietavainen@nokia.com>
Thu, 11 Mar 2021 08:30:20 +0000 (10:30 +0200)
Following SDL APIs are defined so that multiple channel-event pairs can be
given as function argument but actual SDL implementation utilized such a DBAAS
(Redis) module what expect to get only one channel-event pair. Fix the
implementation of these SDL APIs to use the correct DBAAS module what support
multiple channel-event pairs:
* set_if_and_publish()
* set_if_not_exists_and_publish()
* remove_if_and_publish()

Please note that in runtime environment DBAAS service needs to run on DBAAS
image version 0.4.0 or newer. Older images do not have multiple channel-event
pairs support as a Redis module.

Updated also start_event_listener() and handle_events() description to
emphasize that channel-event subscription must be done before calling those
functions, otherwise open-source Redis client application could throw an
exception due to not having any subscription when it receives some event, for
example a ping event in case of the DBAAS Sentinel deployment.

Issue-ID: RIC-758

Signed-off-by: Timo Tietavainen <timo.tietavainen@nokia.com>
Change-Id: I75d4e3316d7387b7a1a735fe3eb357de7ef794bb

docs/release-notes.rst
ricsdl-package/README.md
ricsdl-package/examples/notify.py
ricsdl-package/examples/sync.py [changed mode: 0644->0755]
ricsdl-package/ricsdl/__init__.py
ricsdl-package/ricsdl/backend/redis.py
ricsdl-package/ricsdl/syncstorage_abc.py
ricsdl-package/tests/backend/test_redis.py

index 2e04473..496254c 100644 (file)
@@ -33,6 +33,10 @@ This document provides the release notes of the ricsdl library.
 Version history
 ---------------
 
+[2.1.1] - 2021-03-09
+
+* Take DBAAS multi-channel publishing Redis modules into use.
+
 [2.1.0] - 2020-08-26
 
 * Add support for notifications
index d32c8b4..621047e 100755 (executable)
@@ -37,7 +37,11 @@ database is used as a backend data storage solution.
 
 Notifications
 
-Notifications functionality provide SDL clients the possibility to receive notifications about data changes in SDL namespaces. SDL client receiving notifications about data changes is referred to as "subscriber", while the SDL client modifying data and publishing notifications is referred to as "publisher".
+Notifications functionality provide SDL clients the possibility to receive
+notifications about data changes in SDL namespaces. SDL client receiving
+notifications about data changes is referred to as "subscriber", while the SDL
+client modifying data and publishing notifications is referred to as
+"publisher".
 
 Install
 -------
index 7d2f203..3041d80 100755 (executable)
@@ -23,11 +23,11 @@ Execution of  these examples requires:
  * Following Redis extension commands have been installed to runtime environment:
    - MSETPUB
    - SETIE
-   - SETIEPUB
-   - SETNXPUB
-   - DELPUB
+   - SETIEMPUB
+   - SETNXMPUB
+   - DELMPUB
    - DELIE
-   - DELIEPUB
+   - DELIEMPUB
    Redis v4.0 or greater is required. Older versions do not support extension modules.
    Implementation of above commands is produced by RIC DBaaS:
    https://gerrit.o-ran-sc.org/r/admin/repos/ric-plt/dbaas
@@ -134,6 +134,9 @@ def listen_thread():
             pass
         time.sleep(0.001)
 
+# Subscribe to MY_CHANNEL. We expect that anytime we receive a message in the
+# channel, cb function will be called.
+_try_func_return(lambda: mysdl.subscribe_channel(MY_NS, cb, MY_CHANNEL))
 
 # As mentioned above, there are two available methods for applications to
 # handle notifications
@@ -143,10 +146,6 @@ else:
     thread = threading.Thread(target=listen_thread)
     thread.start()
 
-# Subscribe to MY_CHANNEL. We expect that anytime we receive a message in the
-# channel, cb function will be called.
-_try_func_return(lambda: mysdl.subscribe_channel(MY_NS, cb, MY_CHANNEL))
-
 # 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.
 _try_func_callback_return(
old mode 100644 (file)
new mode 100755 (executable)
index 6ea9ef9..1fa5742
@@ -25,11 +25,11 @@ Execution of  these examples requires:
  * Following Redis extension commands have been installed to runtime environment:
    - MSETPUB
    - SETIE
-   - SETIEPUB
-   - SETNXPUB
-   - DELPUB
+   - SETIEMPUB
+   - SETNXMPUB
+   - DELMPUB
    - DELIE
-   - DELIEPUB
+   - DELIEMPUB
    Redis v4.0 or greater is required. Older versions do not support extension modules.
    Implementation of above commands is produced by RIC DBaaS:
    https://gerrit.o-ran-sc.org/r/admin/repos/ric-plt/dbaas
index df7e9c2..b4c587c 100644 (file)
@@ -31,7 +31,7 @@ from .exceptions import (
 )
 
 
-__version__ = '2.1.0'
+__version__ = '2.1.1'
 
 
 __all__ = [
index c67be2d..c319bf0 100755 (executable)
@@ -205,7 +205,7 @@ class RedisBackend(DbBackendAbc):
             values = self.__redis.mget(db_keys)
             for idx, val in enumerate(values):
                 # return only key values, which has a value
-                if val:
+                if val is not None:
                     ret[keys[idx]] = val
             return ret
 
@@ -285,7 +285,7 @@ class RedisBackend(DbBackendAbc):
         channels_and_events_prepared = []
         channels_and_events_prepared, _ = self._prepare_channels(ns, channels_and_events)
         with _map_to_sdl_exception():
-            ret = self.__redis.execute_command("SETIEPUB", db_key, new_data, old_data,
+            ret = self.__redis.execute_command("SETIEMPUB", db_key, new_data, old_data,
                                                *channels_and_events_prepared)
             return ret == b"OK"
 
@@ -294,7 +294,7 @@ class RedisBackend(DbBackendAbc):
         db_key = self._add_key_ns_prefix(ns, key)
         channels_and_events_prepared, _ = self._prepare_channels(ns, channels_and_events)
         with _map_to_sdl_exception():
-            ret = self.__redis.execute_command("SETNXPUB", db_key, data,
+            ret = self.__redis.execute_command("SETNXMPUB", db_key, data,
                                                *channels_and_events_prepared)
             return ret == b"OK"
 
@@ -316,7 +316,7 @@ class RedisBackend(DbBackendAbc):
         db_key = self._add_key_ns_prefix(ns, key)
         channels_and_events_prepared, _ = self._prepare_channels(ns, channels_and_events)
         with _map_to_sdl_exception():
-            ret = self.__redis.execute_command("DELIEPUB", db_key, data,
+            ret = self.__redis.execute_command("DELIEMPUB", db_key, data,
                                                *channels_and_events_prepared)
             return bool(ret)
 
index 2c58e37..61dd67f 100755 (executable)
@@ -896,6 +896,10 @@ class SyncStorageAbc(ABC):
         events from subscriptions. The registered callback function will be called
         when an event is received.
 
+        It should be noted that subscribe_channel must be called before calling
+        start_event_listener to do at least one subscription before event loop
+        starts.
+
         Raises:
             SdlTypeError: If function's argument is of an inappropriate type.
             NotConnected: If SDL is not connected to the backend data storage.
@@ -915,6 +919,10 @@ class SyncStorageAbc(ABC):
         event loop. Calling this function after start_event_listener raises an exception.
         If there are no notifications, these returns None.
 
+        It should be noted that subscribe_channel must be called before calling of the
+        handle_events in an event loop. At least one subscription must be done before
+        events handling starts.
+
         Returns:
             Tuple: (channel: str, message: str)
 
index 29cb582..c5e1b03 100755 (executable)
@@ -340,7 +340,7 @@ class TestRedisBackend:
         self.mock_redis.execute_command.return_value = b"OK"
         ret = self.db.set_if_and_publish(self.ns, self.channels_and_events, self.key, self.old_data,
                                          self.new_data)
-        self.mock_redis.execute_command.assert_called_once_with('SETIEPUB', self.key_redis,
+        self.mock_redis.execute_command.assert_called_once_with('SETIEMPUB', self.key_redis,
                                                                 self.new_data, self.old_data,
                                                                 *self.channels_and_events_redis)
         assert ret is True
@@ -349,7 +349,7 @@ class TestRedisBackend:
         self.mock_redis.execute_command.return_value = None
         ret = self.db.set_if_and_publish(self.ns, self.channels_and_events, self.key, self.old_data,
                                          self.new_data)
-        self.mock_redis.execute_command.assert_called_once_with('SETIEPUB', self.key_redis,
+        self.mock_redis.execute_command.assert_called_once_with('SETIEMPUB', self.key_redis,
                                                                 self.new_data, self.old_data,
                                                                 *self.channels_and_events_redis)
         assert ret is False
@@ -364,7 +364,7 @@ class TestRedisBackend:
         self.mock_redis.execute_command.return_value = b"OK"
         ret = self.db.set_if_not_exists_and_publish(self.ns, self.channels_and_events, self.key,
                                                     self.new_data)
-        self.mock_redis.execute_command.assert_called_once_with('SETNXPUB', self.key_redis,
+        self.mock_redis.execute_command.assert_called_once_with('SETNXMPUB', self.key_redis,
                                                                 self.new_data,
                                                                 *self.channels_and_events_redis)
         assert ret is True
@@ -373,7 +373,7 @@ class TestRedisBackend:
         self.mock_redis.execute_command.return_value = None
         ret = self.db.set_if_not_exists_and_publish(self.ns, self.channels_and_events, self.key,
                                                     self.new_data)
-        self.mock_redis.execute_command.assert_called_once_with('SETNXPUB', self.key_redis,
+        self.mock_redis.execute_command.assert_called_once_with('SETNXMPUB', self.key_redis,
                                                                 self.new_data,
                                                                 *self.channels_and_events_redis)
         assert ret is False
@@ -395,7 +395,7 @@ class TestRedisBackend:
         self.mock_redis.execute_command.return_value = 1
         ret = self.db.remove_if_and_publish(self.ns, self.channels_and_events, self.key,
                                             self.new_data)
-        self.mock_redis.execute_command.assert_called_once_with('DELIEPUB', self.key_redis,
+        self.mock_redis.execute_command.assert_called_once_with('DELIEMPUB', self.key_redis,
                                                                 self.new_data,
                                                                 *self.channels_and_events_redis)
         assert ret is True
@@ -404,7 +404,7 @@ class TestRedisBackend:
         self.mock_redis.execute_command.return_value = 0
         ret = self.db.remove_if_and_publish(self.ns, self.channels_and_events, self.key,
                                             self.new_data)
-        self.mock_redis.execute_command.assert_called_once_with('DELIEPUB', self.key_redis,
+        self.mock_redis.execute_command.assert_called_once_with('DELIEMPUB', self.key_redis,
                                                                 self.new_data,
                                                                 *self.channels_and_events_redis)
         assert ret is False