Ruby 2.5未満でRuby 2.5のStructのkeyword_initを使う

追記: backports v3.11.0の時点ではstruct/new.rbの冒頭に変なputsがあるなど使われている形跡がありません。またフィールド名を予約語にすると使えないという問題もありました。一旦自前のmonkey patchを使うことにしてます。

NEWS-2.5.0 - Documentation for Ruby trunk

Struct.new takes keyword_init: true option to initialize members with keyword arguments. [Feature #11925]

これですね。

こんな感じのモンキーパッチでいけそうです。

# frozen_string_literal: true

# Backport Ruby 2.5's `keyword_init: true`
module Ruby25StructExtension
  module ClassMethods
    def new(*args, keyword_init: false, &block)
      super(*args) do
        if keyword_init
          define_method(:initialize) do |**kwargs|
            super(*members.map { |k| kwargs[k] })
          end
        end

        if block
          class_eval(&block)
        end
      end
    end
  end
end


if RUBY_VERSION < "2.5.0"
  Struct.singleton_class.prepend(Ruby25StructExtension::ClassMethods)
end

実装したあとで、そういえばbackports gemがあったなと思って確認すると実装済でした😇

backports/new.rb at master · marcandre/backports · GitHub

なのでRuby 2.5未満で keyword_init が欲しければ(そして他のRuby 2.5で追加されたメソッドも!)backports gemを使えばいいですね。

IntelliJ IDEAで特定行のGitHub PRを開く"Find Pull Request" pluginがまじイノベーティブ

Kazuho's Weblog: git blameでプルリクエストの番号を表示する

をみて、vscodeとかIntelliJ IDEAのpluginないかな〜と思っていたら、IntelliJ IDEA用にはすでにありました。インストールして数週間使っていますが、git blame を眺める機会が激減していい感じです。ぼくはIDEやeditorにはプラグインを最低限しかいれない派なんですが、これは便利なので "最低限のプラグイン" に入りそうです。

plugins.jetbrains.com

作者は @shiraj_i さんで、ソースコードも公開されてます。

github.com

保育所の公式データは統一されたフォーマットで提供してほしい

三行まとめ

  • 保育所*1の電子化データは提供元によってファイル形式もデータの構造も異なるためプログラムで加工しにくい
  • 東京都の場合、認可保育所一覧はそれぞれの区が管理しており探すのが大変
  • 保育所のデータはプログラムで加工しやすい統一されたフォーマットで提供してほしい

保育所データの現状

保育所を検索するAndroidアプリでも作ってみるかなと思って保育所のデータを探しているのですが、いまのところプログラムで簡単に加工できるデータを見つけられていません。

たとえば、東京都の場合、代表的な保育所の種類としては「認可」「認証」「認可外」などがあります。

認可保育所の一覧は23区などの自治体が提供しているため、それぞれの区のサイトを探す必要があります。

たとえば港区と目黒区をみるとこんな感じです:

他の区は調べていませんが、上記三区と同じように区ごとにまったく異なる方式で提供されていると思われます。

認証保育所は都内の一覧があります。

認可外保育施設も同様に一覧はありますが、認可・認証と同様に利用できる「保育所」でないものも含むため、認可・認証の保育所とは比較できないかもしれません*2

このように、ファイル形式や提供されている情報、あるいは分類方法も区ごと・保育所の種類ごとにバラバラなため、プログラムで電子化データとして加工するのは非常に手間がかかります。プログラムで処理するよりもクラウドソーシングなどのほうが簡単なのではないかと思えるほどです。

一覧データを販売している施設サイトもあるにはありますが、一次情報は公開されているのでできればそれを利用したいところです。

(追記) 全保育所の一覧らしきものはありました。
東京都福祉保健局 > 福祉保健の基盤づくり > 社会福祉法人・施設情報 > 社会福祉施設等一覧 > 施設等一覧
http://www.fukushihoken.metro.tokyo.jp/kiban/fukushi_shisetsu/shs_list/shisetsuitiran.html (Excel)

なぜプログラムで加工しやすいことが重要か

保育所を必要とする人が適切な保育所を探すコストを減らすためです。

プログラムで加工しやすい電子データがあれば、低コストで信頼性のたかい関連サービスやアプリを作れます*3。関連サービスというのは、たとえば保育所検索サイトや口コミサイトなどです。今は基本的に自治体が配布している冊子を眺めてメモを取りながら保育所を探すというのが一般的なフローだと思いますが、これは非常にコストの高いタスクです。質のよい検索サイトがあればその時間を大幅に削減できるはずで、それはとりもなおさず子育てのコスト自体を減らすことにつながります。

保育所データに望むこと

ファイル形式はCSVで、純粋にデータとして提供してほしい

  • Excelで提供されているデータも少しありますが、PDFの原本として使われるためか余計な文章(見出し、注意書き)が入っていてプログラムでは加工しにい状態です
  • 純粋に保育所一覧データのみのCSVとして提供されるとプログラムで加工しやすいので助かります

提供されるデータの質の統一

  • 現状、提供されているデータで共通なのは名前と住所と電話番号くらいで、そのほか収容人数や種類(=認可・認証など)、経営主体などは提供されていることもあれば、されていないこともあります
  • データの提供元(=区や都などの自治体)が違っても同じ質のデータを得られるとプログラムで加工しやすいので助かります

提供されるデータへの統一されたアクセス

  • 現状、区や都のサイトを探し回らないといけないため、非常に時間がかかります
  • 都道府県のウェブサイトの1ページで全保育所の一覧にアクセスできるとデータの収集が簡単で助かります

*1:保育園

*2:認可外保育施設について http://www.fukushihoken.metro.tokyo.jp/kodomo/hoiku/ninkagai/qa.html

*3:東京都の保育所検索アプリは、iPhone用はあるようですが、Android用は見つけられませんでした。

2017年まとめ

キャリア

  • Bit Journey(as エンジニア)とSpeee(as 技術顧問)は引き続き
    • スタートアップでサービス開発するのは学びがあってよい
    • 技術顧問業は難しい。成果はあったりなかったりする
  • 35歳になった
    • 2017年の初頭に 定年説をめぐって — 1. ミームの濫用 – To Phantasien *1 を読んでキャリアについて考えた
      • ぼくはこれを「35歳定年説は捨てていいとして、その後50代や60代を見据えたキャリアプランを真面目に考えるべきでは?」という話だと解釈した
    • 自分に関して言うと、まだ Plan B(≒ プログラマ以外の職種)は考えられないが…
  • 子供が産まれた
    • 家事と子育てに割く時間が非常に大きい
    • これは好むと好まざるとに関わらず、キャリアに対する影響は大きい
      • 時間もそうだが、家事と子育てで疲労がたまることにより他のことをする気力がなくなるという問題もある
        • 結果、子供が産まれてから個人プロジェクトの進捗がほぼゼロに
    • 子供が産まれたのは嬉しいし、家事も子育てもやぶさかではない
      • ただキャリアとの兼ね合いが難しいのは確かだと思う
      • 現職はwork from home可能なので、家庭と仕事の両立という点ではだいぶ恵まれてはいる
    • こういうときこそ write code every day

技術的なアクティビティ

*1:これは三部作の第一部で、どちらかというと第二部、第三部のほうが重要。

GraphQLの基本用語

GraphQLって用語が分かりにくいんですよね…ということで社内用に作った用語集を公開しておきます。

  • GraphQL http://graphql.org/
    • グラフキューエル
    • query language
    • リクエストのフォーマットがGraphQLということ
    • レスポンスはJSON(でもMessagePackでもなんでも)
  • GraphiQL https://github.com/graphql/graphiql
    • グラフィクル
    • GraphQL用のAPIコンソールというかAPIエクスプローラとかそういう類のもの
      • 補完のサポートを受けながらqueryを書けるので "GraphQL IDE" とも呼ばれる
    • 実体はReactベースのウェブアプリ(フロントエンドアプリ)
      • graphiql-rails はこのフロントエンドアプリをRails Mountable Engineとして扱うためのgem
    • GitHubのGraphQL APIもGraphiQL製: https://developer.github.com/v4/explorer/
  • Schema
    • GraphQLのスキーマ定義。Rails/DBのスキーマとは別
  • Type
    • GraphQLの型定義。RubyのクラスやModelとは別
  • Relay https://facebook.github.io/relay/
    1. Facebook製 UI framework
    2. ↑が要求するAPI仕様
      • サーバーサイドで「Relay」という場合はだいたいこちらで
      • Relay (1) を使わなくてもRelay (2)には準拠しておくとよい
  • Relay ID
    • Relay (2) が定めるIDのフォーマット
    • 単体でTypeとそのインスタンスIDを表す
    • 典型的な実装は base64encode([type_name, database_id]) という感じ
  • Relay Connection Type
    • Relay (2) が定めるリソースリストのフォーマット
    • ただのJSON配列ではなくラッパーオブジェクトを必須としているため、totalCountなどのカスタムフィールドも簡単に入れられる
    • cursorベースのpagination interfaceを提供
    • graphql-rubyだとAR::RelationやArrayを透過的にconnection型として扱える

caniuse.com が素晴らしいのでパトロンとして登録した

https://caniuse.com/ というサービスがあって、ブラウザごとのJSやHTMLの機能を調べられる(caniuse = Can I use X?) 超便利なサービスなのです。

自分のいるリージョンなども登録できて、ある機能を利用できるユーザーのシェアを数字でみられます。たとえばぼくはJapanとGlobalを表示するように設定していて、こんな感じです。

f:id:gfx:20171221134846p:plain

これによれば、2017年12月時点だとwebpはglobalだと70%、JPだと40%程度のブラウザで利用可能というわけですね。

で、超便利サービスだし寄付できないかなと思ってちょっとサイトを眺めると、subscriptionで $1/month の寄付を受け付けるコースがあるようです。Patreonというパトロン管理サービスを利用しているようです。せっかくなのでパトロン登録してみることにしました。これにより広告がなくなったり新機能をいち早く使えたりなどの特典もあるようですが、まあもともと十分に便利なのでそのへんはいいでしょう。

caniuseがひどく使いにくくなるなどの事情でもない限りはしばらく継続しようと思います。

High Sierraには「アプリのリンクを踏んでもChromeの空白ページが開くだけ」というバグがあるっぽい

一時的な回避策ですが、このバグが発現する状態になったら「Chromeを再起動する」で回復するようです。このエントリを書いている時点でのmacOSの最新版は 10.13.2 (High Sierra) なので、以降のバージョンでは直ってるかもしれません。

ぼく自身はまだSierraのままなので伝聞です*1。ただTwitterのタイムラインで頻繁にこれ系の悩みを見かけて、そのたびに返信しているのでもっと知られるべきだなと思い、キーボードを叩くことにしました。


追記: Chromeのアップデートがきているときに起きやすいようですが、アップデートが来ていなくても起きるようです。

*1:macOSはリリース後半年ほど様子をみることにしています。

railsdmでマルチテナント・ウェブアプリの話をしました

railsdm.github.io

発表:「マルチテナント・ウェブアプリケーションの実践」

一文でまとめると「HTTPのリクエストごと、あるいはjobの実行ごとにストレージの名前空間違うから気をつけような!!」ってのを常に意識する必要がありますって話でした。

なおElasticsearchのnamespacing v2はまさに先週の話なのですが、遅くなったというのは勘違いでした。 というのも少しだけ日付けをずらしてfuzzy searchの実験もしており、それがパフォーマンスに悪影響を与えていたようです。fuzzy searchはノイズが増えるので結局無効にすることにしたため、トータルではnamepsacing v2はnamespacing v1よりパフォーマンスがよくなっています。

またブコメでも指摘されているように、テナント横断のことをやり始めるとまたいろいろと新しい課題がでてくると思われます。楽しみですね😁

参考文献

おまけ

DroidKaigi Prelude というイベントでDroidKaigiのセッションを徹底解説します!

DroidKaigi 2018 (2/8-2/9)の関連イベント*1としてDroidKaigi Preludeというイベントを企画しました。現在、参加者&LTを募集中です。

connpass.com

メインセッションの「DroidKaigiセッション解説」を @shiraj_i さんと @muumuumuumuu さんといっしょにやります*2採択されたセッション が沢山あるのでどのセッションに参加するか迷うと思いますが、それぞれのセッションの魅力を余すところなく解説してますます迷ってもらおうという企画です😝

この企画ですが、 RubyKaigi 2017のRejectKaigiのセッションで「タイムテーブル徹底解説」というものがあり *3、これが非常にすばらしかったのでDroidKaigiでもぜひやりたいと思っていたところ、 @okoysmさんと@tarappoさんがイベントの開催をやっていただけるということで、無事 "DroidKaigi Prelude" というイベントとなりました。DroidKaigiの非公式イベントですが、DroidKaigi代表の mhidaka さんも一参加者としてきていただけそうです。

ところで個人的な話になりますが、最近子供がうまれまして*4平日就業時間後や休日のミートアップに行く機会が激減してきました。そんな折にこういうツイートを見て「これだ!!!」と思いたち、本イベントもプレミアムフライデーの 16時開催というスケジュールにしてもらいました。

この時間帯だと家庭があっても参加しやすいので、もっと増えてほしいですね*5

*1:非公式イベントなので主催はDroidKaigi実行委員会ではありません。

*2:たまたまですが、ぼくを含めて三人とも dex.fm のそれぞれ #33, #36, #37 で出演してますね。

*3:#rejectkaigi2017 を開催しました - Speee DEVELOPER BLOG

*4:ハンドルネームは "mfx" です。

*5:とはいえ月末の金曜はなかなか早く帰りづらいところがあると思うので、プレミアムフライデーを月初の金曜ということに再定義してほしい気がしなくもないですが、まあそれはまた別の話。

ISUCON7本戦で5位でした

ISUCON7 にチーム「スギャブロエックス」で参加して5位でした。

f:id:gfx:20171127210235p:plain

ベストスコアが29944点なので、せめてこれを最終スコアにしたかった…!

f:id:gfx:20171201150932p:plain

f:id:gfx:20171201150856p:plain

チームメイト

リポジトリ

github.com

所感

今回は "Chair Constructor Online" という ロジックが超複雑でサーバーのCPU負荷の高いリアルタイムオンラインゲーム ということで、非常に面白いテーマでした。BigIntが核心だったのもよくて、いちソフトウェアエンジニアとして地力を試された感じでした。運営の皆さん大変おつかれさまでした。

チームとしての動きはだいたいすぎゃーんが書いたとおりなので、ほかにいくつか特筆すべきことを私の視点から書きます。

NodeJSを選んだことについて

予選とおなじくNodeJSでした。

問題設定がWebSocket + BitInt だったのでNodeJSとの相性も悪くなさそうだし、予選でチームメンバーもNodeJSにある程度慣れたと思われるので問題はないだろうというところです。NodeJSでBigIntを触ったことがなかったのでライブラリを確認したところ、gmpの薄いラッパーだったのでこれも問題はないだろいうという判断でした。

https://github.com/kdawes/node-bigint

もっともこの実装は npmjs.org にリリースされていないようで、pakage.jsonのなかでrepository直指定でした。nodejs界隈ではあまりBigIntは使われないのかもしれないですね。

ちなみに tc39/proposa-bigintがstage 3でV8での実装も進んでいるようなので*1、来年くらいになればNodeJSでBigIntも普通に(あるいは --harmony-bigint 付きで)使えたのかもしれません。スコアには大きな影響はないでしょうが、四則演算子を普通に使えるのでコードを書くのは楽だったはず。

TypeScript化

すぎゃーんとkazeburoさんが計画を練っているときに私がコードをTypeScript化しました。コード自体は一瞬でできて、ついでにdeploy時にコンパイルしたりする仕組みも作ってミッション完了。30分くらいでできたと思います。

ESLintとTypeScriptどちらにするか悩んだのですが、変換さえしてしまえばTypeScriptのほうが書き味がよいこと、変換作業は直近でも何度かやっているのでそれほどハマらずにできる自信があったこと、などからTypeScriptを推すことにしました。

実際わりと好評だったと思います。

全体のストラテジ

2コアを活かすためにnodejsサーバのプロセスを増やすというタイミングで、kazeburoさんからは「systemdのサービスを2つ作ったほうがいいと思う」といわれたのですが、「面倒なので一旦cluster化します!!」という判断をしてしまいました。そして後にこれが大失敗だったなと思うにいたりました。

今回はroomごとにデータが完全に独立しているので、1サーバにウェブアプリとMySQLを同居させるという構成で分散させています。ここで最初は1サーバ1アプリプロセスだったので、他のroomとデータが干渉せずmutable dataでも安全にメモリキャッシュが行える状態でした。

ここでさらにnodejs1とnodejs2がそれぞれ別のポートをlistenすれば、そのアプリプロセスのなかでメモリキャッシュができたわけです。しかしclusterだとどのように分散するか不明なので、適切にパージが必要なmutable dataをキャッシュできません。なのでメモリキャッシュがimmutable dataに対してしかできないという制約があるまま最後までいくことになってしまったのでした。これは完全に私のやらかしです。ここでめんどくさがってはいけなかったのだ…。

NodeJSのプロファイラ

これは完全にNodeJSを選んで失敗だったのですが、なんとプロファイラが使えませんでした。正確には、プロファイルデータは出せるものの整形コマンドが数十分まっても戻ってこないため、結果をみれませんでした。

なのでCPU時間については測定せずに最適化するという苦しい戦いでした。これについては別途しらべないといけないですね…。

全体

予選・本戦と大変楽しく過ごせました。バランスの良いチームだっただけに、優勝できなかったのは残念極まりない…!!

来年は問題の移植作業を手伝いたいので参加はしないつもりですが、またいつかこのチームで本戦に参加できたらと思います!