29f1f9a95988ea403aed29058eb393cae730408d
[ric-plt/sdl.git] / include / sdl / syncstorage.hpp
1 /*
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
17 /*
18  * This source code is part of the near-RT RIC (RAN Intelligent Controller)
19  * platform project (RICP).
20 */
21
22 #ifndef SHAREDDATALAYER_SYNCSTORAGE_HPP_
23 #define SHAREDDATALAYER_SYNCSTORAGE_HPP_
24
25 #include <cstdint>
26 #include <functional>
27 #include <map>
28 #include <memory>
29 #include <set>
30 #include <string>
31 #include <utility>
32 #include <vector>
33 #include <chrono>
34 #include <sdl/exception.hpp>
35 #include <sdl/publisherid.hpp>
36
37 namespace shareddatalayer
38 {
39     /**
40      * @brief Class providing synchronous access to shared data layer storage.
41      *
42      * SyncStorage class provides synchronous access to all namespaces in
43      * shared data layer storage. Data can be saved, read and removed based on keys known
44      * to clients. Keys are unique within a namespace, namespace identifier is passed as
45      * a parameter to all operations.
46      *
47      * SyncStorage is primarily intended for command-line interface-typed applications, or
48      * non-event-loop based, such as multi-threaded applications, where shareddatalayer
49      * operations are carried out in a separate thread.
50      *
51      * @note The same instance of SyncStorage must not be shared between multiple threads
52      *       without explicit application level locking.
53      *
54      * @see AsyncStorage for asynchronous interface.
55      * @see AsyncStorage::SEPARATOR for namespace format restrictions.
56      */
57     class SyncStorage
58     {
59     public:
60         SyncStorage(const SyncStorage&) = delete;
61
62         SyncStorage& operator = (const SyncStorage&) = delete;
63
64         SyncStorage(SyncStorage&&) = delete;
65
66         SyncStorage& operator = (SyncStorage&&) = delete;
67
68         virtual ~SyncStorage() = default;
69
70         using Namespace = std::string;
71
72         using Key = std::string;
73
74         using Data = std::vector<uint8_t>;
75
76         using DataMap = std::map<Key, Data>;
77
78         /**
79          * Wait for the service to become ready to serve. There is typically a waiting period
80          * when product cluster is brought up (deploying container orchestrated service,
81          * container restart etc.). The function can be called and used to synchronize the
82          * application startup with the readiness of the underlying data storage.
83          *
84          * This function can also be used if SDL returns an error indicating connection cut
85          * to backend data storage. In that situation client can use this function to get an
86          * indication when SDL is again ready to serve. SDL error code documentation indicates
87          * the error codes for which the usage of this function is applicable.
88          *
89          * Exceptions thrown (excluding standard exceptions such as std::bad_alloc) are all
90          * derived from shareddatalayer::Exception base class. Client can catch only that
91          * exception if separate handling for different shareddatalayer error situations is not
92          * needed.
93          *
94          * @param ns Namespace under which this operation is targeted. As it is possible to
95          *           use different DB backend instances for namespaces, it is necessary to
96          *           provide namespace for this call to test the DB backend readiness of that
97          *           particular namespace. it is recommended to call this always when starting
98          *           to use new namespace.
99          *
100          * @param timeout Timeout value after which readiness waiting will be expired in case
101          *                the SDL service won't come up. It is recommended to use rather long
102          *                timeout value to have enough time for a system to recover for example
103          *                from restart scenarios. Suitable timeout value depends greatly from
104          *                the environment itself. As an example an application could use 10
105          *                seconds timeout value and have a loop what does a re-try every time
106          *                when previous waitReady call has failed. On a side note, timeout
107          *                value 0 means that readiness is waited interminable.
108          *
109          * @throw NotConnected if shareddatalayer is not connected to the backend data storage.
110          * @throw RejectedBySdl if SDL rejects the request.
111          * @throw BackendError if the backend data storage fails to process the request.
112          */
113         virtual void waitReady(const Namespace& ns, const std::chrono::steady_clock::duration& timeout) = 0;
114
115         /**
116          * Write data to shared data layer storage. Writing is done atomically, i.e. either
117          * all succeeds or all fails.
118          *
119          * Exceptions thrown (excluding standard exceptions such as std::bad_alloc) are all derived from
120          * shareddatalayer::Exception base class. Client can catch only that exception if separate handling
121          * for different shareddatalayer error situations is not needed.
122          *
123          * @param ns Namespace under which this operation is targeted.
124          * @param dataMap Data to be written.
125          *
126          * @throw BackendError if the backend data storage fails to process the request.
127          * @throw NotConnected if shareddatalayer is not connected to the backend data storage.
128          * @throw OperationInterrupted if shareddatalayer does not receive a reply from the backend data storage.
129          * @throw InvalidNamespace if given namespace does not meet the namespace format restrictions.
130          */
131         virtual void set(const Namespace& ns,
132                          const DataMap& dataMap) = 0;
133
134         /**
135          * Conditionally modify the value of a key if the current value in data storage
136          * matches the user's last known value.
137          *
138          * Exceptions thrown (excluding standard exceptions such as std::bad_alloc) are all derived from
139          * shareddatalayer::Exception base class. Client can catch only that exception if separate handling
140          * for different shareddatalayer error situations is not needed.
141          *
142          * @param ns Namespace under which this operation is targeted.
143          * @param key Key for which data modification will be executed.
144          * @param oldData Last known data.
145          * @param newData Data to be written.
146          *
147          * @return True for successful modification, false if the user's last known data did
148          *         not match the current value in data storage.
149          *
150          * @throw BackendError if the backend data storage fails to process the request.
151          * @throw NotConnected if shareddatalayer is not connected to the backend data storage.
152          * @throw OperationInterrupted if shareddatalayer does not receive a reply from the backend data storage.
153          * @throw InvalidNamespace if given namespace does not meet the namespace format restrictions.
154          */
155         virtual bool setIf(const Namespace& ns,
156                            const Key& key,
157                            const Data& oldData,
158                            const Data& newData) = 0;
159
160         /**
161          * Conditionally set the value of a key. If key already exists, then it's value
162          * is not modified. Checking the key existence and potential set operation is done
163          * as a one atomic operation.
164          *
165          * Exceptions thrown (excluding standard exceptions such as std::bad_alloc) are all derived from
166          * shareddatalayer::Exception base class. Client can catch only that exception if separate handling
167          * for different shareddatalayer error situations is not needed.
168          *
169          * @param ns Namespace under which this operation is targeted.
170          * @param key Key.
171          * @param data Data to be written.
172          *
173          * @return True if key didn't exist yet and set operation was executed, false if
174          *         key already existed and thus its value was left untouched.
175          *
176          * @throw BackendError if the backend data storage fails to process the request.
177          * @throw NotConnected if shareddatalayer is not connected to the backend data storage.
178          * @throw OperationInterrupted if shareddatalayer does not receive a reply from the backend data storage.
179          * @throw InvalidNamespace if given namespace does not meet the namespace format restrictions.
180          */
181         virtual bool setIfNotExists(const Namespace& ns,
182                                     const Key& key,
183                                     const Data& data) = 0;
184
185         using Keys = std::set<Key>;
186
187         /**
188          * Read data from shared data layer storage. Only those entries that are found will
189          * be returned.
190          *
191          * Exceptions thrown (excluding standard exceptions such as std::bad_alloc) are all derived from
192          * shareddatalayer::Exception base class. Client can catch only that exception if separate handling
193          * for different shareddatalayer error situations is not needed.
194          *
195          * @param ns Namespace under which this operation is targeted.
196          * @param keys Data to be read.
197          *
198          * @return Data from the storage.
199          *
200          * @throw BackendError if the backend data storage fails to process the request.
201          * @throw NotConnected if shareddatalayer is not connected to the backend data storage.
202          * @throw OperationInterrupted if shareddatalayer does not receive a reply from the backend data storage.
203          * @throw InvalidNamespace if given namespace does not meet the namespace format restrictions.
204          */
205         virtual DataMap get(const Namespace& ns,
206                             const Keys& keys) = 0;
207
208         /**
209          * Remove data from shared data layer storage. Existing keys are removed. Removing
210          * is done atomically, i.e. either all succeeds or all fails.
211          *
212          * Exceptions thrown (excluding standard exceptions such as std::bad_alloc) are all derived from
213          * shareddatalayer::Exception base class. Client can catch only that exception if separate handling
214          * for different shareddatalayer error situations is not needed.
215          *
216          * @param ns Namespace under which this operation is targeted.
217          * @param keys Data to be removed.
218          *
219          * @throw BackendError if the backend data storage fails to process the request.
220          * @throw NotConnected if shareddatalayer is not connected to the backend data storage.
221          * @throw OperationInterrupted if shareddatalayer does not receive a reply from the backend data storage.
222          * @throw InvalidNamespace if given namespace does not meet the namespace format restrictions.
223          */
224         virtual void remove(const Namespace& ns,
225                             const Keys& keys) = 0;
226
227         /**
228          * Conditionally remove data from shared data layer storage if the current data value
229          * matches the user's last known value.
230          *
231          * Exceptions thrown (excluding standard exceptions such as std::bad_alloc) are all derived from
232          * shareddatalayer::Exception base class. Client can catch only that exception if separate handling
233          * for different shareddatalayer error situations is not needed.
234          *
235          * @param ns Namespace under which this operation is targeted.
236          * @param key Data to be removed.
237          * @param data Last known value of data
238          *
239          * @return True if successful removal, false if the user's last known data did
240          *         not match the current value in data storage.
241          *
242          * @throw BackendError if the backend data storage fails to process the request.
243          * @throw NotConnected if shareddatalayer is not connected to the backend data storage.
244          * @throw OperationInterrupted if shareddatalayer does not receive a reply from the backend data storage.
245          * @throw InvalidNamespace if given namespace does not meet the namespace format restrictions.
246          */
247         virtual bool removeIf(const Namespace& ns,
248                               const Key& key,
249                               const Data& data) = 0;
250
251         /**
252          * Find all keys matching search key prefix under the namespace. No prior knowledge about the keys in the given
253          * namespace exists, thus operation is not guaranteed to be atomic or isolated.
254          *
255          * Exceptions thrown (excluding standard exceptions such as std::bad_alloc) are all derived from
256          * shareddatalayer::Exception base class. Client can catch only that exception if separate handling
257          * for different shareddatalayer error situations is not needed.
258          *
259          * @param ns Namespace under which this operation is targeted.
260          * @param keyPrefix Only keys starting with given keyPrefix are returned. Passing empty string as
261          *                  keyPrefix will return all keys.
262          *
263          * @return Found keys.
264          *
265          * @throw BackendError if the backend data storage fails to process the request.
266          * @throw NotConnected if shareddatalayer is not connected to the backend data storage.
267          * @throw OperationInterrupted if shareddatalayer does not receive a reply from the backend data storage.
268          * @throw InvalidNamespace if given namespace does not meet the namespace format restrictions.
269          */
270         virtual Keys findKeys(const Namespace& ns,
271                               const std::string& keyPrefix) = 0;
272
273         /**
274          * Remove all keys under the namespace. Found keys are removed atomically, i.e.
275          * either all succeeds or all fails.
276          *
277          * Exceptions thrown (excluding standard exceptions such as std::bad_alloc) are all derived from
278          * shareddatalayer::Exception base class. Client can catch only that exception if separate handling
279          * for different shareddatalayer error situations is not needed.
280          *
281          * @param ns Namespace under which this operation is targeted.
282          *
283          * @throw BackendError if the backend data storage fails to process the request.
284          * @throw NotConnected if shareddatalayer is not connected to the backend data storage.
285          * @throw OperationInterrupted if shareddatalayer does not receive a reply from the backend data storage.
286          * @throw InvalidNamespace if given namespace does not meet the namespace format restrictions.
287          */
288         virtual void removeAll(const Namespace& ns) = 0;
289
290         /**
291          * Set a timeout value for the synchronous SDL read, write and remove operations.
292          * By default synchronous read, write and remove operations do not have any timeout
293          * for the backend data storage readiness, operations are pending interminable to
294          * finish until backend is ready. With this API function default behaviour can be
295          * changed and when a timeout happens, an error exception is risen for the SDL
296          * operation in question. To avoid unnecessary timeout failure due to a temporal
297          * connection issue, it is recommended not to set too short timeout value.
298          * Reasonable timeout value is 5 seconds or bigger value. On a side note, timeout
299          * value 0 means interminable pending time.
300          *
301          * @param timeout Timeout value to set.
302          */
303          virtual void setOperationTimeout(const std::chrono::steady_clock::duration& timeout) = 0;
304
305         /**
306          * Create a new instance of SyncStorage.
307          *
308          * @return New instance of SyncStorage.
309          *
310          * @throw EmptyNamespace if namespace string is an empty string.
311          * @throw InvalidNamespace if namespace contains illegal characters.
312          *
313          */
314         static std::unique_ptr<SyncStorage> create();
315
316     protected:
317         SyncStorage() = default;
318     };
319 }
320
321 #endif