概要
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")
結果はもちろん、全部同じ値がとれます。
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にスター付けてもらえるとうれしいです。