// GetMulti is batch version of Get
GetMulti is batch version of Get
// GetMulti is batch version of Get
func (c *Client) GetMulti(ctx context.Context, keys []*datastore.Key, dst interface{}) error {
	vals := reflect.ValueOf(dst)
	if vals.Kind() != reflect.Slice {
		return fmt.Errorf("dscache.Client.GetMulti: dst must be slice of pointers")
	}
	if len(keys) != vals.Len() {
		return fmt.Errorf("dscache.Client.GetMulti: keys and dst must be same length")
	}
	var (
		cKeys       []string
		findInCache []int
		findInDS    []int
		dsKeys      []*datastore.Key
	)
	toCache := make(map[string]interface{})
	keyErrs := make(map[*datastore.Key]error)
	c.localCacheLock.RLock()
	for i := range keys {
		elem := vals.Index(i)
		cKey := cacheKey(keys[i])
		if v, ok := c.localCache[cKey]; ok {
			setValue(keys[i], elem, v)
		} else {
			findInCache = append(findInCache, i)
			cKeys = append(cKeys, cKey)
		}
	}
	c.localCacheLock.RUnlock()
	if len(findInCache) > 0 {
		if bMap, err := c.Cache.GetMulti(cKeys...); err != nil {
			if merr, ok := err.(MultiError); ok {
				for _, i := range findInCache {
					var serr error
					key := keys[i]
					cKey := cacheKey(key)
					val := vals.Index(i)
					if merr.Key(cKey) == nil {
						serr := setValue(key, val, bMap[cKey])
						if serr == nil {
							toCache[cKey] = val.Interface()
						}
					} else {
						serr = merr
					}
					if serr != nil {
						dsKeys = append(dsKeys, key)
						findInDS = append(findInDS, i)
					}
				}
			} else {
				findInDS = findInCache
				for _, i := range findInCache {
					dsKeys = append(dsKeys, keys[i])
				}
			}
		} else {
			for _, i := range findInCache {
				key := keys[i]
				cKey := cacheKey(key)
				val := vals.Index(i)
				serr := setValue(key, val, bMap[cKey])
				if serr == nil {
					toCache[cKey] = val.Interface()
				}
			}
		}
		if len(toCache) > 0 {
			c.putMultiLocalCache(toCache)
			toCache = make(map[string]interface{})
		}
	}
	if len(findInDS) > 0 {
		dsVals := reflect.MakeSlice(reflect.TypeOf(dst), len(dsKeys), len(dsKeys))
		for k, i := range findInDS {
			dsVals.Index(k).Set(vals.Index(i))
		}
		cKeys = make([]string, 0)
		if err := c.DSClient.GetMulti(ctx, dsKeys, dsVals.Interface()); err != nil {
			if merr, ok := err.(datastore.MultiError); ok {
				for i, key := range dsKeys {
					keyErrs[key] = merr[i]
					if merr[i] == nil {
						cKey := cacheKey(key)
						cKeys = append(cKeys, cKey)
						toCache[cKey] = dsVals.Index(i).Interface()
					}
				}
			}
		} else {
			for i, key := range dsKeys {
				cKey := cacheKey(key)
				cKeys = append(cKeys, cKey)
				toCache[cKey] = dsVals.Index(i).Interface()
			}
		}
		for k, i := range findInDS {
			vals.Index(i).Set(dsVals.Index(k))
		}
		if len(toCache) > 0 {
			if err := c.putMultiCache(toCache); err != nil {
				if err2 := c.Cache.Delete(cKeys...); err2 != nil {
					log.Printf("dscache.Client.GetMulti: permanent failure possible stale data for keys: %v err: %v delerr: %v", cKeys, err, err2)
				}
			}
		}
	}
	if len(keyErrs) > 0 {
		realErrs := 0
		merr := make(datastore.MultiError, len(keys))
		for i, key := range keys {
			err := keyErrs[key]
			if err == datastore.ErrNoSuchEntity {
				merr[i] = nil
			} else {
				merr[i] = err
				if err != nil {
					realErrs++
				}
			}
		}
		if realErrs > 0 {
			return merr
		}
	}
	return nil
}
func ( c * Client ) GetMulti ( ctx context . Context , keys [ ] * datastore . Key , dst interface { } ) error { vals := reflect . ValueOf ( dst ) if vals . Kind ( ) != reflect . Slice { return fmt . Errorf ( " " ) } if len ( keys ) != vals . Len ( ) { return fmt . Errorf ( " " ) } var ( cKeys [ ] string findInCache [ ] int findInDS [ ] int dsKeys [ ] * datastore . Key ) toCache := make ( map [ string ] interface { } ) keyErrs := make ( map [ * datastore . Key ] error ) c . localCacheLock . RLock ( ) for i := range keys { elem := vals . Index ( i ) cKey := cacheKey ( keys [ i ] ) if v , ok := c . localCache [ cKey ] ; ok { setValue ( keys [ i ] , elem , v ) } else { findInCache = append ( findInCache , i ) cKeys = append ( cKeys , cKey ) } } c . localCacheLock . RUnlock ( ) if len ( findInCache ) > 0 { if bMap , err := c . Cache . GetMulti ( cKeys ... ) ; err != nil { if merr , ok := err . ( MultiError ) ; ok { for _ , i := range findInCache { var serr error key := keys [ i ] cKey := cacheKey ( key ) val := vals . Index ( i ) if merr . Key ( cKey ) == nil { serr := setValue ( key , val , bMap [ cKey ] ) if serr == nil { toCache [ cKey ] = val . Interface ( ) } } else { serr = merr } if serr != nil { dsKeys = append ( dsKeys , key ) findInDS = append ( findInDS , i ) } } } else { findInDS = findInCache for _ , i := range findInCache { dsKeys = append ( dsKeys , keys [ i ] ) } } } else { for _ , i := range findInCache { key := keys [ i ] cKey := cacheKey ( key ) val := vals . Index ( i ) serr := setValue ( key , val , bMap [ cKey ] ) if serr == nil { toCache [ cKey ] = val . Interface ( ) } } } if len ( toCache ) > 0 { c . putMultiLocalCache ( toCache ) toCache = make ( map [ string ] interface { } ) } } if len ( findInDS ) > 0 { dsVals := reflect . MakeSlice ( reflect . TypeOf ( dst ) , len ( dsKeys ) , len ( dsKeys ) ) for k , i := range findInDS { dsVals . Index ( k ) . Set ( vals . Index ( i ) ) } cKeys = make ( [ ] string , 0 ) if err := c . DSClient . GetMulti ( ctx , dsKeys , dsVals . Interface ( ) ) ; err != nil { if merr , ok := err . ( datastore . MultiError ) ; ok { for i , key := range dsKeys { keyErrs [ key ] = merr [ i ] if merr [ i ] == nil { cKey := cacheKey ( key ) cKeys = append ( cKeys , cKey ) toCache [ cKey ] = dsVals . Index ( i ) . Interface ( ) } } } } else { for i , key := range dsKeys { cKey := cacheKey ( key ) cKeys = append ( cKeys , cKey ) toCache [ cKey ] = dsVals . Index ( i ) . Interface ( ) } } for k , i := range findInDS { vals . Index ( i ) . Set ( dsVals . Index ( k ) ) } if len ( toCache ) > 0 { if err := c . putMultiCache ( toCache ) ; err != nil { if err2 := c . Cache . Delete ( cKeys ... ) ; err2 != nil { log . Printf ( " " , cKeys , err , err2 ) } } } } if len ( keyErrs ) > 0 { realErrs := 0 merr := make ( datastore . MultiError , len ( keys ) ) for i , key := range keys { err := keyErrs [ key ] if err == datastore . ErrNoSuchEntity { merr [ i ] = nil } else { merr [ i ] = err if err != nil { realErrs ++ } } } if realErrs > 0 { return merr } } return nil }
GetMulti is batch version of Get
GetMulti is batch version of Get
// check local cache first // check redis cache // some are found or all failed // failed to get from cache // all found // find in datastore // re-assign results to dst // delete on failure to put into cache // we consider no such entity as not error