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