Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?

In this piece of code I'm trying to:

  • Check the local cache to see if the value exists

  • If not, use singleflight to ensure one request is made to fetch the value

  • Fetch the value from a third party API

  • Store that value in the cache.

func (vc *client) getAndCacheMessage(ctx context.Context, start, end time.Time, data url.Values) (*CacheResult, error) {
	page, err := vc.client.Messages.GetMessagesInRange(start, end, data).Next(ctx)
	if err != nil {
		return nil, err
	}
	key := hash("messages", data.Encode(), start, end)
	vc.cache.Set(key, page, frontPageTimeout)
	return &CacheResult{Value: page}, nil
}

func (vc *client) cacheToMsg(user *config.User, val interface{}) (*MessagePage, uint64, error) {
	result, ok := val.(*CacheResult)
	if !ok {
		return nil, 0, errors.New("Could not cast fetch result to a CacheResult")
	}
	page, ok := result.Value.(*twilio.MessagePage)
	if !ok {
		return nil, 0, errors.New("Could not cast fetch result to a MessagePage")
	}
	mp, err := NewMessagePage(page, vc.permission, user)
	return mp, result.Time, err
}

func (vc *client) GetMessagePageInRange(ctx context.Context, user *config.User, start time.Time, end time.Time, data url.Values) (*MessagePage, uint64, error) {
	key := hash("messages", data.Encode(), start, end)
	val, err := vc.group.Do(key, func() (interface{}, error) {
		page := new(twilio.MessagePage)
		t, err := vc.cache.Get(key, page)
		if err == nil {
			return &CacheResult{t, page}, nil
		}
		return vc.getAndCacheMessage(ctx, start, end, data)
	})
	if err != nil {
		return nil, 0, err
	}
	return vc.cacheToMsg(user, val)
}

The code is here: https://github.com/kevinburke/logrole/blob/master/views/client.go#L272-L299

Of the function calls to accomplish this, the following require interfaces:

  • Checking the local cache. The API is:

    // Get gets the value at the key and decodes it into val. Returns the time
    // the value was stored in the cache, or an error, if the value was not
    // found, expired, or could not be decoded into val.
    func (c *Cache) Get(key string, val interface{}) (uint64, error)
  • Using singleflight. The API is:

    // Do executes and returns the results of the given function, making sure
    // that only one execution is in-flight for a given key at a time. If a
    // duplicate comes in, the duplicate caller waits for the original to
    // complete and receives the same results.
    func (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, error)
  • Fetching the value from the third party API. This does not require interfaces, though there is this deeper in the API client.

    // GetResource retrieves an instance resource with the given path part (e.g.
    // "/Messages") and sid (e.g. "MM123").
    func (c *Client) GetResource(ctx context.Context, pathPart string, sid string, v interface{}) error
    
    // CreateResource makes a POST request to the given resource.
    func (c *Client) CreateResource(ctx context.Context, pathPart string, data url.Values, v interface{}) error {
    
    func (c *Client) UpdateResource(ctx context.Context, pathPart string, sid string, data url.Values, v interface{}) error
    
    func (c *Client) ListResource(ctx context.Context, pathPart string, data url.Values, v interface{}) error {
  • Store the value in the cache. The API is:

    func (c *Cache) Set(key string, val interface{}, timeout time.Duration)

All of these required interface{} in some places; it would be nice if that wasn't as necessary.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.