blog.syfm

徒然なるままに考えていることなどを書いていくブログ

GitHub に草を生やし続けて一年が経った

去年の今頃から毎日 GitHub に commit しようと決心し、最近 365 日を達成しました!

f:id:ktr_0731:20171130052118p:plainf:id:ktr_0731:20171130052142p:plain
いい感じ

なぜかデフォルト表示の方の左上が白くなってますが、ちゃんと一年経っています。

意図

そもそもなぜ始めようとしたかというと、技術の習得には input と output が大事で、input で得たものが output によって定着すると思っていたからです。
output の手段には、ブログや Qiita を書いたり、LT だとかハッカソンでもいいかもしれません。実際、自分も LT でよく発表したりしています。
とはいえ、それらはどのくらい output できているかの可視化ができないので、モチベーションを保つのは少し大変かもしれないと思っていました。
そのため、GitHub の草を output の可視化として利用したらいい感じになるんじゃないかなと思って始めました 🙌

今年の春ごろに LT でこのことについて発表していたので、それも貼っておきます

speakerdeck.com

どう変わったか?

以下は草を生やし始めたころの画像です

f:id:ktr_0731:20171130052957p:plain

この頃と比べて 2.6 倍くらいコミット数が増えました。
また、今年作ったリポジトリの数は、プライベートなものも含めると大体 70 個でした。
ライブラリみたいなものもあれば、コマンドラインツールや、Web アプリのフロント/サーバ、そもそもソフトウェアではないものもあります。
アルバイト先でも GitHub を利用しているので、そのコミットも入っています。

毎日コードを書く時間を意図的に確保することで必然的にコードと向かい合う習慣が付くので、あまり苦に感じたりはしませんでした。
また、当然 OSS を触るので、自分が書いたコードを読む/新しく書く以外にも、OSS の実装を読んだり、バグを踏んだものを fork、修正して Pull Request を送ったりもしていました。
OSS と聞くとハードルが高そうな感じがしますが、実際はあんまりそういうことはなくて、ちょっとしたバグであれば一時間もかからずに直せたりします。

ちなみに、今年パッチを送った最大のソフトウェアは Go でした!(これも一瞬で修正できました ✨)

syfm.hatenablog.com

また、モチベーションを上げるために pocke さんのつくったサービスである、Shibart で草グッズを作ったりもしていましたが、めちゃくちゃ愛着が湧くのでおすすめです

f:id:ktr_0731:20171130055310p:plain

shibart.pocke.me

悪かった点

毎日というと、旅行や病気などで、どうしてもコードを書けないときが出てきます。
そういう時は当然ながら dotfiles をいじったりするだけだったりしてあまり意味のあることはできませんでした。
毎日しっかりコードを書く、というのは正直限界があって、そこをどうするかは結構大切な気がします。
連続 commit 日数が途切れてもいいならば、無理やり commit する必要もないと思います。
自分の場合は、上述のとおり、dotfiles をいじったり、作りたいものを issue / project として管理するリポジトリを用意してそこに新しく登録したりしていました。

改善すべき点もありましたが、これからも続く限りやっていきたいと思っています 💪
モチベーションが上がらない人はぜひ試してみて下さい

より気軽に、簡単にテストができる gRPC クライアントをつくった

先日の A(izu)LT 0x09 で「Evans: more expressive universal gRPC client」 というタイトルで発表してきました。
タイトルの通り、gRPC クライアントをつくった話です 🙌

atnd.org

ALT は、会津大学の有志によって年に二回ほど開催されている LT (Lightning Talk) 会で、次回でついに 0x0a 回目を迎えます!めでたい 🎉

発表したスライドはアップロードしています。大体のことは以下に載っているけど、ちょっと補足をしておきたいと思います。

speakerdeck.com

つくったもの

github.com

Evans の目指したもの

Evans は既存の gRPC クライアントより簡単に扱えることを第一目標に作っています。
(gRPC に限らずですが、) gRPC を使っているプロダクト自体の品質を保ちたければ、gRPC を使ったテストも書いて CI で回せばいい話なので、gRPC クライアントはそれ以外のユースケースを補うためにあるべきだと思っています。

では補うべき部分とは一体なんなのかというと、

  • スクリプトによって何かのタスクを自動化したい
  • API を手動で検証したい

この二つだと思っています。
そして、この二つはそれぞれ以下の二つのモードと対応しています。

🖥️ コマンドラインモード

コマンドラインモードは、その名の通り一回のコマンドのみで完結するステートレスなモードで、既存の gRPC クライアントの Polyglot が採用している方式でもあります。

github.com

コマンドラインモードは、単なるコマンドなので、UNIX 哲学に則ってつくっています。
例えば、stdin から入力を受け取ることでフィルターとしての役割を果たすことができますし、その一方で出力は stdout へ JSON 形式で行われます。そのため、jq コマンドなどで適当に加工したりすることが可能です。

$ echo '{ "name": "ktr" }' | evans --package hello --service Greeter --call SayHello hello.proto | jq -r '.message'
# hello, ktr!

また、ファイルへ入力の JSON を保存して、何度も使いたい場合などのケースのために --file (-f) オプションを用意しており、こちらでも同じような動作ができます。

$ cat hello.json
{
  "name": "ktr"
}

$ evans -f hello.json --package hello --service Greeter --call SayHello hello.proto | jq -r '.message'
# hello, ktr!

ちなみに、コマンドラインモードでは事前に使用する package、service、RPC 名を知っている必要があるため、インタラクティブモードとは違い引数を省略できません。しかし、Git プロジェクトルートに .evans.toml がある場合は、デフォルトで使用するものを記述することができます。

[default]
package = "hello"
service = "Greeter"

こうすると以下のようにシンプルにコマンドを指定できます

$ evans -f hello.json --call SayHello hello.proto

🤼 インタラクティブモード

インタラクティブモードは、「API を手動で検証したい」時のためのモードです。
こちらの方は、なにも考えなくても使えるようにつくっています。
補完機能が使えるため、package 名や service 名、RPC 名を覚えていなくても大丈夫です!
このモードは MySQL のクライアントである、 dbcli/mycli を意識しています。

github.com

実際のデモはこんな感じです 👇

demo

読み込んでいる proto ファイル

syntax = "proto3";

package helloworld;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloResponse) {}
}

enum Language {
  ENGLISH = 0;
  JAPANESE = 1;
}

message Person {
  string name = 1;
  repeated string others = 2;
}

message HelloRequest {
  Person person = 1;
  Language language = 2;
}

message HelloResponse {
  string message = 1;
}

サーバの実装

package main

import (
    "fmt"
    "log"
    "net"
    "strings"

    "golang.org/x/net/context"

    helloworld "github.com/ktr0731/evans/tmp"
    "google.golang.org/grpc"
)

type Greeter struct{}

func (t *Greeter) SayHello(ctx context.Context, req *helloworld.HelloRequest) (*helloworld.HelloResponse, error) {
    msg := "Hello, %s also %s!"
    if req.GetLanguage() == helloworld.Language_JAPANESE {
        msg = "こんにちは、%s と %s !"
    }
    return &helloworld.HelloResponse{
        Message: fmt.Sprintf(msg, req.GetPerson().GetName(), strings.Join(req.GetPerson().GetOthers(), ", ")),
    }, nil
}

func main() {
    l, err := net.Listen("tcp", "localhost:50051")
    if err != nil {
        log.Fatal(err)
    }

    server := grpc.NewServer()
    helloworld.RegisterGreeterServer(server, &Greeter{})
    if err := server.Serve(l); err != nil {
        log.Fatal(err)
    }
}

べんりですね。

インストール

macOS / Linux 向けのバイナリが GitHub Releases にあるので、それを使えます。
もちろん、go get で取得することもできます。
macOS であれば、brew を使ってのインストールもできます 🍺

$ brew tap ktr0731/evans
$ brew install evans

Issue / PR

このツールは LT 駆動開発によって誕生したので、テストがありません 😱
なので、一旦新規の機能開発はやめてテストを充実させようと思っています 🙇‍♂️
その後、スライドでも言及したような TODO タスクを消化していきたいと思っています。
もし使っていただける方がいましたら、ぜひ Issue でも PR でもどんどんいただけたらありがたいです!

ISUCON7 に出た

土曜日に ISUCON7 に出ていた。
チームは、自分と、同じ大学の友人一人。本当はもう一人いる予定だったけど、別な予定と被っていたらしく、二人のチームになった。

トラブルがあったようで、開始の時間が遅れて 13:00 からになったりと、運営の方もとても大変そうだった…。お疲れ様でした!
点数は2万点くらいで、なかなか残念な結果。事前に過去問を解いておくべきだったと思う。
やったことは、icons を MySQL から出すようにするのが一番大きかった気がする。
なぜか MySQL に icon がバイナリごと突っ込まれていて、これが一番のボトルネックになっていた。
なので、DB から取り出そうということになって、一番簡単であろうファイルへの書き出しを行った。
GET /icons/hoge の時は nginx の try_files ディレクティブでディレクトリに存在するか確認して、なければアプリに飛ばすようにし、アプリでは DB からの取り出しとともに書き出しも行うようにして以降はそのファイルが使われるように変更した。
Redis みたいな KVS でキャッシュすればもっと早くなったのかなーとか後で思ったりもした。

他にも、インデックスが貼られていないカラムにインデックスを貼ったり、N+1 を解消したりしていた。

使っていた言語は Go で、サーバ上で上手くビルドできなかった (vendoring ができていなかった?) ので、早々に諦めてローカルでビルドして scp を使って頑張っていた。
あと、ページングで OFFSET が使われていたので、これをどうにかしたかったけど、実装力と時間がなくて間に合わなかった。
普段開発しているとあまり時間に追われることがないので問題にならなかったけど、実装力が低くてつらい。

一番深刻なのが、nginx に関する知識の欠如とパフォーマンス計測の勘所が全然わからなかったことだと思う。
普段手を動かしていないことを本番でやろうとしてもできないということを身をもって痛感した。

この辺の反省を活かして来年は頑張っていきたい 💪