1 # Copyright (c) 2019 AT&T Intellectual Property.
2 # Copyright (c) 2018-2019 Nokia.
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
16 """The module provides synchronous shareddatalayer interface."""
17 from typing import (Dict, Set, List, Union)
18 from abc import ABC, abstractmethod
27 class SyncLockAbc(ABC):
29 An abstract synchronous lock class providing a shared, distributed locking mechanism, which can
30 be utilized by clients to be able to operate with a shared resource in a mutually exclusive way.
32 A lock instance is created per namespace and it is identified by its `name` within a
35 A concrete implementation subclass 'SyncLock' derives from this abstract class.
38 ns (str): Namespace under which this lock is targeted.
39 name (str): Lock name, identifies the lock key in shared data layer storage.
40 expiration (int, float): Lock expiration time after which the lock is removed if it hasn't
41 been released earlier by a 'release' method.
44 def __init__(self, ns: str, name: str, expiration: Union[int, float]) -> None:
48 self._expiration = expiration
50 def __enter__(self, *args, **kwargs):
51 self.acquire(*args, **kwargs)
54 def __exit__(self, exception_type, exception_value, traceback):
57 def acquire(self, retry_interval: Union[int, float] = 0.1,
58 retry_timeout: Union[int, float] = 10) -> bool:
60 Acquire a shared, distributed lock atomically.
62 A lock can be used as a mutual exclusion locking entry for a shared resources.
64 All the exceptions except SdlTypeError are derived from SdlException base class. Client
65 can catch only that exception if separate handling for different shareddatalayer error
66 situations is not needed. Exception SdlTypeError is derived from build-in TypeError and it
67 indicates misuse of the SDL API.
70 retry_interval (int, float): Lock acquiring retry interval in seconds. Supports both
71 integer and float numbers.
72 retry_timeout (int, float): Lock acquiring timeout after which retries are stopped and
73 error status is returned. Supports both integer and float
77 bool: True for successful lock acquiring, false otherwise.
80 SdlTypeError: If function's argument is of an inappropriate type.
81 NotConnected: If shareddatalayer is not connected to the backend data storage.
82 RejectedByBackend: If backend data storage rejects the request.
83 BackendError: If the backend data storage fails to process the request.
88 def release(self) -> None:
90 Release a lock atomically.
92 Release the already acquired lock.
94 Exceptions thrown are all derived from SdlException base class. Client can catch only that
95 exception if separate handling for different shareddatalayer error situations is not
105 NotConnected: If shareddatalayer is not connected to the backend data storage.
106 RejectedByBackend: If backend data storage rejects the request.
107 BackendError: If the backend data storage fails to process the request.
112 def refresh(self) -> None:
114 Refresh the remaining validity time of the existing lock back to an initial value.
116 Exceptions thrown are all derived from SdlException base class. Client can catch only that
117 exception if separate handling for different shareddatalayer error situations is not
127 NotConnected: If shareddatalayer is not connected to the backend data storage.
128 RejectedByBackend: If backend data storage rejects the request.
129 BackendError: If the backend data storage fails to process the request.
134 def get_validity_time(self) -> Union[int, float]:
136 Get atomically the remaining validity time of the lock in seconds.
138 Return atomically time in seconds until the lock expires.
140 Exceptions thrown are all derived from SdlException base class. Client can catch only that
141 exception if separate handling for different shareddatalayer error situations is not
148 (int, float): Validity time of the lock in seconds.
151 NotConnected: If shareddatalayer is not connected to the backend data storage.
152 RejectedByBackend: If backend data storage rejects the request.
153 BackendError: If the backend data storage fails to process the request.
158 class SyncStorageAbc(ABC):
160 An abstract class providing synchronous access to shared data layer storage.
162 This class provides synchronous access to all the namespaces in shared data layer storage.
163 Data can be written, read and removed based on keys known to clients. Keys are unique within
164 a namespace, namespace identifier is passed as a parameter to all the operations.
166 A concrete implementation subclass 'SyncStorage' derives from this abstract class.
170 def set(self, ns: str, data_map: Dict[str, bytes]) -> None:
172 Write data to shared data layer storage.
174 Writing is done atomically, i.e. either all succeeds, or all fails.
175 All the exceptions except SdlTypeError are derived from SdlException base class. Client
176 can catch only that exception if separate handling for different shareddatalayer error
177 situations is not needed. Exception SdlTypeError is derived from build-in TypeError and it
178 indicates misuse of the SDL API.
181 ns (str): Namespace under which this operation is targeted.
182 data_map (dict of str: bytes): Data to be written.
188 SdlTypeError: If function's argument is of an inappropriate type.
189 NotConnected: If shareddatalayer is not connected to the backend data storage.
190 RejectedByBackend: If backend data storage rejects the request.
191 BackendError: If the backend data storage fails to process the request.
197 def set_if(self, ns: str, key: str, old_data: bytes, new_data: bytes) -> bool:
199 Conditionally modify the value of a key if the current value in data storage matches the
200 user's last known value.
202 All the exceptions except SdlTypeError are derived from SdlException base class. Client
203 can catch only that exception if separate handling for different shareddatalayer error
204 situations is not needed. Exception SdlTypeError is derived from build-in TypeError and it
205 indicates misuse of the SDL API.
208 ns (str): Namespace under which this operation is targeted.
209 key (str): Key for which data modification will be executed.
210 old_data (bytes): Last known data.
211 new_data (bytes): Data to be written.
214 bool: True for successful modification, false if the user's last known data did not
215 match the current value in data storage.
218 SdlTypeError: If function's argument is of an inappropriate type.
219 NotConnected: If shareddatalayer is not connected to the backend data storage.
220 RejectedByBackend: If backend data storage rejects the request.
221 BackendError: If the backend data storage fails to process the request.
227 def set_if_not_exists(self, ns: str, key: str, data: bytes) -> bool:
229 Write data to shared data layer storage if key does not exist.
231 Conditionally set the value of a key. If key already exists, then its value is not
232 modified. Checking the key existence and potential set operation is done as a one atomic
234 All the exceptions except SdlTypeError are derived from SdlException base class. Client
235 can catch only that exception if separate handling for different shareddatalayer error
236 situations is not needed. Exception SdlTypeError is derived from build-in TypeError and it
237 indicates misuse of the SDL API.
240 ns (str): Namespace under which this operation is targeted.
241 key (str): Key to be set.
242 data (bytes): Data to be written.
245 bool: True if key didn't exist yet and set operation was executed, false if key already
246 existed and thus its value was left untouched.
249 SdlTypeError: If function's argument is of an inappropriate type.
250 NotConnected: If shareddatalayer is not connected to the backend data storage.
251 RejectedByBackend: If backend data storage rejects the request.
252 BackendError: If the backend data storage fails to process the request.
258 def get(self, ns: str, keys: Union[str, Set[str]]) -> Dict[str, bytes]:
260 Read data from shared data layer storage.
262 Only those entries that are found will be returned.
263 All the exceptions except SdlTypeError are derived from SdlException base class. Client
264 can catch only that exception if separate handling for different shareddatalayer error
265 situations is not needed. Exception SdlTypeError is derived from build-in TypeError and it
266 indicates misuse of the SDL API.
269 ns (str): Namespace under which this operation is targeted.
270 keys (str or set of str): One or multiple keys to be read.
273 (dict of str: bytes): A dictionary mapping of a key to the read data from the storage.
276 SdlTypeError: If function's argument is of an inappropriate type.
277 NotConnected: If shareddatalayer is not connected to the backend data storage.
278 RejectedByBackend: If backend data storage rejects the request.
279 BackendError: If the backend data storage fails to process the request.
285 def find_keys(self, ns: str, key_prefix: str) -> List[str]:
287 Find all keys matching search pattern under the namespace.
289 No prior knowledge about the keys in the given namespace exists, thus operation is not
290 guaranteed to be atomic or isolated.
291 All the exceptions except SdlTypeError are derived from SdlException base class. Client
292 can catch only that exception if separate handling for different shareddatalayer error
293 situations is not needed. Exception SdlTypeError is derived from build-in TypeError and it
294 indicates misuse of the SDL API.
297 ns (str): Namespace under which this operation is targeted.
298 key_prefix (str): Only keys starting with given keyPrefix are returned. Passing empty
299 string as keyPrefix will return all the keys.
302 (list of str): A list of found keys.
305 SdlTypeError: If function's argument is of an inappropriate type.
306 NotConnected: If shareddatalayer is not connected to the backend data storage.
307 RejectedByBackend: If backend data storage rejects the request.
308 BackendError: If the backend data storage fails to process the request.
314 def find_and_get(self, ns: str, key_prefix: str, atomic: bool) -> Dict[str, bytes]:
316 Find keys and get their respective data from shared data layer storage.
318 Only those entries that are matching prefix will be returned.
319 NOTE: In atomic action, if the prefix produces huge number of matches, that can have
320 a severe impact on system performance, due to DB is blocked for long time.
321 All the exceptions except SdlTypeError are derived from SdlException base class. Client
322 can catch only that exception if separate handling for different shareddatalayer error
323 situations is not needed. Exception SdlTypeError is derived from build-in TypeError and it
324 indicates misuse of the SDL API.
327 ns (str): Namespace under which this operation is targeted.
328 key_prefix (str): Only keys starting with given keyPrefix are returned. Passing empty
329 string as keyPrefix will return all the keys.
330 atomic (bool): True to find keys and get their respective data in one atomic operation,
331 false to find keys and get their respective data non-atomically.
334 (dict of str: bytes): A dictionary mapping of a key to the read data from the storage.
337 SdlTypeError: If function's argument is of an inappropriate type.
338 NotConnected: If shareddatalayer is not connected to the backend data storage.
339 RejectedByBackend: If backend data storage rejects the request.
340 BackendError: If the backend data storage fails to process the request.
346 def remove(self, ns: str, keys: Union[str, Set[str]]) -> None:
348 Remove data from shared data layer storage. Existing keys are removed.
350 Removing is done atomically, i.e. either all succeeds, or all fails.
351 All the exceptions except SdlTypeError are derived from SdlException base class. Client
352 can catch only that exception if separate handling for different shareddatalayer error
353 situations is not needed. Exception SdlTypeError is derived from build-in TypeError and it
354 indicates misuse of the SDL API.
357 ns (str): Namespace under which this operation is targeted.
358 keys (str or set of str): One key or multiple keys, which data is to be removed.
364 SdlTypeError: If function's argument is of an inappropriate type.
365 NotConnected: If shareddatalayer is not connected to the backend data storage.
366 RejectedByBackend: If backend data storage rejects the request.
367 BackendError: If the backend data storage fails to process the request.
373 def remove_if(self, ns: str, key: str, data: bytes) -> bool:
375 Conditionally remove data from shared data layer storage if the current data value matches
376 the user's last known value.
378 All the exceptions except SdlTypeError are derived from SdlException base class. Client
379 can catch only that exception if separate handling for different shareddatalayer error
380 situations is not needed. Exception SdlTypeError is derived from build-in TypeError and it
381 indicates misuse of the SDL API.
384 ns (str): Namespace under which this operation is targeted.
385 key (str): Key, which data is to be removed.
386 data (bytes): Last known value of data
389 bool: True if successful removal, false if the user's last known data did not match the
390 current value in data storage.
393 SdlTypeError: If function's argument is of an inappropriate type.
394 NotConnected: If shareddatalayer is not connected to the backend data storage.
395 RejectedByBackend: If backend data storage rejects the request.
396 BackendError: If the backend data storage fails to process the request.
402 def remove_all(self, ns: str) -> None:
404 Remove all keys under the namespace.
406 No prior knowledge about the keys in the given namespace exists, thus operation is not
407 guaranteed to be atomic or isolated.
408 All the exceptions except SdlTypeError are derived from SdlException base class. Client
409 can catch only that exception if separate handling for different shareddatalayer error
410 situations is not needed. Exception SdlTypeError is derived from build-in TypeError and it
411 indicates misuse of the SDL API.
414 ns (str): Namespace under which this operation is targeted.
420 SdlTypeError: If function's argument is of an inappropriate type.
421 NotConnected: If shareddatalayer is not connected to the backend data storage.
422 RejectedByBackend: If backend data storage rejects the request.
423 BackendError: If the backend data storage fails to process the request.
429 def add_member(self, ns: str, group: str, members: Union[bytes, Set[bytes]]) -> None:
431 Add new members to a shared data layer group under the namespace.
433 Shared data layer groups are identified by their name, which is a key in storage. Shared
434 data layer groups are unordered collections of members where each member is unique. If
435 a member to be added is already a member of the group, its addition is silently ignored. If
436 the group does not exist, it is created, and specified members are added to the group.
437 All the exceptions except SdlTypeError are derived from SdlException base class. Client
438 can catch only that exception if separate handling for different shareddatalayer error
439 situations is not needed. Exception SdlTypeError is derived from build-in TypeError and it
440 indicates misuse of the SDL API.
443 ns (str): Namespace under which this operation is targeted.
444 group (str): Group name.
445 members (bytes or set of bytes): One or multiple members to be added.
451 SdlTypeError: If function's argument is of an inappropriate type.
452 NotConnected: If shareddatalayer is not connected to the backend data storage.
453 RejectedByBackend: If backend data storage rejects the request.
454 BackendError: If the backend data storage fails to process the request.
460 def remove_member(self, ns: str, group: str, members: Union[bytes, Set[bytes]]) -> None:
462 Remove members from a shared data layer group.
464 Shared data layer groups are unordered collections of members where each member is unique.
465 If a member to be removed does not exist in the group, its removal is silently ignored. If
466 a group does not exist, it is treated as an empty group and hence members removal is
468 All the exceptions except SdlTypeError are derived from SdlException base class. Client
469 can catch only that exception if separate handling for different shareddatalayer error
470 situations is not needed. Exception SdlTypeError is derived from build-in TypeError and it
471 indicates misuse of the SDL API.
474 ns (str): Namespace under which this operation is targeted.
475 group (str): Group name.
476 members (bytes or set of bytes): One or multiple members to be removed.
482 SdlTypeError: If function's argument is of an inappropriate type.
483 NotConnected: If shareddatalayer is not connected to the backend data storage.
484 RejectedByBackend: If backend data storage rejects the request.
485 BackendError: If the backend data storage fails to process the request.
491 def remove_group(self, ns: str, group: str) -> None:
493 Remove a shared data layer group along with its members.
495 Shared data layer groups are unordered collections of members where each member is unique.
496 If a group to be removed does not exist, its removal is silently ignored.
497 All the exceptions except SdlTypeError are derived from SdlException base class. Client
498 can catch only that exception if separate handling for different shareddatalayer error
499 situations is not needed. Exception SdlTypeError is derived from build-in TypeError and it
500 indicates misuse of the SDL API.
503 ns (str): Namespace under which this operation is targeted.
504 group (str): Group name to be removed.
510 SdlTypeError: If function's argument is of an inappropriate type.
511 NotConnected: If shareddatalayer is not connected to the backend data storage.
512 RejectedByBackend: If backend data storage rejects the request.
513 BackendError: If the backend data storage fails to process the request.
519 def get_members(self, ns: str, group: str) -> Set[bytes]:
521 Get all the members of a shared data layer group.
523 Shared data layer groups are unordered collections of members where each member is unique.
524 If the group does not exist, empty set is returned.
525 All the exceptions except SdlTypeError are derived from SdlException base class. Client
526 can catch only that exception if separate handling for different shareddatalayer error
527 situations is not needed. Exception SdlTypeError is derived from build-in TypeError and it
528 indicates misuse of the SDL API.
531 ns (str): Namespace under which this operation is targeted.
532 group (str): Group name of which members are to be returned.
535 (set of bytes): A set of the members of the group.
539 SdlTypeError: If function's argument is of an inappropriate type.
540 NotConnected: If shareddatalayer is not connected to the backend data storage.
541 RejectedByBackend: If backend data storage rejects the request.
542 BackendError: If the backend data storage fails to process the request.
548 def is_member(self, ns: str, group: str, member: bytes) -> bool:
550 Validate if a given member is in the shared data layer group.
552 Shared data layer groups are unordered collections of members where each member is unique.
553 If the group does not exist, false is returned.
554 All the exceptions except SdlTypeError are derived from SdlException base class. Client
555 can catch only that exception if separate handling for different shareddatalayer error
556 situations is not needed. Exception SdlTypeError is derived from build-in TypeError and it
557 indicates misuse of the SDL API.
560 ns (str): Namespace under which this operation is targeted.
561 group (str): Group name of which member existence is to be validated.
562 member (bytes): A member, which existence is to be validated.
565 bool: True if member was in the group, false otherwise.
568 SdlTypeError: If function's argument is of an inappropriate type.
569 NotConnected: If shareddatalayer is not connected to the backend data storage.
570 RejectedByBackend: If backend data storage rejects the request.
571 BackendError: If the backend data storage fails to process the request.
577 def group_size(self, ns: str, group: str) -> int:
579 Return the number of members in a group.
581 Shared data layer groups are unordered collections of members where each member is unique.
582 If the group does not exist, value 0 is returned.
583 All the exceptions except SdlTypeError are derived from SdlException base class. Client
584 can catch only that exception if separate handling for different shareddatalayer error
585 situations is not needed. Exception SdlTypeError is derived from build-in TypeError and it
586 indicates misuse of the SDL API.
589 ns (str): Namespace under which this operation is targeted.
590 group (str): Group name of which members count is queried.
593 int: Number of members in a group.
596 SdlTypeError: If function's argument is of an inappropriate type.
597 NotConnected: If shareddatalayer is not connected to the backend data storage.
598 RejectedByBackend: If backend data storage rejects the request.
599 BackendError: If the backend data storage fails to process the request.
605 def get_lock_resource(self, ns: str, resource: str,
606 expiration: Union[int, float]) -> SyncLockAbc:
608 Return a lock resource for shared data layer.
610 A lock resource instance is created per namespace and it is identified by its `name` within
611 a namespace. A `get_lock_resource` returns a lock resource instance, it does not acquire
612 a lock. Lock resource provides lock handling methods such as acquiring a lock, extend
613 expiration time and releasing a lock.
614 All the exceptions except SdlTypeError are derived from SdlException base class. Client
615 can catch only that exception if separate handling for different shareddatalayer error
616 situations is not needed. Exception SdlTypeError is derived from build-in TypeError and it
617 indicates misuse of the SDL API.
620 ns (str): Namespace under which this operation is targeted.
621 resource (str): Resource is used within namespace as a key for a lock entry in
623 expiration (int, float): Expiration time of a lock
626 SyncLockAbc: Lock resource instance.
629 SdlTypeError: If function's argument is of an inappropriate type.
630 NotConnected: If shareddatalayer is not connected to the backend data storage.
631 RejectedByBackend: If backend data storage rejects the request.
632 BackendError: If the backend data storage fails to process the request.