36222858dfbd43d653325aee28043e4809a4bf9b
[ric-plt/sdlpy.git] / sdl / syncstorage_abc.py
1 # Copyright (c) 2019 AT&T Intellectual Property.
2 # Copyright (c) 2018-2019 Nokia.
3 #
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
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
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.
15
16 """The module provides synchronous shareddatalayer interface."""
17 from typing import (Dict, Set, List, Union)
18 from abc import ABC, abstractmethod
19
20
21 __all__ = [
22     'SyncStorageAbc',
23     'SyncLockAbc'
24 ]
25
26
27 class SyncLockAbc(ABC):
28     """
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.
31
32     A lock instance is created per namespace and it is identified by its `name` within a
33     namespace.
34
35     A concrete implementation subclass 'SyncLock' derives from this abstract class.
36
37     Args:
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.
42
43     """
44     def __init__(self, ns: str, name: str, expiration: Union[int, float]) -> None:
45         super().__init__()
46         self._ns = ns
47         self._name = name
48         self._expiration = expiration
49
50     def __enter__(self, *args, **kwargs):
51         self.acquire(*args, **kwargs)
52         return self
53
54     def __exit__(self, exception_type, exception_value, traceback):
55         self.release()
56
57     def acquire(self, retry_interval: Union[int, float] = 0.1,
58                 retry_timeout: Union[int, float] = 10) -> bool:
59         """
60         Acquire a shared, distributed lock atomically.
61
62         A lock can be used as a mutual exclusion locking entry for a shared resources.
63
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.
68
69         Args:
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
74                                         numbers.
75
76         Returns:
77             bool: True for successful lock acquiring, false otherwise.
78
79         Raises:
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.
84         """
85         pass
86
87
88     def release(self) -> None:
89         """
90         Release a lock atomically.
91
92         Release the already acquired lock.
93
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
96         needed.
97
98         Args:
99             None
100
101         Returns:
102             None
103
104         Raises:
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.
108         """
109         pass
110
111
112     def refresh(self) -> None:
113         """
114         Refresh the remaining validity time of the existing lock back to an initial value.
115
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
118         needed.
119
120         Args:
121             None
122
123         Returns:
124             None
125
126         Raises:
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.
130         """
131         pass
132
133
134     def get_validity_time(self) -> Union[int, float]:
135         """
136         Get atomically the remaining validity time of the lock in seconds.
137
138         Return atomically time in seconds until the lock expires.
139
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
142         needed.
143
144         Args:
145             None
146
147         Returns:
148             (int, float): Validity time of the lock in seconds.
149
150         Raises:
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.
154         """
155         pass
156
157
158 class SyncStorageAbc(ABC):
159     """
160     An abstract class providing synchronous access to shared data layer storage.
161
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.
165
166     A concrete implementation subclass 'SyncStorage' derives from this abstract class.
167     """
168
169     @abstractmethod
170     def set(self, ns: str, data_map: Dict[str, bytes]) -> None:
171         """
172         Write data to shared data layer storage.
173
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.
179
180         Args:
181             ns (str): Namespace under which this operation is targeted.
182             data_map (dict of str: bytes): Data to be written.
183
184         Returns:
185             None
186
187         Raises:
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.
192         """
193         pass
194
195
196     @abstractmethod
197     def set_if(self, ns: str, key: str, old_data: bytes, new_data: bytes) -> bool:
198         """
199         Conditionally modify the value of a key if the current value in data storage matches the
200         user's last known value.
201
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.
206
207         Args:
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.
212
213         Returns:
214             bool: True for successful modification, false if the user's last known data did not
215                   match the current value in data storage.
216
217         Raises:
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.
222         """
223         pass
224
225
226     @abstractmethod
227     def set_if_not_exists(self, ns: str, key: str, data: bytes) -> bool:
228         """
229         Write data to shared data layer storage if key does not exist.
230
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
233         operation.
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.
238
239         Args:
240             ns (str): Namespace under which this operation is targeted.
241             key (str): Key to be set.
242             data (bytes): Data to be written.
243
244         Returns:
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.
247
248         Raises:
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.
253         """
254         pass
255
256
257     @abstractmethod
258     def get(self, ns: str, keys: Union[str, Set[str]]) -> Dict[str, bytes]:
259         """
260         Read data from shared data layer storage.
261
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.
267
268         Args:
269             ns (str): Namespace under which this operation is targeted.
270             keys (str or set of str): One or multiple keys to be read.
271
272         Returns:
273             (dict of str: bytes): A dictionary mapping of a key to the read data from the storage.
274
275         Raises:
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.
280         """
281         pass
282
283
284     @abstractmethod
285     def find_keys(self, ns: str, key_prefix: str) -> List[str]:
286         """
287         Find all keys matching search pattern under the namespace.
288
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.
295
296         Args:
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.
300
301         Returns:
302             (list of str): A list of found keys.
303
304         Raises:
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.
309         """
310         pass
311
312
313     @abstractmethod
314     def find_and_get(self, ns: str, key_prefix: str, atomic: bool) -> Dict[str, bytes]:
315         """
316         Find keys and get their respective data from shared data layer storage.
317
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.
325
326         Args:
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.
332
333         Returns:
334             (dict of str: bytes): A dictionary mapping of a key to the read data from the storage.
335
336         Raises:
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.
341         """
342         pass
343
344
345     @abstractmethod
346     def remove(self, ns: str, keys: Union[str, Set[str]]) -> None:
347         """
348         Remove data from shared data layer storage. Existing keys are removed.
349
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.
355
356         Args:
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.
359
360         Returns:
361             None
362
363         Raises:
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.
368         """
369         pass
370
371
372     @abstractmethod
373     def remove_if(self, ns: str, key: str, data: bytes) -> bool:
374         """
375         Conditionally remove data from shared data layer storage if the current data value matches
376         the user's last known value.
377
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.
382
383         Args:
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
387
388         Returns:
389             bool: True if successful removal, false if the user's last known data did not match the
390                   current value in data storage.
391
392         Raises:
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.
397         """
398         pass
399
400
401     @abstractmethod
402     def remove_all(self, ns: str) -> None:
403         """
404         Remove all keys under the namespace.
405
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.
412
413         Args:
414             ns (str): Namespace under which this operation is targeted.
415
416         Returns:
417             None
418
419         Raises:
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.
424         """
425         pass
426
427
428     @abstractmethod
429     def add_member(self, ns: str, group: str, members: Union[bytes, Set[bytes]]) -> None:
430         """
431         Add new members to a shared data layer group under the namespace.
432
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.
441
442         Args:
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.
446
447         Returns:
448             None
449
450         Raises:
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.
455         """
456         pass
457
458
459     @abstractmethod
460     def remove_member(self, ns: str, group: str, members: Union[bytes, Set[bytes]]) -> None:
461         """
462         Remove members from a shared data layer group.
463
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
467         silently ignored.
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.
472
473         Args:
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.
477
478         Returns:
479             None
480
481         Raises:
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.
486         """
487         pass
488
489
490     @abstractmethod
491     def remove_group(self, ns: str, group: str) -> None:
492         """
493         Remove a shared data layer group along with its members.
494
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.
501
502         Args:
503             ns (str): Namespace under which this operation is targeted.
504             group (str): Group name to be removed.
505
506         Returns:
507             None
508
509         Raises:
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.
514         """
515         pass
516
517
518     @abstractmethod
519     def get_members(self, ns: str, group: str) -> Set[bytes]:
520         """
521         Get all the members of a shared data layer group.
522
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.
529
530         Args:
531             ns (str): Namespace under which this operation is targeted.
532             group (str): Group name of which members are to be returned.
533
534         Returns:
535             (set of bytes): A set of the members of the group.
536             None
537
538         Raises:
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.
543         """
544         pass
545
546
547     @abstractmethod
548     def is_member(self, ns: str, group: str, member: bytes) -> bool:
549         """
550         Validate if a given member is in the shared data layer group.
551
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.
558
559         Args:
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.
563
564         Returns:
565             bool: True if member was in the group, false otherwise.
566
567         Raises:
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.
572         """
573         pass
574
575
576     @abstractmethod
577     def group_size(self, ns: str, group: str) -> int:
578         """
579         Return the number of members in a group.
580
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.
587
588         Args:
589             ns (str): Namespace under which this operation is targeted.
590             group (str): Group name of which members count is queried.
591
592         Returns:
593             int: Number of members in a group.
594
595         Raises:
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.
600         """
601         pass
602
603
604     @abstractmethod
605     def get_lock_resource(self, ns: str, resource: str,
606                           expiration: Union[int, float]) -> SyncLockAbc:
607         """
608         Return a lock resource for shared data layer.
609
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.
618
619         Args:
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
622                             shareddatalayer.
623             expiration (int, float): Expiration time of a lock
624
625         Returns:
626             SyncLockAbc: Lock resource instance.
627
628         Raises:
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.
633         """
634         pass