学校向けのサービスを作るということ
このエントリは Classi Advent Calendar 2016 - Qiita の24日目です
前回話した複数DBは?
進捗ダメでした
学校向けのサービスを作るということ
7月からClassiにJOINして「学校」というものに久し振りに触れてみて、いろいろ思うことを書いていこうかと思います。
ピークがわかりやすい
これは想像つきやすいと思うのですが、学校という現場においては想定外のイベントによってアクセスが急増するということはそうそうありません。 Classiは校務や授業などの支援だったりご家庭への連絡だったりといろいろ機能がありますが、1日毎でピークタイムがかわるということがないため そのピークを捌けるか? 逆にアイドルタイムに過剰にならないようにということだけを考えていればいいのはかなりうれしい話です。
どのサービスでもピークタイムとアイドルタイムは来まってくるとは思うのですが、まれによるあるスパイクみたいなのが少ないのは運用する側としては安心できる要素の1つです。
「学校」は思ったよりも多様
学校といったときにどういう学校を想像するでしょう? 多くの場合、それは自分が通っていた学校がベースになってくると思います。
しかし学校といってもいろんな形の学校、たとえば偏差値的な話もそうですし地域だったりいろいろな学校がありそれぞれに抱えている悩みややりたいことは異なっているようです。
機能だったりサービスを作るときに、変に自分の学校での経験をベースに考えてしまうと実際の現場と合わなかったり、ある学校には非常にマッチする機能が別の学校では問題になりかねないということがあったりして悩ましい限りです。
顧客はだれか?
Classiは「学校向け」サービスという形なので、先生からの要望が多く上がってきます。
ただ学校という中にはいろんな登場人物がいるわけです。
先生と言ってもいろんな役割の方がいますし、もちろん生徒さんたち、そしてその保護者の方々…
どこかの立場を尊重した結果、他の立場の方に不満を抱かせるようなことのなように慎重に考えなくてはいけません。
自分個人としては、学校における主役は生徒さんたちだと思っているので全ての要望にたいして生徒のためになるか?という基準で考えているつもりです。
当初の予定を変更してポエムっぽいものを書きましたが、こんなことを考えているエンジニアに興味がある方はぜひ話を聞きにきていただればと思います
日本の教育を変える!Classiを一緒に良くするRailsエンジニア募集! - Classi株式会社のWeb エンジニア中途の求人 - Wantedly
最終日は部長の @sasata299 が唐揚げを揚げるんでしょうか、乞御期待!
ジェネレータ系コマンドを使う際に便利なgitサブコマンドを作った
a_matsudaさんの My Favorite Tools // Speaker Deck というスライドにあった↓の話
こういうのやらないとなと感じてはいたけど、ついついジェネレータ呼んだ後にすぐに編集を始めてしまっていた。 なのでそもそもジェネレータを呼ぶと同時にコミットをしちゃえばいいだろうということでgitサブコマンドを作った
gist781654396ade961ced79894cfefa4a53
パッと作っただけなのでエラー処理とか全然ないけど使ってみたら便利感あった ↓のようにgit recordの後にコマンドを実行した結果に出来たファイルをすぐコミットしてくれる コミットメッセージに実行したコマンドが記録されるので後から何やったかわかりやすくていいんじゃないかな
git record rails generate model user name:string
Railsと複数DBとPreloadingと
このエントリは Classi Advent Calendar 2016 8日目の記事です。
そういえばいまだに転職エントリーを書いていませんが7月に転職をしていましてClassiのファースト社員となりました 💪
複数DB on Rails
Classiではセキュリティやら何やらの関係でみんな大好き複数DB on Railsになっています。
R/W Splittingやシャーディングではなく、1つの共通DBとテナント毎のDBがあるマルチテナンシー(?)構成です。
class User < ActiveRecord::Base has_many :entries # (共通DBへの接続情報) end class Entry < ActiveRecord::Base belongs_to :user # (テナントDBへの接続情報) end User.connection # => 共通DBのdatabase名のconnection Entry.connection # => テナントDBのdatabase名のconnection
といった感じで使用するmodelに応じて適切にコネクションが切り替わるようになっています
異DB間association
基本的にはテナント側で閉じる世界観になるのですが、稀によく頻繁につらいことに共通側とテナント側のモデル間でassociationが貼られていたりもします。
ここで問題になるのがPreloadingの処理です。
preload
を使う分にはそれぞれのモデルに対して問い合わせが走るのでコネクションもいい感じに切り替わってくれるのですが、joins
やeager_load
の場合はそうも行きません😱
# OK User.preload(:entries) # NG User.joins(:entries) User.eager_load(:entries) #=> Mysql2::Error: Table 'entries' doesn't exist
そもそもそんなこと出来るの?って思っていたんですが↓の記事を見た感じ、MySQLの場合テーブル名の前にdatabase名を入れることで可能とのこと(もちろん同じDBサーバ内にそれぞれのdatabaseが入ってる必要はあります)
MySQL5:異なるデータベース間のテーブル結合 DB名:db1 TABLE名… - 人力検索はてな
たとえばこんな感じ?
SELECT common.users.*, tenant_1.entries.* FROM common.users JOIN tenant_1.entries ON common.users.id = tenant_1.entries.user_id
Classiの場合はこの条件を満たしていたので、後はRailsが発行するクエリのテーブル名の前に[database名].
を追加するだけで上手くいきそうです。
たとえばtable_name_prefixを使う
困ったときのStack Overflowを頼ったところ以下のようなものが。 Rails 3 - Multiple database with joins condition - Stack Overflow
テーブル名の先頭にprefixを付けるとこで、database名を追加している感じ。 ということで試してました。
class User < ActiveRecord::Base + def self.table_name_prefix + "common." + end end
class Entry < ActiveRecord::Base + def self.table_name_prefix + "tenant." + end end
早速JOINを試してみます
[0] pry(main)> User.joins(:entries).to_sql SELECT `common`.`users`.* FROM `common`.`users` JOIN `tenant`.`entries` ON `common`.`users`.id = `tenant`.`entries`.user_id
ということで上手くいきました。
実際にはbatchなどでtenantを切り替えたりする処理があったりしてtable_name_prefixの更新などが必要になってきますが、
ActiveRecordの reset_table_nameを使えばそれもなんとかなりそうです。
と、ここまで書きましたが深淵なる理由によりこのコードはClassiの内部ではまだ動いていません 😫
本当はこの処理をいい感じにgem化して今日までにリリース!!とかっこいい感じにしたかったのですが、自分が 怪盗業 や プロデューサー業 で多忙だったため進捗ダメでした…
あと1回Advent Calendarが回ってくる予定なのでそれまでに何らかの形にできればなと思ってます
こんな風に複数DBが大好き!!、または日本の教育を変えたい!!というRailsエンジニアの方はぜひClassiに話を聞きに来てください!
明日は @kenjiskywalker の「🍣」です
宿の名は。
去年の前フリ
Airbnbで怖いとこは確保した宿が無くなるとか条例で使えなくなるとかだよな
— cなんとかe (@chiastolite) 2015年12月15日
RubyKaigiの前泊当日にホストからキャンセルをお願いされる
airbnbってホスト都合で当日ドタキャンあるんだ…というのを目の当たりにした
— chezou (@chezou) 2016年9月7日
ありえねぇ…
— cなんとかe (@chiastolite) 2016年9月7日
ウケる(ウケない
— cなんとかe (@chiastolite) 2016年9月7日
モリタハウス、始まる前から盛り上がってる
— cなんとかe (@chiastolite) 2016年9月7日
そもそも
モリタハウスは概念
— cなんとかe (@chiastolite) 2016年9月7日
モリタハウス、もう少しで馬肉食っただけの架空の存在になるところだった
— chezou (@chezou) 2016年9月7日
シン・モリタハウス
【悲報】rubykaigi前日にairbnbでとった宿がオーナーからドタキャン喰らった
— sue445 (@sue445) 2016年9月7日
【朗報】 .@chiastolite さんがすぐに新しい宿をとりなおしてくれたので神
モリタハウス 2.0リリース
— cなんとかe (@chiastolite) 2016年9月7日
みんな楽しんでもらえたかな?
— cなんとかe (@chiastolite) 2016年9月7日
コーヒー楽しい
昨年末にウィッシュリストに入れてたこの手動ミルをもらったのをきっかけにコーヒーを始めてすっかりはまってる。
ハリオ 手挽きコーヒーミル セラミック 手動 スケルトン ブラック MSCS-2B
- 出版社/メーカー: ハリオ
- メディア: ホーム&キッチン
- この商品を含むブログを見る
ついに電動ミルにまで手を出したので記念に今使ってるもののまとめエントリーを書いておく。
ドリッパー
ハリオのV60を使い続けてる。 特にこだわりがあったわけでもなく手頃だったから選んだ感じ。
ハリオ 透過コーヒードリッパー V60 02 クリア 1~4杯用 VD-02T
- 出版社/メーカー: ハリオ
- メディア: ホーム&キッチン
- この商品を含むブログを見る
ドリップポット
当初は急須にスキッパーをつけて注いでたがさすがにつらくなって、 MONOQLO (モノクロ) 2015年 10月号 [雑誌] のコーヒー特集で評価の高かったこれを使ってる
- 出版社/メーカー: Kalita (カリタ)
- メディア: ホーム&キッチン
- 購入: 1人 クリック: 11回
- この商品を含むブログ (7件) を見る
狙ったとこに落とせるしちゃんとドリップ時にコーヒーが膨らむようになって大満足。
豆
珈琲きゃろっと の定期便で毎月400gを買って、足りないときに近所の自家焙煎のお店で購入して密封びんに入れて保存してる。
- 出版社/メーカー: 星硝
- メディア: ホーム&キッチン
- 購入: 4人 クリック: 13回
- この商品を含むブログ (5件) を見る
保存は最初は冷凍庫に入れるようにしてたけど、自家焙煎のとこの店長に「冷蔵庫に入れるぐらいの量を買うのではなく、常温でいいので1週間で使い切るほうがよい」と言われてそうしてる。
豆は自分でローストするのも考えたんだけど、先述の店長が「豆を焼くのはいいんだけどハンドピックが大変」とかぼやいてたのと、ハンドピックで捨てる豆を見せてもらって自分じゃこれの良し悪しの判断つかないなと思ってあきらめた。
電動ミル
- 出版社/メーカー: DeLonghi (デロンギ)
- メディア: ホーム&キッチン
- 購入: 4人 クリック: 43回
- この商品を含むブログ (18件) を見る
プロペラ式でもいいのかなと最初は思ってたけど、触る機会あって試したら挽加減難しいしムラもあって微妙かなと思ってこれにした。 噂のナイスカットミルも欲しかったんだけど、ちょうど製造終了のタイミングらしく在庫は高騰してたのであきらめた。
デロンギのはネットとかだと1つの目盛で挽きの細かさが大きく変わってしまうみたいなのを聞いたり粗挽きが苦手とか言われてたけど、特に今のところ不満はない。 それ以上に電動の威力がすごくて豆の消費量が一気に増えた。
自分はめんどくさがりなので手間のかかるハンドドリップは飽きちゃうかなと心配してたけど順調に沼にはまっていってる気がする。 おすすめのグッズだったり豆だったりお店だったり知ってる人がいたらぜひ教えてどんどん沼に引きずっていってほしい。
gitの別リポジトリを合流させる
TLでtyru さんの 全く履歴が被らない (別リポジトリとかの) ブランチ同士を無理矢理マージ - Humanity を見て、gitなら履歴を残してマージ出来るのでは?と思ったのでメモ
やること
全く別々のリポジトリAとBの、それぞれhogeブランチとfooブランチをマージする
# リポジトリAをブランチを指定してclone $ git clone -b hoge (リポジトリA) # リモートブランチとしてBを追加 $ git remote add repoB (リポジトリB) # Bの内容を取得 $ git fetch repoB # 現在のブランチ(Aのhoge)にBのfooをマージ $ git merge repoB/foo
HerokuのReviewAppsを使うときの小ネタ(HEROKU_APP_NAME,MySQL)
アプリ毎の名前やホスト名を設定したい
ReviewAppsがPR1つずつにアプリ名を環境変数で渡してくれる仕組みがあるのでそれを使うとよい
Review Apps | Heroku Dev Center
app.jsonに以下のような設定を追記すればOK
{ "env":{ "HEROKU_APP_NAME": { "required": true } } }
HEROKU_APP_NAMEは (appname)-pr-(pull request No.)
といったフォーマットが渡される。
自分のプロジェクトではこれを使って、S3へアップロードする処理でPR毎に出力ディレクトリを変更するようにしている
mysql2.gemを使いたい
プロジェクトのDBはMySQLなのでReviewAppsでもMySQL(ClearDB MySQL)を使った。
この時、DBへの接続URLは環境変数CLEARDB_DATABASE_URLに入ってくるのだが、URLmysql2://
ではなく mysql://
から始まるためこのままだとmysql2.gemが使えない。
単体のアプリなら CLEARDB_DATABASE_URL を置き換えればいいのだが、ReviewAppsだとPR1つ1つに対して置き換えを行う必要が出てくるので現実的ではない。
結局以下のように強引に置き換えることにした
development: <<: *default url: <%= (ENV['CLEARDB_DATABASE_URL'] || ENV['DATABASE_URL'] || 'mysql2://localhost').gsub(/^mysql:/, 'mysql2:') %>