Update go-memdb and go-lru dependencies

This commit is contained in:
Daniel Nephin 2020-06-02 14:18:19 -04:00
parent ffc948383b
commit cabd6b6105
9 changed files with 296 additions and 36 deletions

View File

@ -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
```

34
vendor/github.com/hashicorp/go-memdb/changes.go generated vendored Normal file
View File

@ -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
}

View File

@ -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()

View File

@ -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...)

View File

@ -1 +1,3 @@
module github.com/hashicorp/golang-lru
go 1.12

View File

@ -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.

View File

@ -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()

View File

@ -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
}

4
vendor/modules.txt vendored
View File

@ -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