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  }