Example #1
0
File: app.go Project: 0x7cc/rsc
func updateCacheTime(c appengine.Context, seq int64) {
	const key = rootMemcacheKey
	bseq := []byte(strconv.FormatInt(seq, 10))
	for tries := 0; tries < 10; tries++ {
		item, err := memcache.Get(c, key)
		if err != nil {
			c.Infof("memcache.Get %q: %v", key, err)
			err = memcache.Add(c, &memcache.Item{Key: key, Value: bseq})
			if err == nil {
				c.Infof("memcache.Add %q %q ok", key, bseq)
				return
			}
			c.Infof("memcache.Add %q %q: %v", key, bseq, err)
		}
		v, err := strconv.ParseInt(string(item.Value), 10, 64)
		if err != nil {
			c.Criticalf("memcache.Get %q = %q (%v)", key, item.Value, err)
			return
		}
		if v >= seq {
			return
		}
		item.Value = bseq
		err = memcache.CompareAndSwap(c, item)
		if err == nil {
			c.Infof("memcache.CAS %q %d->%d ok", key, v, seq)
			return
		}
		c.Infof("memcache.CAS %q %d->%d: %v", key, v, seq, err)
	}
	c.Criticalf("repeatedly failed to update root key")
}
Example #2
0
func cacheSafeSet(c appengine.Context, item *memcache.Item) error {
	var buf bytes.Buffer
	err := gob.NewEncoder(&buf).Encode(item.Object)
	if err != nil {
		return err
	}

	swap := item.Value != nil
	item.Value = buf.Bytes()

	if swap {
		err = memcache.CompareAndSwap(c, item)
		switch err {
		case memcache.ErrCASConflict:
			// OK, cache item set by another request
			return nil
		case memcache.ErrNotStored:
			// Item expired. Try adding below.
		default:
			return err
		}
	}

	err = memcache.Add(c, item)
	if err == memcache.ErrNotStored {
		// OK, cache item set by another request
		err = nil
	}
	return err
}
Example #3
0
func (self *channelAPI) sendSlowNotifications(perma_blobref string) {
	// Try 10 times then give up
	for i := 0; i < 10; i++ {
		item, err := memcache.Get(self.c, "notify-"+perma_blobref)
		if err == memcache.ErrCacheMiss {
			// Try to start a task
			if item != nil {
				binary.LittleEndian.PutUint64(item.Value, 1)
				err = memcache.CompareAndSwap(self.c, item)
			} else {
				item = &memcache.Item{Key: "notify-" + perma_blobref, Value: make([]byte, 8)}
				binary.LittleEndian.PutUint64(item.Value, 1)
				err = memcache.Add(self.c, item)
			}
			if err == memcache.ErrCASConflict {
				// Somebody else managed to launch the task first
				return
			}
			// Enqueue the task
			t := taskqueue.NewPOSTTask("/internal/notify", map[string][]string{"perma": {perma_blobref}})
			t.Delay = 30 * 1000000
			if _, err := taskqueue.Add(self.c, t, ""); err != nil {
				log.Printf("ERR: " + err.String())
				return
			}
			return
		} else if err != nil {
			continue
		}
		val := binary.LittleEndian.Uint64(item.Value)
		val++
		binary.LittleEndian.PutUint64(item.Value, val)
		err = memcache.CompareAndSwap(self.c, item)
		// The task is enqueued and has not yet sent notifications
		if err == nil {
			return
		}
	}
}
func bumpGeneration(c appengine.Context) {
	if item, err := memcache.Get(c, generationKey); err == memcache.ErrCacheMiss {
		initialValue, _ := json.Marshal(0)
		newItem := &memcache.Item{
			Key:   generationKey,
			Value: []byte(initialValue),
		}
		memcache.Set(c, newItem)
	} else {
		var oldValue int
		json.Unmarshal(item.Value, &oldValue)
		newValue := oldValue + 1
		item.Value, _ = json.Marshal(newValue)
		memcache.CompareAndSwap(c, item)
	}
}
Example #5
0
File: api.go Project: beaubol/cyph
func channelSetup(h HandlerArgs) (interface{}, int) {
	id := h.Vars["id"]
	channelDescriptor := ""
	status := http.StatusOK

	if len(id) == config.AllowedCyphIdLength && config.AllowedCyphIds.MatchString(id) {
		if item, err := memcache.Get(h.Context, id); err != memcache.ErrCacheMiss {
			oldValue := item.Value
			item.Value = []byte{}

			if err := memcache.CompareAndSwap(h.Context, item); err != memcache.ErrCASConflict {
				valueLines := strings.Split(string(oldValue), "\n")
				timestamp, _ := strconv.ParseInt(valueLines[0], 10, 64)

				if time.Now().Unix()-timestamp < config.NewCyphTimeout {
					channelDescriptor = valueLines[1]
				}
			}
		} else {
			channelDescriptor = h.Request.FormValue("channelDescriptor")

			if len(channelDescriptor) > config.MaxChannelDescriptorLength {
				channelDescriptor = ""
			}

			if channelDescriptor != "" {
				memcache.Set(h.Context, &memcache.Item{
					Key:        id,
					Value:      []byte(strconv.FormatInt(time.Now().Unix(), 10) + "\n" + channelDescriptor),
					Expiration: config.MemcacheExpiration,
				})
			}
		}
	}

	if channelDescriptor == "" {
		status = http.StatusNotFound
	}

	return channelDescriptor, status
}
Example #6
0
/*
CAS will replace expected with replacement in memcache if expected is the current value.
*/
func CAS(c TransactionContext, key string, expected, replacement interface{}) (success bool, err error) {
	keyHash, err := Keyify(key)
	if err != nil {
		return
	}
	var item *memcache.Item
	if item, err = memcache.Get(c, keyHash); err != nil {
		if err == memcache.ErrCacheMiss {
			err = nil
		} else {
			err = errors.Errorf("Error doing Get %#v: %v", keyHash, err)
		}
		return
	}
	var encoded []byte
	if encoded, err = Codec.Marshal(expected); err != nil {
		return
	}
	if bytes.Compare(encoded, item.Value) != 0 {
		success = false
		return
	}
	if encoded, err = Codec.Marshal(replacement); err != nil {
		return
	}
	item.Value = encoded
	if err = memcache.CompareAndSwap(c, item); err != nil {
		if err == memcache.ErrCASConflict {
			err = nil
		} else {
			marshalled, _ := Codec.Marshal(replacement)
			err = errors.Errorf("Error doing CompareAndSwap %#v to %v bytes: %v", item.Key, len(marshalled), err)
		}
		return
	}
	success = true
	return
}
Example #7
0
func CompareAndSwap(c appengine.Context, item *memcache.Item) error {
	return memcache.CompareAndSwap(c, item)
}
// McacheSet is a generic memcache saving function.
// It takes scalars as well as structs.
//
// Integers and strings   are put into the memcache Value []byte
// structs			      are put into the memcache *Object* - using memcache.JSON
// Todo: types WrapString and WrapInt should be handled like string/int
//
// Scalars are tentatively saved using the CAS (compare and save) methods
func McacheSet(c appengine.Context, skey string, str_int_struct interface{}) {

	var err error
	var val string

	tMold := reflect.TypeOf(str_int_struct)
	stMold := tMold.Name()                     // strangely this is empty
	stMold = fmt.Sprintf("%T", str_int_struct) // unlike this

	if stMold != "int" &&
		stMold != "string" &&
		stMold != "dsu.WrapInt" &&
		stMold != "dsu.WrapString" {
		// struct - save it with JSON encoder
		n := tMold.NumField()
		_ = n
		miPut := &memcache.Item{
			Key:        skey,
			Value:      []byte(tMold.Name()), // sadly - value is ignored
			Object:     &str_int_struct,
			Expiration: 3600 * time.Second,
		}
		memcache.JSON.Set(c, miPut)
		c.Infof("mcache set obj key %v[%s]  - err %v", skey, stMold, err)

	} else {
		// scalar value - save it
		switch chamaeleon := str_int_struct.(type) {
		default:
			panic(fmt.Sprintf("only string or int - instead: -%T", str_int_struct))
		case nil:
			val = ""
		case WrapString:
			val = chamaeleon.S
		case string:
			val = chamaeleon
		case int:
			val = util.Itos(chamaeleon)
		case WrapInt:
			val = util.Itos(chamaeleon.I)
		}

		/*
			This is a Compare and Set (CAS) implementation of "set".
			It implements optimistic locking.

			We fetch the item first, then modify it, then put it back.
			We rely on the hidden "casID" of the memcache item,
				to detect intermittent changes by competitors.

			Biggest downside is the additional roundtrip for the fetch.
			Second downside: We should implement a retry after failure.
				Instead I resorted to a simple "SET"

			Upside: Prevention of race conditions.
				But race conditions only matter if newval = f(oldval)
				Otherwise last one wins should apply anyway.

		*/

		maxTries := 3

		miCas, eget := memcache.Get(c, skey) // compare and swap

		for i := 0; i <= maxTries; i++ {

			if i == maxTries {
				panic(fmt.Sprintf("memcache set CAS failed after %v attempts", maxTries))
			}

			var eput error
			var putMode = ""
			if eget != memcache.ErrCacheMiss {
				putMode = "CAS"
				miCas.Value = []byte(val)
				eput = memcache.CompareAndSwap(c, miCas)
			} else {
				putMode = "ADD"
				miCas := &memcache.Item{
					Key:   skey,
					Value: []byte(val),
				}
				eput = memcache.Add(c, miCas)
			}

			if eput == memcache.ErrCASConflict {
				c.Errorf("\t memcache CAS  FAILED - concurrent update?")
				// we brutally fallback to set():
				miCas := &memcache.Item{
					Key:   skey,
					Value: []byte(val),
				}
				eset := memcache.Set(c, miCas)
				util_err.Err_log(eset)
				time.Sleep(10 * time.Millisecond)
				continue
			}
			if eput == memcache.ErrNotStored {
				c.Errorf("\t memcache save FAILED - no idea why it would")
				time.Sleep(10 * time.Millisecond)
				continue
			}

			c.Infof("mcache set scalar %v[%T]=%v - mode %v - eget/eput: %v/%v",
				skey, str_int_struct, val, putMode, eget, eput)
			break
		}

	}

}