せらぴんブログ

サークル「せらぴん」のうのはな透です。やっぱり眼鏡っ娘が好き!!

遅延処理に使えそうなgem

業務で遅延処理が必要だったので、Rubyで下記のような感じで実装しました。

  • 遅延処理の情報(実行メソッド、実行予定時刻、パラメータ)をDBのワーカーテーブルに書き込む
  • ワーカースレッドがワーカーテーブルを定期的に読み込んで処理を実行する

業務が終わって調べたんですが、それっぽいgemが当然のようにありました(震え声)


github.com
qiita.com


delayed_job自体は非同期処理をするためのgemっぽいんだけど、
:run_atを指定することで、その時間に処理を発火させることができるみたい(未検証)。

また車輪の再発明をしてしまったらしい……。

処理速度を向上させようとして失敗した話

気付けばもう2018年です。
去年は結局ブログのエントリを1本しか出しませんでした。
ブログのエントリは、twitterよりはまとまった話がしやすいですし大概追いやすいので、少し去年の四方山話をまとめておこうと思います。


閑話休題
去年の12月、実装していたプログラムの納期が迫っていた頃の失敗談です。
そのプログラムは、複数のラジコンを制御するようなプログラムで、
MQTT*1を受信した際の処理とリアルタイム処理の二本柱で制御していました。

このリアルタイム処理が曲者で、
「0.2秒毎にDBのテーブルXの全レコードを最新の状態に更新し処理Aを行う」スレッドAと
「0.5秒毎にDBのテーブルXの全レコードを最新の状態に更新し処理Bを行う」スレッドBが併存していました*2
この「全レコードを最新の状態に更新」は処理A、処理Bとも”直前”*3に行う必要があるため、まとめることができなかったわけです。

レコード件数は高々10件程度ですが、
プログラムの性質上、当該更新処理にかかる数十ミリ秒は馬鹿にできない時間でした。
かと言って、スレッドでの処理間隔を不用意に開けることはできません。
なぜなら処理間隔が開きすぎるとラジコンの制御が間に合わず、ラジコン同士が衝突したりコースアウトしたりするリスクが増大するためです。

なんとかできないか……。

そこで着目したのは「MQTT受信処理」の方でした。
下記のように処理を修正すれば全レコードを更新するタイミングが減ると考え、実際にプログラムを修正して様子を見ました。

  1. 特定のMQTTを受信した際にスレッドA,Bで行っている処理を行うべきタイミングかチェックする
  2. 処理を行うべきタイミングでなければ、行うべきタイミングになるまで待機し、再度チェックを掛ける*4

結論から言うと、これは大失敗でした。
修正後のプログラムを走らせると、10分くらい経過した時点でDBエラーが発生するようになりました。
ログを検証すると「全レコードを更新している途中のスレッドが殺された」ために起きていた現象のようでした。
プログラムを修正した際に一部のスレッドは「そのスレッドが請負う処理を行う必要がなくなった場合*5、外部から当該スレッドを殺す」ようにしていました。

「全レコードを更新している途中でスレッドを殺したらそりゃダメよなぁ」ということで、「レコード更新中はフラグを立てて、外部からスレッドを殺せないようにする」よう修正を掛けましたが、これは何故か失敗。
「面倒だし、スレッドを殺すこと自体を止めよう」と再度修正を掛けましたが、今度はスレッドが無尽蔵に作成され、処理がパンクする事態に。
ここに至ってこの修正部分にこれ以上時間を費やすことは不毛であると判断し、結局、以前通りのリアルタイム処理に巻き戻しました。


この件の教訓としては、概ね以下のような感じです。

  • スレッドを外部から殺すような処理を設ける場合、DBなどの外部リソースを変更している最中にスレッドを殺さないようにする
  • そもそも一度立てたスレッドはできるだけ外部から殺さない。スレッド内の適切なタイミングで終了チェック処理などを設ける
  • 上記に関連して)スレッドなどの非同期処理は基本Fire&Forget(撃ちっぱなし)の精神を厳守する
  • 常駐スレッドが無尽蔵に立つような設計は厳に慎む(例えあるスレッドの生存期間が短くても、そのスレッドが後続のスレッドを延々と立て続けるなら、それは常駐と変わらない)
  • エンバグなどで困った時のために、修正部分をいつでも切り捨てられるようにする(修正用ブランチを切ってそこで処理するなど)
  • 処理速度が致命的に遅いわけでないなら、わざわざ処理速度を上げるための修正をしない。プログラムの修正には常にエンバグのリスクが伴う

*1:HTTPの簡易版みたいなもの

*2:実際には他に1~2本ほどテーブルの全レコードの更新を必要とするスレッドが走っていました

*3:10ミリ秒以上はずらしたくない

*4:一見再チェックは必要ないように見えますが、待機中に状況が変わることもあるためチェックは必須でした

*5:上述の"待機中に状況が変わった"場合、と言い換えてもいいです

3/4 #MegaCiv1703 レポ

3/4にねいじまさん主催の隔月MegaCiv会に参加してきました!
MegaCivって何?って人はこれを見よう!!

gamemarket.jp

今回の参加者は9人で、西側マップでのプレイ。毎度ながら12時間の長丁場となりました。
見知り合いがパチもんさんだけという(私にとっては)新鮮味あふれる面子でのプレイです。
クジで順序を決め好きな国家を選んでいった結果、私はヘラス担当になりました。
混沌極まる欧州情勢の真っ直中にあるヘラス……これは刺激的なプレイが期待できそうです。

以下おぼえがき。

石器時代

1~5ターンは都市のない荒涼とした石器時代
今までの経験で”5ターン目に32人口から2都市建設、20人口残し”*1が最適戦略と
いう認識があったので、その通りに進める。
この段階で一番重要なのは国境線の策定
今までのプレイでは(ルールブックにある)”歴史的領土に則りお互い公平に”というスタンスで進めていたのだが、今回は歴史的領土の確認を怠っており、とりあえず目一杯拡大してから交渉しようという方針で進めていった。
その結果5ターン目時点のヘラスの領土が

  • 南側はATHENAIまで
  • 北側はWESTERN SARAMATIA/EASTERN SARAMATIAまで
  • 東側はCREMEAまで
  • 西側はPANNONIAまで

お前拡大しすぎなんじゃというくらい歴史的領土を侵犯した版図になってしまった*2
流石に周辺諸国から「それは広すぎでは……?」と警告を受けたのでその後徐々に縮小していくことになるのだが、今振り返ってみると、警告で済まされず全面戦争が起きてもおかしくない侵犯ぶりだと思う。

こうなってしまったのは隣接するローマ/ケルトがどちらも初プレイだったことが原因と考えられる。特にローマは4ターン目で1都市建設したのが裏目だったか版図を拡大できず、終盤まで苦しい戦いを強いられていた。

5ターン目の2都市はODESSUS/SARONAEに建設した。片方ローマ用の都市用地なのは気にしない。また注目すべきは4ターン目でイベリアが早々にカルタゴ領に攻め入っていたことか。初プレイ*3だとこういう事故も起きる……。

初期青銅器時代

6~8ターンは都市を乱立させつつ交易を賑わせる初期青銅器時代
前述の通り大帝国を確立したヘラスであるので2都市→4都市→7都市→9都市と瞬く間に栄華を極めてしまった。
もちろん9都市を維持できるとは思っていなかったが、今回は8都市を3ターンほど維持するという快挙を達成できた。ひとえに他国の歴史的領土(主にローマ/ケルト領)を占領しつつ、戦争をのらりくらりと回避していたおかげなのだが。

さて交易の方はどうだったかというと今ひとつ振るわず
7ターン目に金属加工、8ターン目に君主論&機織とL2にタッチできないまま時代を駆け抜けた。

中期青銅器時代

9~11ターンは災害と戦いつつ技術を買い漁る中期青銅器時代
謀反が起きたり、黒海に(ヒッタイト経由で)暴風雨が吹き荒れたり、逆に暴風雨を自引きしてヒッタイトに打撃を与えたり、伝染病をもらったりと、そんな感じ。印象としてはカルタゴが災害引きまくっていたようだ。
特に暴風雨ぶち当てに関しては、ヒッタイトがL5までの自災害を必死で調整した中でコレが命中した結果、ヒッタイトのASTを1つ遅らせる事態になってしまった。これが尾を引いたか、ヒッタイトが優勝争いから一歩退く結果になってしまった*4

また、この頃になると他国がL3技術にタッチし始めてくる。
ヒッタイトが政治を先んじて取り、次いでケルトが世界的偉業を取る。
ん? 世界的偉業?!
政治は許せても*5世界的偉業*6は許せん。ということでケルトを懲らしめることに。

ちょうどいいことに、以前ケルトに謀反で取られた都市が1つある上に、ケルトがヘラスの国境付近に12トークンを集めている*7。そこで謀反都市に攻め入りつつ、都市計画地にヘラスから1トーク*8を送り込んで都市計画を破綻させることにした。
そしてケルトから交易品カードを1枚いただくのだが、選んだ結果が象牙*9
これはケルトも怒り心頭だったので、象牙は交易で丁重にお返ししておいた。その後ケルトは(戦争を恐れてか?)徐々に西方に退いていった。空いた土地には人口に余裕のあるヒッタイトが侵食していった。

技術に関しては、識字→建築→法と進めた。この時点で赤メイン路線はほぼ確定。
「カードをケチっても農業は取れるが、ヘラスに農業は不要だろ」という判断で多少無理して建築を取ったのだが、これが後々活きることになった。

後期青銅器時代以降

12~16ターンは優勝争いが本格化し、ついに収束する後期青銅器時代~後期鉄器時代
12~13ターン目は洪水を自引きしたり、ヒッタイトが引いた洪水でこちらのトークンが多数流されたりしつつ、民主主義、高度軍事、都市様式、記録文書を取得した。ここまでくると赤のL1技術はほぼタダで取得できる。

14ターン目

さて14ターン目、この時点での主観的評価は下記の通り。

  • アッシリアは動きは地味ながらL3技術を買い始めておりかつASTもほぼ遅れていない
  • ヒッタイトはASTは遅れているが、L3技術にタッチしているので先頭がまごつくと危険
  • ヘラスはL3技術にはタッチできてないが都市数は多いしASTは先頭
  • ケルトL3技術にタッチもしているしASTも先頭
  • ミノアはL3技術がいち早く3枚に達しており、かつASTもほぼ遅れていない
  • カルタゴ/イベリアは技術はあんまり進んでないようにみえるが、都市数はすごい
  • ローマは内乱などの受益で育ちつつあるが、脅威ではない
  • エジプトはあまり見れてない

ということで主観的にはケルト≒ミノア≧ヘラス≒アッシリア≧他という感じである。
ここで戦況が動く。ミノアがL3技術で明らかに先んじているのが判明した結果、ヒッタイトの主導にヘラス/カルタゴが応じる形で対ミノア同盟が結託され、ミノアに連合軍が雪崩込んだ。とは言え戦況はミノア有利。一応ヘラスが金属加工&高度軍事持ちだったが、他2国は非赤系、対するミノアは軍事&海上戦&高度軍事持ちという盤石の状態だったためだ。結局、ミノアの余剰トークンを消費しきったかどうか、というレベルで収束し、メインのお仕置きは災害頼りとなった。
ちなみに14ターン目のヘラス技術購入は前ターンで札を使い切っていたため、神権政治神秘主義のみ。青50が赤5クレジット増える彫刻でなく神秘主義なのは迷信を恐れたため。

15ターン目

続く15ターン目。このターン後期鉄器INの恐れがあるヘラスとケルトに矛先が向く。ヘラスはミノア/ヒッタイト連合から攻撃を受けることが明白な状況であった。さらに厳しいことに先手番だったので、高度軍事を活かしつつ、都市の建設が可能な配置を心掛ける。
そして紛争。ヒッタイトの都市攻めは退けたが、ミノアの方は圧倒的物量を前になすすべもなく、1都市陥落。
ミノアに交易品カードを1枚取られることになるのだが、4枚中3枚ハーブ1枚ワックスというデンジャラス構成*10で、取られたのはワックス。九死に一生を得た。
そして交易品獲得。この時点で5都市しかないのでほぼ鎖国状態になるかなと予想していた状態で、まさかの迷信神秘主義のおかげで2都市衰退で済んだが、海賊都市でさらに1都市取られてしまい、2都市フィニッシュ。当然ながらASTは進めず。ケルトも同様に2都市に落ち込み、勝負は次ターンに持ち越された。

16ターン目

ASTトラックで先頭を走っていたヘラス&ケルトに、ミノア、アッシリア他諸国が追いつく。都市数を削られた2国に変わり、ミノアとアッシリアに矛先が向く。ヘラスは対ミノア同盟には参加せず、6都市建設ムーブで最後の交易*11(&災害)に望みを賭ける。ここで運良く4枚目のハーブを獲得でき、なんとか3枚目のL3技術獲得の目処を立てる。
そして災害。ここで後期鉄器IN確実かと思われたミノアに、エーゲ海の暴風雨が襲いかかるヒッタイトの神業じみた神風だ。そしてヘラスも巻き添えで1都市衰退。さらに特殊能力によりアッシリアケルトがそれぞれ1都市を併合される。
この結果、ヘラスのみが後期鉄器INすることとなり、ゲームは終了した。

最終的にはヘラスは122点。ありがたいことに1位を獲得できた。
ちなみに2位は終盤8~9都市を維持しつつ技術を買い漁っていたイベリアであった。イベリアが退行でAST遅れてなければ危うかったかも。

感想

正直1位になれるとは思ってなかった。針の糸を通すかのような偶然の連続だった。
ざっと挙げるだけでも

  • 14ターン目に神秘主義でなく彫刻を取っていたら
  • 15ターン目で「建設の効果は1ターン1回まで」というルールに気付けなかったら
  • 15ターン目にミノアにハーブを取られていたら
  • 16ターン目に4枚目のハーブを獲得できなかったら
  • 16ターン目にヒッタイトが暴風雨を引いていなかったら
  • 16ターン目に発生していた飢饉の二次被災を受けていたら
  • 2位のイベリアが退行を食らっていなかったら

今回の勝利には繋がらなかったに違いない。
今回はローマのつまづきに乗じる形で前半勢力を拡大しつつ、後半は反らせるだけ矛先を反らしながら逃げ切った、それに尽きると思う。
個人的には今回のMVPはヒッタイトかなと思う。最初から最後まで盤面に影響を与え続けていたから。

ヘラスに関しての感想

今回は歴史的領土を大いに侵犯したプレイだったため、あまり参考になるかはわからないが……。

  • 都市用地は6つ。荒地都市を建てやすい立地が多く、7都市維持は比較的容易と思われる。ただしすべて沿岸都市なので攻められると弱い
  • 周辺国はヒッタイト/ミノア/ケルト/ローマ。互いが全力で国境を推し進めれば、基本的には歴史的領土に落ち着くはずである。注意しないといけないのがミノアで、ここは絶対に圧力を掛けてはいけない。ミノアは領土に関して人一倍敏感である
  • 一方でミノアが伸びた場合はメインで処す役目も帯びている
  • 農業は不要。機織はミノアに攻め込むなら有効。天文航法は基本的には不要(遠洋に接している土地が少なすぎる)

以上、おぼえがき終わり!!
参加された皆様、本当にありがとうございました!!

*1:ASTトラックをノンストップで進めるためには「5ターン目までに2都市」が必要。

*2:唯一南側だけはミノアの知見により歴史的領土に収まった。

*3:イベリアが。

*4:キングメーカー的な動きは多分にしていたが。

*5:というよりヒッタイトは処せる地理にない。

*6:交易カードを1都市分多く貰え、かつAST進行も1都市分有利になる。最強技術の一角。

*7:荒地都市を建てる構えである。

*8:ここで7ターン目に取った金属加工が活きる。脅威の1対12交換である。

*9:レベル9という最高級の交易品カード。

*10:7,7,7,2。1枚でもハーブを取られれば35点損。

*11:ヘラスが後期鉄器INするためにはL3技術がもう一枚必要。

積んでるゲーム紹介10:魔壊神トリリオン

www.adventar.org

積んでるゲーム Advent Calendar 10日目です。

折角なのでVita枠その2、本日紹介するのはPSVitaソフト「魔壊神トリリオン」。

このソフトの存在を知るきっかけになったのは、
死亡シーン集動画漁りをしていた時に見つけたトリリオンプレイ動画でした。
このトリリオンとか言うゲーム、聞けばかなりのマゾゲーなようで、
7人の美少女魔王を1人ずつラスボスと戦わせて供物に焼べるゲーム(意訳)とのこと。
「手塩にかけて親愛度まで上げた美少女を死地に送り出すのはどんな気持ちなんだろう」と
気になって気になって仕方なかったのでPSVitaが手に入ったらぜひ買おうと思っていたんですよね。

で、昨日の記事にあるようにPSVitaを手に入れる機会があったので、購入に至ったわけです。

オープニングを見たところで、綿密にドナドナする順を検討し、プレイ開始。
最初の一人はマモン。サボってなければ門番としてラスボスにヌッコロされるはずだったし、順当だよね(
最初は木人にも勝てなかったマモンだが、修行を繰り返してメキメキ強くなる。
トリリオン1戦目は要領を間違えほとんどダメージを与えられないうちに撤退。
さらに修行を繰り返して2戦目はそれなりにダメージが入る。
そして最後の3戦目。最終防衛ラインまで攻撃を繰り返すもののラスボスは健在。
「まだマモン戦えるけど、ライン越えたらどうなるんだろ……?」と思ったけど、
ラスボスの絶対殺すビームを食らってあえなく撃沈。

望むべくして得た絶望感と、これがあと5人分*1味わえるのかという期待感。
このときは思いましたね。「このゲーム、買って正解だった」と。

ただ2人目の白羽の矢をペルペルに立ててから徐々に失速。
このゲーム、個々の魔王に対してやることあんまり変わんないですよね
そこに若干のマンネリ感があるのは否めず。

でも負けない! いつかCVアサミンゴスレヴィアと一緒にトリリオンを打ち倒すまでは!!
――と思っていたけど今HP確認したら1周目は3人目レヴィアたん固定らしい。つらい!!ちくしょう!!!!!

やっぱトリリオンってマゾゲーだよね

*1:最後の1人には流石に勝ってもらわないと…

積んでるゲーム紹介9:絶対迎撃ウォーズ

www.adventar.org

積んでるゲーム Advent Calendar 9日目です。

今日紹介するのは「絶対迎撃ウォーズ」。
もともと「あ~~~~~なんか面白いゲームないの~~~~?」と
PS3Playstation Storeで体験版を漁っていた時に発見したのがこいつの体験版でした。

ゲームとしては「アクション性を伴ったタワーディフェンス」とでも言いましょうか。
砲台の搭載された3連リングをガチャガチャ回して、360度全方角から出てくるモンスターをやっつけていくゲームです。
ゲーム性も気に入りましたが、何より気に入ったのがキャラの一人モナリーちゃん
露出高めのおっぱい眼鏡で、トドメにCV山本彩乃とか破壊力高すぎでしょ!!!

他にも魅力的な眼鏡っ娘が2人もいる*1ということで、
「これは買いたいな!」
駅メモの10連に突っ込むくらいならこっち買うか!!」と
Amazonでポチったんですよ。

で、届いたんですよ。

PSVita版が。

私Vita持ってないんだけど!!


マルチプラットフォームの罠でした……。
もちろんよく確認せずに買った私が悪いのですが、これは辛い。
このままでは本当の意味で積んでしまうところでしたが、
運良くVitaの共同購入話が持ち上がったので、それに乗っかって無事プレイすることが出来ました。

それでも積んでしまうんだよねぇ……。
2体目のボス戦中だったかな、
激戦の結果、タワーのHPギリギリの状態の中、ボスを撃破した。
撃破したと思ったんですよ。
ボスの中から子供がわんさか出てきて対処に遅れてタワーが決壊……。
ラス殺しってあぁいうのを言うんだろうね……。

でもそろそろモナリーちゃんのおっぱい眺めたいから再開したいな(

*1:関係ないけど、キーヨ役の遠藤ゆりかさんは今期だと怪獣娘でウィンダムやってますね

積んでるゲーム紹介8:ゲームボーイウォーズアドバンス1+2

www.adventar.org

積んでるゲーム Advent Calendar 8日目です。

今日紹介するのは「ゲームボーイウォーズアドバンス1+2」。
ファミコンウォーズから連綿と続くウォーシミュのシリーズ作です。
私はGBAは持っていませんが、今ならWiiバーチャルコンソールでプレイできるぞ!!

そう、バーチャルコンソール……。
今回積んでる理由の遠因はそのWiiがある場所なんです。
自宅じゃないんです。

こいつがインストールされたWiiUがあるのは、職場の休憩室なんだ。

社員の福利厚生のために置かれたWiiUの中にインストールされているわけですが、
ただでさえ社員同士でWiiUの奪い合いが発生する*1上に、
業務が終わった*2開放感から休憩室に寄らずに買える確率が99%以上。

同様の理由でスプラトゥーンも最近やってないなぁ。
スプラトゥーンもあれはあれで終わりがないゲームだから“積む”とは……と思ったけどヒーローモード積んでた(

*1:言うほど発生はしてない

*2:さすがに業務時間中には遊べない