2022-07-26 12:02:32 +00:00
|
|
|
package mdb
|
|
|
|
|
|
|
|
import (
|
|
|
|
"math/rand"
|
|
|
|
|
|
|
|
"sync"
|
|
|
|
"sync/atomic"
|
|
|
|
|
|
|
|
"git.crumpington.com/private/mdb/keyedmutex"
|
|
|
|
"git.crumpington.com/private/mdb/kvstore"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Implements ItemMap and ItemUniqueIndex interfaces.
|
|
|
|
type itemMap[T any] struct {
|
|
|
|
primary bool
|
|
|
|
kv *kvstore.KV
|
|
|
|
collection string
|
|
|
|
idLock keyedmutex.KeyedMutex[uint64]
|
|
|
|
mapLock sync.Mutex
|
|
|
|
m map[uint64]*T
|
|
|
|
getID func(*T) uint64
|
|
|
|
maxID uint64
|
|
|
|
}
|
|
|
|
|
|
|
|
func newItemMap[T any](kv *kvstore.KV, collection string, getID func(*T) uint64) *itemMap[T] {
|
|
|
|
m := &itemMap[T]{
|
|
|
|
primary: kv.Primary(),
|
|
|
|
kv: kv,
|
|
|
|
collection: collection,
|
|
|
|
idLock: keyedmutex.New[uint64](),
|
|
|
|
m: map[uint64]*T{},
|
|
|
|
getID: getID,
|
|
|
|
}
|
|
|
|
|
|
|
|
kv.Iterate(collection, func(id uint64, data []byte) {
|
|
|
|
item := decode[T](data)
|
|
|
|
if id > m.maxID {
|
|
|
|
m.maxID = id
|
|
|
|
}
|
|
|
|
m.m[id] = item
|
|
|
|
})
|
|
|
|
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *itemMap[T]) load(src map[uint64]*T) error {
|
|
|
|
// No-op: The itemmap is the source for loading all other indices.
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
func (m *itemMap[T]) Get(id uint64) (*T, bool) {
|
|
|
|
return m.mapGet(id)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Should hold item lock when calling.
|
|
|
|
func (idx *itemMap[T]) insert(item *T) {
|
|
|
|
id := idx.getID(item)
|
|
|
|
if idx.primary {
|
|
|
|
idx.kv.Store(idx.collection, id, encode(item))
|
|
|
|
}
|
|
|
|
idx.mapSet(id, item)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Should hold item lock when calling. old and new MUST have the same ID.
|
|
|
|
func (idx *itemMap[T]) update(old, new *T) {
|
|
|
|
idx.insert(new)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Should hold item lock when calling.
|
|
|
|
func (idx *itemMap[T]) delete(item *T) {
|
|
|
|
id := idx.getID(item)
|
|
|
|
if idx.primary {
|
|
|
|
idx.kv.Delete(idx.collection, id)
|
|
|
|
}
|
|
|
|
idx.mapDelete(id)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
func (idx *itemMap[T]) mapSet(id uint64, item *T) {
|
|
|
|
idx.mapLock.Lock()
|
|
|
|
idx.m[id] = item
|
|
|
|
idx.mapLock.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (idx *itemMap[T]) mapDelete(id uint64) {
|
|
|
|
idx.mapLock.Lock()
|
|
|
|
delete(idx.m, id)
|
|
|
|
idx.mapLock.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (idx *itemMap[T]) mapGet(id uint64) (*T, bool) {
|
|
|
|
idx.mapLock.Lock()
|
|
|
|
item, ok := idx.m[id]
|
|
|
|
idx.mapLock.Unlock()
|
|
|
|
return item, ok
|
|
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
func (idx *itemMap[T]) nextID() uint64 {
|
2022-07-26 21:55:29 +00:00
|
|
|
n := 1 + rand.Int63n(256)
|
2022-07-26 12:02:32 +00:00
|
|
|
return atomic.AddUint64(&idx.maxID, uint64(n))
|
|
|
|
}
|