Update go-memdb and go-lru dependencies
This commit is contained in:
parent
ffc948383b
commit
cabd6b6105
|
@ -37,45 +37,57 @@ The full documentation is available on [Godoc](http://godoc.org/github.com/hashi
|
|||
Example
|
||||
=======
|
||||
|
||||
Below is a simple example of usage
|
||||
Below is a [simple example](https://play.golang.org/p/gCGE9FA4og1) of usage
|
||||
|
||||
```go
|
||||
// Create a sample struct
|
||||
type Person struct {
|
||||
Email string
|
||||
Name string
|
||||
Age int
|
||||
Email string
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
|
||||
// Create the DB schema
|
||||
schema := &memdb.DBSchema{
|
||||
Tables: map[string]*memdb.TableSchema{
|
||||
"person": &memdb.TableSchema{
|
||||
Name: "person",
|
||||
Indexes: map[string]*memdb.IndexSchema{
|
||||
"id": &memdb.IndexSchema{
|
||||
Name: "id",
|
||||
Unique: true,
|
||||
Indexer: &memdb.StringFieldIndex{Field: "Email"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Tables: map[string]*memdb.TableSchema{
|
||||
"person": &memdb.TableSchema{
|
||||
Name: "person",
|
||||
Indexes: map[string]*memdb.IndexSchema{
|
||||
"id": &memdb.IndexSchema{
|
||||
Name: "id",
|
||||
Unique: true,
|
||||
Indexer: &memdb.StringFieldIndex{Field: "Email"},
|
||||
},
|
||||
"age": &memdb.IndexSchema{
|
||||
Name: "age",
|
||||
Unique: false,
|
||||
Indexer: &memdb.IntFieldIndex{Field: "Age"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Create a new data base
|
||||
db, err := memdb.NewMemDB(schema)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Create a write transaction
|
||||
txn := db.Txn(true)
|
||||
|
||||
// Insert a new person
|
||||
p := &Person{"joe@aol.com", "Joe", 30}
|
||||
if err := txn.Insert("person", p); err != nil {
|
||||
panic(err)
|
||||
// Insert some people
|
||||
people := []*Person{
|
||||
&Person{"joe@aol.com", "Joe", 30},
|
||||
&Person{"lucy@aol.com", "Lucy", 35},
|
||||
&Person{"tariq@aol.com", "Tariq", 21},
|
||||
&Person{"dorothy@aol.com", "Dorothy", 53},
|
||||
}
|
||||
for _, p := range people {
|
||||
if err := txn.Insert("person", p); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Commit the transaction
|
||||
|
@ -88,11 +100,47 @@ defer txn.Abort()
|
|||
// Lookup by email
|
||||
raw, err := txn.First("person", "id", "joe@aol.com")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Say hi!
|
||||
fmt.Printf("Hello %s!", raw.(*Person).Name)
|
||||
fmt.Printf("Hello %s!\n", raw.(*Person).Name)
|
||||
|
||||
// List all the people
|
||||
it, err := txn.Get("person", "id")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println("All the people:")
|
||||
for obj := it.Next(); obj != nil; obj = it.Next() {
|
||||
p := obj.(*Person)
|
||||
fmt.Printf(" %s\n", p.Name)
|
||||
}
|
||||
|
||||
// Range scan over people with ages between 25 and 35 inclusive
|
||||
it, err = txn.LowerBound("person", "age", 25)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println("People aged 25 - 35:")
|
||||
for obj := it.Next(); obj != nil; obj = it.Next() {
|
||||
p := obj.(*Person)
|
||||
if p.Age > 35 {
|
||||
break
|
||||
}
|
||||
fmt.Printf(" %s is aged %d\n", p.Name, p.Age)
|
||||
}
|
||||
// Output:
|
||||
// Hello Joe!
|
||||
// All the people:
|
||||
// Dorothy
|
||||
// Joe
|
||||
// Lucy
|
||||
// Tariq
|
||||
// People aged 25 - 35:
|
||||
// Joe is aged 30
|
||||
// Lucy is aged 35
|
||||
```
|
||||
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
package memdb
|
||||
|
||||
// Changes describes a set of mutations to memDB tables performed during a
|
||||
// transaction.
|
||||
type Changes []Change
|
||||
|
||||
// Change describes a mutation to an object in a table.
|
||||
type Change struct {
|
||||
Table string
|
||||
Before interface{}
|
||||
After interface{}
|
||||
|
||||
// primaryKey stores the raw key value from the primary index so that we can
|
||||
// de-duplicate multiple updates of the same object in the same transaction
|
||||
// but we don't expose this implementation detail to the consumer.
|
||||
primaryKey []byte
|
||||
}
|
||||
|
||||
// Created returns true if the mutation describes a new object being inserted.
|
||||
func (m *Change) Created() bool {
|
||||
return m.Before == nil && m.After != nil
|
||||
}
|
||||
|
||||
// Updated returns true if the mutation describes an existing object being
|
||||
// updated.
|
||||
func (m *Change) Updated() bool {
|
||||
return m.Before != nil && m.After != nil
|
||||
}
|
||||
|
||||
// Deleted returns true if the mutation describes an existing object being
|
||||
// deleted.
|
||||
func (m *Change) Deleted() bool {
|
||||
return m.Before != nil && m.After == nil
|
||||
}
|
|
@ -73,7 +73,7 @@ func (s *StringFieldIndex) FromObject(obj interface{}) (bool, []byte, error) {
|
|||
|
||||
if isPtr && !fv.IsValid() {
|
||||
val := ""
|
||||
return true, []byte(val), nil
|
||||
return false, []byte(val), nil
|
||||
}
|
||||
|
||||
val := fv.String()
|
||||
|
|
|
@ -33,9 +33,25 @@ type Txn struct {
|
|||
rootTxn *iradix.Txn
|
||||
after []func()
|
||||
|
||||
// changes is used to track the changes performed during the transaction. If
|
||||
// it is nil at transaction start then changes are not tracked.
|
||||
changes Changes
|
||||
|
||||
modified map[tableIndex]*iradix.Txn
|
||||
}
|
||||
|
||||
// TrackChanges enables change tracking for the transaction. If called at any
|
||||
// point before commit, subsequent mutations will be recorded and can be
|
||||
// retrieved using ChangeSet. Once this has been called on a transaction it
|
||||
// can't be unset. As with other Txn methods it's not safe to call this from a
|
||||
// different goroutine than the one making mutations or committing the
|
||||
// transaction.
|
||||
func (txn *Txn) TrackChanges() {
|
||||
if txn.changes == nil {
|
||||
txn.changes = make(Changes, 0, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// readableIndex returns a transaction usable for reading the given
|
||||
// index in a table. If a write transaction is in progress, we may need
|
||||
// to use an existing modified txn.
|
||||
|
@ -101,6 +117,7 @@ func (txn *Txn) Abort() {
|
|||
// Clear the txn
|
||||
txn.rootTxn = nil
|
||||
txn.modified = nil
|
||||
txn.changes = nil
|
||||
|
||||
// Release the writer lock since this is invalid
|
||||
txn.db.writer.Unlock()
|
||||
|
@ -265,6 +282,14 @@ func (txn *Txn) Insert(table string, obj interface{}) error {
|
|||
indexTxn.Insert(val, obj)
|
||||
}
|
||||
}
|
||||
if txn.changes != nil {
|
||||
txn.changes = append(txn.changes, Change{
|
||||
Table: table,
|
||||
Before: existing, // might be nil on a create
|
||||
After: obj,
|
||||
primaryKey: idVal,
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -332,6 +357,14 @@ func (txn *Txn) Delete(table string, obj interface{}) error {
|
|||
}
|
||||
}
|
||||
}
|
||||
if txn.changes != nil {
|
||||
txn.changes = append(txn.changes, Change{
|
||||
Table: table,
|
||||
Before: existing,
|
||||
After: nil, // Now nil indicates deletion
|
||||
primaryKey: idVal,
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -376,6 +409,19 @@ func (txn *Txn) DeletePrefix(table string, prefix_index string, prefix string) (
|
|||
if !ok {
|
||||
return false, fmt.Errorf("object missing primary index")
|
||||
}
|
||||
if txn.changes != nil {
|
||||
// Record the deletion
|
||||
idTxn := txn.writableIndex(table, id)
|
||||
existing, ok := idTxn.Get(idVal)
|
||||
if ok {
|
||||
txn.changes = append(txn.changes, Change{
|
||||
Table: table,
|
||||
Before: existing,
|
||||
After: nil, // Now nil indicates deletion
|
||||
primaryKey: idVal,
|
||||
})
|
||||
}
|
||||
}
|
||||
// Remove the object from all the indexes except the given prefix index
|
||||
for name, indexSchema := range tableSchema.Indexes {
|
||||
if name == deletePrefixIndex {
|
||||
|
@ -413,6 +459,7 @@ func (txn *Txn) DeletePrefix(table string, prefix_index string, prefix string) (
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if foundAny {
|
||||
indexTxn := txn.writableIndex(table, deletePrefixIndex)
|
||||
|
@ -629,6 +676,82 @@ func (txn *Txn) LowerBound(table, index string, args ...interface{}) (ResultIter
|
|||
return iter, nil
|
||||
}
|
||||
|
||||
// objectID is a tuple of table name and the raw internal id byte slice
|
||||
// converted to a string. It's only converted to a string to make it comparable
|
||||
// so this struct can be used as a map index.
|
||||
type objectID struct {
|
||||
Table string
|
||||
IndexVal string
|
||||
}
|
||||
|
||||
// mutInfo stores metadata about mutations to allow collapsing multiple
|
||||
// mutations to the same object into one.
|
||||
type mutInfo struct {
|
||||
firstBefore interface{}
|
||||
lastIdx int
|
||||
}
|
||||
|
||||
// Changes returns the set of object changes that have been made in the
|
||||
// transaction so far. If change tracking is not enabled it wil always return
|
||||
// nil. It can be called before or after Commit. If it is before Commit it will
|
||||
// return all changes made so far which may not be the same as the final
|
||||
// Changes. After abort it will always return nil. As with other Txn methods
|
||||
// it's not safe to call this from a different goroutine than the one making
|
||||
// mutations or committing the transaction. Mutations will appear in the order
|
||||
// they were performed in the transaction but multiple operations to the same
|
||||
// object will be collapsed so only the effective overall change to that object
|
||||
// is present. If transaction operations are dependent (e.g. copy object X to Y
|
||||
// then delete X) this might mean the set of mutations is incomplete to verify
|
||||
// history, but it is complete in that the net effect is preserved (Y got a new
|
||||
// value, X got removed).
|
||||
func (txn *Txn) Changes() Changes {
|
||||
if txn.changes == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// De-duplicate mutations by key so all take effect at the point of the last
|
||||
// write but we keep the mutations in order.
|
||||
dups := make(map[objectID]mutInfo)
|
||||
for i, m := range txn.changes {
|
||||
oid := objectID{
|
||||
Table: m.Table,
|
||||
IndexVal: string(m.primaryKey),
|
||||
}
|
||||
// Store the latest mutation index for each key value
|
||||
mi, ok := dups[oid]
|
||||
if !ok {
|
||||
// First entry for key, store the before value
|
||||
mi.firstBefore = m.Before
|
||||
}
|
||||
mi.lastIdx = i
|
||||
dups[oid] = mi
|
||||
}
|
||||
if len(dups) == len(txn.changes) {
|
||||
// No duplicates found, fast path return it as is
|
||||
return txn.changes
|
||||
}
|
||||
|
||||
// Need to remove the duplicates
|
||||
cs := make(Changes, 0, len(dups))
|
||||
for i, m := range txn.changes {
|
||||
oid := objectID{
|
||||
Table: m.Table,
|
||||
IndexVal: string(m.primaryKey),
|
||||
}
|
||||
mi := dups[oid]
|
||||
if mi.lastIdx == i {
|
||||
// This was the latest value for this key copy it with the before value in
|
||||
// case it's different. Note that m is not a pointer so we are not
|
||||
// modifying the txn.changeSet here - it's already a copy.
|
||||
m.Before = mi.firstBefore
|
||||
cs = append(cs, m)
|
||||
}
|
||||
}
|
||||
// Store the de-duped version in case this is called again
|
||||
txn.changes = cs
|
||||
return cs
|
||||
}
|
||||
|
||||
func (txn *Txn) getIndexIterator(table, index string, args ...interface{}) (*iradix.Iterator, []byte, error) {
|
||||
// Get the index value to scan
|
||||
indexSchema, val, err := txn.getIndexValue(table, index, args...)
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
module github.com/hashicorp/golang-lru
|
||||
|
||||
go 1.12
|
||||
|
|
|
@ -37,7 +37,7 @@ func (c *Cache) Purge() {
|
|||
c.lock.Unlock()
|
||||
}
|
||||
|
||||
// Add adds a value to the cache. Returns true if an eviction occurred.
|
||||
// Add adds a value to the cache. Returns true if an eviction occurred.
|
||||
func (c *Cache) Add(key, value interface{}) (evicted bool) {
|
||||
c.lock.Lock()
|
||||
evicted = c.lru.Add(key, value)
|
||||
|
@ -71,8 +71,8 @@ func (c *Cache) Peek(key interface{}) (value interface{}, ok bool) {
|
|||
return value, ok
|
||||
}
|
||||
|
||||
// ContainsOrAdd checks if a key is in the cache without updating the
|
||||
// recent-ness or deleting it for being stale, and if not, adds the value.
|
||||
// ContainsOrAdd checks if a key is in the cache without updating the
|
||||
// recent-ness or deleting it for being stale, and if not, adds the value.
|
||||
// Returns whether found and whether an eviction occurred.
|
||||
func (c *Cache) ContainsOrAdd(key, value interface{}) (ok, evicted bool) {
|
||||
c.lock.Lock()
|
||||
|
@ -85,18 +85,52 @@ func (c *Cache) ContainsOrAdd(key, value interface{}) (ok, evicted bool) {
|
|||
return false, evicted
|
||||
}
|
||||
|
||||
// Remove removes the provided key from the cache.
|
||||
func (c *Cache) Remove(key interface{}) {
|
||||
// PeekOrAdd checks if a key is in the cache without updating the
|
||||
// recent-ness or deleting it for being stale, and if not, adds the value.
|
||||
// Returns whether found and whether an eviction occurred.
|
||||
func (c *Cache) PeekOrAdd(key, value interface{}) (previous interface{}, ok, evicted bool) {
|
||||
c.lock.Lock()
|
||||
c.lru.Remove(key)
|
||||
defer c.lock.Unlock()
|
||||
|
||||
previous, ok = c.lru.Peek(key)
|
||||
if ok {
|
||||
return previous, true, false
|
||||
}
|
||||
|
||||
evicted = c.lru.Add(key, value)
|
||||
return nil, false, evicted
|
||||
}
|
||||
|
||||
// Remove removes the provided key from the cache.
|
||||
func (c *Cache) Remove(key interface{}) (present bool) {
|
||||
c.lock.Lock()
|
||||
present = c.lru.Remove(key)
|
||||
c.lock.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Resize changes the cache size.
|
||||
func (c *Cache) Resize(size int) (evicted int) {
|
||||
c.lock.Lock()
|
||||
evicted = c.lru.Resize(size)
|
||||
c.lock.Unlock()
|
||||
return evicted
|
||||
}
|
||||
|
||||
// RemoveOldest removes the oldest item from the cache.
|
||||
func (c *Cache) RemoveOldest() {
|
||||
func (c *Cache) RemoveOldest() (key interface{}, value interface{}, ok bool) {
|
||||
c.lock.Lock()
|
||||
c.lru.RemoveOldest()
|
||||
key, value, ok = c.lru.RemoveOldest()
|
||||
c.lock.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// GetOldest returns the oldest entry
|
||||
func (c *Cache) GetOldest() (key interface{}, value interface{}, ok bool) {
|
||||
c.lock.Lock()
|
||||
key, value, ok = c.lru.GetOldest()
|
||||
c.lock.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Keys returns a slice of the keys in the cache, from oldest to newest.
|
||||
|
|
|
@ -73,6 +73,9 @@ func (c *LRU) Add(key, value interface{}) (evicted bool) {
|
|||
func (c *LRU) Get(key interface{}) (value interface{}, ok bool) {
|
||||
if ent, ok := c.items[key]; ok {
|
||||
c.evictList.MoveToFront(ent)
|
||||
if ent.Value.(*entry) == nil {
|
||||
return nil, false
|
||||
}
|
||||
return ent.Value.(*entry).value, true
|
||||
}
|
||||
return
|
||||
|
@ -142,6 +145,19 @@ func (c *LRU) Len() int {
|
|||
return c.evictList.Len()
|
||||
}
|
||||
|
||||
// Resize changes the cache size.
|
||||
func (c *LRU) Resize(size int) (evicted int) {
|
||||
diff := c.Len() - size
|
||||
if diff < 0 {
|
||||
diff = 0
|
||||
}
|
||||
for i := 0; i < diff; i++ {
|
||||
c.removeOldest()
|
||||
}
|
||||
c.size = size
|
||||
return diff
|
||||
}
|
||||
|
||||
// removeOldest removes the oldest item from the cache.
|
||||
func (c *LRU) removeOldest() {
|
||||
ent := c.evictList.Back()
|
||||
|
|
|
@ -10,7 +10,7 @@ type LRUCache interface {
|
|||
// updates the "recently used"-ness of the key. #value, isFound
|
||||
Get(key interface{}) (value interface{}, ok bool)
|
||||
|
||||
// Check if a key exsists in cache without updating the recent-ness.
|
||||
// Checks if a key exists in cache without updating the recent-ness.
|
||||
Contains(key interface{}) (ok bool)
|
||||
|
||||
// Returns key's value without updating the "recently used"-ness of the key.
|
||||
|
@ -31,6 +31,9 @@ type LRUCache interface {
|
|||
// Returns the number of items in the cache.
|
||||
Len() int
|
||||
|
||||
// Clear all cache entries
|
||||
// Clears all cache entries.
|
||||
Purge()
|
||||
|
||||
// Resizes cache, returning number evicted
|
||||
Resize(int) int
|
||||
}
|
||||
|
|
|
@ -208,7 +208,7 @@ github.com/hashicorp/go-discover/provider/vsphere
|
|||
github.com/hashicorp/go-hclog
|
||||
# github.com/hashicorp/go-immutable-radix v1.1.0
|
||||
github.com/hashicorp/go-immutable-radix
|
||||
# github.com/hashicorp/go-memdb v1.0.3
|
||||
# github.com/hashicorp/go-memdb v1.1.0
|
||||
github.com/hashicorp/go-memdb
|
||||
# github.com/hashicorp/go-msgpack v0.5.5
|
||||
github.com/hashicorp/go-msgpack/codec
|
||||
|
@ -230,7 +230,7 @@ github.com/hashicorp/go-syslog
|
|||
github.com/hashicorp/go-uuid
|
||||
# github.com/hashicorp/go-version v1.2.0
|
||||
github.com/hashicorp/go-version
|
||||
# github.com/hashicorp/golang-lru v0.5.1
|
||||
# github.com/hashicorp/golang-lru v0.5.4
|
||||
github.com/hashicorp/golang-lru
|
||||
github.com/hashicorp/golang-lru/simplelru
|
||||
# github.com/hashicorp/hcl v1.0.0
|
||||
|
|
Loading…
Reference in New Issue