X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=blobdiff_plain;f=docs%2Fuser-guide.rst;fp=docs%2Fuser-guide.rst;h=ed75ce14ea1055ecbdf866fd11d3b3e5b0d8b402;hb=02cf8dfcafb7380cda35beaa2aad783ffea87fa3;hp=0000000000000000000000000000000000000000;hpb=2f5d035eb3d2392a565713a4b2fc1c6f3e978e8e;p=ric-plt%2Fsdl.git diff --git a/docs/user-guide.rst b/docs/user-guide.rst new file mode 100644 index 0000000..ed75ce1 --- /dev/null +++ b/docs/user-guide.rst @@ -0,0 +1,519 @@ +.. +.. Copyright (c) 2019 AT&T Intellectual Property. +.. Copyright (c) 2019 Nokia. +.. +.. Licensed under the Creative Commons Attribution 4.0 International +.. Public License (the "License"); you may not use this file except +.. in compliance with the License. You may obtain a copy of the License at +.. +.. https://creativecommons.org/licenses/by/4.0/ +.. +.. Unless required by applicable law or agreed to in writing, documentation +.. distributed under the License is distributed on an "AS IS" BASIS, +.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +.. +.. See the License for the specific language governing permissions and +.. limitations under the License. +.. + + +########## +User Guide +########## + +.. raw:: pdf + + PageBreak + +.. contents:: + :depth: 3 + :local: + +.. raw:: pdf + + PageBreak + +Introduction +************ + +This is the user guide of O-RAN SC SDL C++ library. +Shared Data Layer (SDL) provides a lightweight, high-speed interface (API) for +accessing shared data storage. SDL can be used for storing and sharing any +data. Data can be shared at VNF level. One typical use case for SDL is sharing +the state data of stateful application processes. Thus enabling stateful +application processes to become stateless, conforming with, e.g., the +requirements of the fifth generation mobile networks. + +Figure below illustrates some main points of SDL: + +.. image:: ./_static/sdl_intro.png + :align: center + :alt: SDL introduction + +SDL has been implemented in many languages: + +* C++ Linux shared library +* Golang package +* Python package + +This document focuses on C++ implementation of SDL but general principles are +the same in all implementations. + +.. raw:: pdf + + PageBreak + +Key Concepts +************ + +**Backend Data Storage** + +Backend data storage refers to data storage technology behind SDL API which +handles the actual data storing. SDL API hides the backend data storage +implementation from SDL API clients, and therefore backend data storage +technology can be changed without affecting SDL API clients. Currently, Redis +database is the most commonly used backend data storage implementation. + +Figure below illustrates how SDL API hides backend data storage technology +from application: + +.. image:: ./_static/backend_data_storage.png + :align: center + :alt: SDL API hides backend data storage technology from application + +`SDL Deployment section <#sdl-deployment>`_ provides further information +about backend data storage deployment options. + +**Namespace** + +Namespaces provide data isolation within SDL data storage. That is, data in +certain namespace is isolated from the data in other namespaces. Each SDL +client uses one or more namespaces. + +Namespaces can be used, for example, to isolate data belonging to different +use cases. + +Figure below shows an example of the SDL namespace concept. There are two SDL +clients, both accessing SDL backend data storage using an SDL API instance +(C++ object). Client 1 uses both namespaces: A and B, while client 2 uses only +namespace: B. Therefore, data in the namespace: A is visible only to client 1 +and data in namespace: B is shared between clients 1 and 2: + +.. image:: ./_static/sdl_namespaces.png + :align: center + :alt: SDL namespace concept example + +Namespace management is planned to be moved under a managing entity which +enforces some control over how the namespaces are created. For now, however, +namespace naming needs to be manually coordinated between clients. + +**Keys and Data** + +Clients save key-data pairs. Data is passed as byte vectors. SDL stores the +data as it is. Any structure that this data may have (e.g. a serialized JSON) +is meaningful only to the client itself. Clients are responsible for managing +the keys. As namespaces provide data isolation, keys in different namespaces +always access different data. + +.. raw:: pdf + + PageBreak + +APIs +**** + +SDL provides currently following APIs: + +* Asynchronous API for accessing SDL storage *shareddatalayer::AsyncStorage* +* Synchronous API for accessing SDL storage shareddatalayer::SyncStorage + +Same SDL client can use one or more SDL APIs. There should rarely be need to +create several instances of the same SDL API though. All individual operations +done using SDL API functions are targeted to one namespace (accessing several +namespaces requires multiple operations). + +SDL API functions are not thread-safe, meaning that same SDL instance must +not be shared between multiple threads without explicit locking in SDL client. + +SDL API functions are atomic unless otherwise indicated. Indication of the +non-atomic behavior of certain function can be found from one or many of the +following: + +* Function name +* Function parameters +* Function doxygen documentation (see below) + +Refer to doxygen generated SDL API documentation below for further information +about SDL APIs and the functions they contain. + +Doxygen Generated SDL API Documentation +======================================= + +Pre-built online version of SDL API Doxygen documentation is not yet available. + +Doxygen documentation can be generated manually. Follow instructions found from +:ref:`SDL developer guide `. + +.. raw:: pdf + + PageBreak + +Building Clients Using SDL +************************** + +SDL API functions can be used by including SDL public headers and by linking +SDL shared library. + +The necessary compilation and linker flags can be acquired with the +*pkg-config* tool:: + + pkg-config --cflags libsdl + pkg-config --libs libsdl + +SDL internal implementation uses C++14, thus SDL clients need to be build +using a C++ compiler supporting C++14. However, SDL public API header files +contain only features which are available in C++11, thus SDL clients do not +need to be implemented (and compiled) using C++14 (C++11 is enough). The +compiler just needs to have support for C++14. + +.. raw:: pdf + + PageBreak + +Using SDL in Application Pod +**************************** + +SDL binary artifacts including Debian (.deb) and RPM Package Manager (.rpm) +packages are available in O-RAN-SC PackageCloud.io repository. + +In runtime environment SDL needs also a database backend service, currently +SDL supports only Redis database. Recommended solution is to use DBaaS +component of the official RIC platform deployment. + +**Deploying SDL database backend with DBaaS service in the RIC** + +Download RIC deployment artifacts:: + + git clone "https://gerrit.o-ran-sc.org/r/it/dep" + +The **ric-platform** directory contains Helm chart and scripts to deploy RIC +platform components, including also DBaaS component. + +RIC DBaaS service must be running before starting application pod which is +using SDL API. DBaaS defines environment variables which are used to contact +DBaaS service (offering backend for SDL). Those environment variables are +exposed inside application container only if DBaaS service is running when +application container is started. Refer to +`Database Backend Configuration section <#database-backend-configuration>`_, +for information about available environment variables. +You may test SDL connectivity to its backend with the *sdltool* command inside +your application container:: + + sdltool test-connectivity + +*sdltool* comes in SDL binary artifacts which are available in O-RAN-SC +PackageCloud.io repository. + +For more information, see also `README `_ +file of the *dbaas* O-RAN-SC gerrit repository. + +.. raw:: pdf + + PageBreak + +Configuration +************* + +Certain aspects in SDL functionality can be configured by using environment +variables. + +Database Backend Configuration +============================== + +Database backend configuration can be used to configure, to which database +backend SDL instance connects. A list of available environment variables to +configure database backend: + +* DBAAS_SERVICE_HOST +* DBAAS_SERVICE_PORT +* DBAAS_SERVICE_SENTINEL_PORT +* DBAAS_MASTER_NAME + +After DBaaS service is installed, environment variables are exposed to +application containers. SDL library will automatically use these environment +variables. If DBaaS service is not used, above environment variables needs to +be set manually so that SDL backend can connect to correct database. + +**Examples** + +An example how environment variables can be set in bash shell, when standalone +Redis server is running in a Kubernetes Pod with k8s service name of *dbaas* and +port *6379*:: + + export DBAAS_SERVICE_HOST=dbaas + export DBAAS_SERVICE_PORT=6379 + +Besides hostname, IPv4 and IPv6 addresses can be set to *DBAAS_SERVICE_HOST*. + +An example how environment variables can be set in bash shell, when Redis +HA deployment is used. Please note that DBaaS does not support yet HA +deployment option. Below environment variables are only in the form of an +example to show how HA deployment would be configured:: + + export DBAAS_MASTER_NAME=my-master-sentinel + export DBAAS_SERVICE_HOST=dbaas + export DBAAS_SERVICE_SENTINEL_PORT=23550 + +.. raw:: pdf + + PageBreak + +Errors +****** + +`Doxygen generated SDL API documentation <#doxygen-generated-sdl-api-documentation>`_ +describes which error codes are returned and which exceptions are thrown from +each SDL API function. Generally, asynchronous SDL APIs return error codes and +synchronous SDL APIs throw exceptions in error situations. + +Handling Error Codes Returned From Asynchronous SDL APIs +======================================================== + +Asynchronous SDL APIs return *std::error_code* based error codes in error +situations. Typically, error code is returned as a parameter in the related +callback function. + +Returned error code contains detailed information about the error which has +occurred. This information is valuable for SDL developers in case the issue +needs further investigation, but usually this information is too detailed for +SDL client error handling logic. For SDL client error handling purposes SDL +provides *shareddatalayer::error* constants and the returned *std::error_code* +can be compared against these constants. + +Therefore SDL clients are recommended to store the returned *std::error_code* +somewhere (for example to the log) and implement the error handling logic based +on *shareddatalayer::error* constants. C++ code example below illustrates this: + +.. code-block:: c++ + + if (error) + { + log.error() << "SDL operation failed, error: " << error + << " message: " << error.message() << std::endl; + + if (error == shareddatalayer::Error::NOT_CONNECTED) + // Error handling logic for shareddatalayer::Error::NOT_CONNECTED + else if (error == shareddatalayer::Error::OPERATION_INTERRUPTED) + // Error handling logic for shareddatalayer::Error::OPERATION_INTERRUPTED + else if (error == shareddatalayer::Error::BACKEND_FAILURE) + // Error handling logic for shareddatalayer::Error::BACKEND_FAILURE + else if (error == shareddatalayer::Error::REJECTED_BY_BACKEND) + // Error handling logic for shareddatalayer::Error::REJECTED_BY_BACKEND + } + +*error* in the code block above is *std::error_code* type variable which is +returned from some asynchronous SDL API function. *log* is a logging service +what an SDL client is using. Note that this is a simple and incomplete example +for demonstration purposes and not meant to be used as such in real +environment. Complete error handling implementation depends on SDL client and +SDL API function which returned the error. For example, in some cases common +handling for several *shareddatalayer::error* constants might be sufficient. + +**Instructions for Error Handling Logic Implementation** + +Doxygen documentation contains detailed description for all +shareddatalayer::Error constants. This information helps to design error +handling logic for each shareddatalayer::Error constant. For example, following +information can be found from there: + +* What has happened +* Is data modified in the backend data storage +* How to recover from error situation + + +Handling Exceptions Thrown by Synchronous SDL APIs +================================================== + +Synchronous SDL APIs throw exceptions in error situations. There are +corresponding exceptions for all *shareddatalayer::error* constants returned +by asynchronous APIs (see previous section). All exceptions thrown by SDL are +derived from *shareddatalayer::Exception*. +Therefore, a client can catch *shareddatalayer::Exception* in case the client +wants to implement common handling for some SDL originated exceptions. Note +that external services, which SDL uses, can throw exceptions which are not +derived from *shareddatalayer::Exception*. + +Below is a C++ code example of a scenario where SDL client does common error +handling for all exceptions thrown from synchronous SDL API: + +.. code-block:: c++ + + try + { + //Code which executes synchronous SDL API function + } + catch (const shareddatalayer::Exception& e) + { + log.error() << "SDL operation failed, error: " << e.what() << std::endl; + //Common error handling logic for all SDL errors + } + //Catch also non-SDL exceptions (like std::exception) if needed + +Below C++ code example has separate handling for shareddatalayer::BackendError +exception and common handling for all other exceptions thrown by SDL: + +.. code-block:: c++ + + try + { + //Code which executes synchronous SDL API function + } + catch (const shareddatalayer::BackendError& e) + { + log.error() << "SDL operation failed, error: " << e.what() << std::endl; + //Error handling logic for BackendError + } + catch (const shareddatalayer::Exception& e) + { + log.error() << "SDL operation failed, error: " << e.what() << std::endl; + //Common error handling logic for all other SDL errors than BackendError + } + //Catch also non-SDL exceptions (like std::exception) if needed + +*log* is a logging service what an SDL client is using. Note that these are +simple and incomplete examples for demonstration purposes and they are not +meant to be used as such in real environment. + +**Instructions for Error Handling Logic Implementation** + +Doxygen documentation contains documentation for all exceptions thrown by SDL. +This documentation contains information which helps to design error handling +logic for each exception. For exceptions having corresponding error code, +exception documentation is usually a reference to corresponding error code +documentation. + +Each SDL API function, which throws exceptions, has a link to the documentation +of those exceptions. This link can be found from the Doxygen documentation of +given SDL API function. + +.. raw:: pdf + + PageBreak + +SDL Properties +************** + +This chapter discusses how certain general data storage related aspects work in +SDL. Discussed subjects include, for example, concurrency control and data +persistency. + +SDL Deployment +============== + +Production environments are typically deployed so that SDL backend data storage +and SDL clients are in different nodes (e.g. VM, container). + +There are two different supported deployment modes for SDL backend data +storage: + +* Standalone (single DB node without redundancy) +* Redundant (DB node pair working in master/slave redundancy model) + +SDL does not currently have any intelligent logic (e.g. dynamic scaling) on +which storage node each namespace data is stored. This area might be developed +further in the future. + +SDL does not prevent backend data storage to be deployed in the same node with +the SDL client. Such deployments are, however, typically used only in +development/testing type of environments. + +Concurrency Control +=================== + +SDL does not support transactions doing one or more units of work in ACID +manner (pessimistic concurrency control). + +SDL supports optimistic concurrency control by providing Check and Set (CAS) +type conditional functions. These conditional functions provide possibility +to do certain data modification operations only if data value matches the SDL +client's last known value. Thus a SDL client can check that someone else has +not changed the data after it was read by the SDL client. If the data would +have been changed, SDL does not do the modification operation and this is +indicated to the SDL client. The SDL client can then decide how to handle the +situation (for example read the latest data and retry modification). + +*AsyncStorage::setIfAsync* is an example of a conditional function discussed +above. Other conditional functions exist as well. + +Data Persistency +================ + +Currently all data stored to SDL is stored to in-memory backend data storage. +Meaning that, data is not preserved over DB node restart. DB node restart does +not necessarily cause data loss for SDL client though. Refer to +`SDL Deployment section <#sdl-deployment>`_, for information about SDL backend +data storage redundancy models. + +.. raw:: pdf + + PageBreak + +Best Practices +************** + +This chapter gives recommendations on how to use SDL. + +Building Clients Using SDL +========================== + +* Use *pkg-config* tool to acquire needed compilation and linking flags, + instead of hardcoding them. This ensures that flags are always up-to-date. + See more information from `here <#building-clients-using-sdl>`_. + +Using SDL APIs +============== + +* SDL APIs are not thread-safe. If same SDL API instance is shared between + multiple threads, SDL client has to use explicit locking to ensure that only + one thread at time executes SDL API functions. +* Each SDL instance establishes own connection to backend data storage, which + requires resources (how heavy this exactly is depends on used backend data + storage type). Thus, from performance point of view, only one SDL instance + per one SDL API should be used if reasonably possible. One SDL instance can + access multiple SDL namespaces when using *AsyncStorage* and *SyncStorage* + APIs. +* Use waitReadyAsync() function before doing first operation via asynchronous + APIs to ensure that SDL and backend data storage are ready to handle + operations. See waitReadyAsync() function + `doxygen documentation <#doxygen-generated-sdl-api-documentation>`_ + for corresponding asynchronous API for details. +* Avoid using heavy search functions (for example: *AsyncStorage::findKeys()*). + Rather define your keys so that you know which keys should be read. + +Using SDL Namespaces +==================== + +* As namespace naming is currently on SDL client's responsibility, use enough + specific namespace names that same name is surely not used by someone else + (unless you want to share given namespace data with that someone else). +* Data entities related to each other should be placed under the same + namespace (unless there is a good reason not to). For example, accessing + multiple data entities with one SDL operation is possible only for data + entities belonging to same namespace. +* Identically named keys can be used in different namespaces. Creating own + namespaces for different use cases and unrelated data provides more freedom + into key name selection. + +Data Management +=============== + +* Writing or reading one big junk of data at once is more efficient than + writing/reading the same amount of data in small steps. For example, create a + key list and read it once, rather than reading each key in a loop. +* If rolling upgrade needs to be supported, consider using Google Protocol + Buffers (or something similar) to make it possible to parse data which is + written by older or newer application version. + +.. raw:: pdf + + PageBreak