| Index: impl/memory/gkvlite_utils.go
|
| diff --git a/impl/memory/gkvlite_utils.go b/impl/memory/gkvlite_utils.go
|
| index ed7aa5cd5b0f0cacdfa650769fe26f8e897092d4..adbc45df0d69c3de9ad0c22d3c8b0b9930e1ed3d 100644
|
| --- a/impl/memory/gkvlite_utils.go
|
| +++ b/impl/memory/gkvlite_utils.go
|
| @@ -9,13 +9,21 @@ import (
|
| "runtime"
|
| "sync"
|
|
|
| + "github.com/luci/gae/service/datastore"
|
| "github.com/luci/gkvlite"
|
| )
|
|
|
| -func gkvCollide(o, n *memCollection, f func(k, ov, nv []byte)) {
|
| +func gkvCollide(o, n memCollection, f func(k, ov, nv []byte)) {
|
| + if o != nil && !o.IsReadOnly() {
|
| + panic("old collection is r/w")
|
| + }
|
| + if n != nil && !n.IsReadOnly() {
|
| + panic("new collection is r/w")
|
| + }
|
| +
|
| // TODO(riannucci): reimplement in terms of *iterator.
|
| oldItems, newItems := make(chan *gkvlite.Item), make(chan *gkvlite.Item)
|
| - walker := func(c *memCollection, ch chan<- *gkvlite.Item, wg *sync.WaitGroup) {
|
| + walker := func(c memCollection, ch chan<- *gkvlite.Item, wg *sync.WaitGroup) {
|
| defer close(ch)
|
| defer wg.Done()
|
| if c != nil {
|
| @@ -66,78 +74,128 @@ func gkvCollide(o, n *memCollection, f func(k, ov, nv []byte)) {
|
| // This is reasonable for in-memory Store objects, since the only errors that
|
| // should occur happen with file IO on the underlying file (which of course
|
| // doesn't exist).
|
| -type memStore gkvlite.Store
|
| +type memStore interface {
|
| + datastore.TestingSnapshot
|
|
|
| -func (*memStore) ImATestingSnapshot() {}
|
| + GetCollection(name string) memCollection
|
| + GetCollectionNames() []string
|
| + GetOrCreateCollection(name string) memCollection
|
| + Snapshot() memStore
|
|
|
| -func newMemStore() *memStore {
|
| - ret, err := gkvlite.NewStore(nil)
|
| - memoryCorruption(err)
|
| - return (*memStore)(ret)
|
| + IsReadOnly() bool
|
| +}
|
| +
|
| +// memCollection is a gkvlite.Collection which will panic for anything which
|
| +// might otherwise return an error.
|
| +//
|
| +// This is reasonable for in-memory Store objects, since the only errors that
|
| +// should occur happen with file IO on the underlying file (which of course
|
| +// doesn't exist.
|
| +type memCollection interface {
|
| + Name() string
|
| + Delete(k []byte) bool
|
| + Get(k []byte) []byte
|
| + GetTotals() (numItems, numBytes uint64)
|
| + MinItem(withValue bool) *gkvlite.Item
|
| + Set(k, v []byte)
|
| + VisitItemsAscend(target []byte, withValue bool, visitor gkvlite.ItemVisitor)
|
| +
|
| + IsReadOnly() bool
|
| }
|
|
|
| -func (ms *memStore) Snapshot() *memStore {
|
| - ret := (*memStore)((*gkvlite.Store)(ms).Snapshot())
|
| - runtime.SetFinalizer((*gkvlite.Store)(ret), func(s *gkvlite.Store) {
|
| - go s.Close()
|
| - })
|
| +type memStoreImpl struct {
|
| + s *gkvlite.Store
|
| + ro bool
|
| +}
|
| +
|
| +var _ memStore = (*memStoreImpl)(nil)
|
| +
|
| +func (*memStoreImpl) ImATestingSnapshot() {}
|
| +
|
| +func (ms *memStoreImpl) IsReadOnly() bool { return ms.ro }
|
| +
|
| +func newMemStore() memStore {
|
| + store, err := gkvlite.NewStore(nil)
|
| + memoryCorruption(err)
|
| + ret := memStore(&memStoreImpl{store, false})
|
| + if *logMemCollectionFolder != "" {
|
| + ret = wrapTracingMemStore(ret)
|
| + }
|
| return ret
|
| }
|
|
|
| -func (ms *memStore) MakePrivateCollection(cmp gkvlite.KeyCompare) *memCollection {
|
| - return (*memCollection)((*gkvlite.Store)(ms).MakePrivateCollection(cmp))
|
| +func (ms *memStoreImpl) Snapshot() memStore {
|
| + if ms.ro {
|
| + return ms
|
| + }
|
| + ret := ms.s.Snapshot()
|
| + runtime.SetFinalizer(ret, func(s *gkvlite.Store) { go s.Close() })
|
| + return &memStoreImpl{ret, true}
|
| }
|
|
|
| -func (ms *memStore) GetCollection(name string) *memCollection {
|
| - return (*memCollection)((*gkvlite.Store)(ms).GetCollection(name))
|
| +func (ms *memStoreImpl) GetCollection(name string) memCollection {
|
| + coll := ms.s.GetCollection(name)
|
| + if coll == nil {
|
| + return nil
|
| + }
|
| + return &memCollectionImpl{coll, ms.ro}
|
| }
|
|
|
| -func (ms *memStore) SetCollection(name string, cmp gkvlite.KeyCompare) *memCollection {
|
| - return (*memCollection)((*gkvlite.Store)(ms).SetCollection(name, cmp))
|
| +func (ms *memStoreImpl) GetOrCreateCollection(name string) memCollection {
|
| + coll := ms.GetCollection(name)
|
| + if coll == nil {
|
| + coll = &memCollectionImpl{(ms.s.SetCollection(name, nil)), ms.ro}
|
| + }
|
| + return coll
|
| }
|
|
|
| -func (ms *memStore) GetCollectionNames() []string {
|
| - return (*gkvlite.Store)(ms).GetCollectionNames()
|
| +func (ms *memStoreImpl) GetCollectionNames() []string {
|
| + return ms.s.GetCollectionNames()
|
| }
|
|
|
| -// memCollection is a gkvlite.Collection which will panic for anything which
|
| -// might otherwise return an error.
|
| -//
|
| -// This is reasonable for in-memory Store objects, since the only errors that
|
| -// should occur happen with file IO on the underlying file (which of course
|
| -// doesn't exist.
|
| -type memCollection gkvlite.Collection
|
| +type memCollectionImpl struct {
|
| + c *gkvlite.Collection
|
| + ro bool
|
| +}
|
| +
|
| +var _ memCollection = (*memCollectionImpl)(nil)
|
|
|
| -func (mc *memCollection) Get(k []byte) []byte {
|
| - ret, err := (*gkvlite.Collection)(mc).Get(k)
|
| +func (mc *memCollectionImpl) Name() string { return mc.c.Name() }
|
| +func (mc *memCollectionImpl) IsReadOnly() bool { return mc.ro }
|
| +
|
| +func (mc *memCollectionImpl) Get(k []byte) []byte {
|
| + ret, err := mc.c.Get(k)
|
| memoryCorruption(err)
|
| return ret
|
| }
|
|
|
| -func (mc *memCollection) MinItem(withValue bool) *gkvlite.Item {
|
| - ret, err := (*gkvlite.Collection)(mc).MinItem(withValue)
|
| +func (mc *memCollectionImpl) MinItem(withValue bool) *gkvlite.Item {
|
| + ret, err := mc.c.MinItem(withValue)
|
| memoryCorruption(err)
|
| return ret
|
| }
|
|
|
| -func (mc *memCollection) Set(k, v []byte) {
|
| - err := (*gkvlite.Collection)(mc).Set(k, v)
|
| +func (mc *memCollectionImpl) Set(k, v []byte) {
|
| + err := mc.c.Set(k, v)
|
| memoryCorruption(err)
|
| }
|
|
|
| -func (mc *memCollection) Delete(k []byte) bool {
|
| - ret, err := (*gkvlite.Collection)(mc).Delete(k)
|
| +func (mc *memCollectionImpl) Delete(k []byte) bool {
|
| + ret, err := mc.c.Delete(k)
|
| memoryCorruption(err)
|
| return ret
|
| }
|
|
|
| -func (mc *memCollection) VisitItemsAscend(target []byte, withValue bool, visitor gkvlite.ItemVisitor) {
|
| - err := (*gkvlite.Collection)(mc).VisitItemsAscend(target, withValue, visitor)
|
| +func (mc *memCollectionImpl) VisitItemsAscend(target []byte, withValue bool, visitor gkvlite.ItemVisitor) {
|
| + if !mc.ro {
|
| + panic("attempting to VisitItemsAscend from r/w memCollection")
|
| + }
|
| + err := mc.c.VisitItemsAscend(target, withValue, visitor)
|
| memoryCorruption(err)
|
| }
|
|
|
| -func (mc *memCollection) GetTotals() (numItems, numBytes uint64) {
|
| - numItems, numBytes, err := (*gkvlite.Collection)(mc).GetTotals()
|
| +func (mc *memCollectionImpl) GetTotals() (numItems, numBytes uint64) {
|
| + numItems, numBytes, err := mc.c.GetTotals()
|
| memoryCorruption(err)
|
| return
|
| }
|
|
|