sunnuntai 19. kesäkuuta 2016

Cache on the background with golang

Caching based on requests

Usually caches on the web are made so that the cache is filled with requests coming in from the browser. Positive about this is that you cache the most used requests and not the ones which are not used. The downside is that in case of errors or timeouts, you really have to think what to do. And what about expiration and the size of the cache?

Caching on the background

If you know what to fetch and you know the requests what to get before hand, you can easily schedule the cache fetching. Although I'm not using a popular cache like LRU here, it's certainly possible.

A simple example

This is a simple example for a cache. You can get, add and remove items from the cache.
package main

import (
    "github.com/streamrail/concurrent-map"
)

var cache cmap.ConcurrentMap

func init() {
    cache = cmap.New()
}

func AddItemToCache(key string, value string) {
    cache.Set(key, value)
}

func GetItemFromCache(key string) string {
    if cache.Has(key) {
        if tmp, ok := cache.Get(key); ok {
            return tmp.(string)
        }
    }
    return ""
}

func removeItem(key string) {
    cache.Remove(key)
}

Let’s get weather information on the background.
func fetchWeather() *Weather {
    var w Weather
    resp, err := client.Get(weatherURL)
    if err != nil {
        fmt.Println(err)
        return nil
    }
    defer resp.Body.Close()
    buf, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println(err)
        return nil
    }
    if err = json.Unmarshal(buf, &w); err != nil {
        fmt.Println(err)
    }
    return &w
}

Marshal it to json string
func weatherToCache() {
    fmt.Println("fetching weather")
    if w := fetchWeather(); w != nil {
        j, err := json.Marshal(&w.Query.Results.Channel.Item)
        if err != nil {
            fmt.Println(err)
        } else {
            AddItemToCache(weatherURL, string(j))
        }
    }
}

and schedule fetching it
func weatherSchedule() {
    for range time.Tick(time.Hour * 1) {
        weatherToCache()
    }
}

In main function We use iris to get the cached weather information after we put the weather scheduling to run on the background.
func main() {
    weatherToCache()
    go weatherSchedule()
    iris.Get("/weather", func(c *iris.Context) {
        c.Text(200, GetItemFromCache(weatherURL))
    })
    iris.Listen(":8080")
}

Used or mentioned dependencies
iris-go.com
Concurrent map
LRU cache

See the whole project at https://github.com/jelinden/go-background-cache-example