2019年の振り返り
1月, 2月
前年に引き続き、リアルタイム通信サーバ・クライアントの開発をやってた。
3月
3月の途中で新卒から4年間働いていた会社を退職して、有給消化期間にギリシャに旅行に行った。
クレタ島 というところにほとんどいたが、まだ寒かったのであまり人もいなくて快適だった。
4月
今の会社に転職した。
直前2年くらいC++で開発をやってたのが、久しぶりにRailsでの開発になったので、少し懐かしい感じがあった。
5月
いろいろ好き勝手やってた気がする。
6月, 7月, 8月, 9月
ちょっと大きめの機能開発をやった。
すでに5年以上メンテナンスされているシステムのロジックと折り合いをつけながら開発するのが結構大変だった。
10月, 11月, 12月
Railsでの開発からはちょっと離れて、Goでのサーバ開発をやり始めた。
社会人になってからRails、C++、RailsときてたのでGoは初めてだったが、最初は書きやすいC言語みたいな雰囲気を感じてた(なんでこの言語がWeb系でよく扱われるようになったのかが分かってない)が、Ruby(というかRails)よりも表現力がちょっと乏しくどうしても記述量が増えちゃう感じが否めないと思った。
が、一方で事前にコンパイルすることで実行前に一定のミスに気づくことができたり、実行時間が速いというメリットも感じることができたので、来年も引き続き使って試したい。
イロイロと、試したい・やってみたいと感じたことは見つかってきたので、来年は実際に行動に移してアウトプットするところまでする。
ruby-style-guideを読んだら知らないことがイロイロあって勉強になった
2年ぶりに業務でRubyを書きはじめたので、改めてruby-style-guideを読んだら知らなかったことがイロイロあったのでメモ。
2019/02/28当時の日本語版: 2bac495を読みました。
1. 本文のないクラスは1行のフォーマットを用いましょう。
# 悪い例 class FooError < StandardError end # 悪くない例 class FooError < StandardError; end # 良い例 FooError = Class.new(StandardError)
「悪くない例」を書きがちだったので。
2. forは、どうしても使わなければいけない明確な理由が明言できる人以外は、使ってはいけません。 多くの場合は代わりにイテレータを使うべきです。 forはeachをつかって実装されています(だから、より遠回しです)が、 forは(eachと違い)新しいスコープを導入せず、 そのブロック内で定義された変数は、ブロックの外からも見えます。
arr = [1, 2, 3] # 悪い例 for elem in arr do puts elem end # elemはループの外からも参照できることに注意しましょう elem # => 3 # 良い例 arr.each { |elem| puts elem } # elemはeachブロックの外からは参照できません elem # => NameError: undefined local variable or method `elem'
なんとなく、「Rubyでfor文は使わないものだ」という認識はあったし、実際for文は使ったことがなかったけど、なんで使わないのか、避けるべき理由は何なのかを意識していませんでした。
↑の例に書いてあるようにfor文内でつかった変数が、外に漏れ出てくるのはなんかのきっかけでハマりそうだし、やっぱりfor文は避けるべきなんだなと思いました。
3. あまりに暗号めいている String#%
メソッドよりも sprintf
や format
を使いましょう。
# 悪い例 '%d %d' % [20, 10] # => '20 10' # 良い例 sprintf('%d %d', 20, 10) # => '20 10' # 良い例 sprintf('%<first>d %<second>d', first: 20, second: 10) # => '20 10' format('%d %d', 20, 10) # => '20 10' # 良い例 format('%<first>d %<second>d', first: 20, second: 10) # => '20 10'
単純に String#%
の存在を知らなかったです。 (推奨されてないし、あえてこれから使おうとは思わないですが)
4. 名前付きフォーマット文字列を使用する場合、 %{name}
よりも %<name>s
を使いましょう。 %<name>s
は値の型に関する情報をエンコードするためです。
# 悪い例 format('Hello, %{name}', name: 'John') # 良い例 format('Hello, %<name>s', name: 'John')
どっちの記法も知りませんでした。
5. あまりに暗号めいている Array#*
メソッドよりも Array#join
を使いましょう。
# 悪い例 %w[one two three] * ', ' # => 'one, two, three' # 良い例 %w[one two three].join(', ') # => 'one, two, three'
Array#*
を知りませんでした。(使わないけど)
6. size
の代わりに count
を用いてはいけません。 Array
以外の Enumerable
オブジェクトでは、 count
を使うと要素数の計算のためにコレクション全体を走査してしまいます。
# 悪い例 some_hash.count # 良い例 some_hash.size
これも知りませんでした。
標準ライブラリはこのルールに従うかもですが、ActiveRecord とか外部ライブラリだとまたルールが異なってきそうなので、結局のところdocumentなりソースなりをちゃんと読むべきな気がしました。
7. Struct.new
の使用を考えましょう、 それは、単純なアクセサ、コンストラクタや比較演算子を定義してくれます。
# 良い例 class Person attr_accessor :first_name, :last_name def initialize(first_name, last_name) @first_name = first_name @last_name = last_name end end # より良い例 Person = Struct.new(:first_name, :last_name) do end
Struct.new
にブロックを渡せることを、知りませんでした。
こういうふうに使えそうですね。
Person = Struct.new(:first_name, :last_name) do def full_name "#{first_name} #{last_name}" end end p = Person.new('Hoge', 'Fuga') p.full_name #=> "Hoge Fuga"
ただ、メソッドを用意する必要があるような場合は Classを使うような気もするので、使いどころが多いかは分からないです。
8. 例外は fail
より raise
を使いましょう。
# 悪い例 fail SomeException, 'message' # 良い例 raise SomeException, 'message'
「自分が例外を射出するときは fail
、 rescue
などで補足した例外を再射出するときは raise
」のような使い分けがあった気がしましたが、今は全部 raise
で良いようでした。
9. きちんとインデントされた複数行の文字列には、Ruby 2.3 の、インデントされた ヒアドキュメントを使いましょう。
# 悪い例 - Powerpack の String#strip_margin を使用しています。 code = <<-RUBY.strip_margin('|') |def test | some_method | other_method |end RUBY # こちらも悪い例 code = <<-RUBY def test some_method other_method end RUBY # 良い例 code = <<~RUBY def test some_method other_method end RUBY
<<~
で書くヒアドキュメントを知りませんでした。
10. キャプチャした結果を使う必要のないときは、キャプチャしないグループを用いましょう。
# 悪い例 /(first|second)/ # 良い例 /(?:first|second)/
これも単純に知らなかったシリーズ
まだまだ知らないことが多く、たまに読み返すと学びがあって良いなと思いました(小並感)
C++で書いたプログラムを、docker-composeで立ち上げたUbuntu 18.04で動かして、macOS上のCLionからデバッグする。
タイトルのとおりです。
(普通にkqueueで良いんじゃないのと思いはしたものの、)Linuxのepollシステムコールを使ってソケット処理を書こうとするとどうなるのかを試してみたかったのですが、手元にあったマシンがmacOSだったので、ちょうど最近業務でも使い始めたがまだ慣れていないdocker-composeで開発環境を用意してみようとやってみました。
軽い気持ちでやり始めたものの、なんだかんだで結構ハマったのでメモを残します。
やろうと思えば、docker-composeの操作もすべてCLion上で完結できるようですが、なんかうまくいかないことがあったので、docker-compose startとかはコンソール上でやって、デバッグの部分だけCLionでやってます。
(一応公式ヘルプに使い方 Docker - ヘルプ | CLion が書いてありましたが)
また、以下のソースはリポジトリ GitHub - rkmathi/dockerfiles に置いてます。
動作環境は
- MacBook Pro Late 2018, 2.6 GHz Intel Core i7, 32 GB 2400 MHz DDR4
- macOS 10.14.4
- CLion 2019.1
- Docker version 18.09.2, build 6247962 (普通のDocker for mac)
- (docker上) Ubuntu 18.04
な感じです。
とりあえず、CLionなのでまずは CMakeLists.txt
を用意しました。
cmake_minimum_required(VERSION 3.0) set(CMAKE_VERBOSE_MAKEFILE OFF) set(out_dir ${CMAKE_BINARY_DIR}/out) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${out_dir}) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${out_dir}) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${out_dir}) project(work) enable_language(CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CXX_EXTENSIONS OFF) ADD_COMPILE_OPTIONS(-Werror -Wall -Wextra) add_executable(work main.cc)
main.cc
も最初なので適当です。
#include <iostream> int main() { std::cout << "main.cc" << std::endl; return 0; }
Dockerfile
は、イロイロなところを見ながら次のようにしました。
流石にプロダクション環境で使うことは全く想定していないので、rootユーザにパスワードログインしちゃいます。
FROM ubuntu:18.04 # Configure working directory ADD . /work WORKDIR /work # Install packages RUN apt-get update && \ apt-get upgrade -y && \ apt-get install -y build-essential cmake gcc g++ gdb gdbserver openssh-server rsync # Enable ssh for root user # see also: https://docs.docker.com/engine/examples/running_ssh_service/ RUN mkdir /var/run/sshd && \ echo 'root:root' | chpasswd && \ sed -i 's/#\?PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config && \ sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd ENV NOTVISIBLE "in users profile" RUN echo "export VISIBLE=now" >> /etc/profile # Expose port for ssh & gdbserver EXPOSE 22 7777 # Run sshd CMD ["/usr/sbin/sshd", "-D"]
docker-compose.yml
は、こんな感じです。
本当は version: '3'
の部分はマイナーバージョンまで指定したほうが良いのかもしれないですがまだ良くわかってないので一旦適当になってます。
version: '3' services: ubuntu-gdbserver: build: . security_opt: # For gdb debugging - seccomp:unconfined - apparmor:unconfined ports: - "7776:22" # For ssh - "7777:7777" # For gdbserver volumes: - .:/work
1. ↑で用意したubuntu-gdbserverサービスを立ち上げておく。
$ docker-compose build ubuntu-gdbserver
$ docker-compose start ubuntu-gdbserver
この後、 ssh root@localhost -p 7776
を試してSSHできることを確認した。 (パスワードは↑のままセットアップしたなら root
)
2. CLionを開いて、Docker integrationプラグインをインストールする。
3. Preferences → Build, Execution, Deployment → Toolchainsから追加する。
Nameは何でも良いが、Nameの隣の入力欄は Remote Host
にする。
4. Preferences → Build, Execution, Deployment → CMakeから追加する。
ここもNameは何でも良い
5. CLion上で適当にbrake pointをはる
6. Debug実行するときのターゲットで、さっき作ったToolchain選択する。
7. Debug実行すると、ちゃんとremote debugできるようになる。
おわり
転職しました
ISUCON7「Railsへの執着はもはや煩悩(ry」で本戦4位だった
2017/11/26に、「railsへの執着はもはや煩悩の域であり、開発者一同は瞑想したほうがいいと思います。」(@cnosuke, @k0kubun, @rkmathi)というチームで、ISUCON7の本戦に参加して4位でした。
最終スコアは27,304でした。
毎回取る余裕がなかったのでかなり中途半端ですが、気づいたときにかいたスコア置き場はこれ
https://gist.github.com/rkmathi/73630698d4350882288599497a96d759
cnosukeさんが公開してくれた、本戦のリポジトリはこれ
https://github.com/cnosuke/isucon7-final
k0kubunがとても詳しく書いてくれたので、具体的にどういう手を打ったのかとかはそちらを是非
もう一人のcnosukeの記事はこちら
チーム名の通り(?)、ISUCON3から毎回ずっとRubyで参加してましたが、今回の本戦は途中でGoに切り替えました。
予選と同じように、基本的には「計測→ボトルネックを潰す→計測→ボトルネックを潰す」をひたすら繰り返す感じでした。
自分はGoを使ったことなかった( Rubyもここ1年くらい使ってない )のですが、とにかく計測するためにpprofを導入したりしてました。
これをするだけで計測ができて、しかも関数内のボトルネックがぱっと見えたり、結果をSVG画像で吐かせたりできて便利でした。
こんな感じ
ISUCON3から毎年参加していて、ISUCON4では学生枠で本戦行けたもののISUCON5,6はダメだったので、久しぶりに本戦に行けて嬉しかったです。
最後になりましたが、運営の皆さま本当にありがとうございました!!!