func TestGetMultiLockReturnEntitySetValueFail(t *testing.T) { c, closeFunc := NewContext(t) defer closeFunc() type testEntity struct { IntVal int64 } keys := []*datastore.Key{} entities := []testEntity{} for i := int64(1); i < 3; i++ { keys = append(keys, datastore.NewKey(c, "Entity", "", i, nil)) entities = append(entities, testEntity{i}) } if _, err := nds.PutMulti(c, keys, entities); err != nil { t.Fatal(err) } // Fail to unmarshal test. memcacheGetChan := make(chan func(c context.Context, keys []string) ( map[string]*memcache.Item, error), 2) memcacheGetChan <- memcache.GetMulti memcacheGetChan <- func(c context.Context, keys []string) (map[string]*memcache.Item, error) { items, err := memcache.GetMulti(c, keys) if err != nil { return nil, err } pl := datastore.PropertyList{ datastore.Property{"One", 1, false, false}, } value, err := nds.MarshalPropertyList(pl) if err != nil { return nil, err } items[keys[0]].Flags = nds.EntityItem items[keys[0]].Value = value items[keys[1]].Flags = nds.EntityItem items[keys[1]].Value = value return items, nil } nds.SetMemcacheGetMulti(func(c context.Context, keys []string) (map[string]*memcache.Item, error) { f := <-memcacheGetChan return f(c, keys) }) response := make([]testEntity, len(keys)) if err := nds.GetMulti(c, keys, response); err != nil { t.Fatal(err) } defer nds.SetMemcacheGetMulti(memcache.GetMulti) for i := 0; i < len(keys); i++ { if entities[i].IntVal != response[i].IntVal { t.Fatal("IntVal not equal") } } }
// GetMulti fetches dst ([]Kind or []*Kind) by multiple keys func (d *Driver) GetMulti(keys []string, dst interface{}) error { var errors = make([]error, 0) hits, err := memcache.GetMulti(d.ctx, keys) if err == nil { dstValue := reflect.ValueOf(dst) for i, key := range keys { if item, ok := hits[key]; ok { objValue := dstValue.Index(i) if objValue.Kind() == reflect.Ptr { if string(item.Value) == jsonNullValue { continue } obj := reflect.New(objValue.Type().Elem()).Interface() if err = json.Unmarshal(item.Value, obj); err != nil { errors = append(errors, fmt.Errorf("Could not unmarshal JSON: %v", err)) continue } objValue.Set(reflect.ValueOf(obj)) } else { obj := objValue.Interface() if err = json.Unmarshal(item.Value, &obj); err != nil { errors = append(errors, fmt.Errorf("Could not unmarshal JSON: %v", err)) continue } } } } if len(errors) > 0 { return fmt.Errorf("MultiError: %v", errors) } return nil } return err }
// bool means 'found' func BytesFromShardedMemcache(c context.Context, key string) ([]byte, bool) { keys := []string{} for i := 0; i < 32; i++ { keys = append(keys, fmt.Sprintf("=%d=%s", i*chunksize, key)) } if items, err := memcache.GetMulti(c, keys); err != nil { log.Errorf(c, "fdb memcache multiget: %v", err) return nil, false } else { b := []byte{} for i := 0; i < 32; i++ { if item, exists := items[keys[i]]; exists == false { break } else { log.Infof(c, " #=== Found '%s' !", item.Key) b = append(b, item.Value...) } } log.Infof(c, " #=== Final read len: %d", len(b)) if len(b) > 0 { return b, true } else { return nil, false } } }
func TestGetMultiLockReturnUnknown(t *testing.T) { c, closeFunc := NewContext(t) defer closeFunc() type testEntity struct { IntVal int64 } keys := []*datastore.Key{} entities := []testEntity{} for i := int64(1); i < 3; i++ { keys = append(keys, datastore.NewKey(c, "Entity", "", i, nil)) entities = append(entities, testEntity{i}) } if _, err := nds.PutMulti(c, keys, entities); err != nil { t.Fatal(err) } memcacheGetChan := make(chan func(c context.Context, keys []string) ( map[string]*memcache.Item, error), 2) memcacheGetChan <- memcache.GetMulti memcacheGetChan <- func(c context.Context, keys []string) (map[string]*memcache.Item, error) { items, err := memcache.GetMulti(c, keys) if err != nil { return nil, err } // Unknown lock values. items[keys[0]].Flags = 23 items[keys[1]].Flags = 24 return items, nil } nds.SetMemcacheGetMulti(func(c context.Context, keys []string) (map[string]*memcache.Item, error) { f := <-memcacheGetChan return f(c, keys) }) response := make([]testEntity, len(keys)) if err := nds.GetMulti(c, keys, response); err != nil { t.Fatal(err) } defer nds.SetMemcacheGetMulti(memcache.GetMulti) for i := 0; i < len(keys); i++ { if entities[i].IntVal != response[i].IntVal { t.Fatal("IntVal not equal") } } }
func (m mcImpl) GetMulti(keys []string, cb mc.RawItemCB) error { realItems, err := memcache.GetMulti(m.aeCtx, keys) if err != nil { return err } for _, k := range keys { itm := realItems[k] if itm == nil { cb(nil, memcache.ErrCacheMiss) } else { cb(mcItem{itm}, nil) } } return nil }
func TestGetMultiPaths(t *testing.T) { expectedErr := errors.New("expected error") type memcacheGetMultiFunc func(c context.Context, keys []string) (map[string]*memcache.Item, error) memcacheGetMultiFail := func(c context.Context, keys []string) (map[string]*memcache.Item, error) { return nil, expectedErr } type memcacheAddMultiFunc func(c context.Context, items []*memcache.Item) error memcacheAddMultiFail := func(c context.Context, items []*memcache.Item) error { return expectedErr } type memcacheCompareAndSwapMultiFunc func(c context.Context, items []*memcache.Item) error memcacheCompareAndSwapMultiFail := func(c context.Context, items []*memcache.Item) error { return expectedErr } type datastoreGetMultiFunc func(c context.Context, keys []*datastore.Key, vals interface{}) error datastoreGetMultiFail := func(c context.Context, keys []*datastore.Key, vals interface{}) error { return expectedErr } type marshalFunc func(pl datastore.PropertyList) ([]byte, error) marshalFail := func(pl datastore.PropertyList) ([]byte, error) { return nil, expectedErr } type unmarshalFunc func(data []byte, pl *datastore.PropertyList) error /* unmarshalFail := func(data []byte, pl *datastore.PropertyList) error { return expectedErr } */ c, closeFunc := NewContext(t) defer closeFunc() type testEntity struct { IntVal int64 } keysVals := func(c context.Context, count int64) ( []*datastore.Key, []testEntity) { keys, vals := make([]*datastore.Key, count), make([]testEntity, count) for i := int64(0); i < count; i++ { keys[i] = datastore.NewKey(c, "Entity", "", i+1, nil) vals[i] = testEntity{i + 1} } return keys, vals } tests := []struct { description string // Number of keys used to as GetMulti params. keyCount int64 // Number of times GetMulti is called. callCount int // There are 2 memcacheGetMulti calls for every GetMulti call. memcacheGetMultis []memcacheGetMultiFunc memcacheAddMulti memcacheAddMultiFunc memcacheCompareAndSwapMulti memcacheCompareAndSwapMultiFunc datastoreGetMulti datastoreGetMultiFunc marshal marshalFunc // There are 2 unmarshal calls for every GetMultiCall. unmarshals []unmarshalFunc expectedErrs []error }{ { "no errors", 20, 1, []memcacheGetMultiFunc{ memcache.GetMulti, memcache.GetMulti, }, memcache.AddMulti, memcache.CompareAndSwapMulti, datastore.GetMulti, nds.MarshalPropertyList, []unmarshalFunc{ nds.UnmarshalPropertyList, nds.UnmarshalPropertyList, }, []error{nil}, }, { "datastore unknown error", 2, 1, []memcacheGetMultiFunc{ memcache.GetMulti, memcache.GetMulti, }, memcache.AddMulti, memcache.CompareAndSwapMulti, datastoreGetMultiFail, nds.MarshalPropertyList, []unmarshalFunc{ nds.UnmarshalPropertyList, nds.UnmarshalPropertyList, }, []error{expectedErr}, }, { "datastore unknown multierror", 2, 1, []memcacheGetMultiFunc{ memcache.GetMulti, memcache.GetMulti, }, memcache.AddMulti, memcache.CompareAndSwapMulti, func(c context.Context, keys []*datastore.Key, vals interface{}) error { me := make(appengine.MultiError, len(keys)) for i := range me { me[i] = expectedErr } return me }, nds.MarshalPropertyList, []unmarshalFunc{ nds.UnmarshalPropertyList, nds.UnmarshalPropertyList, }, []error{ appengine.MultiError{expectedErr, expectedErr}, }, }, { "marshal error", 5, 1, []memcacheGetMultiFunc{ memcache.GetMulti, memcache.GetMulti, }, memcache.AddMulti, memcache.CompareAndSwapMulti, datastore.GetMulti, marshalFail, []unmarshalFunc{ nds.UnmarshalPropertyList, nds.UnmarshalPropertyList, }, []error{nil}, }, { "total memcache fail", 20, 1, []memcacheGetMultiFunc{ memcacheGetMultiFail, memcacheGetMultiFail, }, memcacheAddMultiFail, memcacheCompareAndSwapMultiFail, datastore.GetMulti, nds.MarshalPropertyList, []unmarshalFunc{ nds.UnmarshalPropertyList, nds.UnmarshalPropertyList, }, []error{nil}, }, { "lock memcache fail", 20, 1, []memcacheGetMultiFunc{ memcache.GetMulti, memcacheGetMultiFail, }, memcache.AddMulti, memcache.CompareAndSwapMulti, datastore.GetMulti, nds.MarshalPropertyList, []unmarshalFunc{ nds.UnmarshalPropertyList, nds.UnmarshalPropertyList, }, []error{nil}, }, { "memcache corrupt", 2, 2, []memcacheGetMultiFunc{ // Charge memcache. memcache.GetMulti, memcache.GetMulti, // Corrupt memcache. func(c context.Context, keys []string) ( map[string]*memcache.Item, error) { items, err := memcache.GetMulti(c, keys) // Corrupt items. for _, item := range items { item.Value = []byte("corrupt string") } return items, err }, memcache.GetMulti, }, memcache.AddMulti, memcache.CompareAndSwapMulti, datastore.GetMulti, nds.MarshalPropertyList, []unmarshalFunc{ nds.UnmarshalPropertyList, nds.UnmarshalPropertyList, nds.UnmarshalPropertyList, nds.UnmarshalPropertyList, }, []error{nil, nil}, }, { "memcache flag corrupt", 2, 2, []memcacheGetMultiFunc{ // Charge memcache. memcache.GetMulti, memcache.GetMulti, // Corrupt memcache flags. func(c context.Context, keys []string) ( map[string]*memcache.Item, error) { items, err := memcache.GetMulti(c, keys) // Corrupt flags with unknown number. for _, item := range items { item.Flags = 56 } return items, err }, memcache.GetMulti, }, memcache.AddMulti, memcache.CompareAndSwapMulti, datastore.GetMulti, nds.MarshalPropertyList, []unmarshalFunc{ nds.UnmarshalPropertyList, nds.UnmarshalPropertyList, nds.UnmarshalPropertyList, nds.UnmarshalPropertyList, }, []error{nil, nil}, }, { "lock memcache value fail", 20, 1, []memcacheGetMultiFunc{ memcache.GetMulti, func(c context.Context, keys []string) ( map[string]*memcache.Item, error) { items, err := memcache.GetMulti(c, keys) // Corrupt flags with unknown number. for _, item := range items { item.Value = []byte("corrupt value") } return items, err }, }, memcache.AddMulti, memcache.CompareAndSwapMulti, datastore.GetMulti, nds.MarshalPropertyList, []unmarshalFunc{ nds.UnmarshalPropertyList, nds.UnmarshalPropertyList, }, []error{nil}, }, { "lock memcache value none item", 2, 1, []memcacheGetMultiFunc{ memcache.GetMulti, func(c context.Context, keys []string) ( map[string]*memcache.Item, error) { items, err := memcache.GetMulti(c, keys) // Corrupt flags with unknown number. for _, item := range items { item.Flags = nds.NoneItem } return items, err }, }, memcache.AddMulti, memcache.CompareAndSwapMulti, datastore.GetMulti, nds.MarshalPropertyList, []unmarshalFunc{ nds.UnmarshalPropertyList, nds.UnmarshalPropertyList, }, []error{ appengine.MultiError{ datastore.ErrNoSuchEntity, datastore.ErrNoSuchEntity, }, }, }, { "memcache get no entity unmarshal fail", 2, 1, []memcacheGetMultiFunc{ memcache.GetMulti, func(c context.Context, keys []string) ( map[string]*memcache.Item, error) { items, err := memcache.GetMulti(c, keys) // Corrupt flags with unknown number. for _, item := range items { item.Flags = nds.EntityItem } return items, err }, }, memcache.AddMulti, memcache.CompareAndSwapMulti, datastore.GetMulti, nds.MarshalPropertyList, []unmarshalFunc{ nds.UnmarshalPropertyList, nds.UnmarshalPropertyList, }, []error{nil}, }, } for _, test := range tests { t.Log("Start", test.description) keys, putVals := keysVals(c, test.keyCount) if _, err := nds.PutMulti(c, keys, putVals); err != nil { t.Fatal(err) } memcacheGetChan := make(chan memcacheGetMultiFunc, len(test.memcacheGetMultis)) for _, fn := range test.memcacheGetMultis { memcacheGetChan <- fn } nds.SetMemcacheGetMulti(func(c context.Context, keys []string) ( map[string]*memcache.Item, error) { fn := <-memcacheGetChan return fn(c, keys) }) nds.SetMemcacheAddMulti(test.memcacheAddMulti) nds.SetMemcacheCompareAndSwapMulti(test.memcacheCompareAndSwapMulti) nds.SetDatastoreGetMulti(test.datastoreGetMulti) nds.SetMarshal(test.marshal) unmarshalChan := make(chan unmarshalFunc, len(test.unmarshals)) for _, fn := range test.unmarshals { unmarshalChan <- fn } nds.SetUnmarshal(func(data []byte, pl *datastore.PropertyList) error { fn := <-unmarshalChan return fn(data, pl) }) for i := 0; i < test.callCount; i++ { getVals := make([]testEntity, test.keyCount) err := nds.GetMulti(c, keys, getVals) expectedErr := test.expectedErrs[i] if expectedErr == nil { if err != nil { t.Fatal(err) } for i := range getVals { if getVals[i].IntVal != putVals[i].IntVal { t.Fatal("incorrect IntVal") } } continue } if err == nil { t.Fatal("expected error") } expectedMultiErr, isMultiErr := expectedErr.(appengine.MultiError) if isMultiErr { me, ok := err.(appengine.MultiError) if !ok { t.Fatal("expected appengine.MultiError but got", err) } if len(me) != len(expectedMultiErr) { t.Fatal("appengine.MultiError length incorrect") } for i, e := range me { if e != expectedMultiErr[i] { t.Fatal("non matching errors", e, expectedMultiErr[i]) } if e == nil { if getVals[i].IntVal != putVals[i].IntVal { t.Fatal("incorrect IntVal") } } } } } // Reset App Engine API calls. nds.SetMemcacheGetMulti(memcache.GetMulti) nds.SetMemcacheAddMulti(memcache.AddMulti) nds.SetMemcacheCompareAndSwapMulti(memcache.CompareAndSwapMulti) nds.SetDatastoreGetMulti(datastore.GetMulti) nds.SetMarshal(nds.MarshalPropertyList) nds.SetUnmarshal(nds.UnmarshalPropertyList) if err := nds.DeleteMulti(c, keys); err != nil { t.Fatal(err) } t.Log("End", test.description) } }
func index(ctx context.Context, w http.ResponseWriter, r *http.Request) { keys := make([]string, modulus) for i := range keys { keys[i] = fmt.Sprintf(keyPart, i*distance) } items, err := memcache.GetMulti(ctx, keys) if err != nil { return } ars := allrequestStats{} for _, v := range items { t := statsPart{} err := gob.NewDecoder(bytes.NewBuffer(v.Value)).Decode(&t) if err != nil { continue } r := requestStats(t) ars = append(ars, &r) } sort.Sort(reverse{ars}) requestByID := make(map[int]*requestStats, len(ars)) idByRequest := make(map[*requestStats]int, len(ars)) requests := make(map[int]*statByName) byRequest := make(map[int]map[string]cVal) for i, v := range ars { idx := i + 1 requestByID[idx] = v idByRequest[v] = idx requests[idx] = &statByName{ RequestStats: v, } byRequest[idx] = make(map[string]cVal) } requestByPath := make(map[string][]int) byCount := make(map[string]cVal) byRPC := make(map[skey]cVal) for _, t := range ars { id := idByRequest[t] requestByPath[t.Path] = append(requestByPath[t.Path], id) for _, r := range t.RPCStats { rpc := r.Name() v := byRequest[id][rpc] v.count++ v.cost += r.Cost byRequest[id][rpc] = v v = byCount[rpc] v.count++ v.cost += r.Cost byCount[rpc] = v v = byRPC[skey{rpc, t.Path}] v.count++ v.cost += r.Cost byRPC[skey{rpc, t.Path}] = v } } for k, v := range byRequest { stats := statsByName{} for rpc, s := range v { stats = append(stats, &statByName{ Name: rpc, Count: s.count, Cost: s.cost, }) } sort.Sort(reverse{stats}) requests[k].SubStats = stats } statsByRPC := make(map[string]statsByName) pathStats := make(map[string]statsByName) for k, v := range byRPC { statsByRPC[k.a] = append(statsByRPC[k.a], &statByName{ Name: k.b, Count: v.count, Cost: v.cost, }) pathStats[k.b] = append(pathStats[k.b], &statByName{ Name: k.a, Count: v.count, Cost: v.cost, }) } for k, v := range statsByRPC { sort.Sort(reverse{v}) statsByRPC[k] = v } pathStatsByCount := statsByName{} for k, v := range pathStats { total := 0 var cost int64 for _, stat := range v { total += stat.Count cost += stat.Cost } sort.Sort(reverse{v}) pathStatsByCount = append(pathStatsByCount, &statByName{ Name: k, Count: total, Cost: cost, SubStats: v, Requests: len(requestByPath[k]), RecentReqs: requestByPath[k], }) } sort.Sort(reverse{pathStatsByCount}) allStatsByCount := statsByName{} for k, v := range byCount { allStatsByCount = append(allStatsByCount, &statByName{ Name: k, Count: v.count, Cost: v.cost, SubStats: statsByRPC[k], }) } sort.Sort(reverse{allStatsByCount}) v := struct { Env map[string]string Requests map[int]*statByName RequestStatsByCount map[int]*statByName AllStatsByCount statsByName PathStatsByCount statsByName }{ Env: map[string]string{ "APPLICATION_ID": appengine.AppID(ctx), }, Requests: requests, AllStatsByCount: allStatsByCount, PathStatsByCount: pathStatsByCount, } _ = templates.ExecuteTemplate(w, "main", v) }
// GetMulti is a batch version of Get. // // dst must be a *[]S, *[]*S, *[]I, []S, []*S, or []I, for some struct type S, // or some interface type I. If *[]I or []I, each element must be a struct pointer. func (g *Goon) GetMulti(dst interface{}) error { keys, err := g.extractKeys(dst, false) // don't allow incomplete keys on a Get request if err != nil { return err } v := reflect.Indirect(reflect.ValueOf(dst)) if g.inTransaction { // todo: support getMultiLimit in transactions return datastore.GetMulti(g.Context, keys, v.Interface()) } var dskeys []*datastore.Key var dsdst []interface{} var dixs []int var memkeys []string var mixs []int g.cacheLock.RLock() for i, key := range keys { m := memkey(key) vi := v.Index(i) if vi.Kind() == reflect.Struct { vi = vi.Addr() } if s, present := g.cache[m]; present { if vi.Kind() == reflect.Interface { vi = vi.Elem() } reflect.Indirect(vi).Set(reflect.Indirect(reflect.ValueOf(s))) } else { memkeys = append(memkeys, m) mixs = append(mixs, i) dskeys = append(dskeys, key) dsdst = append(dsdst, vi.Interface()) dixs = append(dixs, i) } } g.cacheLock.RUnlock() if len(memkeys) == 0 { return nil } multiErr, any := make(appengine.MultiError, len(keys)), false tc, cf := context.WithTimeout(g.Context, MemcacheGetTimeout) memvalues, err := memcache.GetMulti(tc, memkeys) cf() if appengine.IsTimeoutError(err) { g.timeoutError(err) err = nil } else if err != nil { g.error(err) // timing out or another error from memcache isn't something to fail over, but do log it // No memvalues found, prepare the datastore fetch list already prepared above } else if len(memvalues) > 0 { // since memcache fetch was successful, reset the datastore fetch list and repopulate it dskeys = dskeys[:0] dsdst = dsdst[:0] dixs = dixs[:0] // we only want to check the returned map if there weren't any errors // unlike the datastore, memcache will return a smaller map with no error if some of the keys were missed for i, m := range memkeys { d := v.Index(mixs[i]).Interface() if v.Index(mixs[i]).Kind() == reflect.Struct { d = v.Index(mixs[i]).Addr().Interface() } if s, present := memvalues[m]; present { err := deserializeStruct(d, s.Value) if err == datastore.ErrNoSuchEntity { any = true // this flag tells GetMulti to return multiErr later multiErr[mixs[i]] = err } else if err != nil { g.error(err) return err } else { g.putMemory(d) } } else { dskeys = append(dskeys, keys[mixs[i]]) dsdst = append(dsdst, d) dixs = append(dixs, mixs[i]) } } if len(dskeys) == 0 { if any { return realError(multiErr) } return nil } } goroutines := (len(dskeys)-1)/getMultiLimit + 1 var wg sync.WaitGroup wg.Add(goroutines) for i := 0; i < goroutines; i++ { go func(i int) { defer wg.Done() var toCache []interface{} var exists []byte lo := i * getMultiLimit hi := (i + 1) * getMultiLimit if hi > len(dskeys) { hi = len(dskeys) } gmerr := datastore.GetMulti(g.Context, dskeys[lo:hi], dsdst[lo:hi]) if gmerr != nil { any = true // this flag tells GetMulti to return multiErr later merr, ok := gmerr.(appengine.MultiError) if !ok { g.error(gmerr) for j := lo; j < hi; j++ { multiErr[j] = gmerr } return } for i, idx := range dixs[lo:hi] { if merr[i] == nil { toCache = append(toCache, dsdst[lo+i]) exists = append(exists, 1) } else { if merr[i] == datastore.ErrNoSuchEntity { toCache = append(toCache, dsdst[lo+i]) exists = append(exists, 0) } multiErr[idx] = merr[i] } } } else { toCache = append(toCache, dsdst[lo:hi]...) exists = append(exists, bytes.Repeat([]byte{1}, hi-lo)...) } if len(toCache) > 0 { if err := g.putMemcache(toCache, exists); err != nil { g.error(err) // since putMemcache() gives no guarantee it will actually store the data in memcache // we log and swallow this error } } }(i) } wg.Wait() if any { return realError(multiErr) } return nil }