Skip to content

Commit

Permalink
database: put missing predicates in consts and un-expose some of them
Browse files Browse the repository at this point in the history
Fixes #16
  • Loading branch information
Quentin-M committed Dec 4, 2015
1 parent 1fefacf commit 9fc29e2
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 80 deletions.
4 changes: 2 additions & 2 deletions database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ import (
)

const (
// FieldIs is the graph predicate defining the type of an entity.
FieldIs = "is"
// fieldIs is the graph predicate defining the type of an entity.
fieldIs = "is"
)

var (
Expand Down
6 changes: 3 additions & 3 deletions database/database_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,13 @@ func TestToValue(t *testing.T) {
assert.Equal(t, "", v, "toValue should return an error and an empty string if the path leads to multiple values")

// toValues()
vs, err := toValues(cayley.StartPath(store, "CoreOS").Out(FieldIs))
vs, err := toValues(cayley.StartPath(store, "CoreOS").Out(fieldIs))
assert.Nil(t, err, "toValues should work even if the requested path leads to nothing")
assert.Len(t, vs, 0, "toValue should return an empty array if the requested path leads to nothing")
words := []string{"powerful", "lightweight"}
for i, word := range words {
store.AddQuad(cayley.Quad("CoreOS", FieldIs, word, ""))
v, err := toValues(cayley.StartPath(store, "CoreOS").Out(FieldIs))
store.AddQuad(cayley.Quad("CoreOS", fieldIs, word, ""))
v, err := toValues(cayley.StartPath(store, "CoreOS").Out(fieldIs))
assert.Nil(t, err, "toValues should have worked")
assert.Len(t, v, i+1, "toValues did not return the right amount of values")
for _, e := range words[:i+1] {
Expand Down
13 changes: 9 additions & 4 deletions database/flag.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ import (
"github.com/google/cayley"
)

const (
fieldFlagValue = "value"
flagNodePrefix = "flag"
)

// UpdateFlag creates a flag or update an existing flag's value
func UpdateFlag(name, value string) error {
if name == "" || value == "" {
Expand All @@ -36,11 +41,11 @@ func UpdateFlag(name, value string) error {
}

// Build transaction
name = "flag:" + name
name = flagNodePrefix + ":" + name
if currentValue != "" {
t.RemoveQuad(cayley.Quad(name, "value", currentValue, ""))
t.RemoveQuad(cayley.Quad(name, fieldFlagValue, currentValue, ""))
}
t.AddQuad(cayley.Quad(name, "value", value, ""))
t.AddQuad(cayley.Quad(name, fieldFlagValue, value, ""))

// Apply transaction
if err = store.ApplyTransaction(t); err != nil {
Expand All @@ -54,5 +59,5 @@ func UpdateFlag(name, value string) error {

// GetFlagValue returns the value of the flag given by its name (or an empty string if the flag does not exist)
func GetFlagValue(name string) (string, error) {
return toValue(cayley.StartPath(store, "flag:"+name).Out("value"))
return toValue(cayley.StartPath(store, flagNodePrefix+":"+name).Out(fieldFlagValue))
}
55 changes: 28 additions & 27 deletions database/layer.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,17 @@ import (
)

const (
FieldLayerIsValue = "layer"
FieldLayerID = "id"
FieldLayerParent = "parent"
FieldLayerSuccessors = "successors"
FieldLayerOS = "os"
FieldLayerInstalledPackages = "adds"
FieldLayerRemovedPackages = "removes"
FieldLayerEngineVersion = "engineVersion"

FieldLayerPackages = "adds/removes"
FieldLayerID = "id"
FieldLayerParent = "parent"
FieldLayerSuccessors = "successors"
FieldLayerOS = "os"
FieldLayerEngineVersion = "engineVersion"
FieldLayerPackages = "adds/removes"

// These fields are not selectable and are for internal use only.
fieldLayerIsValue = "layer"
fieldLayerInstalledPackages = "adds"
fieldLayerRemovedPackages = "removes"
)

var FieldLayerAll = []string{FieldLayerID, FieldLayerParent, FieldLayerSuccessors, FieldLayerOS, FieldLayerPackages, FieldLayerEngineVersion}
Expand All @@ -54,7 +55,7 @@ type Layer struct {
// GetNode returns the node name of a Layer
// Requires the key field: ID
func (l *Layer) GetNode() string {
return FieldLayerIsValue + ":" + utils.Hash(l.ID)
return fieldLayerIsValue + ":" + utils.Hash(l.ID)
}

// InsertLayer insert a single layer in the database
Expand Down Expand Up @@ -97,28 +98,28 @@ func InsertLayer(layer *Layer) error {

if existingLayer == nil {
// Create case: add permanent nodes
t.AddQuad(cayley.Quad(layer.Node, FieldIs, FieldLayerIsValue, ""))
t.AddQuad(cayley.Quad(layer.Node, fieldIs, fieldLayerIsValue, ""))
t.AddQuad(cayley.Quad(layer.Node, FieldLayerID, layer.ID, ""))
t.AddQuad(cayley.Quad(layer.Node, FieldLayerParent, layer.ParentNode, ""))
} else {
// Update case: remove everything before we add updated data
t.RemoveQuad(cayley.Quad(layer.Node, FieldLayerOS, existingLayer.OS, ""))
for _, pkg := range existingLayer.InstalledPackagesNodes {
t.RemoveQuad(cayley.Quad(layer.Node, FieldLayerInstalledPackages, pkg, ""))
t.RemoveQuad(cayley.Quad(layer.Node, fieldLayerInstalledPackages, pkg, ""))
}
for _, pkg := range existingLayer.RemovedPackagesNodes {
t.RemoveQuad(cayley.Quad(layer.Node, FieldLayerRemovedPackages, pkg, ""))
t.RemoveQuad(cayley.Quad(layer.Node, fieldLayerRemovedPackages, pkg, ""))
}
t.RemoveQuad(cayley.Quad(layer.Node, FieldLayerEngineVersion, strconv.Itoa(existingLayer.EngineVersion), ""))
}

// Add OS/Packages
t.AddQuad(cayley.Quad(layer.Node, FieldLayerOS, layer.OS, ""))
for _, pkg := range layer.InstalledPackagesNodes {
t.AddQuad(cayley.Quad(layer.Node, FieldLayerInstalledPackages, pkg, ""))
t.AddQuad(cayley.Quad(layer.Node, fieldLayerInstalledPackages, pkg, ""))
}
for _, pkg := range layer.RemovedPackagesNodes {
t.AddQuad(cayley.Quad(layer.Node, FieldLayerRemovedPackages, pkg, ""))
t.AddQuad(cayley.Quad(layer.Node, fieldLayerRemovedPackages, pkg, ""))
}
t.AddQuad(cayley.Quad(layer.Node, FieldLayerEngineVersion, strconv.Itoa(layer.EngineVersion), ""))

Expand Down Expand Up @@ -162,16 +163,16 @@ func deleteLayerTreeFrom(node string, t *graph.Transaction) error {
}

// Remove layer.
t.RemoveQuad(cayley.Quad(layer.Node, FieldIs, FieldLayerIsValue, ""))
t.RemoveQuad(cayley.Quad(layer.Node, fieldIs, fieldLayerIsValue, ""))
t.RemoveQuad(cayley.Quad(layer.Node, FieldLayerID, layer.ID, ""))
t.RemoveQuad(cayley.Quad(layer.Node, FieldLayerParent, layer.ParentNode, ""))
t.RemoveQuad(cayley.Quad(layer.Node, FieldLayerOS, layer.OS, ""))
t.RemoveQuad(cayley.Quad(layer.Node, FieldLayerEngineVersion, strconv.Itoa(layer.EngineVersion), ""))
for _, pkg := range layer.InstalledPackagesNodes {
t.RemoveQuad(cayley.Quad(layer.Node, FieldLayerInstalledPackages, pkg, ""))
t.RemoveQuad(cayley.Quad(layer.Node, fieldLayerInstalledPackages, pkg, ""))
}
for _, pkg := range layer.RemovedPackagesNodes {
t.RemoveQuad(cayley.Quad(layer.Node, FieldLayerRemovedPackages, pkg, ""))
t.RemoveQuad(cayley.Quad(layer.Node, fieldLayerRemovedPackages, pkg, ""))
}

// Apply transaction if root call.
Expand Down Expand Up @@ -199,7 +200,7 @@ func FindOneLayerByID(ID string, selectedFields []string) (*Layer, error) {

// FindOneLayerByNode finds and returns a single package by its node, selecting the specified fields
func FindOneLayerByNode(node string, selectedFields []string) (*Layer, error) {
l, err := toLayers(cayley.StartPath(store, node).Has(FieldIs, FieldLayerIsValue), selectedFields)
l, err := toLayers(cayley.StartPath(store, node).Has(fieldIs, fieldLayerIsValue), selectedFields)
if err != nil {
return nil, err
}
Expand All @@ -217,7 +218,7 @@ func FindOneLayerByNode(node string, selectedFields []string) (*Layer, error) {
// FindAllLayersByAddedPackageNodes finds and returns all layers that add the
// given packages (by their nodes), selecting the specified fields
func FindAllLayersByAddedPackageNodes(nodes []string, selectedFields []string) ([]*Layer, error) {
layers, err := toLayers(cayley.StartPath(store, nodes...).In(FieldLayerInstalledPackages), selectedFields)
layers, err := toLayers(cayley.StartPath(store, nodes...).In(fieldLayerInstalledPackages), selectedFields)
if err != nil {
return []*Layer{}, err
}
Expand All @@ -234,7 +235,7 @@ func FindAllLayersByAddedPackageNodes(nodes []string, selectedFields []string) (
// }
//
// // Get all the layers which remove the package
// layersNodesRemoving, err := toValues(cayley.StartPath(store, node).In(FieldLayerRemovedPackages).Has(FieldIs, FieldLayerIsValue))
// layersNodesRemoving, err := toValues(cayley.StartPath(store, node).In(fieldLayerRemovedPackages).Has(fieldIs, fieldLayerIsValue))
// if err != nil {
// return []*Layer{}, err
// }
Expand All @@ -243,7 +244,7 @@ func FindAllLayersByAddedPackageNodes(nodes []string, selectedFields []string) (
// layersNodesRemovingMap[l] = struct{}{}
// }
//
// layersToBrowse, err := toLayers(cayley.StartPath(store, node).In(FieldLayerInstalledPackages).Has(FieldIs, FieldLayerIsValue), only)
// layersToBrowse, err := toLayers(cayley.StartPath(store, node).In(fieldLayerInstalledPackages).Has(fieldIs, fieldLayerIsValue), only)
// if err != nil {
// return []*Layer{}, err
// }
Expand All @@ -270,7 +271,7 @@ func FindAllLayersByAddedPackageNodes(nodes []string, selectedFields []string) (
func toLayers(path *path.Path, selectedFields []string) ([]*Layer, error) {
var layers []*Layer

saveFields(path, selectedFields, []string{FieldLayerSuccessors, FieldLayerPackages, FieldLayerInstalledPackages, FieldLayerRemovedPackages})
saveFields(path, selectedFields, []string{FieldLayerSuccessors, FieldLayerPackages, fieldLayerInstalledPackages, fieldLayerRemovedPackages})
it, _ := path.BuildIterator().Optimize()
defer it.Close()
for cayley.RawNext(it) {
Expand All @@ -295,16 +296,16 @@ func toLayers(path *path.Path, selectedFields []string) ([]*Layer, error) {
layer.OS = store.NameOf(tags[FieldLayerOS])
case FieldLayerPackages:
var err error
it, _ := cayley.StartPath(store, layer.Node).OutWithTags([]string{"predicate"}, FieldLayerInstalledPackages, FieldLayerRemovedPackages).BuildIterator().Optimize()
it, _ := cayley.StartPath(store, layer.Node).OutWithTags([]string{"predicate"}, fieldLayerInstalledPackages, fieldLayerRemovedPackages).BuildIterator().Optimize()
defer it.Close()
for cayley.RawNext(it) {
tags := make(map[string]graph.Value)
it.TagResults(tags)

predicate := store.NameOf(tags["predicate"])
if predicate == FieldLayerInstalledPackages {
if predicate == fieldLayerInstalledPackages {
layer.InstalledPackagesNodes = append(layer.InstalledPackagesNodes, store.NameOf(it.Result()))
} else if predicate == FieldLayerRemovedPackages {
} else if predicate == fieldLayerRemovedPackages {
layer.RemovedPackagesNodes = append(layer.RemovedPackagesNodes, store.NameOf(it.Result()))
}
}
Expand Down
45 changes: 26 additions & 19 deletions database/lock.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ import (
"github.com/google/cayley/graph/path"
)

const (
fieldLockLocked = "locked"
fieldLockLockedValue = "locked"
fieldLockLockedBy = "locked_by"
fieldLockLockedUntil = "locked_until"
)

// Lock tries to set a temporary lock in the database.
// If a lock already exists with the given name/owner, then the lock is renewed
//
Expand All @@ -37,25 +44,25 @@ func Lock(name string, duration time.Duration, owner string) (bool, time.Time) {
untilString := strconv.FormatInt(until.Unix(), 10)

// Try to get the expiration time of a lock with the same name/owner
currentExpiration, err := toValue(cayley.StartPath(store, name).Has("locked_by", owner).Out("locked_until"))
currentExpiration, err := toValue(cayley.StartPath(store, name).Has(fieldLockLockedBy, owner).Out(fieldLockLockedUntil))
if err == nil && currentExpiration != "" {
// Renew our lock
if currentExpiration == untilString {
return true, until
}

t := cayley.NewTransaction()
t.RemoveQuad(cayley.Quad(name, "locked_until", currentExpiration, ""))
t.AddQuad(cayley.Quad(name, "locked_until", untilString, ""))
t.RemoveQuad(cayley.Quad(name, fieldLockLockedUntil, currentExpiration, ""))
t.AddQuad(cayley.Quad(name, fieldLockLockedUntil, untilString, ""))
// It is not necessary to verify if the lock is ours again in the transaction
// because if someone took it, the lock's current expiration probably changed and the transaction will fail
return store.ApplyTransaction(t) == nil, until
}

t := cayley.NewTransaction()
t.AddQuad(cayley.Quad(name, "locked", "locked", "")) // Necessary to make the transaction fails if the lock already exists (and has not been pruned)
t.AddQuad(cayley.Quad(name, "locked_until", untilString, ""))
t.AddQuad(cayley.Quad(name, "locked_by", owner, ""))
t.AddQuad(cayley.Quad(name, fieldLockLocked, fieldLockLockedValue, "")) // Necessary to make the transaction fails if the lock already exists (and has not been pruned)
t.AddQuad(cayley.Quad(name, fieldLockLockedUntil, untilString, ""))
t.AddQuad(cayley.Quad(name, fieldLockLockedBy, owner, ""))

glog.SetStderrThreshold("FATAL")
success := store.ApplyTransaction(t) == nil
Expand All @@ -67,16 +74,16 @@ func Lock(name string, duration time.Duration, owner string) (bool, time.Time) {
// Unlock unlocks a lock specified by its name if I own it
func Unlock(name, owner string) {
unlocked := 0
it, _ := cayley.StartPath(store, name).Has("locked", "locked").Has("locked_by", owner).Save("locked_until", "locked_until").BuildIterator().Optimize()
it, _ := cayley.StartPath(store, name).Has(fieldLockLocked, fieldLockLockedValue).Has(fieldLockLockedBy, owner).Save(fieldLockLockedUntil, fieldLockLockedUntil).BuildIterator().Optimize()
defer it.Close()
for cayley.RawNext(it) {
tags := make(map[string]graph.Value)
it.TagResults(tags)

t := cayley.NewTransaction()
t.RemoveQuad(cayley.Quad(name, "locked", "locked", ""))
t.RemoveQuad(cayley.Quad(name, "locked_until", store.NameOf(tags["locked_until"]), ""))
t.RemoveQuad(cayley.Quad(name, "locked_by", owner, ""))
t.RemoveQuad(cayley.Quad(name, fieldLockLocked, fieldLockLockedValue, ""))
t.RemoveQuad(cayley.Quad(name, fieldLockLockedUntil, store.NameOf(tags[fieldLockLockedUntil]), ""))
t.RemoveQuad(cayley.Quad(name, fieldLockLockedBy, owner, ""))
err := store.ApplyTransaction(t)
if err != nil {
log.Errorf("failed transaction (Unlock): %s", err)
Expand All @@ -97,14 +104,14 @@ func Unlock(name, owner string) {
// LockInfo returns the owner of a lock specified by its name and its
// expiration time
func LockInfo(name string) (string, time.Time, error) {
it, _ := cayley.StartPath(store, name).Has("locked", "locked").Save("locked_until", "locked_until").Save("locked_by", "locked_by").BuildIterator().Optimize()
it, _ := cayley.StartPath(store, name).Has(fieldLockLocked, fieldLockLockedValue).Save(fieldLockLockedUntil, fieldLockLockedUntil).Save(fieldLockLockedBy, fieldLockLockedBy).BuildIterator().Optimize()
defer it.Close()
for cayley.RawNext(it) {
tags := make(map[string]graph.Value)
it.TagResults(tags)

tt, _ := strconv.ParseInt(store.NameOf(tags["locked_until"]), 10, 64)
return store.NameOf(tags["locked_by"]), time.Unix(tt, 0), nil
tt, _ := strconv.ParseInt(store.NameOf(tags[fieldLockLockedUntil]), 10, 64)
return store.NameOf(tags[fieldLockLockedBy]), time.Unix(tt, 0), nil
}
if it.Err() != nil {
log.Errorf("failed query in LockInfo: %s", it.Err())
Expand All @@ -119,24 +126,24 @@ func pruneLocks() {
now := time.Now()

// Delete every expired locks
it, _ := cayley.StartPath(store, "locked").In("locked").Save("locked_until", "locked_until").Save("locked_by", "locked_by").BuildIterator().Optimize()
it, _ := cayley.StartPath(store, "locked").In("locked").Save(fieldLockLockedUntil, fieldLockLockedUntil).Save(fieldLockLockedBy, fieldLockLockedBy).BuildIterator().Optimize()
defer it.Close()
for cayley.RawNext(it) {
tags := make(map[string]graph.Value)
it.TagResults(tags)

n := store.NameOf(it.Result())
t := store.NameOf(tags["locked_until"])
o := store.NameOf(tags["locked_by"])
t := store.NameOf(tags[fieldLockLockedUntil])
o := store.NameOf(tags[fieldLockLockedBy])
tt, _ := strconv.ParseInt(t, 10, 64)

if now.Unix() > tt {
log.Debugf("lock %s owned by %s has expired.", n, o)

tr := cayley.NewTransaction()
tr.RemoveQuad(cayley.Quad(n, "locked", "locked", ""))
tr.RemoveQuad(cayley.Quad(n, "locked_until", t, ""))
tr.RemoveQuad(cayley.Quad(n, "locked_by", o, ""))
tr.RemoveQuad(cayley.Quad(n, fieldLockLocked, fieldLockLockedValue, ""))
tr.RemoveQuad(cayley.Quad(n, fieldLockLockedUntil, t, ""))
tr.RemoveQuad(cayley.Quad(n, fieldLockLockedBy, o, ""))
err := store.ApplyTransaction(tr)
if err != nil {
log.Errorf("failed transaction (pruneLocks): %s", err)
Expand Down

0 comments on commit 9fc29e2

Please sign in to comment.