github.com/jeffjen/go-libkv@v0.0.0-20151212051932-5df59a45a168/libkv/store.go (about) 1 // Package libkv provides key value storage for embedded go application. 2 package libkv 3 4 import ( 5 expire "github.com/jeffjen/go-libkv/timer" 6 7 "sync" 8 "time" 9 ) 10 11 const ( 12 GET int = iota 13 SET 14 DEL 15 EXPIRE 16 GONE 17 GETSET 18 LPUSH 19 LTRIM 20 ) 21 22 // Event delivers keyspace changes to registerd Watcher 23 type Event struct { 24 Action int `desc: action taken e.g. GET, SET, EXPIRE` 25 Iden string `desc: key that was affected` 26 } 27 28 // avent is a control object for Store event hub to deliver Event 29 type avent struct { 30 c chan<- *Event `desc: channel to send the event into` 31 h <-chan struct{} `desc: control channel to indicate unregister` 32 } 33 34 // kv_avent is holds metadata about how to distribute keyspace Event 35 type kv_avent struct { 36 sync.RWMutex 37 38 src chan *Event `desc: Event source channel from keyspace` 39 list map[int64]*avent `desc: list of registerd party for Event` 40 halt chan struct{} `desc: control channel to stop Event distribution` 41 counter int64 `desc: counter to tag Watcher` 42 } 43 44 // thing is an object stored in Store 45 type thing struct { 46 X interface{} `desc: the object to store` 47 T *time.Time `desc: the expiration date on this thing` 48 } 49 50 // store is the actual KV store 51 type store struct { 52 store map[string]thing `desc: the actual KV store` 53 index map[string]int64 `desc: the index mapper for key to schdule index` 54 } 55 56 // Store is a simple key value in memory storage. 57 // Upon initialization, Store provides general get, set, del, list operations, 58 // as well as the ability to expire a key and watch a key change. 59 type Store struct { 60 sync.RWMutex 61 62 e *expire.Timer `desc: scheduler for keyspace expiration` 63 m *store `desc: the actual store` 64 s *kv_avent `desc: keyspace event hub` 65 } 66 67 func (s *Store) event_hub() (ok <-chan bool) { 68 ack := make(chan bool, 1) 69 go func() { 70 ack <- true 71 for yay := true; yay; { 72 select { 73 case <-s.s.halt: 74 yay = false 75 case one_event := <-s.s.src: 76 s.s.Lock() 77 for idx, ev := range s.s.list { 78 select { 79 case <-ev.h: 80 delete(s.s.list, idx) 81 case ev.c <- one_event: 82 continue 83 } 84 } 85 s.s.Unlock() 86 } 87 } 88 }() 89 return ack 90 } 91 92 // init setups the key value storage. 93 // Upon initialization, the Store spawns expiration scheduler and keyspace event hub. 94 func (s *Store) init() (ok <-chan bool) { 95 s.e.Tic() 96 return s.event_hub() 97 } 98 99 // NewStore creates a Store object. 100 // Store object is fully initialzed upon creation. 101 func NewStore() (s *Store) { 102 s = &Store{ 103 e: expire.NewTimer(), 104 m: &store{ 105 store: make(map[string]thing), 106 index: make(map[string]int64), 107 }, 108 s: &kv_avent{ 109 src: make(chan *Event, 8), 110 list: make(map[int64]*avent), 111 halt: make(chan struct{}), 112 counter: 1, // initialzed to positive value 113 }, 114 } 115 <-s.init() // make sure both scheduler and event hub is running 116 return 117 } 118 119 // Close stops the Store scheduler and event hub. 120 func (s *Store) Close() { 121 close(s.s.halt) 122 s.e.Toc() 123 } 124 125 func (s *Store) pushEvent(event ...*Event) { 126 for _, ev := range event { 127 s.s.src <- ev 128 } 129 } 130 131 // del removes an item from the Store keyspace 132 func (s *Store) del(iden string, jobId int64) bool { 133 // check key exist 134 if _, ok := s.m.store[iden]; !ok { 135 return false 136 } 137 138 // check key job id; if exist then job id must match 139 if jid, ok := s.m.index[iden]; ok && jobId != jid { 140 // jobId will ALWAYS be positve value 141 if jid == -1 { 142 delete(s.m.index, iden) 143 } 144 return false 145 } 146 147 // remove key from store 148 delete(s.m.store, iden) 149 delete(s.m.index, iden) 150 151 return true 152 } 153 154 func (s *Store) del_main(iden string) { 155 jobId, _ := s.m.index[iden] 156 if s.del(iden, jobId) { 157 s.pushEvent(&Event{DEL, iden}) 158 } 159 } 160 161 // get retrieves an item x identified by iden 162 func (s *Store) get(iden string) (x interface{}) { 163 if obj, ok := s.m.store[iden]; ok { 164 x = obj.X 165 } 166 return 167 } 168 169 // set adds an item to the Store keyspace; sets expiration handler when 170 // appropriate 171 func (s *Store) set(iden string, x interface{}, exp *time.Time) bool { 172 if idx, ok := s.m.index[iden]; ok { 173 s.e.Cancel(idx) 174 s.m.index[iden] = -1 // invalidate any fired expire handler 175 } 176 s.m.store[iden] = thing{X: x, T: exp} 177 if exp != nil { 178 s.expire(iden, *exp) 179 } 180 return true 181 } 182 183 func (s *Store) expire(iden string, exp time.Time) { 184 id := iden 185 s.m.index[iden] = s.e.SchedFunc(exp, func(jobId int64) { 186 s.Lock() 187 defer s.Unlock() 188 if s.del(id, jobId) { 189 s.pushEvent(&Event{GONE, iden}) 190 } 191 }) 192 } 193 194 // Set puts an aribtrary item x into Store identified by iden 195 func (s *Store) Set(iden string, x interface{}) (ret bool) { 196 s.Lock() 197 defer s.Unlock() 198 ret = s.set(iden, x, nil) 199 if ret { 200 s.pushEvent(&Event{SET, iden}) 201 } 202 return 203 } 204 205 // Set puts an aribtrary item x into Store identified by iden to be expired at 206 // exp 207 func (s *Store) Setexp(iden string, x interface{}, exp time.Time) (ret bool) { 208 s.Lock() 209 defer s.Unlock() 210 ret = s.set(iden, x, &exp) 211 if ret { 212 s.pushEvent(&Event{SET, iden}, &Event{EXPIRE, iden}) 213 } 214 return 215 } 216 217 // Get retrieves an item x identified by iden 218 func (s *Store) Get(iden string) (x interface{}) { 219 s.RLock() 220 defer s.RUnlock() 221 if x = s.get(iden); x != nil { 222 if _, ok := x.([]thing); !ok { 223 s.pushEvent(&Event{GET, iden}) 224 } else { 225 x = nil 226 } 227 } 228 return 229 } 230 231 // Getset retrieves an item y identified by iden and replace it with item x 232 func (s *Store) Getset(iden string, x interface{}) (y interface{}) { 233 s.Lock() 234 defer s.Unlock() 235 y = s.get(iden) 236 s.set(iden, x, nil) 237 s.pushEvent(&Event{GETSET, iden}) 238 return 239 } 240 241 // Getexp retrieves an item x identified by iden and set expiration 242 func (s *Store) Getexp(iden string, exp time.Time) (x interface{}) { 243 s.Lock() 244 defer s.Unlock() 245 if x = s.get(iden); x != nil { 246 s.pushEvent(&Event{GET, iden}) 247 s.expire(iden, exp) 248 } 249 return 250 } 251 252 // TTL reports the life time left on the item identified by iden 253 func (s *Store) TTL(iden string) (in time.Duration) { 254 s.RLock() 255 defer s.RUnlock() 256 if obj, ok := s.m.store[iden]; ok && obj.T != nil { 257 in = obj.T.Sub(time.Now()) 258 if in < 0 { 259 in = time.Duration(0) 260 } 261 } 262 return 263 } 264 265 // Expire puts item identified by iden to expire at exp 266 func (s *Store) Expire(iden string, exp time.Time) bool { 267 s.Lock() 268 defer s.Unlock() 269 if _, ok := s.m.store[iden]; !ok { 270 return false 271 } else { 272 s.expire(iden, exp) 273 s.pushEvent(&Event{EXPIRE, iden}) 274 return true 275 } 276 } 277 278 // Del removes the item identified by iden from Store 279 func (s *Store) Del(iden string) { 280 s.Lock() 281 defer s.Unlock() 282 s.del_main(iden) 283 } 284 285 // Lpush appends an item to item identified by iden 286 // creates new list item 287 // returns size of the item after operation; -1 for failed attempt 288 func (s *Store) Lpush(iden string, x interface{}) (size int64) { 289 s.Lock() 290 defer s.Unlock() 291 if obj, ok := s.m.store[iden]; !ok { 292 // the "thing" to store is a []thing with one new item 293 s.m.store[iden] = thing{ 294 X: []thing{ 295 thing{X: x, T: nil}, 296 }, 297 T: nil, 298 } 299 size = 1 300 s.pushEvent(&Event{LPUSH, iden}) 301 } else if lobj, ok := obj.X.([]thing); ok { 302 // find the "thing", check that it is a []thing, and append to it 303 lobj = append([]thing{thing{X: x, T: nil}}, lobj...) 304 s.m.store[iden] = thing{X: lobj, T: obj.T} 305 size = int64(len(lobj)) + 1 306 s.pushEvent(&Event{LPUSH, iden}) 307 } else { 308 size = -1 309 } 310 return 311 } 312 313 func (s *Store) rangeidx(start, stop, length int64) (begin, end int64) { 314 if start >= length { 315 begin = length 316 } else if start >= 0 && start < length { 317 begin = start 318 } else { 319 begin = length + start 320 if begin < 0 { 321 begin = 0 322 } 323 } 324 if stop >= length { 325 end = length 326 } else if stop >= 0 && stop < length { 327 end = stop 328 } else { 329 end = length + stop + 1 330 } 331 if end < begin { 332 end = begin 333 } 334 return 335 } 336 337 // Lrange returns a slice of items within start and stop. 338 func (s *Store) Lrange(iden string, start, stop int64) (items []interface{}) { 339 s.Lock() 340 defer s.Unlock() 341 if obj, ok := s.m.store[iden]; !ok { 342 items = nil 343 } else if lobj, ok := obj.X.([]thing); !ok { 344 items = nil 345 } else { 346 begin, end := s.rangeidx(start, stop, int64(len(lobj))) 347 for _, obj := range lobj[begin:end] { 348 items = append(items, obj.X) 349 } 350 } 351 return 352 } 353 354 // Ltrim keeps items specified in start and stop range and remove all other 355 // items. 356 // start and stop can be negative values. If the value is -1, it indicates the 357 // end of list; if it is greater then the actual length, it is clamped to the 358 // boundary beteween 0 and length of item 359 // returns size of the item after operation; -1 for failed attempt 360 func (s *Store) Ltrim(iden string, start, stop int64) (size int64) { 361 s.Lock() 362 defer s.Unlock() 363 if obj, ok := s.m.store[iden]; !ok { 364 size = -1 365 } else if lobj, ok := obj.X.([]thing); !ok { 366 size = -1 367 } else { 368 begin, end := s.rangeidx(start, stop, int64(len(lobj))) 369 lobj = lobj[begin:end] 370 s.m.store[iden] = thing{X: lobj, T: obj.T} 371 size = int64(len(lobj)) 372 s.pushEvent(&Event{LTRIM, iden}) 373 } 374 return 375 } 376 377 // register takes a control object avent and place it into the Wather list in 378 // keyspace event hub. 379 func (s *Store) register(inn *avent) { 380 s.s.Lock() 381 defer s.s.Unlock() 382 r := s.s.counter 383 s.s.counter = s.s.counter + 1 384 s.s.list[r] = inn 385 } 386 387 // Watch provides interested party to monitor keyspace changes. 388 // A Watcher (caller of Watch function) provides a stopping condition, and gets 389 // a channel for future Event in keyspace. 390 // Stop a Watcher by closeing the stop channel 391 func (s *Store) Watch(stop <-chan struct{}) <-chan *Event { 392 output := make(chan *Event, 8) 393 go func() { 394 defer close(output) 395 incoming := make(chan *Event, 1) 396 end := make(chan struct{}) 397 s.register(&avent{incoming, end}) 398 defer close(end) 399 for yay := true; yay; { 400 select { 401 case <-stop: 402 yay = false 403 case ev := <-incoming: 404 select { 405 default: 406 yay = false 407 case output <- ev: 408 continue 409 } 410 } 411 } 412 }() 413 return output 414 } 415 416 // Key retrieves the full list of item key in Store. 417 func (s *Store) Key() (items []string) { 418 s.RLock() 419 defer s.RUnlock() 420 items = make([]string, len(s.m.store)) 421 idx := 0 422 for k, _ := range s.m.store { 423 items[idx] = k 424 idx++ 425 } 426 return 427 } 428 429 // Keyexp retrieves the full list of item key in Store that has an expiration. 430 func (s *Store) Keyexp() (items []string) { 431 s.RLock() 432 defer s.RUnlock() 433 items = make([]string, len(s.m.index)) 434 idx := 0 435 for k, _ := range s.m.index { 436 items[idx] = k 437 idx++ 438 } 439 return 440 } 441 442 type Value struct { 443 K string 444 X interface{} 445 R bool 446 } 447 448 // IterateW provides iteration construct to loop over key set and modify 449 func (s *Store) IterateW() (it <-chan *Value, mod chan<- *Value) { 450 iterator, modify := make(chan *Value), make(chan *Value) 451 go func() { 452 defer close(iterator) 453 s.Lock() 454 defer s.Unlock() 455 for k, v := range s.m.store { 456 iterator <- &Value{k, v.X, false} 457 vv, ok := <-modify 458 if ok { 459 if vv.R { 460 s.del_main(k) 461 } else { 462 s.set(k, vv.X, v.T) 463 } 464 } 465 } 466 }() 467 return iterator, modify 468 } 469 470 // IterateR provides iteration construct to loop over key in read only mode 471 func (s *Store) IterateR() (it <-chan *Value) { 472 iterator := make(chan *Value) 473 go func() { 474 defer close(iterator) 475 s.RLock() 476 defer s.RUnlock() 477 for k, v := range s.m.store { 478 iterator <- &Value{k, v.X, false} 479 } 480 }() 481 return iterator 482 }