Build names and links at runtime
Don't rely on sqlite db for name registration and linking. Instead register names and links when the daemon starts to an in-memory store. Signed-off-by: Brian Goff <cpuguy83@gmail.com>
This commit is contained in:
parent
390876ba18
commit
c1ed831486
2 changed files with 246 additions and 0 deletions
127
registrar/registrar.go
Normal file
127
registrar/registrar.go
Normal file
|
@ -0,0 +1,127 @@
|
|||
// Package registrar provides name registration. It reserves a name to a given key.
|
||||
package registrar
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNameReserved is an error which is returned when a name is requested to be reserved that already is reserved
|
||||
ErrNameReserved = errors.New("name is reserved")
|
||||
// ErrNameNotReserved is an error which is returned when trying to find a name that is not reserved
|
||||
ErrNameNotReserved = errors.New("name is not reserved")
|
||||
// ErrNoSuchKey is returned when trying to find the names for a key which is not known
|
||||
ErrNoSuchKey = errors.New("provided key does not exist")
|
||||
)
|
||||
|
||||
// Registrar stores indexes a list of keys and their registered names as well as indexes names and the key that they are registred to
|
||||
// Names must be unique.
|
||||
// Registrar is safe for concurrent access.
|
||||
type Registrar struct {
|
||||
idx map[string][]string
|
||||
names map[string]string
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// NewRegistrar creates a new Registrar with the an empty index
|
||||
func NewRegistrar() *Registrar {
|
||||
return &Registrar{
|
||||
idx: make(map[string][]string),
|
||||
names: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
// Reserve registers a key to a name
|
||||
// Reserve is idempotent
|
||||
// Attempting to reserve a key to a name that already exists results in an `ErrNameReserved`
|
||||
// A name reservation is globally unique
|
||||
func (r *Registrar) Reserve(name, key string) error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
if k, exists := r.names[name]; exists {
|
||||
if k != key {
|
||||
return ErrNameReserved
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
r.idx[key] = append(r.idx[key], name)
|
||||
r.names[name] = key
|
||||
return nil
|
||||
}
|
||||
|
||||
// Release releases the reserved name
|
||||
// Once released, a name can be reserved again
|
||||
func (r *Registrar) Release(name string) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
key, exists := r.names[name]
|
||||
if !exists {
|
||||
return
|
||||
}
|
||||
|
||||
for i, n := range r.idx[key] {
|
||||
if n != name {
|
||||
continue
|
||||
}
|
||||
r.idx[key] = append(r.idx[key][:i], r.idx[key][i+1:]...)
|
||||
break
|
||||
}
|
||||
|
||||
delete(r.names, name)
|
||||
|
||||
if len(r.idx[key]) == 0 {
|
||||
delete(r.idx, key)
|
||||
}
|
||||
}
|
||||
|
||||
// Delete removes all reservations for the passed in key.
|
||||
// All names reserved to this key are released.
|
||||
func (r *Registrar) Delete(key string) {
|
||||
r.mu.Lock()
|
||||
for _, name := range r.idx[key] {
|
||||
delete(r.names, name)
|
||||
}
|
||||
delete(r.idx, key)
|
||||
r.mu.Unlock()
|
||||
}
|
||||
|
||||
// GetNames lists all the reserved names for the given key
|
||||
func (r *Registrar) GetNames(key string) ([]string, error) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
names, exists := r.idx[key]
|
||||
if !exists {
|
||||
return nil, ErrNoSuchKey
|
||||
}
|
||||
return names, nil
|
||||
}
|
||||
|
||||
// Get returns the key that the passed in name is reserved to
|
||||
func (r *Registrar) Get(name string) (string, error) {
|
||||
r.mu.Lock()
|
||||
key, exists := r.names[name]
|
||||
r.mu.Unlock()
|
||||
|
||||
if !exists {
|
||||
return "", ErrNameNotReserved
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// GetAll returns all registered names
|
||||
func (r *Registrar) GetAll() map[string][]string {
|
||||
out := make(map[string][]string)
|
||||
|
||||
r.mu.Lock()
|
||||
// copy index into out
|
||||
for id, names := range r.idx {
|
||||
out[id] = names
|
||||
}
|
||||
r.mu.Unlock()
|
||||
return out
|
||||
}
|
119
registrar/registrar_test.go
Normal file
119
registrar/registrar_test.go
Normal file
|
@ -0,0 +1,119 @@
|
|||
package registrar
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestReserve(t *testing.T) {
|
||||
r := NewRegistrar()
|
||||
|
||||
obj := "test1"
|
||||
if err := r.Reserve("test", obj); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := r.Reserve("test", obj); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
obj2 := "test2"
|
||||
err := r.Reserve("test", obj2)
|
||||
if err == nil {
|
||||
t.Fatalf("expected error when reserving an already reserved name to another object")
|
||||
}
|
||||
if err != ErrNameReserved {
|
||||
t.Fatal("expected `ErrNameReserved` error when attempting to reserve an already reserved name")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRelease(t *testing.T) {
|
||||
r := NewRegistrar()
|
||||
obj := "testing"
|
||||
|
||||
if err := r.Reserve("test", obj); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
r.Release("test")
|
||||
r.Release("test") // Ensure there is no panic here
|
||||
|
||||
if err := r.Reserve("test", obj); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetNames(t *testing.T) {
|
||||
r := NewRegistrar()
|
||||
obj := "testing"
|
||||
names := []string{"test1", "test2"}
|
||||
|
||||
for _, name := range names {
|
||||
if err := r.Reserve(name, obj); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
r.Reserve("test3", "other")
|
||||
|
||||
names2, err := r.GetNames(obj)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(names, names2) {
|
||||
t.Fatalf("Exepected: %v, Got: %v", names, names2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
r := NewRegistrar()
|
||||
obj := "testing"
|
||||
names := []string{"test1", "test2"}
|
||||
for _, name := range names {
|
||||
if err := r.Reserve(name, obj); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
r.Reserve("test3", "other")
|
||||
r.Delete(obj)
|
||||
|
||||
_, err := r.GetNames(obj)
|
||||
if err == nil {
|
||||
t.Fatal("expected error getting names for deleted key")
|
||||
}
|
||||
|
||||
if err != ErrNoSuchKey {
|
||||
t.Fatal("expected `ErrNoSuchKey`")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
r := NewRegistrar()
|
||||
obj := "testing"
|
||||
name := "test"
|
||||
|
||||
_, err := r.Get(name)
|
||||
if err == nil {
|
||||
t.Fatal("expected error when key does not exist")
|
||||
}
|
||||
if err != ErrNameNotReserved {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := r.Reserve(name, obj); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err = r.Get(name); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
r.Delete(obj)
|
||||
_, err = r.Get(name)
|
||||
if err == nil {
|
||||
t.Fatal("expected error when key does not exist")
|
||||
}
|
||||
if err != ErrNameNotReserved {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue