## gomemcache

This is a memcache client library for the Go programming language. It is similar to the [Google app engine version of memcache implementation](http://code.google.com/appengine/docs/go/memcache/reference.html) and [bradfitz's version of gomemcache](https://github.com/bradfitz/gomemcache), but this library supports memcached clusters with the [Auto Discovery feature](http://docs.aws.amazon.com/AmazonElastiCache/latest/UserGuide/AutoDiscovery.html). It also allows for [consistent hashing](https://en.wikipedia.org/wiki/Consistent_hashing), making it easy to deal with resizing of your memcached cluster.

### Godoc
[foundation/gomemcache Godoc](https://godoc.internal.justin.tv/code.justin.tv/foundation/gomemcache/memcache)

### Supported features (more coming...)
- [Get](https://godoc.internal.justin.tv/code.justin.tv/foundation/gomemcache/memcache#Client.Get)
- [GetMulti](https://godoc.internal.justin.tv/code.justin.tv/foundation/gomemcache/memcache#Client.GetMulti)
- [Delete](https://godoc.internal.justin.tv/code.justin.tv/foundation/gomemcache/memcache#Client.Delete)
- [DeleteAll](https://godoc.internal.justin.tv/code.justin.tv/foundation/gomemcache/memcache#Client.DeleteAll)
- [Set](https://godoc.internal.justin.tv/code.justin.tv/foundation/gomemcache/memcache#Client.Set) (with overwrite)
- [Add](https://godoc.internal.justin.tv/code.justin.tv/foundation/gomemcache/memcache#Client.Add) (without overwrite)
- [Replace](https://godoc.internal.justin.tv/code.justin.tv/foundation/gomemcache/memcache#Client.Replace) (replace only if server hold the key)
- [Touch](https://godoc.internal.justin.tv/code.justin.tv/foundation/gomemcache/memcache#Client.Touch) (updates the expiry for the given key)
- [Increment](https://godoc.internal.justin.tv/code.justin.tv/foundation/gomemcache/memcache#Client.Increment)
- Decrement
- [FlushAll](https://godoc.internal.justin.tv/code.justin.tv/foundation/gomemcache/memcache#Client.FlushAll)
- [CompareAndSwap](https://godoc.internal.justin.tv/code.justin.tv/foundation/gomemcache/memcache#Client.CompareAndSwap)
- ...


### Quick Start
```
// Conigure with/without autodiscovery feature
func configureMemcached() *memcache.Client {
	addr := config.MustResolve("memcached-host-ports")

	var c *memcache.Client
	if config.MustResolve("memcached-autodiscovery") == "true" {
		c, _ = memcache.Elasticache(addr, 60*time.Second)
	} else {
		c = memcache.New(strings.Split(addr, ",")...)
	}

	c = c.MaxIdleConns(configResolveInt("memcached-conn"))
	c.Timeout = configResolveDuration("memcached-timeout")
	return c
}

// Simple get from cache
func GetProperties(key string, prop interface{}) error {
    memcached := configureMemcached()
	item, err := memcached.Get(cacheKey)
	if err != nil {
		if err == memcache.ErrCacheMiss {
			return backend.ErrCacheMiss
		} else {
			return err
		}
	}

	return json.Unmarshal(item.Value, prop)
}

// Simple set to cache
func CacheProperties(overwrite bool, key string, prop interface{}) error {
	valueBytes, err := json.Marshal(prop)
	if err != nil {
		return err
	}

    item := &memcache.Item{
			Expiration: c.Expiration,
			Key:        key,
			Value:      valueBytes,
			Flags:      0,
		}
		if overwrite {
			err = c.Memcache.Set(item)
		} else {
			err = c.Memcache.Add(item)
            if err == memcache.ErrNotStored {
                return backend.ErrConflict // Expected error, should be handled properly
            }
		}

	return err
```

#### Specifying a custom dialer

Use the `Factory` struct to configure a custom dialer for your client. You can use a custom dialer to do things like communicate with a cluster in another VPC via the SOCKS proxy dialer in `golang.org/x/net/proxy`.

```
var d net.Dialer
pd := proxy.FromEnvironmentUsing(&d)
cd, ok := pd.(proxy.ContextDialer)
if !ok {
	panic()
}

f := &memcache.Factory{
	Timeout:     100 * time.Millisecond,
	DialContext: cd.DialContext,
}
client := f.StaticEndpoints("my-remote-memcache.xarth.tv:11211")
client.Get(context.TODO(), "myKey")
```

## Cache replication
There are usecases when memcache data is replicated across multiple nodes. When using this, set operations must be called on all nodes, but get operation on any one server returns the item. This should be used when read to write ratio is very high.
This does not currently support sharding, but that is coming soon.

```
// Simple get from random node in cache
func GetProperties(key string, prop interface{}) error {
    memcached := configureMemcached()
	item, err := memcached.GetFromRandom(cacheKey)
	if err != nil {
		if err == memcache.ErrCacheMiss {
			return backend.ErrCacheMiss
		} else {
			return err
		}
	}

	return json.Unmarshal(item.Value, prop)
}

// Simple set all nodes in cache
func CacheProperties(overwrite bool, key string, prop interface{}) error {
	valueBytes, err := json.Marshal(prop)
	if err != nil {
		return err
	}

	item := &memcache.Item{
			Expiration: c.Expiration,
			Key:        key,
			Value:      valueBytes,
			Flags:      0,
		}
	err = c.Memcache.SetAll(item)

	return err
}
```

### Example
- [Visage caching](https://git-aws.internal.justin.tv/edge/visage/blob/master/internal/clients/caching/memcache.go#L38)

### Projects using foundation/gomemcache

 - [edge/visage](https://git-aws.internal.justin.tv/edge/visage) (As both cache and rate-limiting data store)
 - [web/users-service](https://git-aws.internal.justin.tv/web/users-service) (As caching layer)

### Contributing
Please read [Guide to contribute](CONTRIBUTING.md)
