LoginSignup
10
9

More than 5 years have passed since last update.

GolangでKey-Valueリソースのキャッシュと分散合意によるコミットをする

Last updated at Posted at 2016-12-08

概要

Key-Valueの単純なリソースに透過的に様々な機能を付けて使うためのtransparentというライブラリを作りました。
単純というのはGet/Setくらいの操作しかしないことで、リソースのイメージとしてはAmazon S3やファイルシステムあたりを想定してしてもらえれば良いと思います。こういったリソースに対してメモリ上へのキャッシュや、分散システムのような値の複製の機能を簡単に追加できます。

使い方

インストール

go get github.com/juntaki/transparent

基本的な使い方

機能に対応するレイヤーを作って、Stackに積んでいきます。
この例では、ダミーソースに対してファイルシステム上のキャッシュとメモリ上のLRUキャッシュを通して値を操作します。

   cacheLayer1, _ := lru.NewCache(10, 100)
   cacheLayer2 := filesystem.NewCache(10, "/tmp")
   sourceLayer := test.NewSource(10)

   stack := transparent.NewStack()
   stack.Stack(sourceLayer)
   stack.Stack(cacheLayer2)
   stack.Stack(cacheLayer1)

   stack.Start()
   defer stack.Stop()

あとは、Stackを操作するだけです。
値のSet, Syncなどが可能です。

   stack.Set("key", []byte("value"))
   stack.Sync()

Getも可能です。

   value, _ := stack.Get("key")

内部的にはSyncされた値はすべてのLayerから同じ値が取れるようになります。
Syncしない場合は、非同期に浸透していきます。

   value, _ = cacheLayer1.Get("key")
   value, _ = cacheLayer2.Get("key")
   value, _ = sourceLayer.Get("key")

結果はもちろん、全部同じ値がとれます。

result
value
value
value
value

詳細はGodocを参照してください。

レイヤーの種類

Source

最終的に値を書き込むリソースをラップしたレイヤーで、特に何か機能を追加するものではありません。何かアプリを作っている場合は、このあたりの処理はすでに関数化されていると思います。
リソースに対するGet/Add/Removeの関数があるなら、custom.NewStorageから新しいSourceレイヤーを簡単に作成することができます。
また、このレイヤーは最下層にしかStack()できません。

Cache

たとえば、lru.NewCache()はメモリ上のLRUキャッシュに値を覚えるレイヤーを返します。ディスクやネットワークの先に問い合わせるようなレイヤーの上に積むとリソースへのアクセスが高速化されます。一度キャッシュされてしまうと下層を見に行かないので複数のクライアントからSetされるようなリソースでは後述するConsensusレイヤーを使う必要があります。
Getは値が得られるまで、再帰的に下層に問い合わせていきます。Setはキャッシュにセットした段階で戻り、非同期で下層にセットしていきます。終了時など最下層まで伝わったことを確定したい場合はSync()すればOK。

App
 |
Cache -> Storage1
 |
Source -> Storage2

Consensus

twopc.NewConsensus()ではTwo phase commitで複数ノードに対して同時にコミットするConsensusレイヤーを返します。このレイヤーを使うには別途リクエストをさばくCoodinatorノードが必要です。詳細な使い方はconsensus_test.goを参照してください。
ConsensusレイヤーはSetだけしか見てないので、Getがほとんどのケースでは下層のCacheにヒットさせるとアクセスの高速化が期待できます。

障害発生から回復するコードを作ってないので、部分的にでも停止すると全体がブロックされます、ちゃんと使うには未完成です。この実装のためにencoding/gobでinterface{}をProtocol buffersに詰め込む話はこちら

App1                     App2 
  `--------.     .--------'
         Consensus
  .--------'     `--------.     
Cache1 -> Storage1       Cache2 -> Storage2
 |                        |
 |                        |
Source1 -> Storage3      Source2 -> Storage4

使用例

自宅で使っているS3をバックエンドにしたWikiで使っています。DBのことを考えなくて良いので気楽なのですが、読み込みごとにAPIでリクエストするのも何なのでページの内容をキャッシュしています。
ちょっと強引に組み込んだので冗長なコードが多いですが、cache.goにtransparentのCacheを使った実装があります。

おわりに

絶賛開発中ですが、transparent使ってみてください!Githubにスター付けてもらえるとうれしいです。

10
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
9