Add first version
[ric-plt/sdl.git] / include / sdl / asyncstorage.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 #ifndef SHAREDDATALAYER_ASYNCSTORAGE_HPP_
18 #define SHAREDDATALAYER_ASYNCSTORAGE_HPP_
19
20 #include <cstdint>
21 #include <functional>
22 #include <map>
23 #include <memory>
24 #include <set>
25 #include <string>
26 #include <system_error>
27 #include <utility>
28 #include <vector>
29 #include <sdl/errorqueries.hpp>
30 #include <sdl/publisherid.hpp>
31
32 namespace shareddatalayer
33 {
34     /**
35      * @brief Class providing asynchronous access to shared data layer storage.
36      *
37      * AsyncStorage class provides asynchronous access to all namespaces in
38      * shared data layer storage. Data can be saved, read and removed based on keys known
39      * to clients. Keys are unique within a namespace, namespace identifier is passed as
40      * a parameter to all operations.
41      *
42      * AsyncStorage is primarily intended for event-loop based applications.
43      *
44      * Asynchronous functions taking const reference parameters do not assume that the
45      * references are valid after the function returns. The underlying implementation will
46      * make copies of the given parameter when needed.
47      *
48      * @see fd
49      * @see handleEvents
50      *
51      * @note The same instance of AsyncStorage must not be shared between multiple
52      *       threads without explicit application level locking.
53      *
54      * @see SyncStorage for synchronous interface.
55      */
56     class AsyncStorage
57     {
58     public:
59         AsyncStorage(const AsyncStorage&) = delete;
60
61         AsyncStorage& operator = (const AsyncStorage&) = delete;
62
63         AsyncStorage(AsyncStorage&&) = delete;
64
65         AsyncStorage& operator = (AsyncStorage&&) = delete;
66
67         virtual ~AsyncStorage() = default;
68
69         using Namespace = std::string;
70
71         /**
72          * Separator character is used by the API internally in namespace handling. Thus, separator
73          * character cannot be part of the namespace identifier string provided by the client.
74          * Also namespace identifier cannot be an empty string.
75          *
76          * API otherwise does not impose additional restrictions to the characters used in namespace
77          * identifiers. Excessive and unnecessary usage of special characters is strongly discouraged
78          * though.
79          */
80         static constexpr char SEPARATOR = ',';
81
82         /**
83          * Get the file descriptor to monitor in application's event loop. The file descriptor
84          * can be monitored for example with <code>select(2)</code>, <code>poll(2)</code> or
85          * <code>epoll_wait(2)</code>. The event to be monitored is <i>input</i> event (like
86          * for example <code>POLLIN</code>).
87          *
88          * Whenever there is input event in the file descriptor, application must call
89          * handleEvents() function. Application must keep monitoring events until all desired
90          * acknowledgement functions have been invoked. Application must stop monitoring events
91          * before destroying <code>this</code>.
92          *
93          * @return The file descriptor to monitor in application's event loop.
94          *
95          * @see handleEvents
96          */
97         virtual int fd() const = 0;
98
99         /**
100          * Function to be called whenever there is an input event in the file descriptor
101          * returned by fd() function. Each call to handleEvents() may invoke zero or more
102          * acknowledgement functions.
103          *
104          * @see fd
105          */
106         virtual void handleEvents() = 0;
107
108         /**
109          * Ready acknowledgement to be called when shared data layer storage is ready to serve.
110          *
111          * @param error Error code describing the status of the request. The <code>std::error_code::category()</code>
112          *              and <code>std::error_code::value()</code> are implementation specific. If ReadyAck
113          *              is passed an error, client is advised to instantiate a new AsyncStorage and
114          *              waitReadyAsync again.
115          */
116         using ReadyAck = std::function<void(const std::error_code& error)>;
117
118         /**
119          * Wait for the service to become ready to serve. There typically is a waiting period
120          * when product cluster is brought up (commissioning, VNFC restart etc.). The function
121          * can be called and used to synchronize the application startup with the readiness of
122          * the underlying data storage.
123          *
124          * In a steady state, the callback is called almost immediately, varying based on runtime
125          * conditions.
126          *
127          * @param ns Namespace under which this operation is targeted. As it is possible to
128          *           configure for each namespace whether DB backend is in use, it is neccasary
129          *           to provide namespace for this call. If it is known that DB backend usage
130          *           configuration is the same for all used namespaces, this call can be done only
131          *           once for certain namespace. If the configuration can vary between namespaces,
132          *           it is recommended to call this always when starting to use new namespace.
133          * @param readyAck The acknowledgement to be called once the request has been handled.
134          *                 The given function is called in the context of handleEvents() function.
135          *
136          * @note Each instance/namespace should be waited on only *once*.
137          */
138         virtual void waitReadyAsync(const Namespace& ns,
139                                     const ReadyAck& readyAck) = 0;
140
141         using Key = std::string;
142
143         using Data = std::vector<uint8_t>;
144
145         using DataMap = std::map<Key, Data>;
146
147         /**
148          * Modify acknowledgement to be called when setAsync/removeAsync/removeAllAsync request has been
149          * handled.
150          *
151          * @param error Error code describing the status of the request. The <code>std::error_code::category()</code>
152          *              and <code>std::error_code::value()</code> are implementation specific. Client is advised
153          *              to compare received error against <code>shareddatalayer::Error</code> constants when
154          *              doing error handling. See documentation: sdl/errorqueries.hpp for further information.
155          *              Received <code>std::error_code</code> and <code>std::error_code::message()</code> can be stored
156          *              and provided to shareddatalayer developers if problem needs further investigation.
157          */
158         using ModifyAck = std::function<void(const std::error_code& error)>;
159
160         /**
161          * Write data to shared data layer storage. Writing is done atomically, i.e. either
162          * all succeeds or all fails.
163          *
164          * @param ns Namespace under which this operation is targeted.
165          * @param dataMap Data to be written.
166          * @param modifyAck The acknowledgement to be called once the request has been handled.
167          *                  The given function is called in the context of handleEvents() function.
168          */
169         virtual void setAsync(const Namespace& ns,
170                               const DataMap& dataMap,
171                               const ModifyAck& modifyAck) = 0;
172
173         /**
174          * Modify acknowledgement to be called when setIfAsync/setIfNotExistsAsync/removeIfAsync request has been handled.
175          *
176          * @param error Error code describing the status of the request. The <code>std::error_code::category()</code>
177          *              and <code>std::error_code::value()</code> are implementation specific. Client is advised
178          *              to compare received error against <code>shareddatalayer::Error</code> constants when
179          *              doing error handling. See documentation: sdl/errorqueries.hpp for further information.
180          *              Received <code>std::error_code</code> and <code>std::error_code::message()</code> can be stored
181          *              and provided to shareddatalayer developers if problem needs further investigation.
182          * @param status Status of the modification. True for successful modification, false if modification
183          *               was not done due to the given prerequisite.
184          */
185         using ModifyIfAck = std::function<void(const std::error_code& error, bool status)>;
186
187         /**
188          * Conditionally modify the value of a key if the current value in data storage
189          * matches the user's last known value.
190          *
191          * @param ns Namespace under which this operation is targeted.
192          * @param key Key for which data modification will be executed.
193          * @param oldData Last known data.
194          * @param newData Data to be written.
195          * @param modifyIfAck The acknowledgement to be called once the request has been handled.
196          *                    The given function is called in the context of handleEvents() function.
197          */
198         virtual void setIfAsync(const Namespace& ns,
199                                 const Key& key,
200                                 const Data& oldData,
201                                 const Data& newData,
202                                 const ModifyIfAck& modifyIfAck) = 0;
203
204         /**
205          * Conditionally set the value of a key. If key already exists, then it's value
206          * is not modified. Checking the key existence and potential set operation is done
207          * as a one atomic operation.
208          *
209          * @param ns Namespace under which this operation is targeted.
210          * @param key Key.
211          * @param data Data to be written.
212          * @param modifyIfAck The acknowledgement to be called once the request has been handled.
213          *                    The given function is called in the context of handleEvents() function.
214          */
215         virtual void setIfNotExistsAsync(const Namespace& ns,
216                                          const Key& key,
217                                          const Data& data,
218                                          const ModifyIfAck& modifyIfAck) = 0;
219
220         using Keys = std::set<Key>;
221
222         /**
223          * Read acknowledgement to be called when getAsync request has been handled.
224          *
225          * @param error Error code describing the status of the request. The <code>std::error_code::category()</code>
226          *              and <code>std::error_code::value()</code> are implementation specific. Client is advised
227          *              to compare received error against <code>shareddatalayer::Error</code> constants when
228          *              doing error handling. See documentation: sdl/errorqueries.hpp for further information.
229          *              Received <code>std::error_code</code> and <code>std::error_code::message()</code> can be stored
230          *              and provided to shareddatalayer developers if problem needs further investigation.
231          * @param dataMap Data from the storage. Empty container is returned in case of error.
232          */
233         using GetAck = std::function<void(const std::error_code& error, const DataMap& dataMap)>;
234
235         /**
236          * Read data from shared data layer storage. Only those entries that are found will
237          * be returned.
238          *
239          * @param ns Namespace under which this operation is targeted.
240          * @param keys Data to be read.
241          * @param getAck The acknowledgement to be called once the request has been handled.
242          *               The given function is called in the context of handleEvents() function.
243          */
244         virtual void getAsync(const Namespace& ns,
245                               const Keys& keys,
246                               const GetAck& getAck) = 0;
247
248         /**
249          * Remove data from shared data layer storage. Existing keys are removed. Removing
250          * is done atomically, i.e. either all succeeds or all fails.
251          *
252          * @param ns Namespace under which this operation is targeted.
253          * @param keys Data to be removed.
254          * @param modifyAck The acknowledgement to be called once the request has been handled.
255          *                  The given function is called in the context of handleEvents() function.
256          */
257         virtual void removeAsync(const Namespace& ns,
258                                  const Keys& keys,
259                                  const ModifyAck& modifyAck) = 0;
260
261         /**
262          * Conditionally remove data from shared data layer storage if the current data value
263          * matches the user's last known value.
264          *
265          * @param ns Namespace under which this operation is targeted.
266          * @param key Data to be removed
267          * @param data Last known value of data
268          * @param modifyIfAck The acknowledgement to be called once the request has been handled.
269          *                    The given function is called in the context of handleEvents() function.
270          */
271         virtual void removeIfAsync(const Namespace& ns,
272                                    const Key& key,
273                                    const Data& data,
274                                    const ModifyIfAck& modifyIfAck) = 0;
275
276         /**
277          * Read acknowledgement to be called when findKeysAsync request has been handled.
278          *
279          * @param error Error code describing the status of the request. The <code>std::error_code::category()</code>
280          *              and <code>std::error_code::value()</code> are implementation specific. Client is advised
281          *              to compare received error against <code>shareddatalayer::Error</code> constants when
282          *              doing error handling. See documentation: sdl/errorqueries.hpp for further information.
283          *              Received <code>std::error_code</code> and <code>std::error_code::message()</code> can be stored
284          *              and provided to shareddatalayer developers if problem needs further investigation.
285          * @param keys Found keys.
286          */
287         using FindKeysAck = std::function<void(const std::error_code& error, const Keys& keys)>;
288
289         /**
290          * Find all keys matching search pattern under the namespace. No prior knowledge about the keys in the given
291          * namespace exists, thus operation is not guaranteed to be atomic or isolated.
292          *
293          * @param ns Namespace under which this operation is targeted.
294          * @param keyPrefix Only keys starting with given keyPrefix are returned. Passing empty string as
295          *                  keyPrefix will return all keys.
296          * @param findKeysAck The acknowledgement to be called once the request has been handled.
297          *                    The given function is called in the context of handleEvents() function.
298          */
299         virtual void findKeysAsync(const Namespace& ns,
300                                    const std::string& keyPrefix,
301                                    const FindKeysAck& findKeysAck) = 0;
302
303         /**
304          * Remove all keys under the namespace. Found keys are removed atomically, i.e.
305          * either all succeeds or all fails.
306          *
307          * @param ns Namespace under which this operation is targeted.
308          * @param modifyAck The acknowledgement to be called once the request has been handled.
309          *                  The given function is called in the context of handleEvents() function.
310          */
311         virtual void removeAllAsync(const Namespace& ns,
312                                     const ModifyAck& modifyAck) = 0;
313
314         /**
315          * Create a new instance of AsyncStorage.
316          *
317          * @return New instance of AsyncStorage.
318          */
319         static std::unique_ptr<AsyncStorage> create();
320
321     protected:
322         AsyncStorage() = default;
323     };
324 }
325
326 #endif