読者です 読者をやめる 読者になる 読者になる

timakin.log

╭( ・ㅂ・)و ̑̑

Goのインメモリキャッシュ用ライブラリ「go-cache」のご紹介

github.com

利用した動機

Perlで言うCache::Memory::Simpleのように、

メモリキャッシュを取り扱うためのライブラリを探してたら、

速攻見つかったので利用してみた。

ユースケース

今GithubAPIを利用したCLIツールを作っている。

その際にユーザーのリポジトリ情報とか取ってくるんだけど、 それを一度ならず繰り返し叩きに行く。

for分回している中で関数を呼んで、その中でAPI叩きに行く実装が入ってたら死ぬ。

なので結果を都度キャッシュしておけるようにgo-cacheを利用した。

内部実装

中身、非常に簡単で、Set用のキーをそのままcacheが内部で持ってるmapのキーとして使ってる。

この中にはunnamed typeなobjectと、有効期限を表すintを持った構造体が入ってる。

どこかに共通のキャッシュインスタンスみたいなのを持っておけば、有効期限まではその中のmapを参照して、キャッシュを得ることができる。

利用方法

あるプロセスの中で繰り返しキャッシュを参照したい場合は、一つのキャッシュ用のインスタンスを利用する必要があるので、以下のようにした。

// キャッシュを利用する場合にそれを格納するためのインスタンスを作る
type Instance struct {
    cache *cache.Cache
    client *github.Client 
}

// インスタンス作成用のメソッド
func New() *Instance {
    httpClient := newAuthenticatedClient()
    client := github.NewClient(httpClient) 

    I := &Instance{
      // 特定のキャッシュを5分間だけ残しておく
      // 30秒ごとに失効済みキャッシュを廃棄する
        cache: cache.New(5*time.Minute, 30*time.Second),
        client: client,
    }

    return I
}

// 自分のユースケースだとGithubのNotification情報を取ってくるので、こんな感じ
func (i Instance) GetNotifications() []github.Notification {

  // cv(cached valueの略のつもり...)
  // キャッシュがあれば取ってきてそれを返す。
  // go-cacheでは第2の戻り値に、キャッシュの存在有無のフラグ(以下ではfound)が入ってくる。
    if cv, found := i.cache.Get("notifications"); found {
        cachedNotifications := cv.([]github.Notification)
        return cachedNotifications
    }
    
    // APIからのresponse
    opt := &github.NotificationListOptions{All: true}
    notifications, _, err := i.client.Activity.ListNotifications(opt)
    if err != nil {
        log.Fatal(err)
    }
    
    // キャッシュがなかった場合は次回のためにSetする
    i.cache.Set("notifications", notifications, cache.DefaultExpiration)
    return notifications
}

総括

Goの利用ケースってjson等々の返却用のAPIか、CLIを作るような気がする、

というかそんなことをGopher Nightで渋川よしきさんがおっしゃってるし、

僕とかもCLIツール作るときに利用してる。

www.slideshare.net

自分が今作ってるツールだと、並行して複数APIを叩くことがないので、

goroutineを使うまでもないけど、そのような場合でもgo-cacheを使うことができれば

尚一層早く処理が終わるのではと考えていて、これはよいものだと思う。