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
     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 +}