| Index: filter/dscache/dscache_test.go
|
| diff --git a/filter/dscache/dscache_test.go b/filter/dscache/dscache_test.go
|
| index 6a09b3f7d94155ea1e7b1663d0e6811882010a92..6501490b9f6d79d65618d5b97910e0433eebcee8 100644
|
| --- a/filter/dscache/dscache_test.go
|
| +++ b/filter/dscache/dscache_test.go
|
| @@ -14,14 +14,17 @@ import (
|
|
|
| "github.com/luci/gae/filter/featureBreaker"
|
| "github.com/luci/gae/impl/memory"
|
| - "github.com/luci/gae/service/datastore"
|
| + ds "github.com/luci/gae/service/datastore"
|
| "github.com/luci/gae/service/datastore/serialize"
|
| - "github.com/luci/gae/service/memcache"
|
| + mc "github.com/luci/gae/service/memcache"
|
| +
|
| "github.com/luci/luci-go/common/clock"
|
| "github.com/luci/luci-go/common/clock/testclock"
|
| "github.com/luci/luci-go/common/data/rand/mathrand"
|
| - . "github.com/smartystreets/goconvey/convey"
|
| +
|
| "golang.org/x/net/context"
|
| +
|
| + . "github.com/smartystreets/goconvey/convey"
|
| )
|
|
|
| type object struct {
|
| @@ -63,10 +66,9 @@ func TestDSCache(t *testing.T) {
|
| c = clock.Set(c, clk)
|
| c = memory.Use(c)
|
|
|
| - dsUnder := datastore.Get(c)
|
| - mc := memcache.Get(c)
|
| + underCtx := c
|
|
|
| - shardsForKey := func(k *datastore.Key) int {
|
| + shardsForKey := func(k *ds.Key) int {
|
| last := k.LastTok()
|
| if last.Kind == "shardObj" {
|
| return int(last.IntID)
|
| @@ -78,40 +80,36 @@ func TestDSCache(t *testing.T) {
|
| }
|
|
|
| numMemcacheItems := func() uint64 {
|
| - stats, err := mc.Stats()
|
| + stats, err := mc.Stats(c)
|
| So(err, ShouldBeNil)
|
| return stats.Items
|
| }
|
|
|
| Convey("enabled cases", func() {
|
| c = FilterRDS(c, shardsForKey)
|
| - ds := datastore.Get(c)
|
| - So(dsUnder, ShouldNotBeNil)
|
| - So(ds, ShouldNotBeNil)
|
| - So(mc, ShouldNotBeNil)
|
|
|
| Convey("basically works", func() {
|
| - pm := datastore.PropertyMap{
|
| - "BigData": datastore.MkProperty([]byte("")),
|
| - "Value": datastore.MkProperty("hi"),
|
| + pm := ds.PropertyMap{
|
| + "BigData": ds.MkProperty([]byte("")),
|
| + "Value": ds.MkProperty("hi"),
|
| }
|
| encoded := append([]byte{0}, serialize.ToBytes(pm)...)
|
|
|
| o := object{ID: 1, Value: "hi"}
|
| - So(ds.Put(&o), ShouldBeNil)
|
| + So(ds.Put(c, &o), ShouldBeNil)
|
|
|
| o = object{ID: 1}
|
| - So(dsUnder.Get(&o), ShouldBeNil)
|
| + So(ds.Get(underCtx, &o), ShouldBeNil)
|
| So(o.Value, ShouldEqual, "hi")
|
|
|
| - itm, err := mc.Get(MakeMemcacheKey(0, ds.KeyForObj(&o)))
|
| - So(err, ShouldEqual, memcache.ErrCacheMiss)
|
| + itm, err := mc.GetKey(c, MakeMemcacheKey(0, ds.KeyForObj(c, &o)))
|
| + So(err, ShouldEqual, mc.ErrCacheMiss)
|
|
|
| o = object{ID: 1}
|
| - So(ds.Get(&o), ShouldBeNil)
|
| + So(ds.Get(c, &o), ShouldBeNil)
|
| So(o.Value, ShouldEqual, "hi")
|
|
|
| - itm, err = mc.Get(itm.Key())
|
| + itm, err = mc.GetKey(c, itm.Key())
|
| So(err, ShouldBeNil)
|
| So(itm.Value(), ShouldResemble, encoded)
|
|
|
| @@ -120,30 +118,30 @@ func TestDSCache(t *testing.T) {
|
|
|
| // delete it, bypassing the cache filter. Don't do this in production
|
| // unless you want a crappy cache.
|
| - So(dsUnder.Delete(ds.KeyForObj(&o)), ShouldBeNil)
|
| + So(ds.Delete(underCtx, ds.KeyForObj(underCtx, &o)), ShouldBeNil)
|
|
|
| - itm, err := mc.Get(MakeMemcacheKey(0, ds.KeyForObj(&o)))
|
| + itm, err := mc.GetKey(c, MakeMemcacheKey(0, ds.KeyForObj(c, &o)))
|
| So(err, ShouldBeNil)
|
| So(itm.Value(), ShouldResemble, encoded)
|
|
|
| - So(ds.Get(&o), ShouldBeNil)
|
| + So(ds.Get(c, &o), ShouldBeNil)
|
| So(o.Value, ShouldEqual, "hi")
|
| })
|
|
|
| Convey("deleting it properly records that fact, however", func() {
|
| o := object{ID: 1}
|
| - So(ds.Delete(ds.KeyForObj(&o)), ShouldBeNil)
|
| + So(ds.Delete(c, ds.KeyForObj(c, &o)), ShouldBeNil)
|
|
|
| - itm, err := mc.Get(MakeMemcacheKey(0, ds.KeyForObj(&o)))
|
| - So(err, ShouldEqual, memcache.ErrCacheMiss)
|
| - So(ds.Get(&o), ShouldEqual, datastore.ErrNoSuchEntity)
|
| + itm, err := mc.GetKey(c, MakeMemcacheKey(0, ds.KeyForObj(c, &o)))
|
| + So(err, ShouldEqual, mc.ErrCacheMiss)
|
| + So(ds.Get(c, &o), ShouldEqual, ds.ErrNoSuchEntity)
|
|
|
| - itm, err = mc.Get(itm.Key())
|
| + itm, err = mc.GetKey(c, itm.Key())
|
| So(err, ShouldBeNil)
|
| So(itm.Value(), ShouldResemble, []byte{})
|
|
|
| // this one hits memcache
|
| - So(ds.Get(&o), ShouldEqual, datastore.ErrNoSuchEntity)
|
| + So(ds.Get(c, &o), ShouldEqual, ds.ErrNoSuchEntity)
|
| })
|
| })
|
|
|
| @@ -156,20 +154,20 @@ func TestDSCache(t *testing.T) {
|
| }
|
| o.BigData = data
|
|
|
| - So(ds.Put(&o), ShouldBeNil)
|
| - So(ds.Get(&o), ShouldBeNil)
|
| + So(ds.Put(c, &o), ShouldBeNil)
|
| + So(ds.Get(c, &o), ShouldBeNil)
|
|
|
| - itm, err := mc.Get(MakeMemcacheKey(0, ds.KeyForObj(&o)))
|
| + itm, err := mc.GetKey(c, MakeMemcacheKey(0, ds.KeyForObj(c, &o)))
|
| So(err, ShouldBeNil)
|
|
|
| So(itm.Value()[0], ShouldEqual, ZlibCompression)
|
| So(len(itm.Value()), ShouldEqual, 653) // a bit smaller than 4k
|
|
|
| // ensure the next Get comes from the cache
|
| - So(dsUnder.Delete(ds.KeyForObj(&o)), ShouldBeNil)
|
| + So(ds.Delete(underCtx, ds.KeyForObj(underCtx, &o)), ShouldBeNil)
|
|
|
| o = object{ID: 2}
|
| - So(ds.Get(&o), ShouldBeNil)
|
| + So(ds.Get(c, &o), ShouldBeNil)
|
| So(o.Value, ShouldEqual, `¯\_(ツ)_/¯`)
|
| So(o.BigData, ShouldResemble, data)
|
| })
|
| @@ -177,56 +175,54 @@ func TestDSCache(t *testing.T) {
|
| Convey("transactions", func() {
|
| Convey("work", func() {
|
| // populate an object @ ID1
|
| - So(ds.Put(&object{ID: 1, Value: "something"}), ShouldBeNil)
|
| - So(ds.Get(&object{ID: 1}), ShouldBeNil)
|
| + So(ds.Put(c, &object{ID: 1, Value: "something"}), ShouldBeNil)
|
| + So(ds.Get(c, &object{ID: 1}), ShouldBeNil)
|
|
|
| - So(ds.Put(&object{ID: 2, Value: "nurbs"}), ShouldBeNil)
|
| - So(ds.Get(&object{ID: 2}), ShouldBeNil)
|
| + So(ds.Put(c, &object{ID: 2, Value: "nurbs"}), ShouldBeNil)
|
| + So(ds.Get(c, &object{ID: 2}), ShouldBeNil)
|
|
|
| // memcache now has the wrong value (simulated race)
|
| - So(dsUnder.Put(&object{ID: 1, Value: "else"}), ShouldBeNil)
|
| - So(ds.RunInTransaction(func(c context.Context) error {
|
| - ds := datastore.Get(c)
|
| + So(ds.Put(underCtx, &object{ID: 1, Value: "else"}), ShouldBeNil)
|
| + So(ds.RunInTransaction(c, func(c context.Context) error {
|
| o := &object{ID: 1}
|
| - So(ds.Get(o), ShouldBeNil)
|
| + So(ds.Get(c, o), ShouldBeNil)
|
| So(o.Value, ShouldEqual, "else")
|
| o.Value = "txn"
|
| - So(ds.Put(o), ShouldBeNil)
|
| + So(ds.Put(c, o), ShouldBeNil)
|
|
|
| - So(ds.Delete(ds.KeyForObj(&object{ID: 2})), ShouldBeNil)
|
| + So(ds.Delete(c, ds.KeyForObj(c, &object{ID: 2})), ShouldBeNil)
|
| return nil
|
| - }, &datastore.TransactionOptions{XG: true}), ShouldBeNil)
|
| + }, &ds.TransactionOptions{XG: true}), ShouldBeNil)
|
|
|
| - _, err := mc.Get(MakeMemcacheKey(0, ds.KeyForObj(&object{ID: 1})))
|
| - So(err, ShouldEqual, memcache.ErrCacheMiss)
|
| - _, err = mc.Get(MakeMemcacheKey(0, ds.KeyForObj(&object{ID: 2})))
|
| - So(err, ShouldEqual, memcache.ErrCacheMiss)
|
| + _, err := mc.GetKey(c, MakeMemcacheKey(0, ds.KeyForObj(c, &object{ID: 1})))
|
| + So(err, ShouldEqual, mc.ErrCacheMiss)
|
| + _, err = mc.GetKey(c, MakeMemcacheKey(0, ds.KeyForObj(c, &object{ID: 2})))
|
| + So(err, ShouldEqual, mc.ErrCacheMiss)
|
| o := &object{ID: 1}
|
| - So(ds.Get(o), ShouldBeNil)
|
| + So(ds.Get(c, o), ShouldBeNil)
|
| So(o.Value, ShouldEqual, "txn")
|
| })
|
|
|
| Convey("errors don't invalidate", func() {
|
| // populate an object @ ID1
|
| - So(ds.Put(&object{ID: 1, Value: "something"}), ShouldBeNil)
|
| - So(ds.Get(&object{ID: 1}), ShouldBeNil)
|
| + So(ds.Put(c, &object{ID: 1, Value: "something"}), ShouldBeNil)
|
| + So(ds.Get(c, &object{ID: 1}), ShouldBeNil)
|
| So(numMemcacheItems(), ShouldEqual, 1)
|
|
|
| - So(ds.RunInTransaction(func(c context.Context) error {
|
| - ds := datastore.Get(c)
|
| + So(ds.RunInTransaction(c, func(c context.Context) error {
|
| o := &object{ID: 1}
|
| - So(ds.Get(o), ShouldBeNil)
|
| + So(ds.Get(c, o), ShouldBeNil)
|
| So(o.Value, ShouldEqual, "something")
|
| o.Value = "txn"
|
| - So(ds.Put(o), ShouldBeNil)
|
| + So(ds.Put(c, o), ShouldBeNil)
|
| return errors.New("OH NOES")
|
| }, nil).Error(), ShouldContainSubstring, "OH NOES")
|
|
|
| // memcache still has the original
|
| So(numMemcacheItems(), ShouldEqual, 1)
|
| - So(dsUnder.Delete(ds.KeyForObj(&object{ID: 1})), ShouldBeNil)
|
| + So(ds.Delete(underCtx, ds.KeyForObj(underCtx, &object{ID: 1})), ShouldBeNil)
|
| o := &object{ID: 1}
|
| - So(ds.Get(o), ShouldBeNil)
|
| + So(ds.Get(c, o), ShouldBeNil)
|
| So(o.Value, ShouldEqual, "something")
|
| })
|
| })
|
| @@ -234,39 +230,39 @@ func TestDSCache(t *testing.T) {
|
| Convey("control", func() {
|
| Convey("per-model bypass", func() {
|
| type model struct {
|
| - ID string `gae:"$id"`
|
| - UseDSCache datastore.Toggle `gae:"$dscache.enable,false"`
|
| + ID string `gae:"$id"`
|
| + UseDSCache ds.Toggle `gae:"$dscache.enable,false"`
|
|
|
| Value string
|
| }
|
|
|
| itms := []model{
|
| {ID: "hi", Value: "something"},
|
| - {ID: "there", Value: "else", UseDSCache: datastore.On},
|
| + {ID: "there", Value: "else", UseDSCache: ds.On},
|
| }
|
|
|
| - So(ds.PutMulti(itms), ShouldBeNil)
|
| - So(ds.GetMulti(itms), ShouldBeNil)
|
| + So(ds.Put(c, itms), ShouldBeNil)
|
| + So(ds.Get(c, itms), ShouldBeNil)
|
|
|
| So(numMemcacheItems(), ShouldEqual, 1)
|
| })
|
|
|
| Convey("per-key shard count", func() {
|
| s := &shardObj{ID: 4, Value: "hi"}
|
| - So(ds.Put(s), ShouldBeNil)
|
| - So(ds.Get(s), ShouldBeNil)
|
| + So(ds.Put(c, s), ShouldBeNil)
|
| + So(ds.Get(c, s), ShouldBeNil)
|
|
|
| So(numMemcacheItems(), ShouldEqual, 1)
|
| for i := 0; i < 20; i++ {
|
| - So(ds.Get(s), ShouldBeNil)
|
| + So(ds.Get(c, s), ShouldBeNil)
|
| }
|
| So(numMemcacheItems(), ShouldEqual, 4)
|
| })
|
|
|
| Convey("per-key cache disablement", func() {
|
| n := &noCacheObj{ID: "nurbs", Value: true}
|
| - So(ds.Put(n), ShouldBeNil)
|
| - So(ds.Get(n), ShouldBeNil)
|
| + So(ds.Put(c, n), ShouldBeNil)
|
| + So(ds.Get(c, n), ShouldBeNil)
|
| So(numMemcacheItems(), ShouldEqual, 0)
|
| })
|
|
|
| @@ -278,32 +274,32 @@ func TestDSCache(t *testing.T) {
|
| Value string
|
| }
|
|
|
| - So(ds.Put(&model{ID: 1, Value: "mooo"}), ShouldBeNil)
|
| - So(ds.Get(&model{ID: 1}), ShouldBeNil)
|
| + So(ds.Put(c, &model{ID: 1, Value: "mooo"}), ShouldBeNil)
|
| + So(ds.Get(c, &model{ID: 1}), ShouldBeNil)
|
|
|
| - itm, err := mc.Get(MakeMemcacheKey(0, ds.KeyForObj(&model{ID: 1})))
|
| + itm, err := mc.GetKey(c, MakeMemcacheKey(0, ds.KeyForObj(c, &model{ID: 1})))
|
| So(err, ShouldBeNil)
|
|
|
| clk.Add(10 * time.Second)
|
| - _, err = mc.Get(itm.Key())
|
| - So(err, ShouldEqual, memcache.ErrCacheMiss)
|
| + _, err = mc.GetKey(c, itm.Key())
|
| + So(err, ShouldEqual, mc.ErrCacheMiss)
|
| })
|
| })
|
|
|
| Convey("screw cases", func() {
|
| Convey("memcache contains bogus value (simulated failed AddMulti)", func() {
|
| o := &object{ID: 1, Value: "spleen"}
|
| - So(ds.Put(o), ShouldBeNil)
|
| + So(ds.Put(c, o), ShouldBeNil)
|
|
|
| sekret := []byte("I am a banana")
|
| - itm := mc.NewItem(MakeMemcacheKey(0, ds.KeyForObj(o))).SetValue(sekret)
|
| - So(mc.Set(itm), ShouldBeNil)
|
| + itm := mc.NewItem(c, MakeMemcacheKey(0, ds.KeyForObj(c, o))).SetValue(sekret)
|
| + So(mc.Set(c, itm), ShouldBeNil)
|
|
|
| o = &object{ID: 1}
|
| - So(ds.Get(o), ShouldBeNil)
|
| + So(ds.Get(c, o), ShouldBeNil)
|
| So(o.Value, ShouldEqual, "spleen")
|
|
|
| - itm, err := mc.Get(itm.Key())
|
| + itm, err := mc.GetKey(c, itm.Key())
|
| So(err, ShouldBeNil)
|
| So(itm.Flags(), ShouldEqual, ItemUKNONWN)
|
| So(itm.Value(), ShouldResemble, sekret)
|
| @@ -311,19 +307,19 @@ func TestDSCache(t *testing.T) {
|
|
|
| Convey("memcache contains bogus value (corrupt entry)", func() {
|
| o := &object{ID: 1, Value: "spleen"}
|
| - So(ds.Put(o), ShouldBeNil)
|
| + So(ds.Put(c, o), ShouldBeNil)
|
|
|
| sekret := []byte("I am a banana")
|
| - itm := (mc.NewItem(MakeMemcacheKey(0, ds.KeyForObj(o))).
|
| + itm := (mc.NewItem(c, MakeMemcacheKey(0, ds.KeyForObj(c, o))).
|
| SetValue(sekret).
|
| SetFlags(uint32(ItemHasData)))
|
| - So(mc.Set(itm), ShouldBeNil)
|
| + So(mc.Set(c, itm), ShouldBeNil)
|
|
|
| o = &object{ID: 1}
|
| - So(ds.Get(o), ShouldBeNil)
|
| + So(ds.Get(c, o), ShouldBeNil)
|
| So(o.Value, ShouldEqual, "spleen")
|
|
|
| - itm, err := mc.Get(itm.Key())
|
| + itm, err := mc.GetKey(c, itm.Key())
|
| So(err, ShouldBeNil)
|
| So(itm.Flags(), ShouldEqual, ItemHasData)
|
| So(itm.Value(), ShouldResemble, sekret)
|
| @@ -331,19 +327,19 @@ func TestDSCache(t *testing.T) {
|
|
|
| Convey("other entity has the lock", func() {
|
| o := &object{ID: 1, Value: "spleen"}
|
| - So(ds.Put(o), ShouldBeNil)
|
| + So(ds.Put(c, o), ShouldBeNil)
|
|
|
| sekret := []byte("r@vmarod!#)%9T")
|
| - itm := (mc.NewItem(MakeMemcacheKey(0, ds.KeyForObj(o))).
|
| + itm := (mc.NewItem(c, MakeMemcacheKey(0, ds.KeyForObj(c, o))).
|
| SetValue(sekret).
|
| SetFlags(uint32(ItemHasLock)))
|
| - So(mc.Set(itm), ShouldBeNil)
|
| + So(mc.Set(c, itm), ShouldBeNil)
|
|
|
| o = &object{ID: 1}
|
| - So(ds.Get(o), ShouldBeNil)
|
| + So(ds.Get(c, o), ShouldBeNil)
|
| So(o.Value, ShouldEqual, "spleen")
|
|
|
| - itm, err := mc.Get(itm.Key())
|
| + itm, err := mc.GetKey(c, itm.Key())
|
| So(err, ShouldBeNil)
|
| So(itm.Flags(), ShouldEqual, ItemHasLock)
|
| So(itm.Value(), ShouldResemble, sekret)
|
| @@ -358,12 +354,12 @@ func TestDSCache(t *testing.T) {
|
| So(binary.Write(&buf, binary.LittleEndian, mr.Int63()), ShouldBeNil)
|
| }
|
| o.BigData = buf.Bytes()
|
| - So(ds.Put(o), ShouldBeNil)
|
| + So(ds.Put(c, o), ShouldBeNil)
|
|
|
| o.BigData = nil
|
| - So(ds.Get(o), ShouldBeNil)
|
| + So(ds.Get(c, o), ShouldBeNil)
|
|
|
| - itm, err := mc.Get(MakeMemcacheKey(0, ds.KeyForObj(o)))
|
| + itm, err := mc.GetKey(c, MakeMemcacheKey(0, ds.KeyForObj(c, o)))
|
| So(err, ShouldBeNil)
|
|
|
| // Is locked until the next put, forcing all access to the datastore.
|
| @@ -371,10 +367,10 @@ func TestDSCache(t *testing.T) {
|
| So(itm.Flags(), ShouldEqual, ItemHasLock)
|
|
|
| o.BigData = []byte("hi :)")
|
| - So(ds.Put(o), ShouldBeNil)
|
| - So(ds.Get(o), ShouldBeNil)
|
| + So(ds.Put(c, o), ShouldBeNil)
|
| + So(ds.Get(c, o), ShouldBeNil)
|
|
|
| - itm, err = mc.Get(itm.Key())
|
| + itm, err = mc.GetKey(c, itm.Key())
|
| So(err, ShouldBeNil)
|
| So(itm.Flags(), ShouldEqual, ItemHasData)
|
| })
|
| @@ -382,16 +378,14 @@ func TestDSCache(t *testing.T) {
|
| Convey("failure on Setting memcache locks is a hard stop", func() {
|
| c, fb := featureBreaker.FilterMC(c, nil)
|
| fb.BreakFeatures(nil, "SetMulti")
|
| - ds := datastore.Get(c)
|
| - So(ds.Put(&object{ID: 1}).Error(), ShouldContainSubstring, "SetMulti")
|
| + So(ds.Put(c, &object{ID: 1}).Error(), ShouldContainSubstring, "SetMulti")
|
| })
|
|
|
| Convey("failure on Setting memcache locks in a transaction is a hard stop", func() {
|
| c, fb := featureBreaker.FilterMC(c, nil)
|
| fb.BreakFeatures(nil, "SetMulti")
|
| - ds := datastore.Get(c)
|
| - So(ds.RunInTransaction(func(c context.Context) error {
|
| - So(datastore.Get(c).Put(&object{ID: 1}), ShouldBeNil)
|
| + So(ds.RunInTransaction(c, func(c context.Context) error {
|
| + So(ds.Put(c, &object{ID: 1}), ShouldBeNil)
|
| // no problems here... memcache operations happen after the function
|
| // body quits.
|
| return nil
|
| @@ -403,7 +397,7 @@ func TestDSCache(t *testing.T) {
|
| Convey("misc", func() {
|
| Convey("verify numShards caps at MaxShards", func() {
|
| sc := supportContext{shardsForKey: shardsForKey}
|
| - So(sc.numShards(ds.KeyForObj(&shardObj{ID: 9001})), ShouldEqual, MaxShards)
|
| + So(sc.numShards(ds.KeyForObj(c, &shardObj{ID: 9001})), ShouldEqual, MaxShards)
|
| })
|
|
|
| Convey("CompressionType.String", func() {
|
| @@ -430,7 +424,7 @@ func TestDSCache(t *testing.T) {
|
| clk.Add(time.Minute*5 + time.Second)
|
| So(IsGloballyEnabled(c), ShouldBeFalse)
|
|
|
| - So(mc.Set(mc.NewItem("test").SetValue([]byte("hi"))), ShouldBeNil)
|
| + So(mc.Set(c, mc.NewItem(c, "test").SetValue([]byte("hi"))), ShouldBeNil)
|
| So(numMemcacheItems(), ShouldEqual, 1)
|
| So(SetGlobalEnable(c, true), ShouldBeNil)
|
| // memcache gets flushed as a side effect
|
|
|