feature
2015-03-16
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.
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/server/store.go Mon Mar 16 22:45:58 2015 -0400 1.3 @@ -0,0 +1,88 @@ 1.4 +package server 1.5 + 1.6 +import ( 1.7 + "fmt" 1.8 + "sync" 1.9 + 1.10 + "code.secondbit.org/feature.hg" 1.11 +) 1.12 + 1.13 +const ( 1.14 + errNotFound = "flag not found" 1.15 + errAlreadyExists = "flag already exists" 1.16 +) 1.17 + 1.18 +type flagError struct { 1.19 + pos int 1.20 + code string 1.21 +} 1.22 + 1.23 +func (f *flagError) Error() string { 1.24 + return fmt.Sprintf("[%d]: %s", f.pos, f.code) 1.25 +} 1.26 + 1.27 +type flagStore interface { 1.28 + create(f feature.Flag) *flagError 1.29 + update(flags []feature.Flag) []flagError 1.30 + destroy(flag string) *flagError 1.31 + list() ([]feature.Flag, *flagError) 1.32 +} 1.33 + 1.34 +type memstore struct { 1.35 + flags map[string]feature.Flag 1.36 + flock sync.RWMutex 1.37 +} 1.38 + 1.39 +func (m *memstore) create(f feature.Flag) *flagError { 1.40 + m.flock.Lock() 1.41 + defer m.flock.Unlock() 1.42 + 1.43 + if _, ok := m.flags[f.ID]; ok { 1.44 + return &flagError{pos: 0, code: errNotFound} 1.45 + } 1.46 + m.flags[f.ID] = f 1.47 + return nil 1.48 +} 1.49 + 1.50 +func (m *memstore) update(flags []feature.Flag) []flagError { 1.51 + m.flock.Lock() 1.52 + defer m.flock.Unlock() 1.53 + 1.54 + errs := []flagError{} 1.55 + for pos, f := range flags { 1.56 + if _, ok := m.flags[f.ID]; !ok { 1.57 + errs = append(errs, flagError{pos: pos, code: errNotFound}) 1.58 + } 1.59 + } 1.60 + if len(errs) > 0 { 1.61 + return errs 1.62 + } 1.63 + for _, flag := range flags { 1.64 + m.flags[flag.ID] = flag 1.65 + } 1.66 + return nil 1.67 +} 1.68 + 1.69 +func (m *memstore) destroy(flag string) *flagError { 1.70 + m.flock.Lock() 1.71 + defer m.flock.Unlock() 1.72 + 1.73 + if _, ok := m.flags[flag]; !ok { 1.74 + return &flagError{pos: 0, code: errNotFound} 1.75 + } 1.76 + delete(m.flags, flag) 1.77 + return nil 1.78 +} 1.79 + 1.80 +func (m *memstore) list() ([]feature.Flag, *flagError) { 1.81 + m.flock.RLock() 1.82 + defer m.flock.RUnlock() 1.83 + 1.84 + flags := make([]feature.Flag, len(m.flags)) 1.85 + pos := 0 1.86 + for _, flag := range m.flags { 1.87 + flags[pos] = flag 1.88 + pos = pos + 1 1.89 + } 1.90 + return flags, nil 1.91 +}