Merge "RIC:1060: Change in PTL"
[ric-plt/sdlgo.git] / doc.go
1 /*
2    Copyright (c) 2019 AT&T Intellectual Property.
3    Copyright (c) 2018-2019 Nokia.
4
5    Licensed under the Apache License, Version 2.0 (the "License");
6    you may not use this file except in compliance with the License.
7    You may obtain a copy of the License at
8
9        http://www.apache.org/licenses/LICENSE-2.0
10
11    Unless required by applicable law or agreed to in writing, software
12    distributed under the License is distributed on an "AS IS" BASIS,
13    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14    See the License for the specific language governing permissions and
15    limitations under the License.
16 */
17
18 /*
19  * This source code is part of the near-RT RIC (RAN Intelligent Controller)
20  * platform project (RICP).
21  */
22
23 /*
24 Package sdlgo provides a lightweight, high-speed interface for accessing shared data storage.
25
26 Shared Data Layer (SDL) is a concept where applications can use and share data using a common
27 storage. The storage must be optimised for very high tranactional throughput and very low
28 latency. Sdlgo is a library which provides applications an API to read and write data
29 to a common storage using key-value paradigm. In addition to this, sdlgo provides an
30 event mechanism that can be used to notify listeners that data was changed.
31
32 This SDL version assumes that the DBAAS service provided by O-RAN community is
33 working as a storage backend.
34
35 All functions except receiving of notifications are safe for concurrent usage by
36 multiple goroutines.
37
38 Namespace
39
40 Namespace concept in a shared data layer connection is to isolate data write and read operations
41 to happen within particular namespace.
42
43 SDL instance
44
45 There are two ways to create SDL instance, the first preferable option is to create so called
46 SDL multi-namespace instance with `sdlgo.NewSyncStorage` call. The second option is to create
47 SDL instance with `sdlgo.NewSdlInstance` call. Latter SDL instance creation method is deprecated
48 and it should not be used anymore in any new application implementations, it is left to SDL API
49 to guarantee backward compatibility for the old application implementations.
50 The difference between multi-namespace `SyncStorage` SDL instance and the old one is that in
51 `SyncStorage` case namespace is not defined at instance creation time, but it is defined when
52 SDL read and write APIs are called. This means that with SDL `SyncStorage` instance it is much
53 easier to write and read data from different namespaces in a single application client compared
54 to the old SDL API where you needed to create own SDL instance for each namespace going to be
55 used later to write and read data.
56
57 Database connection
58
59 When `SyncStorage` instance is created, it also creates database backend connection, this means
60 that sdlgo shall open a tcp connection to backend database. Below is example how to create SDL
61 `SyncStorage` instance and what also connects to database backend under the hood:
62   sdl := sdlgo.NewSyncStorage()
63
64 For the backend database connection a circuit breaker design is used. If the connection fails,
65 an error is returned immediately to application. Restoration of the connection happens
66 automatically by SDL and application should retry the operation again after a while.
67
68 Database service is discovered by using DBAAS* environment variables. For simple standalone
69 DBAAS case there are needed two environment variables: DBAAS_SERVICE_HOST and
70 DBAAS_SERVICE_PORT. If not set, localhost and port 6379 are used by default.
71
72 Keys and data
73
74 Clients save key-value pairs. Keys are allways strings. The types of the values must be of a basic
75 type, i.e. string, integer or byte array or slice. This means that the internal structures, like
76 protobufs or JSON objects, must be serialised to a byte array or slice before passing it to SDL.
77 Clients are responsible for managing the keys within a namespace.
78
79 Some examples on how to set the data using different kind of input parameters:
80
81 Basic usage, keys and values are given as own parameters (with mixed data types)
82   err := s.Set("example-namaspace", "key1", "value1", "key2", 2)
83
84 Keys and values inside a slice (again with mixed types, thus empty interface used as a type)
85   exampleSlice := []interface{"key1", "value1", "key2", 2}
86   err := s.Set("example-namaspace", exampleSlice)
87
88 Data stored to a byte array
89   data := make([]byte), 3
90   data[0] = 1
91   data[1] = 2
92   data[2] = 3
93   s.Set("key", data)
94
95 Keys and values stored into a map (byte array "data" used from previous example)
96   mapData := map[string]interface{
97     "key1" : "data",
98     "key2" : 2,
99     "key3" : data,
100   }
101
102 When data is read from SDL storage, a map is returned where the requested key works as map key.
103 If the key was not found, the value for the given key is nil. It is possible to request several
104 key with one Get() call.
105
106 Groups
107
108 SDL groups are unordered collections of members where each member is unique. Using the SDL API
109 it is possible to add/remove members from a group, remove the whole group or do queries like the
110 size of a group and if member belongs to a group. Like key-value storage, groups are per namespace.
111
112 Events
113
114 Events are a publish-subscribe pattern to indicate interested parties that there has been a change
115 in data. Delivery of the events are not guaranteed. In SDL, events are happening via channels and
116 channels are per namespace. It is possible to publish several kinds of events through one channel.
117
118 In order to publish changes to SDL data, the publisher need to call an API function that supports
119 publishing. E.g.
120    err := sdl.SetAndPublish("example-namespace", []string{
121       "channel1", "event1", "channel2", "event2"}, "key", "value",
122    )
123
124 This example will publish event1 to channel1 and event2 in channel2 after writing the data.
125
126 When subscribing the channels, the application needs to first create an SDL instance for the desired
127 namespace. The subscription happens using the SubscribeChannel() API function. The parameters for
128 the function takes a callback function and one or many channels to be subscribed. When an event is
129 received for the given channel, the given callback function shall be called with one or many events.
130 It is possible to make several subscriptions for different channels using different callback
131 functions if different kind of handling is required.
132
133   sdl := sdlgo.NewSyncStorage()
134
135   cb1 := func(channel string, event ...string) {
136     fmt.Printf("cb1: Received %s from channel %s\n", event, channel)
137   }
138
139   cb2 := func(channel string, event ...string) {
140     fmt.Printf("cb2: Received %s from channel %s\n", event, channel)
141   }
142
143   sdl.SubscribeChannel("example-namespace", cb1, "channel1", "channel2")
144   sdl.SubscribeChannel("example-namespace", cb2, "channel3")
145
146 This example subscribes three channels from "example-namespace" and assigns cb1 for channel1 and channel2
147 whereas channel3 is assigned to cb2.
148
149 The callbacks are called from a context of a goroutine that is listening for the events. When
150 application receives events, the preferred way to do the required processing of an event (e.g. read
151 from SDL) is to do it in another context, e.g. by triggering applications own goroutine using Go
152 channels. By doing like this, blocking the receive routine and possibly loosing events, can be
153 avoided.
154 */
155 package sdlgo