afc4781a966a36fc9f059541b465ba62745b32ac
[ric-plt/sdlgo.git] / sdl.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 package sdlgo
24
25 import (
26         "time"
27
28         "gerrit.o-ran-sc.org/r/ric-plt/sdlgo/internal/sdlgoredis"
29 )
30
31 //SdlInstance provides an API to read, write and modify
32 //key-value pairs in a given namespace.
33 type SdlInstance struct {
34         nameSpace string
35         nsPrefix  string
36         storage   *SyncStorage
37 }
38
39 //Database struct is a holder for the internal database instance. Applications
40 //can use this exported data type to locally store a reference to database
41 //instance returned from NewDabase() function.
42 type Database struct {
43         instance iDatabase
44 }
45
46 //NewDatabase creates a connection to database that will be used
47 //as a backend for the key-value storage. The returned value
48 //can be reused between multiple SDL instances in which case each instance
49 //is using the same connection.
50 func NewDatabase() *Database {
51         return &Database{
52                 instance: sdlgoredis.Create(),
53         }
54 }
55
56 //NewSdlInstance creates a new sdl instance using the given namespace.
57 //The database used as a backend is given as a parameter
58 func NewSdlInstance(NameSpace string, db *Database) *SdlInstance {
59         return &SdlInstance{
60                 nameSpace: NameSpace,
61                 nsPrefix:  "{" + NameSpace + "},",
62                 storage:   newSyncStorage(db),
63         }
64 }
65
66 //SubscribeChannel lets you to subscribe for a events on a given channels.
67 //SDL notifications are events that are published on a specific channels.
68 //Both the channel and events are defined by the entity that is publishing
69 //the events.
70 //
71 //When subscribing for a channel, a callback function is given as a parameter.
72 //Whenever a notification is received from a channel, this callback is called
73 //with channel and notifications as parameter (several notifications could be
74 //packed to a single callback function call). A call to SubscribeChannel function
75 //returns immediatelly, callbacks will be called asyncronously.
76 //
77 //It is possible to subscribe to different channels using different callbacks. In
78 //this case simply use SubscribeChannel function separately for each channel.
79 //
80 //When receiving events in callback routine, it is a good practive to return from
81 //callback as quickly as possible. E.g. reading in callback context should be avoided
82 //and using of Go signals is recommended. Also it should be noted that in case of several
83 //events received from different channels, callbacks are called in series one by one.
84 func (s *SdlInstance) SubscribeChannel(cb func(string, ...string), channels ...string) error {
85         s.storage.SubscribeChannel(s.nameSpace, cb, channels...)
86         return nil
87 }
88
89 //UnsubscribeChannel removes subscription from one or several channels.
90 func (s *SdlInstance) UnsubscribeChannel(channels ...string) error {
91         return s.storage.UnsubscribeChannel(s.nameSpace, channels...)
92 }
93
94 //Close connection to backend database.
95 func (s *SdlInstance) Close() error {
96         return s.storage.Close()
97 }
98
99 //SetAndPublish function writes data to shared data layer storage and sends an event to
100 //a channel. Writing is done atomically, i.e. all succeeds or fails.
101 //Data to be written is given as key-value pairs. Several key-value
102 //pairs can be written with one call.
103 //The key is expected to be string whereas value can be anything, string,
104 //number, slice array or map
105 //
106 //If data was set successfully, an event is sent to a channel.
107 //Channels and events are given as pairs is channelsAndEvents parameter.
108 //It is possible to send several events to several channels by giving several
109 //channel-event pairs.
110 //  E.g. []{"channel1", "event1", "channel2", "event2", "channel1", "event3"}
111 //will send event1 and event3 to channel1 and event2 to channel2.
112 func (s *SdlInstance) SetAndPublish(channelsAndEvents []string, pairs ...interface{}) error {
113         return s.storage.SetAndPublish(s.nameSpace, channelsAndEvents, pairs...)
114 }
115
116 //Set function writes data to shared data layer storage. Writing is done
117 //atomically, i.e. all succeeds or fails.
118 //Data to be written is given as key-value pairs. Several key-value
119 //pairs can be written with one call.
120 //The key is expected to be string whereas value can be anything, string,
121 //number, slice array or map
122 func (s *SdlInstance) Set(pairs ...interface{}) error {
123         return s.storage.Set(s.nameSpace, pairs...)
124 }
125
126 //Get function atomically reads one or more keys from SDL. The returned map has the
127 //requested keys as index and data as value. If the requested key is not found
128 //from SDL, it's value is nil
129 func (s *SdlInstance) Get(keys []string) (map[string]interface{}, error) {
130         return s.storage.Get(s.nameSpace, keys)
131 }
132
133 //SetIfAndPublish atomically replaces existing data with newData in SDL if data matches the oldData.
134 //If replace was done successfully, true will be returned. Also, if publishing was successfull, an event
135 //is published to a given channel.
136 func (s *SdlInstance) SetIfAndPublish(channelsAndEvents []string, key string, oldData, newData interface{}) (bool, error) {
137         return s.storage.SetIfAndPublish(s.nameSpace, channelsAndEvents, key, oldData, newData)
138 }
139
140 //SetIf atomically replaces existing data with newData in SDL if data matches the oldData.
141 //If replace was done successfully, true will be returned.
142 func (s *SdlInstance) SetIf(key string, oldData, newData interface{}) (bool, error) {
143         return s.storage.SetIf(s.nameSpace, key, oldData, newData)
144 }
145
146 //SetIfNotExistsAndPublish conditionally sets the value of a key. If key already exists in SDL,
147 //then it's value is not changed. Checking the key existence and potential set operation
148 //is done atomically. If the set operation was done successfully, an event is published to a
149 //given channel.
150 func (s *SdlInstance) SetIfNotExistsAndPublish(channelsAndEvents []string, key string, data interface{}) (bool, error) {
151         return s.storage.SetIfNotExistsAndPublish(s.nameSpace, channelsAndEvents, key, data)
152 }
153
154 //SetIfNotExists conditionally sets the value of a key. If key already exists in SDL,
155 //then it's value is not changed. Checking the key existence and potential set operation
156 //is done atomically.
157 func (s *SdlInstance) SetIfNotExists(key string, data interface{}) (bool, error) {
158         return s.storage.SetIfNotExists(s.nameSpace, key, data)
159 }
160
161 //RemoveAndPublish removes data from SDL. Operation is done atomically, i.e. either all succeeds or fails.
162 //Trying to remove a nonexisting key is not considered as an error.
163 //An event is published into a given channel if remove operation is successfull and
164 //at least one key is removed (if several keys given). If the given key(s) doesn't exist
165 //when trying to remove, no event is published.
166 func (s *SdlInstance) RemoveAndPublish(channelsAndEvents []string, keys []string) error {
167         return s.storage.RemoveAndPublish(s.nameSpace, channelsAndEvents, keys)
168 }
169
170 //Remove data from SDL. Operation is done atomically, i.e. either all succeeds or fails.
171 func (s *SdlInstance) Remove(keys []string) error {
172         return s.storage.Remove(s.nameSpace, keys)
173 }
174
175 //RemoveIfAndPublish removes data from SDL conditionally and if remove was done successfully,
176 //a given event is published to channel. If existing data matches given data,
177 //key and data are removed from SDL. If remove was done successfully, true is returned.
178 func (s *SdlInstance) RemoveIfAndPublish(channelsAndEvents []string, key string, data interface{}) (bool, error) {
179         return s.storage.RemoveIfAndPublish(s.nameSpace, channelsAndEvents, key, data)
180 }
181
182 //RemoveIf removes data from SDL conditionally. If existing data matches given data,
183 //key and data are removed from SDL. If remove was done successfully, true is returned.
184 func (s *SdlInstance) RemoveIf(key string, data interface{}) (bool, error) {
185         return s.storage.RemoveIf(s.nameSpace, key, data)
186 }
187
188 //GetAll returns all keys under the namespace. No prior knowledge about the keys in the
189 //given namespace exists, thus operation is not guaranteed to be atomic or isolated.
190 func (s *SdlInstance) GetAll() ([]string, error) {
191         return s.storage.GetAll(s.nameSpace)
192 }
193
194 //RemoveAll removes all keys under the namespace. Remove operation is not atomic, thus
195 //it is not guaranteed that all keys are removed.
196 func (s *SdlInstance) RemoveAll() error {
197         return s.storage.RemoveAll(s.nameSpace)
198 }
199
200 //RemoveAllAndPublish removes all keys under the namespace and if successfull, it
201 //will publish an event to given channel. This operation is not atomic, thus it is
202 //not guaranteed that all keys are removed.
203 func (s *SdlInstance) RemoveAllAndPublish(channelsAndEvents []string) error {
204         return s.storage.RemoveAllAndPublish(s.nameSpace, channelsAndEvents)
205 }
206
207 //AddMember adds a new members to a group.
208 //
209 //SDL groups are unordered collections of members where each member is
210 //unique. It is possible to add the same member several times without the
211 //need to check if it already exists.
212 func (s *SdlInstance) AddMember(group string, member ...interface{}) error {
213         return s.storage.AddMember(s.nameSpace, group, member...)
214 }
215
216 //RemoveMember removes members from a group.
217 func (s *SdlInstance) RemoveMember(group string, member ...interface{}) error {
218         return s.storage.RemoveMember(s.nameSpace, group, member...)
219 }
220
221 //RemoveGroup removes the whole group along with it's members.
222 func (s *SdlInstance) RemoveGroup(group string) error {
223         return s.storage.RemoveGroup(s.nameSpace, group)
224 }
225
226 //GetMembers returns all the members from a group.
227 func (s *SdlInstance) GetMembers(group string) ([]string, error) {
228         return s.storage.GetMembers(s.nameSpace, group)
229 }
230
231 //IsMember returns true if given member is found from a group.
232 func (s *SdlInstance) IsMember(group string, member interface{}) (bool, error) {
233         return s.storage.IsMember(s.nameSpace, group, member)
234 }
235
236 //GroupSize returns the number of members in a group.
237 func (s *SdlInstance) GroupSize(group string) (int64, error) {
238         return s.storage.GroupSize(s.nameSpace, group)
239 }
240
241 //LockResource function is used for locking a resource. The resource lock in
242 //practice is a key with random value that is set to expire after a time
243 //period. The value written to key is a random value, thus only the instance
244 //created a lock, can release it. Resource locks are per namespace.
245 func (s *SdlInstance) LockResource(resource string, expiration time.Duration, opt *Options) (*Lock, error) {
246         l, err := s.storage.LockResource(s.nameSpace, resource, expiration, opt)
247         if l != nil {
248                 return &Lock{
249                         s:           s,
250                         storageLock: l,
251                 }, err
252         }
253         return nil, err
254 }
255
256 //ReleaseResource removes the lock from a resource. If lock is already
257 //expired or some other instance is keeping the lock (lock taken after expiration),
258 //an error is returned.
259 func (l *Lock) ReleaseResource() error {
260         return l.storageLock.ReleaseResource(l.s.nameSpace)
261 }
262
263 //RefreshResource function can be used to set a new expiration time for the
264 //resource lock (if the lock still exists). The old remaining expiration
265 //time is overwritten with the given new expiration time.
266 func (l *Lock) RefreshResource(expiration time.Duration) error {
267         return l.storageLock.RefreshResource(l.s.nameSpace, expiration)
268 }
269
270 //CheckResource returns the expiration time left for a resource.
271 //If the resource doesn't exist, -2 is returned.
272 func (s *SdlInstance) CheckResource(resource string) (time.Duration, error) {
273         return s.storage.CheckResource(s.nameSpace, resource)
274 }
275
276 //Options struct defines the behaviour for getting the resource lock.
277 type Options struct {
278         //The number of time the lock will be tried.
279         //Default: 0 = no retry
280         RetryCount int
281
282         //Wait between the retries.
283         //Default: 100ms
284         RetryWait time.Duration
285 }
286
287 func (o *Options) getRetryCount() int {
288         if o != nil && o.RetryCount > 0 {
289                 return o.RetryCount
290         }
291         return 0
292 }
293
294 func (o *Options) getRetryWait() time.Duration {
295         if o != nil && o.RetryWait > 0 {
296                 return o.RetryWait
297         }
298         return 100 * time.Millisecond
299 }
300
301 //Lock struct identifies the resource lock instance. Releasing and adjusting the
302 //expirations are done using the methods defined for this struct.
303 type Lock struct {
304         s           *SdlInstance
305         storageLock *SyncStorageLock
306 }