~whereswaldon/forest-go

634bef637d030d6f7eb43ec251ebcd1117c10dde — Chris Waldon 2 years ago 3672410
Implement CacheStore and unify has/get operations
3 files changed, 60 insertions(+), 38 deletions(-)

M nodes.go
M store.go
M store_test.go
M nodes.go => nodes.go +4 -4
@@ 179,7 179,7 @@ func (n *commonNode) ValidateShallow() error {
func (n *commonNode) ValidateDeep(store Store) error {
	// ensure known parent
	if !n.Parent.Equals(fields.NullHash()) {
		if has, err := store.Has(&n.Parent); !has {
		if _, has, err := store.Get(&n.Parent); !has {
			return fmt.Errorf("Unknown parent %v", n.Parent)
		} else if err != nil {
			return err


@@ 187,7 187,7 @@ func (n *commonNode) ValidateDeep(store Store) error {
	}
	// ensure known author
	if !n.SignatureAuthority.Equals(fields.NullHash()) {
		if has, err := store.Has(&n.SignatureAuthority); !has {
		if _, has, err := store.Get(&n.SignatureAuthority); !has {
			return fmt.Errorf("Unknown SignatureAuthority %v", n.SignatureAuthority)
		} else if err != nil {
			return err


@@ 415,7 415,7 @@ func (c *Community) ValidateShallow() error {

// ValidateDeep checks all referenced nodes for existence within the store.
func (c *Community) ValidateDeep(store Store) error {
	if has, err := store.Has(&c.SignatureAuthority); !has {
	if _, has, err := store.Get(&c.SignatureAuthority); !has {
		return fmt.Errorf("Missing author node %v", c.SignatureAuthority)
	} else if err != nil {
		return err


@@ 538,7 538,7 @@ func (r *Reply) ValidateDeep(store Store) error {
		needed = append(needed, &r.ConversationID)
	}
	for _, neededNode := range needed {
		if has, err := store.Has(neededNode); !has {
		if _, has, err := store.Get(neededNode); !has {
			return fmt.Errorf("Missing required node %v", neededNode)
		} else if err != nil {
			return err

M store.go => store.go +51 -25
@@ 1,15 1,12 @@
package forest

import (
	"fmt"

	"git.sr.ht/~whereswaldon/forest-go/fields"
)

type Store interface {
	Size() (int, error)
	Has(*fields.QualifiedHash) (bool, error)
	Get(*fields.QualifiedHash) (Node, error)
	Get(*fields.QualifiedHash) (Node, bool, error)
	Add(Node) error
}



@@ 25,33 22,17 @@ func (m *MemoryStore) Size() (int, error) {
	return len(m.Items), nil
}

func (m *MemoryStore) Has(id *fields.QualifiedHash) (bool, error) {
func (m *MemoryStore) Get(id *fields.QualifiedHash) (Node, bool, error) {
	idString, err := id.MarshalString()
	if err != nil {
		return false, err
	}
	return m.HasID(idString)
}

func (m *MemoryStore) HasID(id string) (bool, error) {
	_, has := m.Items[id]
	return has, nil
}

func (m *MemoryStore) Get(id *fields.QualifiedHash) (Node, error) {
	idString, err := id.MarshalString()
	if err != nil {
		return nil, err
		return nil, false, err
	}
	return m.GetID(idString)
}

func (m *MemoryStore) GetID(id string) (Node, error) {
func (m *MemoryStore) GetID(id string) (Node, bool, error) {
	item, has := m.Items[id]
	if !has {
		return nil, fmt.Errorf("MemoryStore does not contain id %s", id)
	}
	return item, nil
	return item, has, nil
}

func (m *MemoryStore) Add(node Node) error {


@@ 64,9 45,54 @@ func (m *MemoryStore) Add(node Node) error {

func (m *MemoryStore) AddID(id string, node Node) error {
	// safe to ignore error because we know it can't happen
	if has, _ := m.HasID(id); has {
	if _, has, _ := m.GetID(id); has {
		return nil
	}
	m.Items[id] = node
	return nil
}

type CacheStore struct {
	Cache, Back Store
}

func NewCacheStore(cache, back Store) *CacheStore {
	return &CacheStore{cache, back}
}

// Size returns the effective size of this CacheStore, which is the size of the
// Back Store.
func (m *CacheStore) Size() (int, error) {
	return m.Back.Size()
}

// Get returns the requested node if it is present in either the Cache or the Back Store.
// If the cache is missed by the backing store is hit, the node will automatically be
// added to the cache.
func (m *CacheStore) Get(id *fields.QualifiedHash) (Node, bool, error) {
	if node, has, err := m.Cache.Get(id); err != nil {
		return nil, false, err
	} else if has {
		return node, has, nil
	}
	if node, has, err := m.Back.Get(id); err != nil {
		return nil, false, err
	} else if has {
		if err := m.Cache.Add(node); err != nil {
			return nil, false, err
		}
		return node, has, nil
	}
	return nil, false, nil
}

// Add inserts the given node into both stores of the CacheStore
func (m *CacheStore) Add(node Node) error {
	if err := m.Back.Add(node); err != nil {
		return err
	}
	if err := m.Cache.Add(node); err != nil {
		return err
	}
	return nil
}

M store_test.go => store_test.go +5 -9
@@ 16,13 16,12 @@ func TestMemoryStoreAdd(t *testing.T) {
	id, _, com, rep := MakeReplyOrSkip(t)
	nodes := []forest.Node{id, com, rep}
	for _, i := range nodes {
		if has, err := s.Has(i.ID()); has {
		if node, has, err := s.Get(i.ID()); has {
			t.Errorf("Empty store should not contain element %v", i.ID())
		} else if err != nil {
			t.Errorf("Empty store Has() should not err with %s", err)
		}
		if _, err := s.Get(i.ID()); err == nil {
			t.Errorf("Empty store Get() should err")
			t.Errorf("Empty store Get() should not err with %s", err)
		} else if node != nil {
			t.Errorf("Empty store Get() should return none-nil node %v", node)
		}
	}
	for count, i := range nodes {


@@ 34,13 33,10 @@ func TestMemoryStoreAdd(t *testing.T) {
		} else if size != count+1 {
			t.Errorf("Expected Size() to be %d after %d Add()s, got %d", count+1, count+1, size)
		}
		if has, err := s.Has(i.ID()); !has {
		if node, has, err := s.Get(i.ID()); !has {
			t.Errorf("MemoryStore should contain element %v", i.ID())
		} else if err != nil {
			t.Errorf("MemoryStore Has() should not err with %s", err)
		}
		if node, err := s.Get(i.ID()); err != nil {
			t.Errorf("MemoryStore Get() should not err with %s", err)
		} else if !i.Equals(node) {
			t.Errorf("MemoryStore Get() should return a node equal to the one that was Add()ed. Got %v, expected %v", node, i)
		}