Implement regular expression support for key searching 27/2027/8
authorTimo Tietavainen <timo.tietavainen@nokia.com>
Sun, 15 Dec 2019 18:16:23 +0000 (20:16 +0200)
committerTimo Tietavainen <timo.tietavainen@nokia.com>
Fri, 3 Jan 2020 14:45:14 +0000 (16:45 +0200)
There is a use case to use only some part of the full key as a
searching criterion, as an example of such a criterion could be gNBID,
arp or qci. That's why change find_keys() and find_and_get() API
functions to take in a key search pattern instead of a key prefix.
A pattern can contain glob-style regular expression to match a key.

Removed also 'atomic' parameter of find_and_get(), because only
reasonable selection would be to use non-atomic option, which does not
block the whole Redis for a long time as atomic option could do.

Returned dictionary of matched key values are now sorted by key values
in alphabetical order.

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

docs/release-notes.rst
ricsdl-package/examples/sync.py
ricsdl-package/ricsdl/__init__.py
ricsdl-package/ricsdl/backend/dbbackend_abc.py
ricsdl-package/ricsdl/backend/redis.py
ricsdl-package/ricsdl/syncstorage.py
ricsdl-package/ricsdl/syncstorage_abc.py
ricsdl-package/tests/backend/test_redis.py
ricsdl-package/tests/test_syncstorage.py

index 92a49a6..e91338d 100644 (file)
@@ -33,6 +33,13 @@ This document provides the release notes of the ricsdl library.
 Version history
 ---------------
 
+[2.0.0] - 2020-01-03
+
+* Change find_keys() and find_and_get() API functions to support glob-style
+  regular expression in a key search pattern. API backward incompatible change.
+* Remove 'atomic' parameter of find_and_get() API function. API backward
+  incompatible change.
+
 [1.0.2] - 2019-12-18
 
 * Take Hiredis package into use in Redis database backend.
index c75dfb6..24c8fe8 100644 (file)
@@ -132,13 +132,14 @@ assert my_ret_dict == {}
 
 # Finds keys under given namespace that are matching to given key prefix 'my_k'.
 _try_func_return(lambda: mysdl.set(MY_NS, {'my_key': b'my_value'}))
-ret_keys = _try_func_return(lambda: mysdl.find_keys(MY_NS, ''))
+ret_keys = _try_func_return(lambda: mysdl.find_keys(MY_NS, 'my_k*'))
 assert ret_keys == ['my_key']
 
 
-# Finds keys and their values under given namespace that are matching to given key prefix 'my_k'.
+# Finds keys and their values under given namespace that are matching to given key search
+# pattern 'my_k*'.
 # Note that the type of returned value is bytes.
-ret_key_values = _try_func_return(lambda: mysdl.find_and_get(MY_NS, '', atomic=True))
+ret_key_values = _try_func_return(lambda: mysdl.find_and_get(MY_NS, 'my_k*'))
 assert ret_key_values == {'my_key': b'my_value'}
 
 _try_func_return(lambda: mysdl.remove_all(MY_NS))
index 91eff80..7fd7dd6 100644 (file)
@@ -31,7 +31,7 @@ from .exceptions import (
 )
 
 
-__version__ = '1.0.2'
+__version__ = '2.0.0'
 
 
 __all__ = [
index 4be5737..08f4600 100644 (file)
@@ -54,12 +54,12 @@ class DbBackendAbc(ABC):
         pass
 
     @abstractmethod
-    def find_keys(self, ns: str, key_prefix: str) -> List[str]:
+    def find_keys(self, ns: str, key_pattern: str) -> List[str]:
         """"Return all the keys matching search pattern under a namespace in database."""
         pass
 
     @abstractmethod
-    def find_and_get(self, ns: str, key_prefix: str, atomic: bool) -> Dict[str, bytes]:
+    def find_and_get(self, ns: str, key_pattern: str) -> Dict[str, bytes]:
         """
         Return all the keys with their values matching search pattern under a namespace in
         database.
index afa7450..319469e 100644 (file)
@@ -118,18 +118,17 @@ class RedisBackend(DbBackendAbc):
                     ret[keys[idx]] = val
             return ret
 
-    def find_keys(self, ns: str, key_prefix: str) -> List[str]:
-        escaped_key_prefix = self._escape_characters(key_prefix)
-        db_escaped_key_prefix = self._add_key_ns_prefix(ns, escaped_key_prefix + '*')
+    def find_keys(self, ns: str, key_pattern: str) -> List[str]:
+        db_key_pattern = self._add_key_ns_prefix(ns, key_pattern)
         with _map_to_sdl_exception():
-            ret = self.__redis.keys(db_escaped_key_prefix)
+            ret = self.__redis.keys(db_key_pattern)
             return self._strip_ns_from_bin_keys(ns, ret)
 
-    def find_and_get(self, ns: str, key_prefix: str, atomic: bool) -> Dict[str, bytes]:
+    def find_and_get(self, ns: str, key_pattern: str) -> Dict[str, bytes]:
         # todo: replace below implementation with redis 'NGET' module
         ret = dict()  # type: Dict[str, bytes]
         with _map_to_sdl_exception():
-            matched_keys = self.find_keys(ns, key_prefix)
+            matched_keys = self.find_keys(ns, key_pattern)
             if matched_keys:
                 ret = self.get(ns, matched_keys)
         return ret
@@ -196,24 +195,18 @@ class RedisBackend(DbBackendAbc):
     def _strip_ns_from_bin_keys(cls, ns: str, nskeylist: List[bytes]) -> List[str]:
         ret_keys = []
         for k in nskeylist:
-            nskey = k.decode("utf-8").split(',', 1)
+            try:
+                redis_key = k.decode("utf-8")
+            except UnicodeDecodeError as exc:
+                msg = u'Namespace %s key conversion to string failed: %s' % (ns, str(exc))
+                raise RejectedByBackend(msg)
+            nskey = redis_key.split(',', 1)
             if len(nskey) != 2:
-                msg = u'Illegal namespace %s key:%s' % (ns, nskey)
+                msg = u'Namespace %s key:%s has no namespace prefix' % (ns, redis_key)
                 raise RejectedByBackend(msg)
             ret_keys.append(nskey[1])
         return ret_keys
 
-    @classmethod
-    def _escape_characters(cls, pattern: str) -> str:
-        return pattern.translate(str.maketrans(
-            {"(": r"\(",
-             ")": r"\)",
-             "[": r"\[",
-             "]": r"\]",
-             "*": r"\*",
-             "?": r"\?",
-             "\\": r"\\"}))
-
     def get_redis_connection(self):
         """Return existing Redis database connection."""
         return self.__redis
index 92bb88a..cec5761 100644 (file)
@@ -142,15 +142,17 @@ class SyncStorage(SyncStorageAbc):
 
     @func_arg_checker(SdlTypeError, 1, ns=str, keys=(str, builtins.set))
     def get(self, ns: str, keys: Union[str, Set[str]]) -> Dict[str, bytes]:
-        return self.__dbbackend.get(ns, list(keys))
+        disordered = self.__dbbackend.get(ns, list(keys))
+        return {k: disordered[k] for k in sorted(disordered)}
 
-    @func_arg_checker(SdlTypeError, 1, ns=str, key_prefix=str)
-    def find_keys(self, ns: str, key_prefix: str) -> List[str]:
-        return self.__dbbackend.find_keys(ns, key_prefix)
+    @func_arg_checker(SdlTypeError, 1, ns=str, key_pattern=str)
+    def find_keys(self, ns: str, key_pattern: str) -> List[str]:
+        return self.__dbbackend.find_keys(ns, key_pattern)
 
-    @func_arg_checker(SdlTypeError, 1, ns=str, key_prefix=str, atomic=bool)
-    def find_and_get(self, ns: str, key_prefix: str, atomic: bool) -> Dict[str, bytes]:
-        return self.__dbbackend.find_and_get(ns, key_prefix, atomic)
+    @func_arg_checker(SdlTypeError, 1, ns=str, key_pattern=str)
+    def find_and_get(self, ns: str, key_pattern: str) -> Dict[str, bytes]:
+        disordered = self.__dbbackend.find_and_get(ns, key_pattern)
+        return {k: disordered[k] for k in sorted(disordered)}
 
     @func_arg_checker(SdlTypeError, 1, ns=str, keys=(str, builtins.set))
     def remove(self, ns: str, keys: Union[str, Set[str]]) -> None:
@@ -162,7 +164,7 @@ class SyncStorage(SyncStorageAbc):
 
     @func_arg_checker(SdlTypeError, 1, ns=str)
     def remove_all(self, ns: str) -> None:
-        keys = self.__dbbackend.find_keys(ns, '')
+        keys = self.__dbbackend.find_keys(ns, '*')
         if keys:
             self.__dbbackend.remove(ns, keys)
 
index ff5eea9..9ccd99d 100644 (file)
@@ -290,6 +290,7 @@ class SyncStorageAbc(ABC):
 
         Returns:
             (dict of str: bytes): A dictionary mapping of a key to the read data from the storage.
+                                  Dictionary is sorted by key values in alphabetical order.
 
         Raises:
             SdlTypeError: If function's argument is of an inappropriate type.
@@ -300,12 +301,26 @@ class SyncStorageAbc(ABC):
         pass
 
     @abstractmethod
-    def find_keys(self, ns: str, key_prefix: str) -> List[str]:
-        """
+    def find_keys(self, ns: str, key_pattern: str) -> List[str]:
+        r"""
         Find all keys matching search pattern under the namespace.
 
-        No prior knowledge about the keys in the given namespace exists, thus operation is not
-        guaranteed to be atomic or isolated.
+        Supported glob-style patterns:
+            `?` matches any single character. For example `?at` matches Cat, cat, Bat or bat.
+            `*` matches any number of any characters including none. For example `*Law*` matches
+                Law, GrokLaw, or Lawyer.
+            `[abc]` matches one character given in the bracket. For example `[CB]at` matches Cat or
+                    Bat.
+            `[a-z]` matches one character from the range given in the bracket. For example
+                    `Letter[0-9]` matches Letter0 up to Letter9.
+            `[^abc]` matches any single character what is not given in the bracket. For example
+                     `h[^e]llo` matches hallo, hillo but not hello.
+
+        If searched key itself contains a special character, use a backslash (\) character to
+        escape the special character to match it verbatim.
+
+        NOTE: `find_keys` function is not guaranteed to be atomic or isolated.
+
         All the exceptions except SdlTypeError are derived from SdlException base class. Client
         can catch only that exception if separate handling for different SDL error situations is
         not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
@@ -313,8 +328,7 @@ class SyncStorageAbc(ABC):
 
         Args:
             ns (str): Namespace under which this operation is targeted.
-            key_prefix (str): Only keys starting with given keyPrefix are returned. Passing empty
-                              string as keyPrefix will return all the keys.
+            key_pattern (str): Key search pattern.
 
         Returns:
             (list of str): A list of found keys.
@@ -328,13 +342,26 @@ class SyncStorageAbc(ABC):
         pass
 
     @abstractmethod
-    def find_and_get(self, ns: str, key_prefix: str, atomic: bool) -> Dict[str, bytes]:
-        """
+    def find_and_get(self, ns: str, key_pattern: str) -> Dict[str, bytes]:
+        r"""
         Find keys and get their respective data from SDL storage.
 
-        Only those entries that are matching prefix will be returned.
-        NOTE: In atomic action, if the prefix produces huge number of matches, that can have
-        a severe impact on system performance, due to DB is blocked for long time.
+        Supported glob-style patterns:
+            `?` matches any single character. For example `?at` matches Cat, cat, Bat or bat.
+            `*` matches any number of any characters including none. For example `*Law*` matches
+                Law, GrokLaw, or Lawyer.
+            `[abc]` matches one character given in the bracket. For example `[CB]at` matches Cat or
+                    Bat.
+            `[a-z]` matches one character from the range given in the bracket. For example
+                    `Letter[0-9]` matches Letter0 up to Letter9.
+            `[^abc]` matches any single character what is not given in the bracket. For example
+                     `h[^e]llo` matches hallo, hillo but not hello.
+
+        If searched key itself contains a special character, use a backslash (\) character to
+        escape the special character to match it verbatim.
+
+        NOTE: `find_and_get` function is not guaranteed to be atomic or isolated.
+
         All the exceptions except SdlTypeError are derived from SdlException base class. Client
         can catch only that exception if separate handling for different SDL error situations is
         not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
@@ -342,13 +369,11 @@ class SyncStorageAbc(ABC):
 
         Args:
             ns (str): Namespace under which this operation is targeted.
-            key_prefix (str): Only keys starting with given keyPrefix are returned. Passing empty
-                              string as keyPrefix will return all the keys.
-            atomic (bool): True to find keys and get their respective data in one atomic operation,
-                           false to find keys and get their respective data non-atomically.
+            key_pattern (str): Key search pattern.
 
         Returns:
             (dict of str: bytes): A dictionary mapping of a key to the read data from the storage.
+                                  Dictionary is sorted by key values in alphabetical order.
 
         Raises:
             SdlTypeError: If function's argument is of an inappropriate type.
index d1b8223..72d7c02 100644 (file)
@@ -41,19 +41,18 @@ def redis_backend_fixture(request):
     request.cls.data = b'123'
     request.cls.old_data = b'1'
     request.cls.new_data = b'3'
-    request.cls.keyprefix = 'x?'
-    request.cls.keyprefix_redis = r'{some-ns},x\?*'
-    request.cls.matchedkeys = ['x1', 'x2', 'x3', 'x4', 'x5']
-    request.cls.matchedkeys_redis = [b'{some-ns},x1', b'{some-ns},x2', b'{some-ns},x3',
-                                     b'{some-ns},x4', b'{some-ns},x5']
-    request.cls.matcheddata_dl_redis = [b'10', b'11', b'12', b'13', b'14']
-    request.cls.matcheddata_dm = {'x1': b'10', 'x2': b'11', 'x3': b'12',
-                                  'x4': b'13', 'x5': b'14'}
+    request.cls.keypattern = r'[Aa]bc-\[1\].?-*'
+    request.cls.keypattern_redis = r'{some-ns},[Aa]bc-\[1\].?-*'
+    request.cls.matchedkeys = ['Abc-[1].0-def', 'abc-[1].1-ghi']
+    request.cls.matchedkeys_redis = [b'{some-ns},Abc-[1].0-def',
+                                     b'{some-ns},abc-[1].1-ghi']
+    request.cls.matcheddata_redis = [b'10', b'11']
+    request.cls.matchedkeydata = {'Abc-[1].0-def': b'10',
+                                  'abc-[1].1-ghi': b'11'}
     request.cls.group = 'some-group'
     request.cls.group_redis = '{some-ns},some-group'
     request.cls.groupmembers = set([b'm1', b'm2'])
     request.cls.groupmember = b'm1'
-    request.cls.is_atomic = True
 
     request.cls.configuration = Mock()
     mock_conf_params = _Configuration.Params(db_host=None,
@@ -141,36 +140,77 @@ class TestRedisBackend:
 
     def test_find_keys_function_success(self):
         self.mock_redis.keys.return_value = self.matchedkeys_redis
-        ret = self.db.find_keys(self.ns, self.keyprefix)
-        self.mock_redis.keys.assert_called_once_with(self.keyprefix_redis)
+        ret = self.db.find_keys(self.ns, self.keypattern)
+        self.mock_redis.keys.assert_called_once_with(self.keypattern_redis)
         assert ret == self.matchedkeys
 
     def test_find_keys_function_returns_empty_list_when_no_matching_keys_found(self):
         self.mock_redis.keys.return_value = []
-        ret = self.db.find_keys(self.ns, self.keyprefix)
-        self.mock_redis.keys.assert_called_once_with(self.keyprefix_redis)
+        ret = self.db.find_keys(self.ns, self.keypattern)
+        self.mock_redis.keys.assert_called_once_with(self.keypattern_redis)
         assert ret == []
 
     def test_find_keys_function_can_map_redis_exception_to_sdl_exception(self):
         self.mock_redis.keys.side_effect = redis_exceptions.ResponseError('redis error!')
         with pytest.raises(ricsdl.exceptions.RejectedByBackend):
-            self.db.find_keys(self.ns, self.keyprefix)
+            self.db.find_keys(self.ns, self.keypattern)
+
+    def test_find_keys_function_can_raise_exception_when_redis_key_convert_to_string_fails(self):
+        # Redis returns an illegal key, which conversion to string fails
+        corrupt_redis_key = b'\x81'
+        self.mock_redis.keys.return_value = [corrupt_redis_key]
+        with pytest.raises(ricsdl.exceptions.RejectedByBackend) as excinfo:
+            self.db.find_keys(self.ns, self.keypattern)
+        assert f"Namespace {self.ns} key:{corrupt_redis_key} "
+        "has no namespace prefix" in str(excinfo.value)
+
+    def test_find_keys_function_can_raise_exception_when_redis_key_is_without_prefix(self):
+        # Redis returns an illegal key, which doesn't have comma separated namespace prefix
+        corrupt_redis_key = 'some-corrupt-key'
+        self.mock_redis.keys.return_value = [f'{corrupt_redis_key}'.encode()]
+        with pytest.raises(ricsdl.exceptions.RejectedByBackend) as excinfo:
+            self.db.find_keys(self.ns, self.keypattern)
+        assert f"Namespace {self.ns} key:{corrupt_redis_key} "
+        "has no namespace prefix" in str(excinfo.value)
 
     def test_find_and_get_function_success(self):
         self.mock_redis.keys.return_value = self.matchedkeys_redis
-        self.mock_redis.mget.return_value = self.matcheddata_dl_redis
-        ret = self.db.find_and_get(self.ns, self.keyprefix, self.is_atomic)
-        self.mock_redis.keys.assert_called_once_with(self.keyprefix_redis)
+        self.mock_redis.mget.return_value = self.matcheddata_redis
+        ret = self.db.find_and_get(self.ns, self.keypattern)
+        self.mock_redis.keys.assert_called_once_with(self.keypattern_redis)
         self.mock_redis.mget.assert_called_once_with([i.decode() for i in self.matchedkeys_redis])
-        assert ret == self.matcheddata_dm
+        assert ret == self.matchedkeydata
 
     def test_find_and_get_function_returns_empty_dict_when_no_matching_keys_exist(self):
         self.mock_redis.keys.return_value = list()
-        ret = self.db.find_and_get(self.ns, self.keyprefix, self.is_atomic)
-        self.mock_redis.keys.assert_called_once_with(self.keyprefix_redis)
+        ret = self.db.find_and_get(self.ns, self.keypattern)
+        self.mock_redis.keys.assert_called_once_with(self.keypattern_redis)
         assert not self.mock_redis.mget.called
         assert ret == dict()
 
+    def test_find_and_get_function_can_map_redis_exception_to_sdl_exception(self):
+        self.mock_redis.keys.side_effect = redis_exceptions.ResponseError('redis error!')
+        with pytest.raises(ricsdl.exceptions.RejectedByBackend):
+            self.db.find_and_get(self.ns, self.keypattern)
+
+    def test_find_and_get_function_can_raise_exception_when_redis_key_convert_to_string_fails(self):
+        # Redis returns an illegal key, which conversion to string fails
+        corrupt_redis_key = b'\x81'
+        self.mock_redis.keys.return_value = [corrupt_redis_key]
+        with pytest.raises(ricsdl.exceptions.RejectedByBackend) as excinfo:
+            self.db.find_and_get(self.ns, self.keypattern)
+        assert f"Namespace {self.ns} key:{corrupt_redis_key} "
+        "has no namespace prefix" in str(excinfo.value)
+
+    def test_find_and_get_function_can_raise_exception_when_redis_key_is_without_prefix(self):
+        # Redis returns an illegal key, which doesn't have comma separated namespace prefix
+        corrupt_redis_key = 'some-corrupt-key'
+        self.mock_redis.keys.return_value = [f'{corrupt_redis_key}'.encode()]
+        with pytest.raises(ricsdl.exceptions.RejectedByBackend) as excinfo:
+            self.db.find_and_get(self.ns, self.keypattern)
+        assert f"Namespace {self.ns} key:{corrupt_redis_key} "
+        "has no namespace prefix" in str(excinfo.value)
+
     def test_remove_function_success(self):
         self.db.remove(self.ns, self.keys)
         self.mock_redis.delete.assert_called_once_with(*self.keys_redis)
@@ -353,6 +393,16 @@ class TestRedisBackendLock:
             keys=[self.lockname_redis], args=[123], client=self.mock_redis)
         assert ret == 2
 
+    def test_get_validity_time_function_second_fraction_success(self):
+        self.mock_redis_lock.name = self.lockname_redis
+        self.mock_redis_lock.local.token = 123
+        self.mock_lua_get_validity_time.return_value = 234
+
+        ret = self.lock.get_validity_time()
+        self.mock_lua_get_validity_time.assert_called_once_with(
+            keys=[self.lockname_redis], args=[123], client=self.mock_redis)
+        assert ret == 0.234
+
     def test_get_validity_time_function_can_raise_exception_if_lock_is_unlocked(self):
         self.mock_redis_lock.name = self.lockname_redis
         self.mock_redis_lock.local.token = None
@@ -371,8 +421,17 @@ class TestRedisBackendLock:
         assert f"Getting validity time of a lock {self.lockname} failed with error code: -10" in str(excinfo.value)
 
     def test_redis_backend_lock_object_string_representation(self):
-        str_out = str(self.lock)
-        assert str_out is not None
+        expected_lock_info = {'lock namespace': 'some-ns',
+                              'lock name': 'some-lock-name',
+                              'lock status': 'locked'}
+        assert str(self.lock) == str(expected_lock_info)
+
+    def test_redis_backend_lock_object_string_representation_can_catch_redis_exception(self):
+        self.mock_redis_lock.owned.side_effect = redis_exceptions.LockError('redis lock error!')
+        expected_lock_info = {'lock namespace': 'some-ns',
+                              'lock name': 'some-lock-name',
+                              'lock status': 'Error: redis lock error!'}
+        assert str(self.lock) == str(expected_lock_info)
 
 
 def test_redis_response_error_exception_is_mapped_to_rejected_by_backend_sdl_exception():
index 5f0cba4..00d3ed8 100644 (file)
@@ -32,12 +32,11 @@ def sync_storage_fixture(request):
     request.cls.ns = 'some-ns'
     request.cls.key = 'a'
     request.cls.keys = {'a', 'b'}
-    request.cls.dm = {'a': b'1', 'b': b'2'}
+    request.cls.dm = {'b': b'2', 'a': b'1'}
     request.cls.old_data = b'1'
     request.cls.new_data = b'3'
     request.cls.keyprefix = 'x'
     request.cls.matchedkeys = ['x1', 'x2', 'x3', 'x4', 'x5']
-    request.cls.is_atomic = True
     request.cls.group = 'some-group'
     request.cls.groupmembers = set([b'm1', b'm2'])
     request.cls.groupmember = b'm1'
@@ -119,6 +118,8 @@ class TestSyncStorage:
         assert len(call_args[1]) == len(self.keys)
         assert all(k in call_args[1] for k in self.keys)
         assert ret == self.dm
+        # Validate that SDL returns a dictionary with keys in alphabetical order
+        assert sorted(self.dm)[0] == list(ret.keys())[0]
 
     def test_get_function_can_return_empty_dict_when_no_key_values_exist(self):
         self.mock_db_backend.get.return_value = dict()
@@ -156,25 +157,23 @@ class TestSyncStorage:
 
     def test_find_and_get_function_success(self):
         self.mock_db_backend.find_and_get.return_value = self.dm
-        ret = self.storage.find_and_get(self.ns, self.keyprefix, self.is_atomic)
-        self.mock_db_backend.find_and_get.assert_called_once_with(self.ns, self.keyprefix,
-                                                                  self.is_atomic)
+        ret = self.storage.find_and_get(self.ns, self.keyprefix)
+        self.mock_db_backend.find_and_get.assert_called_once_with(self.ns, self.keyprefix)
         assert ret == self.dm
+        # Validate that SDL returns a dictionary with keys in alphabetical order
+        assert sorted(self.dm)[0] == list(ret.keys())[0]
 
     def test_find_and_get_function_can_return_empty_dict_when_no_keys_exist(self):
         self.mock_db_backend.find_and_get.return_value = dict()
-        ret = self.storage.find_and_get(self.ns, self.keyprefix, self.is_atomic)
-        self.mock_db_backend.find_and_get.assert_called_once_with(self.ns, self.keyprefix,
-                                                                  self.is_atomic)
+        ret = self.storage.find_and_get(self.ns, self.keyprefix)
+        self.mock_db_backend.find_and_get.assert_called_once_with(self.ns, self.keyprefix)
         assert ret == dict()
 
     def test_find_and_get_function_can_raise_exception_for_wrong_argument(self):
         with pytest.raises(SdlTypeError):
-            self.storage.find_and_get(0xbad, self.keyprefix, self.is_atomic)
+            self.storage.find_and_get(0xbad, self.keyprefix)
         with pytest.raises(SdlTypeError):
-            self.storage.find_and_get(self.ns, 0xbad, self.is_atomic)
-        with pytest.raises(SdlTypeError):
-            self.storage.find_and_get(self.ns, self.keyprefix, 0xbad)
+            self.storage.find_and_get(self.ns, 0xbad)
 
     def test_remove_function_success(self):
         self.storage.remove(self.ns, self.keys)
@@ -214,7 +213,7 @@ class TestSyncStorage:
     def test_remove_all_function_success(self):
         self.mock_db_backend.find_keys.return_value = ['a1']
         self.storage.remove_all(self.ns)
-        self.mock_db_backend.find_keys.assert_called_once_with(self.ns, '')
+        self.mock_db_backend.find_keys.assert_called_once_with(self.ns, '*')
         self.mock_db_backend.remove.assert_called_once_with(self.ns,
                                                             self.mock_db_backend.find_keys.return_value)