開発^3

Web開発、宇宙開発、ゲーム開発の3種類についてつらつらと

ISUCON9本選でFailしてきました

TL; DR

後は備忘録的にやったことや、嵌った点などをツラツラと。

スコア

予選と違っていつもと同じリクエスト数基準になりましたが、ベンチの出来が良いのか、スコアはかなり安定していたように思います。
他のチームだとリクエストを捌ききって、3連続で同じスコアとかもあったみたいですが、見た感じこちらでは起きていないので、まだまだ足りなかった様子。最後までCPUボトルネックでした。

  • 初期実装 + 少しIndex: 1,227
  • 1台構成で最適化: 8,181
  • 3台構成(?): 19,023 ※後述
  • 再起動後: 0 (Fail)

f:id:sheile:20191006184046p:plain

やったこと

事前準備

環境構築や測定用のスクリプトを準備。

  • いつものdotfiles
  • pt-query-digest
  • kataribe
  • netdata

1人参加では全体をいじるのはどうやっても手が足りないので、予選と同じく、計測して一番重い場所を直す、の繰り返しで進めることにする。

f:id:sheile:20191006184316j:plain

当日朝

9:15ぐらいに会場入り。すこし悩んだけどキーボードとマウスを持ち込みで。
焦っているときのタイプミスはさらなる焦りを呼ぶので、持って行って良かったと思う。

f:id:sheile:20191005091851j:plain

10:00-11:30

一人参加のメリットを活かして3台のサーバーにいつものdotfiles / 開発環境を構築。
続いて計測用の設定……あれ、/etc/mysqlがスカスカ?ってdocker composeだ!

だいぶ前の記憶を呼び起こしつつ、isutrain/webapp/mysql/conf.dにslowクエリ用設定を書くが、slow_query_log_fileは変更されるのに、slow_query_logがOFFのままという問題に悩まされる。

mysql> show variables like 'slow%';
+---------------------+-------------------------+
| Variable_name       | Value                   |
+---------------------+-------------------------+
| slow_launch_time    | 2                       |
| slow_query_log      | OFF                     |
| slow_query_log_file | /var/log/mysql/slow.log |
+---------------------+-------------------------+
3 rows in set (0.00 sec)

試行錯誤して解決したが、 /var/log/mysql/slow.logへの書き込み権限が無いとOFFになる様子。
(ONだけどファイルが作られないなら、すぐに気づけたと思うんだが)

改めてdocker logs見たら書いてあったので、Docker慣れが足りなくてもったいないことをした。

2019-10-06T01:25:44.909172Z 0 [ERROR] [MY-011263] [Server] Could not use /var/log/mysql/slow.log for logging (error 13 - Permission denied). Turning logging off for the server process. To turn it on again: fix the cause, then either restart the query logging by using "SET GLOBAL SLOW_QUERY_LOG=ON" or restart the MySQL server.

11:30-11:50

Slowクエリを元にIndexを追加。
とりあえず検索結果が見られるようになったのでベンチ実行

【初期スコア 1,227】

12:00-12:30

お昼ご飯食べながら方針検討。
デバッグのしやすさなどを考え、開発中はPythonをDockerコンテナの外に出すことに。
search/seatのN+1がひどいのでそこから手を付けることにするが、200行近くあるメソッドにめまいがする。

塚田農場のご飯美味しい。 f:id:sheile:20191005120429j:plain

13:25

station_masterをオンメモリに。/api/train/searchが少し落ち着いたので/api/train/seat

【スコア 1,503】

14:25

reservationsdeparture_id, arrival_idを追加して、2テーブルの結合で済むように。
優勝チームの話を聞いているとseat_reservationsの方に追加していれば1テーブルで済んだようだ。

【スコア 3,895】

15:50

seatのN+1を解消。

ご丁寧に「他予約の下車駅と自分の乗車駅が同じ場合は予約が取れる」といった仕組みがあるので、不等号にはまる。
あのドタバタの中で<<=の条件を(しかも被る条件が欲しかったので反転して)考えるのは焦りっぷりがすごい。

readlineのバージョン?なのか、mysql-cliで検索クエリに日本語が入れられなかったのも拍車をかける。なんとか修正。

seat_list[seat_row * 5 + seat_column]['is_occupied'] = Trueで直接occupiedフラグを書き換えられないかと思いついたが、先にデータ確認したら4列席の列車があったので断念。

【スコア 4,509】

16:10

あれこれ嵌ってたのもあって、時間がたつのが早い。

手が入れられていないpaymentやロックで待ち時間があるのもあって、CPUの上下移動が激しくなってきたので、(予選でもスコアがはねたし)APサーバーの台数を増やすことに。
とりあえずworkerを増やしてみる。

【スコア 6,715】

~16:53

ロック漏れによる多重発券に悩まされFail連発。
そういえば、事前説明に「ロックがちゃんとしていない」とあったなぁ……

読んではみたものの、300行近くあるreserveで原因追及は残時間では無理!と、広めのテーブルロックをかけて対処する。

【スコア 6,959】

17:00

残り1時間。

時間的には再起動試験とかに入りたいところだが、worker増加の効果が分かっているし3台は使い切りたい。
(実際はこの前にもベンチマーク中にB/Cサーバーの設定などは行っていたのである程度の準備はできていた)

なんとなく一番後ろがDBの印象があるのでサーバーCをDBサーバーにすることに。
docker-compose.db.ymlとして切り出して、マニュアルに従ってDBサーバーを再構築……あれ、重……い……?

原因自体は(おそらくだが)すぐに分かった。 01_schema.sqlにあれこれIndexを張った状態で、時刻表マスタの280万件をいれようとして、死ぬんだ。RAM1GBの非力な環境というのも影響して、Swap地獄になっているっぽい。

状況はみるみる悪化し、SSHはつながらない、psは結果が返らない、systemctl stopも動かないとボロボロに。

[isucon@team103-c]~ % ps -eaf | grep mysql
zsh: fork failed: cannot allocate memory

ぎりぎり残っていたターミナルからsudo systemctl disable isutrain-db; sudo rebootを投げるが反応が無い。エラーにはなっていないので、通るのを祈る。1

これ、もう無理じゃね?と思いつつも1台構成では10,000超えないのもわかっているので、ギリギリまであがくことに。

サーバーCは無事再起動できたとしても、データが破損してるので使えない。
サーバーBをDBサーバーにすることにして、同じ轍を踏まないようにALTER TABLEしていく。
01_schema.sqlにしか変更を残していなかったので、git log -pからALTER TABLEを作るのがまた大変。

並行してサーバーAのnginxにロードバランシングの設定を追加。
これ自体は予選の時にも経験済みなのでさくっと。

17:35ぐらい?

サーバーCがなんとかsudo rebootまで通って再起動してくれたので、DBサーバーからAPサーバーに仕立て直していく。
あわわわわ

17:52

どのタイミングで実行してたのかわからないが、少しだけ高いスコアが記録されていた。
--workers 4あたりだろうか。

【スコア 8,181】

17:55

APをdockerコンテナに戻したらDBに繋がらない!
.envにはMYSQL_PORTが無いことに気づき、慌ててDBサーバーを3306:3306に。

17:56

正直、これがなぜ通っていたのか、いまだによくわからない。
当時は以下の3台構成が動いたと思っていた。

  • A: APサーバー(4worker)
  • B: DBサーバー
  • C: APサーバー(4worker)

が、再起動後の状況を見るとサーバーCは.envが最新化されておらず、DBサーバーへの接続ができていなかった。(ので最終結果がFail)
サーバーAでsystemctl restart nginxしたタイミングが分からないが、ひょっとすると、ロードバランシングの設定書いた後に再起動していない? ただ、下記構成で19,023出るかというと……謎。

  • A: APサーバー(4worker)
  • B: DBサーバー

【スコア 19,023】

17:57

再起動しても問題を直す時間は無さそう。 APサーバー/DBサーバーを頻繁に切り替えたのもあって、systemctl disable/enableの確認と修正に時間を費やす。 (後から考えるとそれでも再起動しておくべきだったか……)

終結

前述のとおり、サーバーCで最後のgit pullが漏れており、.envが最新化されていなかった。
結果、MYSQL_HOSTがサーバーBになっておらず、接続に失敗。

ということで最終結果はFail(0)となりました。

もったいないことしたけど、最後の1時間は全力以上でやり切った感はある……でもスコアが残らないのはやっぱり悔しい。来年の開催があったら残り時間を考えつつ頑張ろう。

最後になりますが、運営・問題作成・サーバー提供をしていただいたLINE様、さくらインターネット様、mercari様、Alibaba Cloud様、楽しいイベントをありがとうございました!


  1. 終了後にサポートチャットを見たら、似た症状で再起動してもらっているチームがあった。なかーま。

ISUCON9 予選

ISUCON9お疲れ様でした。

去年に引き続き「RE: ゼロから始めるISUCON」として1人チームで参加して来ましたが、無事16位に入ることができ、来月の本選に出場できることになりました。 3回目の挑戦で初本選ですね。ばんざーい ∩( ・ω・)∩

予選の感想

出題がSAKURA internetさんと、mercariさんということで、椅子を販売するサイトisucariでした。
ベンチマーク」が「ベンチ」ジャンルに入っていたり、時間に追われながらもくすりとさせてもらいました。

ISUCON的にはRDBボトルネックから始まるものの、割とすぐに外部APIボトルネックが移った印象。 結局API呼び出しをある程度減らしつつ、とにかくWorkerを増やすことでAPI結果待ちの間に他Workerが処理するようにしてスコアを稼いでいました。
感想部屋見ているといろいろ足りていなそうなので、怖いながらも講評が楽しみ。

使ったもの

  • Python実装(Python本選組はうちだけだったらしい)
  • kataribe
  • pt-query-digest
  • netdata
  • 個人契約VPS上のtmux
  • 使い慣れたdotfiles

最終的な構成

1台目:

  • nginx(load balancer, /uploadの画像配信) 1
  • gunicorn (weight: 8)

2台目:

  • gunicorn (weight: 10)

3台目:

  • MySQL
  • gunicorn (weight: 5)

やったこと

オーソドックスに計測・一番重い場所を改良の繰り返し。

items.root_category_idを追加

重いクエリの改善

categoriesテーブルをオンメモリに

親カテゴリ名まで含めて辞書定義

index追加

Slowクエリを元として、主にitemsにindexを追加

外部APIを出来るだけ呼ばずに済むように

この辺りで目立つSlowクエリが無くなって、API呼び出しが支配的に。
正直、これが正しかったのかはわからないですが、netdataで見ているとCPU使用率が0%と100%を行き来していたのでAPIのレスポンス待ちでworkerを使い切っているのかなー、と。
この時はcompaign上げてなかったので、単純にベンチマーカーからリクエストが来ていなかったのかもしれません。

APIレスポンスの内容をJSONに含めて返す必要があったため、遅延処理もできず、workerをがっつり増やして対応。
改めて考えると裏でポーリングしておく手もあったかも?でもwait_shipping以降の物が増えると破綻しそうだなぁ。

APIドキュメント読む→QRコードの実装に迷い込む

APPLICATION_SPEC.md, EXTERNAL_SERVICE_SPEC.mdを読んで、Shipping statusがisucari側である程度把握できる(=APIレスポンスする必要が無いタイミングがある)ことを知る。
/acceptのタイミングがわかれば、大部分はわかるはず。でもこのURLは外部サービスのものだし……はっ!荷物を引き渡すときのQRコードを差し替えて、isucariを経由させれば……と実装。
1.5時間ぐらいかけて実装してみたものの、QRコードのvalidationに引っ掛かってしまってgit stash orz

確かにレスポンスの変更にあたるからルール的に変更不可か……

3台構成に

アプリ側がうまく行かなくて凹んだのでインフラ側へ。
RDB負荷はある程度減らせていたようなので、DBサーバーにもgunicorn入れて数で勝負。 画像ファイルの配置がバラけてしまったのでNFSで無理やり共有。2

/upload以下はnginxで提供。

結果

Job一覧を見ていると、こんな感じか。

  • 13:45まで:色々手は入れているものの初期スコアのまま変化なし
  • 14:00ぐらい: 3000点に上がって喜ぶ
  • 14:20ぐらい: API関連弄って6000点
  • 14:30ぐらい: worker数増やして9560点、一瞬だけ1位に
  • 16:00ぐらい: QRコード系をあきらめてインフラ側へ
  • 18:00ぐらい: ガチャが安定していたので数回走らせて12,960で終わり

最終スコアは12,960、最高スコアは13,560でした。

実はcampaignを上げるタイミングを逃し、後半で上げてみたら外部API呼び出しがエラりまくったので0のままです……(小声)


  1. 後半でワタワタしていたのもあって、デフォルトで/uploadが読めた理由がよくわかっていなかった。見直してみたらFlaskのstatic_folderで提供していたのか。

  2. /sellだけ1サーバーに寄せれば良かったかな、と終わったちょい後に気付いたり。

maimaiでらっくすの問題点

maimaiでらっくすが出て1か月ちょい。 色々困る点はありつつも、それなりに慣れたので改めて問題点を書いてみます。

書いてる人

  • 無印maimaiからなので7年ぐらい
  • FiNALE 8段 Max 15.58(3470クレ)
  • でらっくす 9段 Max8500(75クレ)

それなりには出来るけど、上位の人たちとは大分差がある、でもmaimaiたのしー。な人です。

達成率(スコアシステム)

5月に公式にメールした時の記事にも書いていますが、1.02を受けて改めて。
マシにはなった、マシにはなったんだけど方向性が変わってないので、問題点も変わっていないです。
前回記事でも書きましたが、今作のスコアシステムの問題点は以下の2点(後者が前者の原因なので実質1点か)

  • プレイヤーのうまくできた感と達成率がリンクしていない
  • 極端なBreakの精度ゲー

1.02になっても解消はされておらず、「ヒバナ」のMasterを例にするとこんなことになります。

パターンA(色々失敗したがBreakはCritical Perfect)

Great 10 / Good 1 / Miss 2(-100 * 10 - 250 - 500 * 2 = -2250)
Break2つはどちらも2600
(501500 - 2250) / 501500 + 1%100.5513%

パターンB(ほぼミス無しだがBreakがPerfect)

1グレのみ(-100)
Break2つはどちらも2500
(501500 - 100) / 501500 + 0.5%100.4800%

パターンC(Bと同じ1グレだがBreakがGreat)

1グレのみ(-1000)
Break2つは1500と2500 (501500 - 1000) / 501500 + 0.25%100.0506%

FiNALE以前はBreak1グレ = Tap10グレ~11グレ相当だった でらっくす(ヒバナ)だと0.25% = 1253.75点相当が追加されるので、Break1グレ = Tap23グレ~24グレに相当する

問題点

パターンAを見ればわかりますが、SSS+なんてフルコンすら要らないんですよ。Breakだけ光れば他がボロボロでも乗ります。
逆に、どんだけうまくできてもBreakが光らなければ達成率はボロボロです。

高スコアを出すためには何よりもBreak精度が重要になるわけですが、少なくとも私は、譜面の極一部でCriticalが出るか出ないか(2F)に拘るようなゲームをやりたいわけではありません。
また、1.02の変更で2500にも追加加算が付きました。maimaiは元々Break1グレ = Tap10グレ相当と、Breakの比重が高いゲームですが、(譜面のBreak数次第とはいえ)これ以上あげる必要はあったのでしょうか。

達成率(スコアシステム)その2

maimai FiNALEまでの2600はすごく良かったと思っています。
個人的な難易度は以下のような感じですが

S < FC < 赤FC <<<<< AP <<<< 理論値
※理論値難易度は譜面によって大きく違う

Breakの2600が入ることで数グレの許容ができ、赤FCからAPまでの長い道のりの中で丁度良い中間目標となってくれました。

S < FC < 赤FC << 鳥S <<< AP <<<< 理論値
※鳥S、理論値難易度は譜面によって大きく違う

また、APが取れてもBreakをいくつか落としていることは多々ありましたが、その場合も数百点(数グレ)程度の差だったたため、APには「やり遂げた」という達成感がありました。

さて、でらっくすです。 まず大きな変化として理論値(101.00%)以外の達成感がだいぶ少なくなりました。

  • 99.8%: Break光ってれば鳥S乗ったのに……
  • 100.1%: Break光っただけだしなぁ……
  • 100.4%: Break光ってれば鳥S+乗ったのに……
  • 100.5%: 達成感はあるけどBreak光っただけだしなぁ……そもそもFCすらしてないし……
  • AP: やった……って100.80%……0.20%落ちとか達成率的にはボロボロだぁ
  • 理論値: やった!

自分の腕では多数のBreakを安定して取ることは出来ないので、理論値ペースの後半とかBreak来なけりゃ良いのに……とか思ってますorz
FiNALEの頃は99.9%に落ちたスコアを最後のBreakでギリギリ100%に乗せたり、楽しかったなぁ……

レーティングシステム

Recent枠が無くなったのは良し悪しありますが、まぁ直に受け入れられました。
一応書いておくと個人的なメリデメは以下の通り

  • メリット:苦手曲に手を出しやすくなった
  • デメリット:変化が少なくなったのでずっとこの数字と付き合うことになりそう

問題はBest枠のSSS+上限化。スコアシステムの問題がここにも。

まず、1.02で少しマシになったとはいえ、SSS+は割と失敗しまくってもBreakさえ光れば取れます。
そうすると運よくBreakが光ってSSS+に乗ったプレイがBest枠に乗るわけです。
実際自分のスコアでもSSS+済の未FC+、未FCがちらほら。

さて、プレイヤー的にはまだまだ先があるわけですが、レーティング的にはこの曲を詰める理由はもうありません。
実際はFC埋め、FC+埋めをするのでプレイしないわけではないんですが、AP出してもレーティングが微動だにしないのはちょっと凹みますね。

オンゲキならSSS+(1007500)に乗せるにはほぼABFBが必要になるので、SSS+が上限でも良いんですが、maimaiでらっくすのこのスコアシステムでSSS+(100.50%)が上限なのはダメでしょう。
かといって100.75%を上限にするのもダメです。100.75%を上限にしたらBreak精度をさらに求めるゲームになりますし、それがプレイヤーの腕を反映していないのは前述した通りです。

お友達対戦

メインは語ったので後はさっくりと。

  • 相手の名前出ないので対戦感が少ない
  • 負けた時のゲージ、レーティング減少がでかいので確実に勝てる相手が来るのを待つ作業ゲー

タッチノーツ

大分慣れたものの、やっぱり押していて楽しさが薄いのが困り者。
判定が緩すぎるのと、判定抜けを嫌って手のひら全体で取るので、全体的におおざっぱな感が強く、リズムに合わせてノーツを取っている感じが弱いのが原因だろうか?
スライド中のタッチノーツも「これならスライドだけで良いよなぁ」と思ってしまうことがしばしば。
あと、エフェクトが邪魔で次のノーツが見えないことも多々。

100円3曲固定

筐体スペースやら考えると仕方が無いとは思いつつ、ぼっちにはツライ。

maimai でらっくす のスコアシステムについて

maimai でらっくす のスコアシステムについて、どうにも気になる点があったたため、公式にメールしてみました。 以下、内容。


いつも楽しく遊ばせていただいています。 出先でmaimaiでらっくすのロケテが行われていたため、遊ばせて頂きました。

JAEPOで発表された際も懸念点としては感じていましたが、実際に遊んでみた結果、スコアシステムについてあまりにも受け入れがたい点があったため、意見として送らせていただきます。

スコアシステムの問題点について

問題と感じているのは極端なBreak精度ゲーになっており、プレイヤーの「うまくできた感」とスコアが全くリンクしていない所です。

例として「ヒバナ」のMasterを上げますが、(簡略化のため)Hold/SlideはAll Perfectとした場合のスコアは以下のようになるかと思います。

ノーツ数: Tap 483 / Hold 48 / Slide 138 / Break 2
Break加点除く総得点: 483 * 500 + 48 * 1000 + 138 * 1500 + 2 * 2500 = 501500

パターンA(色々失敗したがBreakはCritical Perfect)

Great 10 / Good 1 / Miss 3(-100 * 10 - 250 - 500 * 3 = -2750)
Break2つはどちらも2600
(501500 - 2750) / 501500 + 1%100.45%

パターンB(ほぼミス無しだがBreakがPerfect)

1グレのみ(-100)
Break2つはどちらも2500
(501500 - 100) / 501500 + 0%99.98%

問題点

プレイヤーからすれば、明らかにパターンBの方がうまくできたにも関わらず、スコアとしては0.5%近くも低いボロボロ。
この問題は、Breakが少ない譜面で顕著ではありますが、多かれ少なかれ同じ問題を抱えることになります。

高スコアを出すためには何よりもBreak精度が重要になるわけですが、少なくとも私は、譜面の極一部でCriticalが出るか出ないかに拘るようなゲームをやりたいわけではありません。
CHUNITHM/オンゲキのように全ノーツに+1%を割り当てる形であれば良かったのですが……
(旧データに精度情報が無く、スコア引継ぎのために、この方法は取れなかったのだとは思いますが)

旧スコアシステムであればパターンAが99.49%、パターンBが99.98%とプレイヤーの感覚とも合致したスコアになっていました。
個人的に101%への統一なんて求めていなかった、という事情もありますが、ここまで酷いスコアシステムにしてまで101%へ統一する必要があるんでしょうか?

色々と考えられた上での実装かとは思いますが、なにとぞご再考のほど、よろしくお願いします。

ISUCON8に1人で参加してきました

ISUCON7は3人チームで参加しましたが、今回は1人参加も可能ということで、ぼっち参加してきました。
1人参加は枠数が限られていたので、慌てて申し込んだ結果、実は見たことのない「RE: ゼロから始めるISUCON 」に。

結果は最高スコア32,596、最終スコア26,873で予選落ちではあるものの、去年の反省を活かした動きである程度のスコアが出せたのは良かったです。
ただ、正直手が付けられていない箇所が多すぎて、不完全燃焼感も。
# 後日の検証でLBだけ有効にしたら79,356でましたが、不安定だし、たらればですね。(でも書いちゃう)

やったことと感想

事前に決めていたこと

1人参加、かつアプリ寄りの人間のため、迷ってる暇はなさそう。インフラ側の変更は最低限にして、アプリの改修を優先しよう。
ISUCON7でも使用したkataribe, pt-query-digest, netdataは今回も使いたいな。特に前回の教訓を元にネットワークの帯域には気を付けよう。
1人だと自由に本番環境を使えるから、手元環境で動作させる必要は無いよね。いつも使ってるdotfilesを本番に入れられる準備はしておこう。
前回同様、複数台構成の可能性もあるし、普段のサーバ(tmux入り)からwindow/paneを切ってつなごう。

事前準備

  • 初期設定・計測用スクリプトの準備
  • tmuxのwindow/pane切り分け
  • 最終手順の手順書準備(不要サービス停止、ログ抑制)

チューニング方針

環境を確認した瞬間「出題DeNAじゃん!!」と叫んでしまったのですが……十分考えられる選択だったよね。
nginxに置き換えた人も多かったようですが、設定をみたら割と読みやすかったのと、事前方針を思い出してh2oのまま行くことにしました。
Evernoteからnginxインストール/設定手順を取り出してダウンロードまでは掛けたんですが)

チューニングの手順としてはpt-query-digestで重い部分を洗い出して修正していくという基本的なもの。
一通りコードを読んでget_events/get_eventが重いことは明らかだったんですが、呼んでいる箇所が多すぎて挙動を変えづらいのが難点でしたね。

結局、cancelの扱いがクエリを複雑にしているので、現在の予約状況を示すテーブルを新しく切って対応しました。

current_reservations(
    id,
    event_id,
    sheet_id,
    num, rank,
    user_id,
    reserved_at,
    reservation_id
)

非正規化の極みとばかりに、どんどん必要になったカラムを増やしていきましたが、最終的にはcurrent_reservations自身がボトルネックになってしまっていたのがちょっと残念かな。

(対応した順番としてはこっちが先ですが)残席数管理はredisにしました。
残席自体もredisのsetで管理しようと考えたんですが、何故か残席のsetじゃなくて、予約席のsetを作ろうと考えてしまい、そうするとreserved_atをどこに持とう?とか考えて、結局RDBにしてしまいました。
atomicなpop処理ができれば、redisだけでロックの代替ができたのかな?

後は細かいところをあれこれと弄っていました。 - get_eventは呼び出し元によってはdetailが要らないので、with_detail引数を増やしたり - sheets情報をpython中にハードコーディング - sha256の計算をhashlibでやってみたり - kataribe的にloginが重いと言われたからやってみたけど、ストレッチングも無いのでそもそも重くないかも - テーブルへのindex追加 - get_login_userを呼ぶ必要のない場所があったので削除 - mysqlをserver3に移動

悔やまれる点

  • admin側が全然見られなかった
  • lock問題についてはそこまで悩まされなかったが、理解して回避したというより、運よく回避した、という感じが強い
  • 実際、スコアが不安定で、failすることもあった
  • 明らかにここ変えたい、という場所に手が回らなかった
  • order by rand()
  • h2oのLBが/etc/hostsでできることを知らなかった
  • /etc/hostsに2レコード書いてping飛ばすところまではやったけど、ダメだったのであきらめた。h2o本体で試すべきだった。
  • ISUCON7予選のトラウマから50Mbpsのグローバル帯域を気にしすぎた

課題・環境について

ポータルがすごく便利。ベンチマークも全く待ち時間が無く、とても快適でした。
課題としては面白かったものの、lockが主題になるとスコアが安定しないのが難点か。
修正が役に立ったのかどうかがもひとつ判断できず、ツライ時間が続いたりしました。

サーバについては、終了後にベンチ環境まで含めて1週間公開してくれるというのがすごくありがたいです。
時間切れで試せなかった場所とか、ロックの関連とかいろいろ見てみたいと思います。
サーバ提供のConoHa GMOインターネット株式会社さまと、このはちゃんには足を向けて眠れませんね!

1人参加のメリット・デメリット

さて、せっかくの1人参加ということで、1人参加のメリットとデメリットも書いておこうと思います。
ISUCON9で1人枠が用意されるかはわかりませんが、参加を考えている人は参考にしてください。

メリット

  • 自宅から参加できる(いつものPC、いつものキーボード、移動時間0分)
  • 本番環境を自由にカスタマイズ可能(zsh, vim
  • 本番環境で直接作業ができる(gunicornをフロントで起動)
  • ベンチマーク実行時とかに他の人の確認を取る必要が無い
  • 何をやったか、どこまで終わったかが常に把握できる

デメリット

  • 手が足りない
  • 嵌ったときにグーグル先生にしか頼れない
  • 改修してもスコアが出ない時間が続くと気持ちが折れやすい
  • 知識のカバー範囲が足りない
  • アプリ1人、インフラ1人、計測&方針決定1人のチームが強そう
  • 打ち上げがちょっと寂しい

出遅れたけどBingoカード生成問題書いてみた

blog.jnito.com

RSS整理してたら再発見したので書いてみた。 回答締切どころか既に模範解答まで出てるけどまぁ、それはそれ。

class Bingo
  def self.generate_card
    values = (1..75).each_slice(15).map(&:shuffle).transpose.first(5)
    values[2][2] = ''

    header = %w(B I N G O)
    ([header] + values).each do |row|
      puts row.map { |col| col.to_s.rjust(2) }.join(' | ')
    end
  end
end

Bingo.generate_card

行列を転置することで割ときれいに書けた気がする。満足。

H-IIA 102型のΔVを計算してみた

Twitterを見ていたらH-IIA 102構成なんてのが話に出ていたのでちと計算。

今までこの類の計算したことないのでググりつつ。違ってもご愛嬌。

さて、まずは公式サイトから各種の値を調べてみよう。

http://www.jaxa.jp/projects/rockets/h2a/index_j.html

第一段 SRB-A(2本合算) 第二段 フェアリング(4S)
重量 114トン 151トン 20トン 1.4トン
 推進薬重量 101.1トン 130トン 16.9トン
 構造体重量 12.9トン 21トン 3.1トン 1.4トン
推力 1098kN 5040kN 137kN
燃焼時間 390秒 100秒 530秒
比推力 440秒 283秒 448秒

SRB-Aは数字を見る限り高圧型ですね。

さて計算開始。

SRB-A燃焼終了まで

最初は1段目とSRBが同時燃焼するので比推力がややこしい。

ググったらこんなQAが引っかかったのでそのまま式を使わせてもらおう。

http://okwave.jp/qa/q4133393.html

Isp = (F_B + F_L) / (F_B×Isp_L + F_L×Isp_B)×Isp_B×Isp_L
Isp = (5040 + 1098) / (5040 * 440 + 1098 * 283) * 283 * 440 ≒ 302.3秒

合算した比推力は302.3秒とでた。うん、感覚的にもあってるっぽい。

打ち上げ時質量

さて、質量を求めます。

1段目、SRB2本、フェアリングで114 + 154 + 1.4 = 269.4t。ペイロードは……とりあえず空で。

SRB燃焼終了時質量

打ち上げ100秒後にSRBが燃焼停止するのでこの時点の質量を求めます。

1段目はMECOまで390秒。推進薬101.1トンの25.6%ほどを消費したとみて、残りの推進薬は75.2トンほど。

エンジン、タンク等の構造体含めて88.1トンですね。SRBは全推進薬を消費したので21トン。

合計で88.1 + 21 + 1.4 = 110.5トンですかね。

ΔVの計算

ツィオルコフスキーの公式からΔVを求めると

9.8m/s^2 * 302.3 * ln(269.4 / 110.5) = 2640m/s

SRB-A分離から1段目燃焼終了まで

残りは1段目が頑張る。

初期質量

SRB-Aを分離したので残りの質量は88.1 + 1.4 = 89.5トンほど。

MECO時質量

打ち上げから390秒後にMECO。この時点の質量は12.9 + 1.4 = 14.3トンぐらい。

実際には途中でフェアリングを放棄しますが、まぁ、ペイロード替わりってことで。

ΔVの計算

同じくツィオルコフスキーの公式からΔVを求めると

9.8m/s^2 * 440 * ln(89.5 / 14.3) = 7908m/s

合計で2640 + 7908 = 10548m/sか。

……あれ、思ったより速い。

もっとボロボロの結果がでて、「やっぱりSSTOは無理だったよ(知ってた)」となると思ったんだが……

重力損失とか空気抵抗とか考えるとSSTOは厳しいにしても、ここまで速度が出せるとはびっくり。

何か計算間違いしている可能性もあるけど気にしない。

まぁ、そうは言っても

3トンのペイロード積んだ場合

9.8m/s^2 * 302.3 * ln(272.4 / 113.5) = 2593m/s
9.8m/s^2 * 440 * ln(92.5 / 17.3) = 7229m/s

2593 + 7229 = 9822m/sとなるので、やはり実用的なSSTOはまだ先の様子。

おまけ:通常のH-IIA 202(ペイロード5トン)の場合

初期質量:114 + 151 + 20 + 1.4 + 5 = 291.4
SRB燃焼終了時質量:88.1 + 21 + 20 + 1.4 + 5 = 135.5
ΔV = 9.8m/s^2 * 302 * ln(291.4 / 135.5) = 2266

SRB分離後質量:88.1 + 20 + 1.4 + 5 = 114.5
MECO時質量:12.9 + 20 + 1.4 + 5 = 39.3
ΔV = 9.8m/s^2 * 440 * ln(114.5 / 39.3) = 4611

1段目/フェアリング分離後質量:20 + 5 = 25
SECO時質量:3.1 + 5 = 8.1
ΔV = 9.8m/s^2 * 448 * ln(25 / 8.1) = 4948

2266 + 4611 + 4948 = 11825

流石。