ぽかぽかウンティの自由帳

ぽかぽか動物園から投稿します。

Redis Streamsって何なんです?

Redis5から使えるようになるらしいのですが Introduction to Redis Streams の英語が読めないのですよ...

Kafka的なのを再実装してみた的な? Messaging System ではなく Time Series Store として考えてみた的な? まずは Apache Kafkaに入門した から読んでみるか...メルカリっょぃ

ストリームにキーが付いていて、ストリームの中にエントリが追記型に書き込まれている感じか。

エントリの追加

  • XADD コマンドで追加するらしい。
  • キーの後のアスタはMySQLのAuto Increment的な指定。自分で指定しなければサーバーがエントリIDを自動生成して返してくれる。
    • フォーマット的には <millisecondsTime>-<sequenceNumber> らしい。エントリIDで範囲検索ができるように時間情報が含められているらしい。
    • もちろん自分で指定したければアスタを使わなければ良いが、自分でインクリメントしないといけない。
  • 第3引数以降はキーと値のペアが続く感じでLTSV的に扱えるデータ構造なのかな。
> XADD mystream * sensor-id 1234 temperature 19.8
1518951480106-0

> XADD somestream 0-1 field value
0-1

> XADD somestream 0-1 foo bar
(error) ERR The ID specified in XADD is equal or smaller than the target stream top item

> XADD somestream 0-2 foo bar
0-2

エントリ件数の確認

XLEN コマンドで件数がわかるのか。

> XLEN mystream
(integer) 1

エントリの取得

範囲検索

  • XRANGE コマンドで first と last のエントリIDを指定して範囲検索ができるらしい。最初と最後は含みで。
  • また、特別な記号として - が最小 + が最大を表すので、それらを指定すれば全部取得できるのか。
  • エントリIDをハイフンで分割した際の左側の時間情報だけでも指定できるらしい。その場合はSequence Numberは最初と最後でよろしく引っかけてくれる。
  • COUNT オプションなんてのもあるらしい。take 2 的な。SQLのLIMIT句みたいなものか。返り値の最後のエントリIDをインクリメントして次に指定すればイテレートできるとのこと。
> XRANGE mystream 1518951480106 1518951480107
1) 1) 1518951480106-0
   2) 1) "sensor-id"
      2) "1234"
      3) "temperature"
      4) "19.8"

> XRANGE mystream - +
1) 1) 1518951480106-0
   2) 1) "sensor-id"
      2) "1234"
      3) "temperature"
      4) "19.8"
2) 1) 1518951482479-0
   2) 1) "sensor-id"
      2) "9999"
      3) "temperature"
      4) "18.2"

> XRANGE mystream - + COUNT 2
1) 1) 1519073278252-0
   2) 1) "foo"
      2) "value_1"
2) 1) 1519073279157-0
   2) 1) "foo"
      2) "value_2"

もちろん逆転させたコマンドもある模様。最小最大の引数順序も逆になるので注意。

> XREVRANGE mystream + - COUNT 1
1) 1) 1519073287312-0
   2) 1) "foo"
      2) "value_10"

tail -f や Pub/Sub のSub的な読み込み

  • XREAD というコマンドを使うらしい。ブロッキングリスト?やPub/Subモデルとは似てるけど違うらしい。コンシューマーグループごとに一貫した操作ができるとのこと。
  • COUNT オプションは必須ではない。
  • STREAMS オプションは必須。
    • STREAMS オプションの引数はストリームキーおよび、読み出し済みの最後のエントリIDが続く。
    • つまり我々は読み出し済みの最後のエントリIDを保持してそれをこのコマンドで STREAMS オプションとして指定すれば、それより新しいエントリを読み出せる。
    • STREAMS オプションは最後に指定しないといけないらしい。
> XREAD COUNT 2 STREAMS mystream 0
1) 1) "mystream"
   2) 1) 1) 1519073278252-0
         2) 1) "foo"
            2) "value_1"
      2) 1) 1519073279157-0
         2) 1) "foo"
            2) "value_2"

> XREAD STREAMS mystream otherstream 0 0
(略)
  • 上記はノンブロッキングモードで、できることが範囲検索とあんまり変わらないが、 XREADブロッキングモードを指定できる。
  • BLOCK オプションで指定でき、その引数はタイムアウトのミリ秒。0は無制限を表す。
  • $ という特別な記号は最後のエントリIDを表すらしい。もちろん普通のエントリIDも指定可能。
  • 動作的にはこれが tail -f 的なイメージか。
> XREAD BLOCK 0 STREAMS mystream $
(略)

コンシューマーグループごとの読み込み

  • コンシューマーやコンシューマーグループなどの登場人物のネーミングに関してはKafka由来だが実装は違うとのこと。
  • コンシューマーやコンシューマーグループを駆使すれば並列に処理できてパフォーマンスが上がる感じか。
  • XGROUP でコンシューマーグループ名を指定して作成し XREADGROUP で読み込むらしい。
  • コンシューマー名やコンシューマーグループ名はこちらで指定するものでCase Sensitiveとのこと。
  • コンシューマー名はコンシューマーグループ内で一意でなければならない。
  • XGROUP コマンドは今は既存のストリームキーを指定しないといけないが近い将来 * 指定でストリームが自動生成できるようになるとのこと。
  • XREADGROUP コマンドには GROUP オプションにコンシューマーグループ名を、その後ろにコンシューマー名を指定する。 GROUP <group-name> <consumer-name>
  • XREADGROUP コマンドでは > が特別な意味を持つ記号で、他の?コンシューマーに届いてない新規メッセージだけを表すらしい。
  • もちろんエントリIDも指定できて、その場合は保留中の XACK されていないメッセージを取得できるらしい。
  • XREADGROUP コマンドは読み込み系コマンドであるが副作用があるのでレプリケーションしている場合はマスターノードにしか実行できないとか。
> XGROUP CREATE mystream mygroup $
OK

> XADD mystream * message apple
1526569495631-0

> XADD mystream * message orange
1526569498055-0

> XADD mystream * message strawberry
1526569506935-0

> XADD mystream * message apricot
1526569535168-0

> XADD mystream * message banana
1526569544280-0

> XREADGROUP GROUP mygroup Alice COUNT 1 STREAMS mystream >
1) 1) "mystream"
   2) 1) 1) 1526569495631-0
         2) 1) "message"
            2) "apple"

> XREADGROUP GROUP mygroup Bob COUNT 2 STREAMS mystream >
1) 1) "mystream"
   2) 1) 1) 1526569498055-0
         2) 1) "message"
            2) "orange"
      2) 1) 1526569506935-0
         2) 1) "message"
            2) "strawberry"
> XREADGROUP GROUP mygroup Alice STREAMS mystream 0
1) 1) "mystream"
   2) 1) 1) 1526569495631-0
         2) 1) "message"
            2) "apple"

> XACK mystream mygroup 1526569495631-0
(integer) 1

> XREADGROUP GROUP mygroup Alice STREAMS mystream 0
1) 1) "mystream"
   2) (empty list or set)

ここから先は Zzz...

GIFアニメーションファイルにLGTMフレームを追加するツールを作成した

supercaracal/lgtm-gif-animation

自分がターミナルで使うことを第一に考えて標準入力で受け付けて標準出力する形にした。だからファイルにリダイレクトしないと大変なことになる。

そしてデフォルトで標準エラー出力に画像ファイルのプロパティ情報を出力するお行儀の悪いつくりになっている。嫌だったら /dev/null にリダイレクトすれば良い。

YouTubeでニコ動から転載されたと思われるブロリーMADに最近ハマってて、ブロリーで画像検索したらGIFアニメーション画像がたくさんでてきた。

それらに無理矢理LGTMを捩じ込んで日々のプルリクレビューのコメントで使いたいなと思った。

もちろんLGTMジェネレーターなんて凄い人たちがもうたくさんつくってて、それを使えば良かったのだがせっかくなので自作にチャレンジした。

まずはGIFアニメーションファイルの仕様から調べることになった。それには以下のサイトが役にたった。

LGTMを追加するのでPlain Text Extensionが使えそうだなと思ってしばらく格闘してたが、仕様には載ってるのにビューアがまったく表示してくれなくて日が暮れた。

後日どっかの記事でan obsolete featureと書いてあって諦めた。

次にLGTM画像のデータを用意しないといけないと思ってLZWアルゴリズムを調査した。

しかしLZWアルゴリズムに関しては訳がわからなかった。まったく理解できない。

これらを読もうとしてそっ閉じした。いつか頑張る。

最後にWindowsのペイントを使って128x64のLGTM画像を作成し、それを使うことにした。

必要なのはカラーパレットとデータブロックだけなのでそれだけ抜いて unsigned char の配列でハードコーディングした。

出来栄えは正直ショボい。アニメーションの最後に2秒間だけ中央付近に背景黒の白文字で控え目のLGTMが表示されるだけだ。

時間があったらもっと工夫したいと思う。

とりあえず苦手で訳がわからなかったビット演算には少し慣れた気がする。

腰を痛めた

今までも何回か腰が痛くなったことはあったけど、たいていは一日寝れば治った。

しかし今回のパターンは初めてで前屈姿勢が特につらく、靴下を履く動作が死ぬほど痛かった。

整形外科に初めて通院し、レントゲンを撮ってもらったが骨に異常はなかった。

問診票の「原因として思い当たる節」欄に「座りすぎ」と書いたが、座る姿勢は椎間板に負担がかかるそうだ。

ロキソニンが処方されて痛くなったら飲むということで、これを飲んで数日経っても痛みが引かず、さらに発熱など伴うようならもう一度来てくださいと言われた。

一過性の確率が高いとのことだが、この先はMRIじゃないとわからないらしく、先生の言っていることがただただ怖かった。(ヘルニアやリウマチとかは聞いたことはあるけど...)

とりあえず仕事+勉強+テレビ鑑賞で日に12時間座り続ける生活は見直さないといけなくなった。椅子に投資してても結局ダメだった。

運動はたまにしかしておらず、やったとしても自重の筋トレなので有酸素運動はしていない。心肺機能は年齢+3, 40歳くらいにはなってそうだ...

今回一番危機感があったのは、腰をかばう形で病院まで歩いていたら、ちょっとした段差で左足首を捻った件だ。

実は今は腰よりこの捻挫の方が痛く、一時は右足にしか体重をかけられずに歩行が困難になってしまっていた。寝返りも無理だった。

普段歩いているとちょっとした段差はいちいち見ずに足の動きを自動調整して走破すると思うが、その能力がなくなっていた。

これは引きこもり体質の弊害だ。人間はやっぱ歩かないといけないらしい...

そして今回の痛みで日常生活で腰は色々な場面で使うということがよくわかった。

トイレの便座にもまともに座れないし朝起きて顔を洗うのも一苦労だった。

家にはトイレにだけ手すりがあるが、手すりがこんなに重要だとは思いもしなかった。お年寄りの気持ちが少しわかった... 気がする。

C, C++を読んでGoを書く

なぜいまさら文章を書くのに鉛筆削りの練習が必要な言語を学ぼうとしているのか。

私は業務レベルでは Java PHP Ruby と順番に触れてきたが、なぜ言語ランキングの上位に CC++ が居座り続けるのかがわからなかった。

こんな記事 もあって学習対象とすることに迷いも生まれたが、現実的に数々のOSSC で書かれて使われ続けているのを考えると時代に逆行して学ぶ必要があると考えた。 C++ はゲーム業界で使われていると聞いてこれも対象にした。

とりあえず読めるようになるだけでもコードに使われている様々なテクニックを吸収できると考えている。そして普段の業務(Webサービス開発)では触らない領域のプログラミングでアウトプットを出すことにより、量産型プログラマから脱出できるのではないかと考えている。

アウトプットに Go を選択したが、これは様々なパラダイムを学ぶ機会は後回しにして、とりあえずコンピュータに近いプログラミングをしてみたいからシンプルで動かしやすい言語が望ましい、という理由である。

便利なシャーペンを触っているだけではカチカチするスキルしか身につかない、という危機感がある。

Apache Solr のクエリビルダー Gem を作成した

LSolr という Apache Solr の標準クエリパーサー用のクエリビルダーライブラリを作成した。

RSolr とセットで使うことを意識して LSolr と命名した。左右のLR的な。L は Lucene の L と思えば違和感はない気がする。

Apache Solr の標準クエリはただの文字列でシンプルなので、こんな抽象化なんて本来はしなくて良いのだが、例えばWebの検索UIで色々な条件を指定できる複雑な検索パネルとかが絡んで来ると、単純な文字列操作だけでは実装が厳しくなってくる。

そこで Apache Solr にとっての予約語エスケープや意味のある記号の除去、および複数クエリ断片の合成機能を有するようにした。

実装的には、内部で LinkedList 型のデータ構造を保持する Composite パターンを選択した。

それでも結果的には、ただの文字列を作成するだけなので他の Gem に依存することなくピュア Ruby の1クラスに納まった。でもやっぱ ActiveSupport くらいは使いたかった感ある...

しかし Apache SolrElasticsearch に比べて過疎ってる感があるので、おそらく自分だけが使い、永遠に枯れることはないだろう...

スマホのSIMと機種を変えた

比較項目 変更前 変更後
SIM NTT DoCoMo LINEモバイル
基本使用料 7000円 1200円
機種 SONY Xperia arc HUAWEI P10 lite
機種代 DoCoMoショップで約50,000円 Amazonで27,108円
プリインストールアプリ 多(容量確保のためroot取って消しまくった...) 少(邪魔なのは健康系アプリの2つくらいでそのうち1つは削除可)
CPU 1core 1GHz 4core 2.1GHz + 4core 1.7GHz
メモリ 512MB 3GB
ストレージ 1GB 32GB
Android ver 4.1(自分で焼いた...) 7.0(独自拡張あり...)

乗り換えはすべて自宅からWebで3日くらいでできた。端末もAmazonで買った。今どき店舗に出向いて数時間待たされるのはナンセンスだ。(そういえばガラケーからXperia arcへの機種変のときは東日本大震災の余震があったなぁ...)

NTT DoCoMoさんとは13年続いていた契約だったがワクワクしながら解除した。端末も6年半くらい使用していたが全く未練はない。

アプリとか勉強していこうと思う。(さてどのJVM言語にするか...)

機種

特筆すべきはXperia arcのストレージ1GB。システムがその半分以上を専有し、Googleアプリの共通基盤アプリが200MB以上使うので、残りは150MB程度しかなく、流行りのアプリなんて何も入れられなかったのだ。もちろん更新もできず...

新しい機種を選定するにあたって、個人的にはベゼルがどうとか、カメラ性能がどうとか、はどうでも良かった。 Pixel2Essential Phone は待ちくたびれたし結局日本はハブられてしまったみたいなので諦めた。値段も高いし...

自分はAndroidのOSアップデートを最優先に考えていて、本当は価格的にも Android ONE あたりが好きだったが、機種ラインナップに魅力がなかったので今回は諦めた。

そこで価格.comAmazonのレビューでも現時点でのコスパ最強と言われてるP10 liteを購入した。もちろん中華スマホにはセキュリティ面で抵抗があるが、家のノートPCもLenovoだしそこは既に諦めている。

HUAWEIAndroidにEMUIという独自の拡張をしているらしく、そのせいで今後のOSアップデートがどこまでなのか気がかりではあるが、とりあえずNougatが入っているので数年は安心できそう...

今のところ使い勝手や性能は予想外に最高。CPUは独自開発らしく、一部のゲームで不具合が出るみたいな報告もあるけど、自分はスマホではゲームはやらないから心配なかった。中国はいい意味でヤバくなってきた。日本は悪い意味でヤバいのでは...

SIM

今までMy DoCoMoの時代を感じるマイページUIに慣れていたせいか、 LINE mobile のUIにはすこぶる感動した。さすがは百戦錬磨のユーザーファースト企業だと思った。料金プランもシンプルだ。マニュアルのデザインからもブランディング力を感じる。

My DoCoMoからMNP転出申請し「ご利用ありがとうございました。よろしければアンケートにご協力ください。」とあったので13年間お世話になったから回答しようとリンクを踏んだら「ただいまアンケートはご利用できません」と言われて頭が沸騰しそうになった。

相変わらずNTT DoCoMoの料金プランは複雑で、自分の端末が古くてLTEとか使えなかったからか、どう組んでも最低7000円はかかっちゃうのが理不尽に感じていた。自分の使い方は電話は数ヶ月に1回するかしないか程度で、普段はWiFiGmailやLINEを使うだけだった。それで7000円は高過ぎである。

なので自分の場合はMVNOの方が適していると判断し、やっと乗り換えに踏み切った。まぁサボってるジム代も毎月6000円かかっているんですがね。

Ruby用RedisクライアントGem(redis-rb)をRedis Cluster対応させたい

啖呵を切ったものの1社目の書類選考に落ちて凹んでる者です。そんなことよりこの プルリク の件である。(取り込まれたとは言っていない)

Redis ClusterはRedis公式の水平分割機能でゆううき神の 記事 でも言及されている。 ElastiCache にも使われているみたいだ。

Redis Clusterの詳細な仕様は こちら (読んだとは言っていない)

長い間Rubyの代表的なRedisクライアントソフトである redis-rb はRedis Clusterに対応していなかった。

Redis生みの親のサルバトーレ神の リファレンス実装 があるからみんなそれを使っているのかもしれない。しかしリファレンス実装なためかGem化されておらず、Railsアプリケーションとかだとvendorディレクトリ配下とかに直接配置せねばならず、いまいち本番環境で使うのに躊躇してしまうこともあるだろう。

Redis Clusterへの対応、とは主にリダイレクションに関してだ。Redis Clusterは SETGET などで指定されたキーからスロットと呼ばれる値を算出して格納するノードを確定してデータを分散させている。Redis Clusterのクライアントは基本的にどのノードにもアクセスしてOKだが、例えばノードAに格納されているキー1をノードBにアクセスして GET しようとすると、その返事は「ノードAから取得してね」という具合で、クライアントソフトにリダイレクト処理を要求する。最低限そこだけ実装できれば正常に動いているRedis Clusterを扱えるようになる。

redis-rb はスター数も少なくはないのに、なぜ今まで放置されていたのだろう。意外なところに穴場があったものだ。 プルリク が取り込まれたらうれしいな。