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.
17 # This source code is part of the near-RT RIC (RAN Intelligent Controller)
18 # platform project (RICP).
22 """The module provides synchronous Shared Data Layer (SDL) interface."""
23 from typing import (Callable, Dict, Set, List, Optional, Tuple, Union)
24 from abc import ABC, abstractmethod
25 from ricsdl.exceptions import (
35 class SyncLockAbc(ABC):
37 An abstract synchronous Shared Data Layer (SDL) lock class providing a shared, distributed
38 locking mechanism, which can be utilized by clients to be able to operate with a shared
39 resource in a mutually exclusive way.
41 A lock instance is created per namespace and it is identified by its `name` within a
44 A concrete implementation subclass 'SyncLock' derives from this abstract class.
47 ns (str): Namespace under which this lock is targeted.
48 name (str): Lock name, identifies the lock key in SDL storage.
49 expiration (int, float): Lock expiration time after which the lock is removed if it hasn't
50 been released earlier by a 'release' method.
53 def __init__(self, ns: str, name: str, expiration: Union[int, float]) -> None:
57 self._expiration = expiration
59 def __enter__(self, *args, **kwargs):
60 if self.acquire(*args, **kwargs):
62 raise RejectedByBackend("Unable to acquire lock within the time specified")
64 def __exit__(self, exception_type, exception_value, traceback):
67 def acquire(self, retry_interval: Union[int, float] = 0.1,
68 retry_timeout: Union[int, float] = 10) -> bool:
70 Acquire a shared, distributed lock atomically.
72 A lock can be used as a mutual exclusion locking entry for a shared resources.
74 All the exceptions except SdlTypeError are derived from SdlException base class. Client
75 can catch only that exception if separate handling for different SDL error situations is
76 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
77 misuse of the SDL API.
80 retry_interval (int, float): Lock acquiring retry interval in seconds. Supports both
81 integer and float numbers.
82 retry_timeout (int, float): Lock acquiring timeout after which retries are stopped and
83 error status is returned. Supports both integer and float
87 bool: True for successful lock acquiring, false otherwise.
90 SdlTypeError: If function's argument is of an inappropriate type.
91 NotConnected: If SDL is not connected to the backend data storage.
92 RejectedByBackend: If backend data storage rejects the request.
93 BackendError: If the backend data storage fails to process the request.
97 def release(self) -> None:
99 Release a lock atomically.
101 Release the already acquired lock.
103 Exceptions thrown are all derived from SdlException base class. Client can catch only that
104 exception if separate handling for different SDL error situations is not needed.
113 NotConnected: If SDL is not connected to the backend data storage.
114 RejectedByBackend: If backend data storage rejects the request.
115 BackendError: If the backend data storage fails to process the request.
119 def refresh(self) -> None:
121 Refresh the remaining validity time of the existing lock back to an initial value.
123 Exceptions thrown are all derived from SdlException base class. Client can catch only that
124 exception if separate handling for different SDL error situations is not needed.
133 NotConnected: If SDL is not connected to the backend data storage.
134 RejectedByBackend: If backend data storage rejects the request.
135 BackendError: If the backend data storage fails to process the request.
139 def get_validity_time(self) -> Union[int, float]:
141 Get atomically the remaining validity time of the lock in seconds.
143 Return atomically time in seconds until the lock expires.
145 Exceptions thrown are all derived from SdlException base class. Client can catch only that
146 exception if separate handling for different SDL error situations is not needed.
152 (int, float): Validity time of the lock in seconds.
155 NotConnected: If SDL is not connected to the backend data storage.
156 RejectedByBackend: If backend data storage rejects the request.
157 BackendError: If the backend data storage fails to process the request.
162 class SyncStorageAbc(ABC):
164 An abstract class providing synchronous access to Shared Data Layer (SDL) storage.
166 This class provides synchronous access to all the namespaces in SDL storage.
167 Data can be written, read and removed based on keys known to clients. Keys are unique within
168 a namespace, namespace identifier is passed as a parameter to all the operations.
170 A concrete implementation subclass 'SyncStorage' derives from this abstract class.
176 Verify SDL storage healthiness.
178 Verify SDL connection to the backend data storage.
184 bool: True if SDL is operational, false otherwise.
194 Close the connection to SDL storage.
203 NotConnected: If SDL is not connected to the backend data storage.
204 RejectedByBackend: If backend data storage rejects the request.
205 BackendError: If the backend data storage fails to process the request.
210 def set(self, ns: str, data_map: Dict[str, bytes]) -> None:
212 Write data to SDL storage.
214 Writing is done atomically, i.e. either all succeeds, or all fails.
215 All the exceptions except SdlTypeError are derived from SdlException base class. Client
216 can catch only that exception if separate handling for different SDL error situations is
217 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
218 misuse of the SDL API.
221 ns (str): Namespace under which this operation is targeted.
222 data_map (dict of str: bytes): Data to be written.
228 SdlTypeError: If function's argument is of an inappropriate type.
229 NotConnected: If SDL is not connected to the backend data storage.
230 RejectedByBackend: If backend data storage rejects the request.
231 BackendError: If the backend data storage fails to process the request.
236 def set_if(self, ns: str, key: str, old_data: bytes, new_data: bytes) -> bool:
238 Conditionally modify the value of a key if the current value in data storage matches the
239 user's last known value.
241 All the exceptions except SdlTypeError are derived from SdlException base class. Client
242 can catch only that exception if separate handling for different SDL error situations is
243 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
244 misuse of the SDL API.
247 ns (str): Namespace under which this operation is targeted.
248 key (str): Key for which data modification will be executed.
249 old_data (bytes): Last known data.
250 new_data (bytes): Data to be written.
253 bool: True for successful modification, false if the user's last known data did not
254 match the current value in data storage.
257 SdlTypeError: If function's argument is of an inappropriate type.
258 NotConnected: If SDL is not connected to the backend data storage.
259 RejectedByBackend: If backend data storage rejects the request.
260 BackendError: If the backend data storage fails to process the request.
265 def set_if_not_exists(self, ns: str, key: str, data: bytes) -> bool:
267 Write data to SDL storage if key does not exist.
269 Conditionally set the value of a key. If key already exists, then its value is not
270 modified. Checking the key existence and potential set operation is done as a one atomic
272 All the exceptions except SdlTypeError are derived from SdlException base class. Client
273 can catch only that exception if separate handling for different SDL error situations is
274 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
275 misuse of the SDL API.
278 ns (str): Namespace under which this operation is targeted.
279 key (str): Key to be set.
280 data (bytes): Data to be written.
283 bool: True if key didn't exist yet and set operation was executed, false if key already
284 existed and thus its value was left untouched.
287 SdlTypeError: If function's argument is of an inappropriate type.
288 NotConnected: If SDL is not connected to the backend data storage.
289 RejectedByBackend: If backend data storage rejects the request.
290 BackendError: If the backend data storage fails to process the request.
295 def get(self, ns: str, keys: Union[str, Set[str]]) -> Dict[str, bytes]:
297 Read data from SDL storage.
299 Only those entries that are found will be returned.
300 All the exceptions except SdlTypeError are derived from SdlException base class. Client
301 can catch only that exception if separate handling for different SDL error situations is
302 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
303 misuse of the SDL API.
306 ns (str): Namespace under which this operation is targeted.
307 keys (str or set of str): One or multiple keys to be read.
310 (dict of str: bytes): A dictionary mapping of a key to the read data from the storage.
311 Dictionary is sorted by key values in alphabetical order.
314 SdlTypeError: If function's argument is of an inappropriate type.
315 NotConnected: If SDL is not connected to the backend data storage.
316 RejectedByBackend: If backend data storage rejects the request.
317 BackendError: If the backend data storage fails to process the request.
322 def find_keys(self, ns: str, key_pattern: str) -> List[str]:
324 Find all keys matching search pattern under the namespace.
326 Supported glob-style patterns:
327 `?` matches any single character. For example `?at` matches Cat, cat, Bat or bat.
328 `*` matches any number of any characters including none. For example `*Law*` matches
329 Law, GrokLaw, or Lawyer.
330 `[abc]` matches one character given in the bracket. For example `[CB]at` matches Cat or
332 `[a-z]` matches one character from the range given in the bracket. For example
333 `Letter[0-9]` matches Letter0 up to Letter9.
334 `[^abc]` matches any single character what is not given in the bracket. For example
335 `h[^e]llo` matches hallo, hillo but not hello.
337 If searched key itself contains a special character, use a backslash (\) character to
338 escape the special character to match it verbatim.
340 NOTE: `find_keys` function is not guaranteed to be atomic or isolated.
342 All the exceptions except SdlTypeError are derived from SdlException base class. Client
343 can catch only that exception if separate handling for different SDL error situations is
344 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
345 misuse of the SDL API.
348 ns (str): Namespace under which this operation is targeted.
349 key_pattern (str): Key search pattern.
352 (list of str): A list of found keys.
355 SdlTypeError: If function's argument is of an inappropriate type.
356 NotConnected: If SDL is not connected to the backend data storage.
357 RejectedByBackend: If backend data storage rejects the request.
358 BackendError: If the backend data storage fails to process the request.
363 def find_and_get(self, ns: str, key_pattern: str) -> Dict[str, bytes]:
365 Find keys and get their respective data from SDL storage.
367 Supported glob-style patterns:
368 `?` matches any single character. For example `?at` matches Cat, cat, Bat or bat.
369 `*` matches any number of any characters including none. For example `*Law*` matches
370 Law, GrokLaw, or Lawyer.
371 `[abc]` matches one character given in the bracket. For example `[CB]at` matches Cat or
373 `[a-z]` matches one character from the range given in the bracket. For example
374 `Letter[0-9]` matches Letter0 up to Letter9.
375 `[^abc]` matches any single character what is not given in the bracket. For example
376 `h[^e]llo` matches hallo, hillo but not hello.
378 If searched key itself contains a special character, use a backslash (\) character to
379 escape the special character to match it verbatim.
381 NOTE: `find_and_get` function is not guaranteed to be atomic or isolated.
383 All the exceptions except SdlTypeError are derived from SdlException base class. Client
384 can catch only that exception if separate handling for different SDL error situations is
385 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
386 misuse of the SDL API.
389 ns (str): Namespace under which this operation is targeted.
390 key_pattern (str): Key search pattern.
393 (dict of str: bytes): A dictionary mapping of a key to the read data from the storage.
394 Dictionary is sorted by key values in alphabetical order.
397 SdlTypeError: If function's argument is of an inappropriate type.
398 NotConnected: If SDL is not connected to the backend data storage.
399 RejectedByBackend: If backend data storage rejects the request.
400 BackendError: If the backend data storage fails to process the request.
405 def remove(self, ns: str, keys: Union[str, Set[str]]) -> None:
407 Remove data from SDL storage. Existing keys are removed.
409 Removing is done atomically, i.e. either all succeeds, or all fails.
410 All the exceptions except SdlTypeError are derived from SdlException base class. Client
411 can catch only that exception if separate handling for different SDL error situations is
412 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
413 misuse of the SDL API.
416 ns (str): Namespace under which this operation is targeted.
417 keys (str or set of str): One key or multiple keys, which data is to be removed.
423 SdlTypeError: If function's argument is of an inappropriate type.
424 NotConnected: If SDL is not connected to the backend data storage.
425 RejectedByBackend: If backend data storage rejects the request.
426 BackendError: If the backend data storage fails to process the request.
431 def remove_if(self, ns: str, key: str, data: bytes) -> bool:
433 Conditionally remove data from SDL storage if the current data value matches the user's
436 All the exceptions except SdlTypeError are derived from SdlException base class. Client
437 can catch only that exception if separate handling for different SDL error situations is
438 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
439 misuse of the SDL API.
442 ns (str): Namespace under which this operation is targeted.
443 key (str): Key, which data is to be removed.
444 data (bytes): Last known value of data
447 bool: True if successful removal, false if the user's last known data did not match the
448 current value in data storage.
451 SdlTypeError: If function's argument is of an inappropriate type.
452 NotConnected: If SDL 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.
459 def remove_all(self, ns: str) -> None:
461 Remove all keys under the namespace.
463 No prior knowledge about the keys in the given namespace exists, thus operation is not
464 guaranteed to be atomic or isolated.
465 All the exceptions except SdlTypeError are derived from SdlException base class. Client
466 can catch only that exception if separate handling for different SDL error situations is
467 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
468 misuse of the SDL API.
471 ns (str): Namespace under which this operation is targeted.
477 SdlTypeError: If function's argument is of an inappropriate type.
478 NotConnected: If SDL is not connected to the backend data storage.
479 RejectedByBackend: If backend data storage rejects the request.
480 BackendError: If the backend data storage fails to process the request.
485 def add_member(self, ns: str, group: str, members: Union[bytes, Set[bytes]]) -> None:
487 Add new members to a SDL group under the namespace.
489 SDL groups are identified by their name, which is a key in storage. SDL groups are
490 unordered collections of members where each member is unique. If a member to be added is
491 already a member of the group, its addition is silently ignored. If the group does not
492 exist, it is created, and specified members are added to the group.
493 All the exceptions except SdlTypeError are derived from SdlException base class. Client
494 can catch only that exception if separate handling for different SDL error situations is
495 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
496 misuse of the SDL API.
499 ns (str): Namespace under which this operation is targeted.
500 group (str): Group name.
501 members (bytes or set of bytes): One or multiple members to be added.
507 SdlTypeError: If function's argument is of an inappropriate type.
508 NotConnected: If SDL is not connected to the backend data storage.
509 RejectedByBackend: If backend data storage rejects the request.
510 BackendError: If the backend data storage fails to process the request.
515 def remove_member(self, ns: str, group: str, members: Union[bytes, Set[bytes]]) -> None:
517 Remove members from a SDL group.
519 SDL groups are unordered collections of members where each member is unique. If a member to
520 be removed does not exist in the group, its removal is silently ignored. If a group does
521 not exist, it is treated as an empty group and hence members removal is silently ignored.
522 All the exceptions except SdlTypeError are derived from SdlException base class. Client
523 can catch only that exception if separate handling for different SDL error situations is
524 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
525 misuse of the SDL API.
528 ns (str): Namespace under which this operation is targeted.
529 group (str): Group name.
530 members (bytes or set of bytes): One or multiple members to be removed.
536 SdlTypeError: If function's argument is of an inappropriate type.
537 NotConnected: If SDL is not connected to the backend data storage.
538 RejectedByBackend: If backend data storage rejects the request.
539 BackendError: If the backend data storage fails to process the request.
544 def remove_group(self, ns: str, group: str) -> None:
546 Remove a SDL group along with its members.
548 SDL groups are unordered collections of members where each member is unique. If a group to
549 be removed does not exist, its removal is silently ignored.
550 All the exceptions except SdlTypeError are derived from SdlException base class. Client
551 can catch only that exception if separate handling for different SDL error situations is
552 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
553 misuse of the SDL API.
556 ns (str): Namespace under which this operation is targeted.
557 group (str): Group name to be removed.
563 SdlTypeError: If function's argument is of an inappropriate type.
564 NotConnected: If SDL is not connected to the backend data storage.
565 RejectedByBackend: If backend data storage rejects the request.
566 BackendError: If the backend data storage fails to process the request.
571 def get_members(self, ns: str, group: str) -> Set[bytes]:
573 Get all the members of a SDL group.
575 SDL groups are unordered collections of members where each member is unique. If the group
576 does not exist, empty set is returned.
577 All the exceptions except SdlTypeError are derived from SdlException base class. Client
578 can catch only that exception if separate handling for different SDL error situations is
579 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
580 misuse of the SDL API.
583 ns (str): Namespace under which this operation is targeted.
584 group (str): Group name of which members are to be returned.
587 (set of bytes): A set of the members of the group.
591 SdlTypeError: If function's argument is of an inappropriate type.
592 NotConnected: If SDL is not connected to the backend data storage.
593 RejectedByBackend: If backend data storage rejects the request.
594 BackendError: If the backend data storage fails to process the request.
599 def is_member(self, ns: str, group: str, member: bytes) -> bool:
601 Validate if a given member is in the SDL group.
603 SDL groups are unordered collections of members where each member is unique. If the group
604 does not exist, false is returned.
605 All the exceptions except SdlTypeError are derived from SdlException base class. Client
606 can catch only that exception if separate handling for different SDL error situations is
607 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
608 misuse of the SDL API.
611 ns (str): Namespace under which this operation is targeted.
612 group (str): Group name of which member existence is to be validated.
613 member (bytes): A member, which existence is to be validated.
616 bool: True if member was in the group, false otherwise.
619 SdlTypeError: If function's argument is of an inappropriate type.
620 NotConnected: If SDL is not connected to the backend data storage.
621 RejectedByBackend: If backend data storage rejects the request.
622 BackendError: If the backend data storage fails to process the request.
627 def group_size(self, ns: str, group: str) -> int:
629 Return the number of members in a group.
631 SDL groups are unordered collections of members where each member is unique. If the group
632 does not exist, value 0 is returned.
633 All the exceptions except SdlTypeError are derived from SdlException base class. Client
634 can catch only that exception if separate handling for different SDL error situations is
635 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
636 misuse of the SDL API.
639 ns (str): Namespace under which this operation is targeted.
640 group (str): Group name of which members count is queried.
643 int: Number of members in a group.
646 SdlTypeError: If function's argument is of an inappropriate type.
647 NotConnected: If SDL is not connected to the backend data storage.
648 RejectedByBackend: If backend data storage rejects the request.
649 BackendError: If the backend data storage fails to process the request.
654 def set_and_publish(self, ns: str, channels_and_events: Dict[str, Union[str, List[str]]],
655 data_map: Dict[str, bytes]) -> None:
657 Publish event to channel after writing data.
659 set_and_publish function writes data to shared data layer storage and sends an
660 event to a channel. Writing is done atomically, i.e. all succeeds or fails.
661 Data to be written is given as key-value pairs. Several key-value pairs can be
662 written with one call.
663 The key is expected to be string whereas value is a byte string.
665 If data was set successfully, an event is sent to a channel.
666 It is possible to send several events to several channels by giving a list of
668 E.g. {"channel1": ["event1", "event3"], "channel2": ["event2"]}
669 will send event1 and event3 to channel1 and event2 to channel2.
672 ns: Namespace under which this operation is targeted.
673 channels_and_events: Channel to publish if data was set successfully.
674 data_map: Data to be written.
680 SdlTypeError: If function's argument is of an inappropriate type.
681 NotConnected: If SDL is not connected to the backend data storage.
682 RejectedByBackend: If backend data storage rejects the request.
683 BackendError: If the backend data storage fails to process the request.
688 def set_if_and_publish(self, ns: str, channels_and_events: Dict[str, Union[str, List[str]]],
689 key: str, old_data: bytes, new_data: bytes) -> bool:
691 Publish event to channel after conditionally modifying the value of a key if the
692 current value in data storage matches the user's last known value.
694 set_if_and_publish atomically replaces existing data with new_data in SDL if data
695 matches the old_data. If replace was done successfully, true will be returned.
696 Also, if publishing was successful, an event is published to a given channel.
699 ns (str): Namespace under which this operation is targeted.
700 channels_and_events (dict): Channel to publish if data was set successfully.
701 key (str): Key for which data modification will be executed.
702 old_data (bytes): Last known data.
703 new_data (bytes): Data to be written.
706 bool: True for successful modification, false if the user's last known data did not
707 match the current value in data storage.
710 SdlTypeError: If function's argument is of an inappropriate type.
711 NotConnected: If SDL is not connected to the backend data storage.
712 RejectedByBackend: If backend data storage rejects the request.
713 BackendError: If the backend data storage fails to process the request.
718 def set_if_not_exists_and_publish(self, ns: str,
719 channels_and_events: Dict[str, Union[str, List[str]]],
720 key: str, data: bytes) -> bool:
722 Publish event to channel after writing data to SDL storage if key does not exist.
724 set_if_not_exists_and_publish conditionally sets the value of a key. If key
725 already exists in SDL, then it's value is not changed. Checking the key existence
726 and potential set operation is done atomically. If the set operation was done
727 successfully, an event is published to a given channel.
730 ns (str): Namespace under which this operation is targeted.
731 channels_and_events (dict): Channel to publish if data was set successfully.
732 key (str): Key to be set.
733 data (bytes): Data to be written.
736 bool: True if key didn't exist yet and set operation was executed, false if key already
737 existed and thus its value was left untouched.
740 SdlTypeError: If function's argument is of an inappropriate type.
741 NotConnected: If SDL is not connected to the backend data storage.
742 RejectedByBackend: If backend data storage rejects the request.
743 BackendError: If the backend data storage fails to process the request.
748 def remove_and_publish(self, ns: str, channels_and_events: Dict[str, Union[str, List[str]]],
749 keys: Union[str, Set[str]]) -> None:
751 Publish event to channel after removing data.
753 remove_and_publish removes data from SDL. Operation is done atomically, i.e.
754 either all succeeds or fails.
755 Trying to remove a nonexisting key is not considered as an error.
756 An event is published into a given channel if remove operation is successful and
757 at least one key is removed (if several keys given). If the given key(s) doesn't
758 exist when trying to remove, no event is published.
761 ns: Namespace under which this operation is targeted.
762 channels_and_events: Channel to publish if data was removed successfully.
763 keys: One key or multiple keys, which data is to be removed.
769 SdlTypeError: If function's argument is of an inappropriate type.
770 NotConnected: If SDL is not connected to the backend data storage.
771 RejectedByBackend: If backend data storage rejects the request.
772 BackendError: If the backend data storage fails to process the request.
777 def remove_if_and_publish(self, ns: str, channels_and_events: Dict[str, Union[str, List[str]]],
778 key: str, data: bytes) -> bool:
780 Publish event to channel after removing key and its data from database if the
781 current data value is expected one.
783 remove_if_and_publish removes data from SDL conditionally, and if remove was done
784 successfully, a given event is published to channel. If existing data matches
785 given data, key and data are removed from SDL. If remove was done successfully,
789 ns (str): Namespace under which this operation is targeted.
790 channels_and_events (dict): Channel to publish if data was removed successfully.
791 key (str): Key, which data is to be removed.
792 data (bytes): Last known value of data
795 bool: True if successful removal, false if the user's last known data did not match the
796 current value in data storage.
799 SdlTypeError: If function's argument is of an inappropriate type.
800 NotConnected: If SDL is not connected to the backend data storage.
801 RejectedByBackend: If backend data storage rejects the request.
802 BackendError: If the backend data storage fails to process the request.
807 def remove_all_and_publish(self, ns: str,
808 channels_and_events: Dict[str, Union[str, List[str]]]) -> None:
810 Publish event to channel after removing all keys under the namespace.
812 remove_all_and_publish removes all keys under the namespace and if successful, it
813 will publish an event to given channel. This operation is not atomic, thus it is
814 not guaranteed that all keys are removed.
817 ns (str): Namespace under which this operation is targeted.
818 channels_and_events (dict): Channel to publish if data was removed successfully.
824 SdlTypeError: If function's argument is of an inappropriate type.
825 NotConnected: If SDL is not connected to the backend data storage.
826 RejectedByBackend: If backend data storage rejects the request.
827 BackendError: If the backend data storage fails to process the request.
832 def subscribe_channel(self, ns: str, cb: Callable[[str, List[str]], None],
833 channels: Union[str, Set[str]]) -> None:
835 Subscribes the client to the specified channels.
837 subscribe_channel lets you to subscribe for events on a given channels.
838 SDL notifications are events that are published on a specific channel.
839 Both the channel and events are defined by the entity that is publishing
842 When subscribing for a channel, a callback function is given as a parameter.
843 Whenever single notification or many notifications are received from a channel,
844 this callback is called with channel and notification list as parameter. A call
845 to subscribe_channel function returns immediately, callbacks will be called
846 synchronously from a dedicated thread.
848 It is possible to subscribe to different channels using different callbacks. In
849 this case simply use subscribe_channel function separately for each channel.
851 When receiving events in callback routine, it is a good practice to return from
852 callback as quickly as possible. Also it should be noted that in case of several
853 events received from different channels, callbacks are called in series one by
857 ns: Namespace under which this operation is targeted.
858 cb: A function that is called when event(s) on channel is received.
859 channels: One channel or multiple channels to be subscribed.
865 SdlTypeError: If function's argument is of an inappropriate type.
866 NotConnected: If SDL is not connected to the backend data storage.
867 RejectedByBackend: If backend data storage rejects the request.
868 BackendError: If the backend data storage fails to process the request.
873 def unsubscribe_channel(self, ns: str, channels: Union[str, Set[str]]) -> None:
875 unsubscribe_channel removes subscription from one or several channels.
878 ns: Namespace under which this operation is targeted.
879 channels: One channel or multiple channels to be unsubscribed.
885 SdlTypeError: If function's argument is of an inappropriate type.
886 NotConnected: If SDL is not connected to the backend data storage.
887 RejectedByBackend: If backend data storage rejects the request.
888 BackendError: If the backend data storage fails to process the request.
893 def start_event_listener(self) -> None:
895 start_event_listener creates an event loop in a separate thread for handling
896 events from subscriptions. The registered callback function will be called
897 when an event is received.
899 It should be noted that subscribe_channel must be called before calling
900 start_event_listener to do at least one subscription before event loop
904 SdlTypeError: If function's argument is of an inappropriate type.
905 NotConnected: If SDL is not connected to the backend data storage.
906 RejectedByBackend: If backend data storage rejects the request.
907 BackendError: If the backend data storage fails to process the request.
912 def handle_events(self) -> Optional[Tuple[str, List[str]]]:
914 handle_events is a non-blocking function that returns a tuple containing channel
915 name and message(s) received from an event. The registered callback function will
916 still be called when an event is received.
918 This function is called if SDL user decides to handle notifications in its own
919 event loop. Calling this function after start_event_listener raises an exception.
920 If there are no notifications, these returns None.
922 It should be noted that subscribe_channel must be called before calling of the
923 handle_events in an event loop. At least one subscription must be done before
924 events handling starts.
927 Tuple: (channel: str, message(s): list of str)
930 SdlTypeError: If function's argument is of an inappropriate type.
931 NotConnected: If SDL is not connected to the backend data storage.
932 RejectedByBackend: If backend data storage rejects the request.
933 BackendError: If the backend data storage fails to process the request.
938 def get_lock_resource(self, ns: str, resource: str,
939 expiration: Union[int, float]) -> SyncLockAbc:
941 Return a lock resource for SDL.
943 A lock resource instance is created per namespace and it is identified by its `name` within
944 a namespace. A `get_lock_resource` returns a lock resource instance, it does not acquire
945 a lock. Lock resource provides lock handling methods such as acquiring a lock, extend
946 expiration time and releasing a lock.
947 All the exceptions except SdlTypeError are derived from SdlException base class. Client
948 can catch only that exception if separate handling for different SDL error situations is
949 not needed. Exception SdlTypeError is derived from build-in TypeError and it indicates
950 misuse of the SDL API.
953 ns (str): Namespace under which this operation is targeted.
954 resource (str): Resource is used within namespace as a key for a lock entry in SDL.
955 expiration (int, float): Expiration time of a lock
958 SyncLockAbc: Lock resource instance.
961 SdlTypeError: If function's argument is of an inappropriate type.
962 NotConnected: If SDL is not connected to the backend data storage.
963 RejectedByBackend: If backend data storage rejects the request.
964 BackendError: If the backend data storage fails to process the request.