feature

Paddy 2015-03-16

0:caad72abc05a Go to Latest

feature/server/store.go

First pass at an implementation. Implement the calculation to determine whether a string is included in the partition or not, using crc32 to coerce the string to an evenly distributed uint32, and a modulo to turn it into a percentage. Implement a store to keep track of available flags and the level of the partition for each flag. Implement an API to retrieve, create, and modify these available flags.

History
paddy@0 1 package server
paddy@0 2
paddy@0 3 import (
paddy@0 4 "fmt"
paddy@0 5 "sync"
paddy@0 6
paddy@0 7 "code.secondbit.org/feature.hg"
paddy@0 8 )
paddy@0 9
paddy@0 10 const (
paddy@0 11 errNotFound = "flag not found"
paddy@0 12 errAlreadyExists = "flag already exists"
paddy@0 13 )
paddy@0 14
paddy@0 15 type flagError struct {
paddy@0 16 pos int
paddy@0 17 code string
paddy@0 18 }
paddy@0 19
paddy@0 20 func (f *flagError) Error() string {
paddy@0 21 return fmt.Sprintf("[%d]: %s", f.pos, f.code)
paddy@0 22 }
paddy@0 23
paddy@0 24 type flagStore interface {
paddy@0 25 create(f feature.Flag) *flagError
paddy@0 26 update(flags []feature.Flag) []flagError
paddy@0 27 destroy(flag string) *flagError
paddy@0 28 list() ([]feature.Flag, *flagError)
paddy@0 29 }
paddy@0 30
paddy@0 31 type memstore struct {
paddy@0 32 flags map[string]feature.Flag
paddy@0 33 flock sync.RWMutex
paddy@0 34 }
paddy@0 35
paddy@0 36 func (m *memstore) create(f feature.Flag) *flagError {
paddy@0 37 m.flock.Lock()
paddy@0 38 defer m.flock.Unlock()
paddy@0 39
paddy@0 40 if _, ok := m.flags[f.ID]; ok {
paddy@0 41 return &flagError{pos: 0, code: errNotFound}
paddy@0 42 }
paddy@0 43 m.flags[f.ID] = f
paddy@0 44 return nil
paddy@0 45 }
paddy@0 46
paddy@0 47 func (m *memstore) update(flags []feature.Flag) []flagError {
paddy@0 48 m.flock.Lock()
paddy@0 49 defer m.flock.Unlock()
paddy@0 50
paddy@0 51 errs := []flagError{}
paddy@0 52 for pos, f := range flags {
paddy@0 53 if _, ok := m.flags[f.ID]; !ok {
paddy@0 54 errs = append(errs, flagError{pos: pos, code: errNotFound})
paddy@0 55 }
paddy@0 56 }
paddy@0 57 if len(errs) > 0 {
paddy@0 58 return errs
paddy@0 59 }
paddy@0 60 for _, flag := range flags {
paddy@0 61 m.flags[flag.ID] = flag
paddy@0 62 }
paddy@0 63 return nil
paddy@0 64 }
paddy@0 65
paddy@0 66 func (m *memstore) destroy(flag string) *flagError {
paddy@0 67 m.flock.Lock()
paddy@0 68 defer m.flock.Unlock()
paddy@0 69
paddy@0 70 if _, ok := m.flags[flag]; !ok {
paddy@0 71 return &flagError{pos: 0, code: errNotFound}
paddy@0 72 }
paddy@0 73 delete(m.flags, flag)
paddy@0 74 return nil
paddy@0 75 }
paddy@0 76
paddy@0 77 func (m *memstore) list() ([]feature.Flag, *flagError) {
paddy@0 78 m.flock.RLock()
paddy@0 79 defer m.flock.RUnlock()
paddy@0 80
paddy@0 81 flags := make([]feature.Flag, len(m.flags))
paddy@0 82 pos := 0
paddy@0 83 for _, flag := range m.flags {
paddy@0 84 flags[pos] = flag
paddy@0 85 pos = pos + 1
paddy@0 86 }
paddy@0 87 return flags, nil
paddy@0 88 }