僕らが高速化したのはRuby実装です。
まずはslow query logの有効化、それからinnodb_buffer_pool_size
を1Gにしました。
今回はサーバーが複数台あるので、手元で叩いて各サーバーにssh経由でgit pull
とsudo systemctl restart
をさせようと思ったのですがなかなかうまくいかず、結局サーバーにsshした上で実行するscriptになってしまいました。
このscriptはちょこちょこ書き替えが発生していました。
CREATE TABLE user (
id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
name VARCHAR(191) UNIQUE,
salt VARCHAR(20),
password VARCHAR(40),
display_name TEXT,
avatar_icon TEXT,
created_at DATETIME NOT NULL,
INDEX(name)
) Engine=InnoDB DEFAULT CHARSET=utf8mb4;
ただ、このQueryを実際に流すのを忘れていて、終了10分前とかに気づいてIndexを張ったというポカミスがあります。
定石というか、なんというか。あわせてAPP_ENV
(Sinatra)もproduction
にしています。
これも定石。しかし、nginxから返すようにしてから数十件の静的ファイルへのリクエストがタイムアウトするようになってしまいました。はじめはnginxが詰まったのだろうか……と考えていましたが、その後Discordでeth0で頻繁にネットワーク遅延が発生していると報告がなされました。もしやと思って僕からnginxへのリクエストがタイムアウトする旨を使えたところ、報告された方も同様の症状だったそうで、「これはインスタンスガチャか……?」となりました。
しかし競技終了後、Cache-control public
やlast-modified
、Etag
の値などは考慮していないことに気付き、やはり自分達に原因があったのでは……となりました。(このへん詳細ちょっと不明です)
これは最初、アプリのコードを読むと静的やんけ〜〜となったので雑にpublic以下にhtmlを生成して置いたところ、ステータスコードでひっかかってダメでした。nginxで返すときにステータスコードも強制的に302に書き替えたりもしてみましたが、Locationヘッダーも必要でアーそうですねとなって一旦撤退しました。
しかしその後、「一度File.openとかで変数に格納して、それを返すようにすればいいのでは?」と気付きを得てそのようにしたところ、有意なスコアの上昇が見られてよかったです。もっと早く気付くべきですね。
/message
と/history/:channel_id
へのGETは、その内部で実行しているSQLにN+1問題があることがわかります。なので、これをjoinして解決することにしました。
statement = db.prepare(
'SELECT message.id, message.created_at, message.content, message.user_id, user.name as user_name, user.display_name as display_name, user.avatar_icon as avatar_icon FROM message INNER JOIN user ON user.id = message.user_id WHERE message.channel_id = ? ORDER BY message.id DESC LIMIT ? OFFSET ?'
)
rows = statement.execute(@channel_id, n, (@page - 1) * n).to_a
statement.close
@messages = []
rows.each do |row|
r = {}
r['id'] = row['id']
statement = db.prepare('SELECT name, display_name, avatar_icon FROM user WHERE id = ?')
r['user'] = statement.execute(row['user_id']).first
r['user'] = { display_name: row['display_name'], avatar_icon: row['avatar_icon'], name: row['user_name'] }
r['date'] = row['created_at'].strftime("%Y/%m/%d %H:%M:%S")
r['content'] = row['content']
@messages << r
end
hashにぶち込んでレスポンスを返すところ、特にuserの情報の部分では、/history/:channel_id
ではerbから参照しているのでhashのkeyがSymbolになっているとベンチマークでエラーが出るのにはちょっとハマりました。
あと競技終了後のDiscordで、statement.close
の漏れがけっこうあることを指摘されていて、この辺の知識がなくstatement.close
の有無がどう影響してくるのか不明で放置してしまっていました。
レギュレーションには/fetch
へのアクセスは採点対象ではないことが書いてあり、/fetch
へのアクセス回数を減らせられればその分リクエストの処理が可能なのではないかと思い5秒までのばしてみましたが、ベンチで有意なスコア上昇が見られなかったことと、pumaのスレッド使い尽したらリクエスト受けられないのでは?と思いrevertしました。短くしようかとも思いましたが時間が足りず挑戦していません。
もともとサーバーそのものは3台提供されていましたが、リクエストはapp1台とdb1台で処理していました。
これをVarnishにより、まずリクエストはapp1で受け、/profile
へのPOSTと静的ファイルへのリクエストはapp2のnginx + pumaで、それ以外はapp1のpumaで受け、裏のdb1にデータを取りに行くという構成にしました。
多分今回のなかで一番大仕事かつ成果のあった作業だと思います。(これでベストスコアが出た)
長時間サーバーを起動しっぱなしにしていると、/initializeが規定時間内に終わらなくなってしまいました。これは、isubata.ruby.service
の再起動で解消されるので、ベンチマーク実行前にserviceの再起動をかけるようにして凌いでました。
去年の提出スコアが0だったことを考えると、スコアが11204というのはもう去年の何倍というスケールには収まらない成長っぷり(?)でめちゃくちゃ嬉しいです。が、もっとやれることあっただろ、というところでもあります。
あと予習とか訓練が足りなかったかなーと思います。(2週間前にisucon4の予選問題でリハーサルをしただけ)
分単位で記入していますが曖昧です。
時刻 | 行動 |
---|---|
9:05 | うなすけ 会場準備 |
11:41 | うなすけ ラブライブ サンシャイン 1期完走 |
13:00 | 競技開始、みんなで予選当日マニュアルのgistを読む |
13:10 | うなすけ Ruby実装を使うようsystemctlで設定、初期実装でのスコアを見るため先頭2台をenqueue (6173) |
13:19 | うなすけ アプリのコードをローカルにscp |
13:24 | うなすけ private git repositoryを用意 |
13:30 以降 | うなすけ → deploy script、のほ → MySQL、やまま → アプリ理解 に分担 |
14:32 | のほ slowquerylog など有効化 |
14:34 | うなすけ deploy scriptの大枠が完成 |
15:00 ごろ | やまま インスタンス内からbundler消失疑惑がありあたふた |
15:26 | やまま systemdの設定とnginxの設定をrepository管理下に |
15:37 | うなすけ RACK_ENVをdeploymentに |
15:43 | のほ innodbbufferpool_sizeを1GBに |
15:45 | うなすけ user.nameにINDEXを張る(コード上のみで実際には張らず) |
15:48:40 | スコア 6227を記録 |
16:03 | うなすけ SQLのJoin化に着手 |
16:07:40 | スコア 6403を記録 |
17:00 ごろ | やまま 静的ファイルをnginxから配信するように悪戦苦闘中 |
17:23 | うなすけ SQLをJoinするように |
17:44:30 | スコア 5698を記録 |
17:45 | やまま pumaとnginxの間をunix domain socketで繋ぐ |
17:46:46 | スコア 8540を記録 |
18:08 | うなすけ /login と /registerのGETをnginxから返すようにする |
18:48 | うなすけ /login と /registerのGETをnginxから返すようにするのをやめる |
18:54:47 | スコア 10852を記録 |
18:56:27 | スコア 8248を記録 |
18:57 | やまま Varnishの導入に着手 |
18:59:24 | スコア 3016を記録 |
19:04:44 | /initializeがTimeoutし始める |
19:17 | うなすけ /fetchで5秒sleepするようにしてみる |
19:27:48 | スコア 5678を記録 |
19:39 | やまま Varnishを使ったrequestの分散に着手 |
19:51:22 | スコア 4032を記録 |
19:57 | うなすけ /fetchで5秒sleepするコードをrevert |
20:00 | やまま Varnishを使ったrequestの分散を完了 |
20:00ごろ | みんなで他になにかできるか確認 |
20:31 | うなすけ /loginと/registerを変数から返すようにする |
20:31:26 | スコア 11246を記録 |
20:35ごろ | あまり覚えてないけど再起動試験とかしてたはず |
20:40 | user.nameにINDEXが張られる |
20:42:42 | スコア 12033を記録(ベストスコア) |
20:51:28 | スコア 11204を記録(提出スコア) |
誤字の修正とメンバー紹介を追加しました。 (ykztsさんありがとうございます )
このイベントで新曲披露できなかったら土下座と宣言して、なんとか土下座せずに済みました。タイトルは色々な音を弄ってみるために適当に付けたプロジェクトファイル名がそのまま残っています。
欲を言えば高専DJ部の度に1曲つくっていきたいけど、できるかな……
スノーで加工されてリアルタイムでVJのおもちゃになってるうなすけ君の様子です #kosendj pic.twitter.com/hozPlyaKed
— とーかさん🦐10/21 kosendj (@touka_tt) 2017年10月21日
うなすけくん、DJだけはどんどんチャラくなっていく #kosendj
— HolyGrail@10/29ハコクラ (@HolyGrail) 2017年10月21日
うなすけくんこのままナイトプールDJとかやったら虚無捨て太郎いけそう(いや、いけない) #kosendj
— HolyGrail@10/29ハコクラ (@HolyGrail) 2017年10月21日
DJ. DJだけチャラ太郎 じゃん #kosendj
— JC手作り料理@10.21(土)茶箱 (@furaji) 2017年10月21日
スピーカーからモテたいという気持ちが伝わってくる #kosendj
— あそなす@高専DJ部10/21早稲田 (@asonas) 2017年10月21日
VJのお気持ちが出てしまった #kosendj pic.twitter.com/H41DB1MuM0
— HolyGrail@10/29ハコクラ (@HolyGrail) 2017年10月21日
これからもチャラチャラズンズンパリピEDMでやっていくぞ!!!!!!!!
経緯です pic.twitter.com/pvO2zH6ODy
— 🌈✨HolyGrail✨🌈 (@HolyGrail) 2017年9月7日
うなすけくんにFF14のゲームカードをプレゼントします! #光のうなすけくん https://t.co/2r1biLIlF7
— 🌈✨HolyGrail✨🌈 (@HolyGrail) 2017年9月18日
プレゼント完了しました!!! #光のうなすけくん pic.twitter.com/5vRGzoHo9u
— 🌈✨HolyGrail✨🌈 (@HolyGrail) 2017年9月18日
#光のうなすけくん pic.twitter.com/p6hUAgyhzG
— うなすけ (@yu_suke1994) 2017年9月23日
詳しくは #光のうなすけくん
でよろしくお願いします。
いやー、おとなってこわいですね!!!!!
敵に絡まれまくるUnasukeちゃんが可愛い。 #光のうなすけくん pic.twitter.com/Uc5uiSbZ9d
— ふーすのー (@fooyuta) 2017年9月23日
ふーすのーありがとう……
とりあえず200日くらいは遊べる(ありがとうございます)のでやります。気が向くか要求されたらYoutube Liveで配信もします。以後よろしくお願いします……
録音で片方のチャンネルがごっそり抜け落ちているので聞いてると違和感があるかと思います。
今回は色々がありましたね…… そうめんが美味しかったです。
さて、
の3曲は、FL Studioを買って編集したものなのですが、打ち上げで「曲作らないの」と言われたので、次回の高専DJ部までに1曲作ることを目標にします。
Hyper.sh - Container-native Cloud
Hyper.shは、とても手軽にDocker containerをhostingできるサービスです。課金は秒単位で行なわれ、一番安いプランだと月に$1ちょっとしか費用がかかりません。
これを使って、特定の単語に反応するTwitter Botを運用してみます。
Twitter Application Management
まずはここからCreate New Appします。Consumer Key
、Consumer Secret
、Access Token
、Access Token Secret
を入手します。
今回つくるBotは、特定の文字列からなるDMを受けとったときに返信するというものです。
require 'logger'
require 'twitter'
logger = Logger.new(STDOUT)
config = {
consumer_key: ENV['CONSUMER_KEY'],
consumer_secret: ENV['CONSUMER_SECRET'],
access_token: ENV['ACCESS_TOKEN'],
access_token_secret: ENV['ACCESS_TOKEN_SECRET'],
}
rest_client = Twitter::REST::Client.new(config)
logger.info 'REST client initialized'
streaming_client = Twitter::Streaming::Client.new(config)
logger.info 'Streaming client initialized'
streaming_client.user do |tweet|
if tweet.is_a?(Twitter::DirectMessage) && %r[\A年収\z].match?(tweet.text)
logger.info "Recieved DM #{tweet.text} from #{tweet.sender.screen_name}"
rest_client.create_direct_message(tweet.sender, "#{ENV['ANNUAL_INCOME']}万円")
end
end
このようになりました。
さて、まずはこのDocker imageをDocker Hubにpushします。 unasuke/annual-income - Docker Hub
次に、hyperの中にpullしてきます。hyperをローカルマシンと同様のものだと考えるとわかりやすいかもしれません。
$ hyper pull unasuke/annual-income:v0.1.1
そして、docker-compose.ymlを作成してあるので、次のコマンドでもうBotが動作し始めます。
# project名にハイフンが使えないと怒られたので直接指定
$ hyper compose up -d -p unasuke_annual_income
という訳で、僕に「年収」とだけ書いたDMを送ると僕の年収が返ってきます。
どうですか、Hyper.sh。ちなみにプランは自動で決まるのか、僕のBotはS4(月$5.18)で実行されています。
以下にinvitation linkを置いておくので是非活用してください。 https://console.hyper.sh/register/invite/yApE4Arn3osDm9RPDG3LuLJPj1BwR8fK
デフォルトでS4になり、docker-compose.yml内で指定できるそうです。
Compose File Reference | Hyper.sh User Guide
もともと去年組んだPCは、VRを楽しむために組んだものでした。でも資産的に厳しい状態が結構続き、VRデバイスには手を出せずにいました。
そしたらなんと、Oculus Riftがいきなり5万円まで値下げしたじゃないですか。数十分悩んだ末、購入を決めました。
というわけで、やっていきましょう pic.twitter.com/PZVuuv5Xmk
— うなすけ (@yu_suke1994) 2017年7月15日
僕は眼鏡をかけているのですが、Oculusを装着するのが一番大変で、コンテンツの視聴やゲームのプレイ自体には支障ありませんでした。
しばらくしたら少し小さめの眼鏡を買おうと思います。
セットアップの過程でセンサーによる部屋の認識が(と言っていいのか?)あるのですが、それにてこずりました。そんなに広くない部屋なので、何度も「もう少し離れてください」と表示されてしまいました。
サマーセールで購入した場合には、5つほどのゲームが無料でバンドルされてきます。そのうちで惹かれたRobo Recallをやってみました。
Robo Recallが、というよりやっぱりVRはすごいもので、VRという意識が薄れていないゲーム開始時のムービーですらヒヤッと思わせられました。没入感がとにかく凄いです。
操作性に関しては、僕があまりゲームをやらないのと、とにかく敵が迫って来て焦るので、終始わちゃわちゃしてしまいました。
ヤバい
上のみくにゃんはリリース記念に描きました。やる気の続く限り、maekawaのリリースごとにみくにゃんを描いていこうかと思います。
いつの日からか、TargetにもIAM Roleを指定できるようになっていたのでそれの対応をしました。
cliで実行するときに、aws credentialのprofileを指定して実行できるようにしました。
今回のリリースのメインです。
Amazon ECS Now Supports Time and Event-Based Task Scheduling
spice lifeでは CloudWatch Eventsからecs taskを実行するのに、今まではlambdaを中間層として実行していました。
しかしTime and Event-Based Task Schedulingによって、中間層となるlambda functionが不要となり、直接ecs taskを実行できるようになりました。
今回のリリースで、maekawaからEcsParametersを取り扱えるようになりました。
いつの日からか、Kinesisにも対応していたのでmaekawaでも取り扱えるよう対応しました。
InputTransformerとRunCommandPrametersには対応していませんし、今後するつもりもありません。
理由として、まずこの2つの機能をspice lifeで使っていないこと、そして実装が困難であることが理由です。
Golangを初めて触って作成した初めてのcli applicationなので、実装にまだ整理されていない部分があります。その上、言語知識がまだ不足しているので、複雑な構造の型への対応が今の僕には困難です。
なので実装予定はありません。
ただ、もちろんPullRequestを頂けるのならありがたく頂戴しますし、Amazon Wishlistなどの手段で圧を掛けてくだされば実装する気持ちになると思います。
outputをもうちょっとかっこよく(色とか差分とか)できたら1.0.0としてリリースしたいですが、いつになるかは未定です。
@yu_suke1994 あなたとはてなスター、いますぐ設置 https://t.co/sihB5jvGDx
— あそなす (@asonas) 2017年5月17日
ということがあり、つくりました。
Tokyo Middleman Meetup#3 - connpass というイベントを開催したのですが、そこでのLTでlive cordingをして、日曜から清書してできあがり、という経緯があったりします。
を参考に、トークンを取得してください。
これは例です。詳しくは はてなスターをブログに貼り付ける - はてなスター日記 を読んでください。
activate :hatenastar,
token: 'your token',
uri: 'h2 a',
title: 'h2 a',
container: 'h2',
entry_node: 'section.article'
<head>
のどこかで = hatenastar_tag
を呼び出して完了です。
また、引数でconfig.rbの設定値を上書くこともできます。
= hatenastar_tag(entry_node: 'div.article')
エントリの複数指定、1エントリ内での複数スター表示にまだ対応できていません。pull requestを送ってくれてもいいんですよ
とりあえずスター連打してください。
middlemanでemoji記法 :example:
をemoji画像に置換するmiddleman pluginをつくりました。
あ、middleman v4以降必須です。
絵文字使いたかった、けどunicode emojiの入力ってけっこうしんどい、なのでGitHub風に絵文字を入力したかった。
できるならemojiのproviderも選べるようにしたかった…… そんな感じです。
Gemfileに次の行を追加して、bundle install
します。
gem 'middleman-somemoji'
$ bundle exec somemoji extract --provider=twemoji --destination=./source/images/emoji
このへんは本家 r7kamura/somemoji を見に行ったほうがいいかもしれません。
activate :somemoji,
provider: 'twemoji',
emojis_dir: '/images/emoji'
上のコマンドそのまま実行すると設定はこんな風になります。
あと、asset_hash
を使っているならemoji画像以下はhashを付けないようにしてください。
# こんなふうに
activate :asset_hash, ignore: 'images/twemoji'
:star:
<pre>
や<code>
の中にあるemoji記法も否応なしに<img>
タグに変換してしまうので、これを直したいです。見通しは立ってます。手を動かすだけです。
これ必要かなぁ……
(2017-05-13 10:53 誤字修正)
こちらになります → lupinus.bouquet.blue
やぎにいが一晩でやってくれました。 dark pink color theme by yagi2 · Pull Request #3 · unasuke/mastodon
ALBが頻繁に500を返すので、ログを見たらpumaが起動直後に死んでるのでインスタンスタイプをt2.mediumに上げたら安定しました。
before
after
つらい。
nodeからpostgresへの接続がうまくいっていないのか、Missing access tokenで怒られている。
https://lupinus.bouquet.blue/@unasuke/59
S3のACLはpublicにreadできるはずなのに、何故。
以下、現状(t2.medium)です。