2019年の振り返り

1月, 2月

前年に引き続き、リアルタイム通信サーバ・クライアントの開発をやってた。

3月

3月の途中で新卒から4年間働いていた会社を退職して、有給消化期間にギリシャに旅行に行った。

クレタ島 というところにほとんどいたが、まだ寒かったのであまり人もいなくて快適だった。

4月

今の会社に転職した。

直前2年くらいC++で開発をやってたのが、久しぶりにRailsでの開発になったので、少し懐かしい感じがあった。

5月

いろいろ好き勝手やってた気がする。

6月, 7月, 8月, 9月

ちょっと大きめの機能開発をやった。

すでに5年以上メンテナンスされているシステムのロジックと折り合いをつけながら開発するのが結構大変だった。

10月, 11月, 12月

Railsでの開発からはちょっと離れて、Goでのサーバ開発をやり始めた。

社会人になってからRailsC++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#% メソッドよりも sprintfformat を使いましょう。

# 悪い例
'%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'

「自分が例外を射出するときは failrescue などで補足した例外を再射出するときは 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 に置いてます。

動作環境は

な感じです。

とりあえず、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選択する。

f:id:rkmathii:20190501155103p:plain

7. Debug実行すると、ちゃんとremote debugできるようになる。

f:id:rkmathii:20190501155231p:plain

おわり

転職しました

2015年4月に新卒で入社してから丸4年間はたらいていた株式会社ディー・エヌ・エーを先月退職しました。

研修後の1年半くらいは、RailsSinatraでモバイルゲームのBaaSを開発・運用するチームにいました。

その後の2年ちょっとは、C++でモバイルゲームのリアルタイム通信基盤を開発・運用するチームにいました。

前半と後半で扱う技術がぜんぜん違う(HTTP↔TCPRubyC++、ステートレスREST↔ステートフル常時接続、オンプレ↔クラウドとか)ので、とても勉強になったし面白かったです。

今日からは、株式会社トレタで頑張ります。

corp.toreta.in

まずは2年ぶりにRubyの勘を取り戻すところから

ISUCON7「Railsへの執着はもはや煩悩(ry」で本戦4位だった

2017/11/26に、「railsへの執着はもはや煩悩の域であり、開発者一同は瞑想したほうがいいと思います。」(@cnosuke, @k0kubun, @rkmathi)というチームで、ISUCON7の本戦に参加して4位でした。

www.instagram.com

最終スコアは27,304でした。

毎回取る余裕がなかったのでかなり中途半端ですが、気づいたときにかいたスコア置き場はこれ

https://gist.github.com/rkmathi/73630698d4350882288599497a96d759

cnosukeさんが公開してくれた、本戦のリポジトリはこれ

https://github.com/cnosuke/isucon7-final

k0kubunがとても詳しく書いてくれたので、具体的にどういう手を打ったのかとかはそちらを是非

k0kubun.hatenablog.com

もう一人のcnosukeの記事はこちら

cnosuke.hatenablog.com


チーム名の通り(?)、ISUCON3から毎回ずっとRubyで参加してましたが、今回の本戦は途中でGoに切り替えました。

予選と同じように、基本的には「計測→ボトルネックを潰す→計測→ボトルネックを潰す」をひたすら繰り返す感じでした。

自分はGoを使ったことなかった( Rubyもここ1年くらい使ってない )のですが、とにかく計測するためにpprofを導入したりしてました。

github.com

これをするだけで計測ができて、しかも関数内のボトルネックがぱっと見えたり、結果をSVG画像で吐かせたりできて便利でした。

こんな感じ

gist.github.com


ISUCON3から毎年参加していて、ISUCON4では学生枠で本戦行けたもののISUCON5,6はダメだったので、久しぶりに本戦に行けて嬉しかったです。

最後になりましたが、運営の皆さま本当にありがとうございました!!!