Allow expiring data, and allow serving stale data if expired and a deadline is not met.

This adds expiration functionality, and functionality to allow serving stale data if a regeneration deadline is not met.

CHANGE IMPACT

The change is backwards compatible with the current API, and has almost no impact on the current code path when the functionality is not explicitly enabled.

To effect this change a supplied timestamp is packed into the to-be-cached data using a new Sink method SetTimestampBytes().  The timestamp is inspected on a cache hit only if an expiration time is set.  The user unpacks their original data and timestamp using a helper method.  In this way, expiration functionality is obtained with minimal changes to the original code.

If not enabled, the impact on the current code path at the machine level is: a load and a compare after a cache hit, additional stack space to pass down a boolean into getFromPeer(), and one more compare in getFromPeer().  The additional latency and memory use should be nearly undetectable.

USE

Use like this:

Set expiration on group:

 myGroup.SetExpiration(duration)

In getter:

func(gctx groupcache.Context, key string, dest groupcache.Sink) error {
    content = MakeMyContent(key)
    return dest.SetTimestampBytes(content, groupcache.GetTime())
}

In call to Get() use AllocatingByteSliceSink, StringSink, or ByteViewSink, then unpack returned result.  Using an unsupported sink implementation will produce an error when calling Get().

 var packedContent []byte
 var timestamp int64
 if err := gcache.Get(nil, key, groupcache.AllocatingByteSliceSink(&packedContent)); err != nil {
 	handleErr()
 }
content, timestamp, err := groupcache.UnpackTimestamp(packedContent)
if err != nil {
  handleErr()
}