Redisライブラリ作りました

2016/03/21 Elixir MeetUp #2 in Drecom

@hagiyat

自己紹介

  • Tomohiro Hagiya
  • @hagiyat
  • 最近マネーフォワードにJoinしました
  • 最近はほぼRailsな毎日です
  • 前職は株式会社nanapi
  • 半年くらい、ほぼ毎日Elixir書いてました

今日の発表について

タイトル通りですが、ElixirでRedisを扱うライブラリを作った話をします

まずお詫び:pray:

前職でやっていた話なので、どういうプロダクトのどういうところで使っていたのか、みたいな話は一切しません

ElixirのRedis事情

Awesome Elixirによると

  • exredis - Redis client for Elixir.
  • red - Persist relationships between objects in Redis, in a graph-like way.
  • redo - Heroku's pipelining redis client for erlang.
  • yar - Yet another Redis client for Elixir.

でなければ

  • eredis - Erlang Redis client.

ElixirのRedis事情

実際のところ、機能的にはexredis一択

  • いいところ:smile:
    • 充実のAPI定義
    • 一通りの機能が揃っている(transaction/pubsubなど)
  • そうでもないところ:fearful:
    • コネクションプーリング機能がないので、毎回clientをstart_linkしてコネクションを作らないといけないので大変
    • transaction/pipeline内ではAPIモジュールは使えない(気がする。Listでコマンドと引数を渡したような?)

ElixirのRedis事情

ちょっとプロダクションコードでガシガシ書くにはつらいかも・・・:astonished:

なければ作るしかない

  • eredisx - Eredisx is a library for writing in Elixir seems syntax an API of Redis.

Redis Weeklyというところで紹介していただいたみたいです:satisfied:

(Elixir MeetUp #1 の@ohrdevの発表を見ていて、今まさにそのへん作ってるよ・・・!と思ってました)

eredisxの特徴

  • Repo概念の導入
  • APIモジュール & APIジェネレーター
  • transaction/piplineをいい感じで書ける
  • redis-object(ruby)風なModel定義

データアクセスライブラリ for Redisのスタンダードっぽくなるように考えました

eredisxの特徴

Repo概念の導入

  • EctoっぽくConnectionをRepoという単位で管理できるようにした
  • かつ、各Repoごとにコネクションプーリングできるようにした
  • databaseごとに(もちろん別ホストでも)Repoがきれたりして便利かなーと思ってます

eredisxの特徴

APIモジュール

ex) Eredisx.Api.Hash.hget

def hget(key, field, options \\ [])

summary: Get the value of a hash field
options:

  • repo: If you want to specify a non-default connection of redis, set the
    name of the connection of redis.
@since: 2.0.0
  • clientは?と思われるかもしれませんが、指定しなければデフォルトのRepoを使ってAPIアクセスします
  • 説明で書いてある通り、optionsにrepoを指定することで別Repoを使用できます

eredisxの特徴

APIジェネレーター

ex) Eredisx.Api.Hash.hget

    generate_apis """
    HGET key field
    summary: Get the value of a hash field
    since: 2.0.0
    """
  • 要はMacroを使ったコードジェネレータです
  • 前ページの関数はこの仕組みで生成されたものです
  • redis-cli help @hashで出力された文字列をペタっと貼るだけ

eredisxの特徴

transaction/piplineをいい感じで書ける

  AnyRepo.pipeline do
    key = "testvalue"
    Eredisx.Api.String.set(key, 100)
    Eredisx.Api.String.incr(key)
    Eredisx.Api.String.set(key, "hogefuga")
  end
  • やっぱりdo-endで書きたい(個人の感想です)のでそうした
  • (Redisに詳しくない方のための補足) 通常は1APIごとに1リクエストが発行されますが、pipelineであれば複数APIの実行が1リクエストで処理されます

eredisxの特徴

redis-object(ruby)風なModel定義

defmodule User.Details do
  use Eredisx.Model.Hash,
    repo: AnyRepo, # デフォルト以外のRepo指定ができる
    keyformat: "user:#id#:details",
    sequence: "user:id",
    schema: [
      nickname: "",
      latest_login_at: Timex.Time.now(:secs),
      updated_at: Timex.Time.now(:secs),
    ] # この定義がdefstructで展開される
end

User.Details.find("user:1:details") # %User{}

eredisxの特徴

redis-object(ruby)風なModel定義

  • model単位でrepoの指定ができます
  • シーケンシャルキーの発行をkeyformat sequence の定義によりある程度意識しなくてすむ
  • Hash型の場合、schema定義によりStructでのデータのやりとりができるようになります
  • find/saveといった、あまりRedisAPIを意識せずにすむような関数を用意しました

特徴としてはこんな感じです

もう少しだけ、狭いスコープの細かい話をします

APIジェネレーター

  • repoやtransactionの概念を取り込むことを考えた時、最初はeredisxなどというものは作るつもりがなく、exredisにpull requestして拡張しようと思っていた
  • しかし、根本な部分を変えすぎるパッチになってしまったので、やっぱり新しく作ろうと思った
  • しかし・・APIは使いたいけど自分で全部実装してかつテストも書かなきゃいけないなんてダルすぎる

そうだ:bulb:

Macroでコード生成して、APIを網羅すればいいじゃない

soreda

APIジェネレーター

  • ドキュメントからコードを生成できるというところがセールスポイントのひとつです
  • Elixirなら、コードジェネレータの結果をコンパイル済みのライブラリとして提供できる
  • つまり、昨今のしっかり整備されているAPIであれば、スクレイピングでもしてドキュメントをとってくれば同じことができる
  • バージョンアップへの追従など、もしかしたらすごくメンテナンスコストの低い仕組みになるんじゃないか!?

みなさまのご意見ご感想、お待ちしております:raising_hand:

切り捨てたポイント

  • Pub/Sub
    • pubsubするためだけの専用のライブラリあるし、そっちと組み合わせて使った方がいいと思います
  • Scripting
  • あとはflushdbとかmonitorとか、redis-cliでしか使わないだろうと思ったものは入れないつもり

機能的にはこんな感じです

が・・・

gomennasai

  • 実はまだ完成していません
  • あたかも完成品みたいな体の発表にしてしまったことをお詫びします

できてないポイント:scream:

  • テストないです
    • 前述のMacroのテストどうやるんだろう・・っていうところから何も書けないままここまで来てしまいました
  • Repoの仕組みはまだ未完成
    • このライブラリの生い立ち的に、とあるプロダクトにベッタリだった実装を引き剥がしたライブラリなので、Repoの仕組みは後付けだったのです
  • 現状の地獄のようなMacroのコードをきれいにする
  • Hexに登録

まとめ

  • Eredisxではcontributorを募集しています
  • Eredisxではライブラリアイコン・ロゴを募集しています
  • Eredisxではsuponsorを募集しています
  • Elixirたのしい:heart_eyes:

おわり

ご清聴ありがとうございました