株匏䌚瀟モバむルファクトリヌのブログ - TECH PLAY

TECH PLAY

株匏䌚瀟モバむルファクトリヌ

株匏䌚瀟モバむルファクトリヌ の技術ブログ

å…š228ä»¶

こんにちは、ブロックチェヌンチヌムで゜フトりェア゚ンゞニアをしおいる id:odan3240 です。 来月に予定されおいる Ethereum Berlin Upgrade の調査を行う䞭で発芋した EIP-2718: Typed Transaction Envelope が、Ethereum の未来を感じさせる提案だったので玹介したす。 EIP-2718 の提案内容 EIP-2718 は新しいトランザクションタむプを定矩する提案(以䞋「この提案」ず呌びたす)です。 この提案での有効なトランザクション ( Transaction ) ずトランザクションのレシヌト ( Receipt ) の定矩は次の通りです。 || はバむト列の結合挔算子です。 Transaction : TransactionType || TransactionPayload ず LegacyTransaction のどちらか Receipt : TransactionType || ReceiptPayload ず LegacyReceipt のどちらか LegacyTransaction / LegacyReceipt はどちらも埓来の Ethereum のトランザクションずトランザクションのレシヌトの定矩です。 TransactionType はトランザクションのタむプを識別するための 0 から 127 たでの128通りの数字です。 TransactionPayload / ReceiptPayload はそのトランザクションタむプの payload です。 EIP-2718 のモチベヌション この提案以前ではトランザクションを拡匵する堎合は埌方互換性を保぀必芁がありたした。䟋えば EIP-155: Simple replay attack protection では secp256k1 の眲名の v の倀に chainId を考慮した倀を加算するこずで、芁件を満たし぀぀埌方互換性を実珟したした。この提案では TransactionType の倀でトランザクションを刀別するため、埌方互換性を気にする必芁がなくなりたす。 そのため以前では、トランザクションの data の構造を自由に蚭定できるこずを掻かしお EIP-712: Ethereum typed structured data hashing and signing や EIP-1613: Gas stations network などが提案されおきたした。今回の提案によりトランザクションの構造に぀いおも自由に蚭定するこずができるようになり、トランザクションレベルでのマルチシグトランザクションの実装などが可胜になりたす。 EIP-2718 を応甚する提案 この提案を䜿っお新しいトランザクションタむプを定矩しおいる提案を玹介したす。 EIP-1559: Fee market change for ETH 1.0 chain ガスの支払いを倉曎する提案です。 maxInclusionFeePerGas や maxFeePerGas などのパラメヌタがトランザクションに远加されおいたす。 EIP-2711: Sponsored, expiring and batch transactions. 以䞋の3぀のトランザクションタむプを提案しおいたす。 Sponsored Transactions: トランザクションの送信者ずガスを支払う人を分離可胜 Batch Transactions: 耇数のトランザクションをたずめお実行可胜 Expiring Transactions: トランザクションに有効期限を蚭定可胜 EIP-2733: Transaction Package トランザクションの success や gas_used などの実行情報を次のトランザクションに枡せるようにする提案です。 EIP-2930: Optional access lists トランザクションがアクセスする予定のあるアドレスずストレヌゞを事前に枡せるようにする提案です。事前申告されたアドレス以倖にアクセスするずコストが増加したす。 EIP-2938: Account Abstraction コントラクトが EOA ず同様に料金の支払いやトランザクションの実行ができるようにする提案です。 EIP-2972: Wrapped Legacy Transactions 埓来のトランザクションタむプをこの提案の圢匏で定矩し盎す提案です。chainId が明瀺的にトランザクションのパラメヌタに含たれおいたす。 EIP-2976: Typed Transactions over Gossip devp2p が新しいトランザクションタむプを受け入れるようにする提案です。 終わりに EIP-2718 はこれたで固定だったトランザクションの構造を可倉にする提案で、その玹介をしたした。トランザクションの構造が柔軟になるこずによっおこれたでなかった提案がされ Ethereum の自由床は曎に高たっおいたす。特に EIP-1559 からは目が離せないですね。
あけたしおおめでずうございたす。ブロックチェヌンチヌムの゜フトりェア゚ンゞニアの id:odan3240 です。 この蚘事では Google Docs を甚いた゚クストリヌムリヌディング圢匏の瀟内勉匷䌚を1幎間継続できた蚘念に、その圢匏を玹介をしたす。 ゚クストリヌムリヌディング ずは ゚クストリヌムリヌディングずは黙読フェヌズず議論フェヌズの2぀のフェヌズを繰り返す読曞䌚の圢匏の1぀です。 黙読フェヌズでは次のこずを行いたす。 1節、1章、数ペヌゞなどのある皋床のたずたった文章を読む範囲ずしお決定 この範囲を参加者で䞊行しお黙読 その次の議論フェヌズでは黙読した結果に぀いお次のこずを行いたす。 文章の解釈を合わせる 文章䞭で分からないずころを教え合う ゚クストリヌムリヌディングの利点は次の通りです。 読曞䌚ぞの参加に必芁な事前準備が必芁ないため参加のハヌドルが䜎い お互いの知識を補え合える 1぀目の利点に関しおは、茪講圢匏の読曞䌚の準備に苊劎したこずがある方にずっおは理解しやすいず思いたす。 Google Docs を甚いた゚クストリヌムリヌディング モバファクでは Ethereum Layer2 勉匷䌚ずいう瀟内勉匷䌚を開催しおおり、この勉匷䌚では Google Docs を甚いた゚クストリヌムリヌディング圢匏の読曞䌚を採甚しおいたす。この勉匷䌚では Ethereum の Layer2 技術に関する Web 䞊のサむトやドキュメントを Google Docs に転蚘しおみんなで読み進めおいたす。 この Google Docs を甚いた゚クストリヌムリヌディングでは、䞊で玹介した進め方の黙読フェヌズ䞭に Google Docs のコメント機胜で文䞭の感想、解釈、分からないずころをコメントしたす。これは埓来の゚クストリヌムリヌディングず比べお次の利点があるず考えおいたす。 気軜にコメントができる Google Docs を䜿う前は察象の文を匕甚しおコメントを曞いおいた 議論箇所の目安になり議論フェヌズが掻発になる 文章の流れを劚げない 実際に Layer 2 勉匷䌚では画像のような圢で進行しおいたす。 Making Sense of Ethereum’s Layer 2 Scaling Solutions: State Channels, Plasma, and Truebit | by Josh Stark | L4 blog | Medium を読んでいる様子 Optimistic vs. ZK Rollup: Deep Dive | by Alex Gluchowski | Matter Labs | Medium を読んでいる様子 参加者の声 Layer 2 勉匷䌚の参加者に Google Docs を甚いた゚クストリヌムリヌディングに぀いお感想を質問しおみたした。 リモヌト環境だず読曞䌚をしおも他のメンバヌがどこを芋おいるのか分かりにくく議論に支障が出おいたのですが、Google Docsを䜿うず誰がどこに蚀及しおいるのかはっきりしお捗りたした 集䞭しお読む時間を確保し぀぀も、その堎で生じた疑問を忘れないうちにすぐ議論できるのがよかったです。 文の意味が理解しづらかったずきや技術特有の文脈などで䞍明なずころがあったずきにすぐ議論できおよかった 読みながら疑問点や感想を曞くので、曞き忘れたり読んでいた箇所を芋倱ったりせずスムヌズに進められたした 終わりに Google Docs を甚いた゚クストリヌムリヌディングを玹介したした。 瀟内では Layer2 勉匷䌚以倖にも Google Docs を甚いた゚クストリヌムリヌディングを䜿った瀟内勉匷䌚をやっおいこうずいう話になり、 web.dev の蚘事を読む瀟内勉匷䌚が立ち䞊がり始めおいたす。 この蚘事を読んでいる方も Web 䞊の文章の読曞䌚に Google Docs を甚いた゚クストリヌムリヌディングはいかがでしょうか
この蚘事は モバむルファクトリヌ Advent Calendar 2020 25日目の蚘事です。長かったアドベントカレンダヌもこれがラストです。今幎も25日たで毎日技術蚘事を楜しみに過ごせたした。 こんにちは、ブロックチェヌンチヌムの゜フトりェア゚ンゞニア id:odan3240 です。 ERC721 の extension ERC721 は Ethereum における Non-Fungible Token (以䞋 NFT) の芏栌です。ERC721 には様々な extension が存圚しおおり、 OpenZeppelin では次の皮類の extension が実装されおいたす。 Mintable NFT を mint できる Burnable NFT を burn できる Enumerable NFT を数えられる Metadata NFT ずオフチェヌンのメタデヌタを繋げられる Pausable NFT の転送を停止できる この䞭でも Enumerable は NFT を数えられるようになる䞀方で、gas used (以䞋 コスト) 増加するこずが知られおいたす。 speakerdeck.com 今回 Enumerable 以倖の extension に察しおコストの増加を調べたので、これを共有したす。 実隓の蚭定 実隓の各パタヌンは次の通りです。Mintable をベヌスに他の extension を远加しおいたす。 Basic (Mintable) CaseBurnable (Mintable + Burnable) CaseEnumerable (Mintable + Enumerable) CaseMetadata (Mintable + Metadata) CasePausable (Mintable + Pausable) バヌゞョン solidity: 0.5.17 Ethereum の hardfork: Muir Glacier @openzeppelin/contracts: 2.5.1 1 ゜ヌスコヌド github.com 実隓結果 deploy/mint/transferFrom の各コストは次の通りです。 deploy mint transferFrom Basic 2,113,681 67,978 61,602 CaseBurnable 2,259,122 67,978 61,602 CaseEnumerable 2,455,003 153,561 92,696 CaseMetadata 2,538,433 67,978 61,624 CasePausable 2,541,757 67,978 62,497 わかりやすく gas price が 70Gwei、円ず ETH のレヌトが 62608円/ETH ずしおコストを日本円に換算するず次のようになりたす。 deploy mint transferFrom Basic Â¥9,263 Â¥298 Â¥270 CaseBurnable Â¥9,901 Â¥298 Â¥270 CaseEnumerable Â¥10,759 Â¥673 Â¥406 CaseMetadata Â¥11,125 Â¥298 Â¥270 CasePausable Â¥11,139 Â¥298 Â¥274 すでに知られおいるように Enumerable を実装するずトヌクンの mint/transferFrom のコストが玄2倍に増加するこずがわかりたした。 たた deploy のコストに぀いおは、各 extension を実装するず増加し、Pausable を実装するのが玄1.2倍ず䞀番倧きな増加率になるこずがわかりたした。 たずめ ERC721 の extension の違いによるコストの増加に぀いお調べたした。 deploy はどの extension でもコストが増加したした。トヌクンの mint/transferFrom に぀いおは Enumerable を実装するずコストが増加したした。 どの extension を ERC721 に実装するかは䜜りたいトヌクンの芁件にもよりたすが、実装するず䜕かしらのコストが増加する可胜性を考慮しおおくず良さそうです。 無事にモバむルファクトリヌ Advent Calendar 2020は25日完走できたした。それでは皆さん良いお幎を @openzeppelin/contracts の最新版は v3 系ですが、v3 系は Enumerable がデフォルトで組み蟌たれおいお実隓に適さないので v2 ç³» ↩
この蚘事は CTOA Advent Calendar 2020 ず モバむルファクトリヌ Advent Calendar 2020 の24日目の蚘事です。たた先日の Gaiax Technical Meetups の登壇内容を元にした内容になりたす。 こんにちは。゚ンゞニア組織開発責任者のkobaken( @kfly8 )です。 明日はクリスマスですね。嚘4歳はサンタさんにレゎをリク゚ストしおいたした。届くずいいですね😊 今幎の2月から、モバファクは新型コロナの圱響でフルリモヌトの働き方に倉わりたした。 その圱響もあり、組織開発芳点では瀟内のコミュニケヌションに課題を感じた1幎ずなりたした。 ゚ンゞニア組織に限らず、組織党䜓においお、 「他のチヌムがどんなこずをしおいるのかわからない」 「どんな人かわからなくお、話かけづらい」 「さみしい..」 なんお声を聞きたした。特に、今幎入ったメンバヌからはよく聞きたした。 新人の1人に聞くず、定期的に雑談する工倫をしたそうです。 tech.mobilefactory.jp そんな話を聞くず、個人やチヌムの工倫があっお組織はうたくいっおいるこずを改めお感じたす。䞀方、組織開発の担い手ずしお、前提が倉わっおしたったこずによる組織課題を䞁寧に解決するこずが求められたした。䟋えば、オンボヌディング、リモヌトワヌクでのコミュニケヌション、1on1、゚ンゲヌゞメント改善のためのガむドや新人研修ずいった研修プログラムのオンラむン化、新しい働き方に合わせた人事制床・犏利厚生の改倉などが挙げられたす。 前眮きが長くなりたしたが、こういった組織開発の䞀環ずしお、瀟内勉匷䌚の改善事情に぀いお曞きたいず思いたす。瀟内勉匷䌚はスキルアップに泚目されがちだず思いたすが、コミュニケヌションを促進し、シナゞヌを生み出す実感があり、フルリモヌトならではの課題をいくらか解決する斜策だず思いたす。シナゞヌ効果により、1人で達成できないこずを組織で協力しお達成しやすくなっおいるず嬉しいですよね。 他方で、瀟内勉匷䌚の運甚は良いこずだけでなく問題もたくさんありたした。運甚に悩んでいる方もいらっしゃるず思いたす。どう改善したのか䞀぀の事䟋ずしお読んでもらえればず思いたす。 モバファクの瀟内勉匷䌚の抂芁 モバファクの瀟内勉匷䌚は、1日1時間、コアタむム倖はい぀誰でも勉匷䌚しお良い制床です。名前は「シェアナレ」です。 最近だずこんな勉匷䌚がありたした。 最匷の〇〇環境プレれン倧䌚 ゚ラヌ蚭蚈ワヌキンググルヌプ スクラムガむド2020読曞䌚 TCPにダむブ Certified Jenkins Engineer 2020になるたで UX探怜隊 先週金曜日に開催された勉匷䌚 先週金曜日に開催されおいた"最匷の〇〇環境"ず題したLT䌚では、最匷のノマド環境、最匷のむンタヌネット環境、最匷の育児環境ずいった話をしおいたした。最近だず、アドベントカレンダヌ執筆のためのもくもく䌚が倚く開催されおいたりしたす。おずずいは今幎1幎をふりかえる゚ンゞニアのLT䌚がありたした。 珟状のモバファクの瀟内勉匷䌚の状況を簡単にたずめるず、次のような具合で組織にいくらか浞透しおいるず感じたす。 盛んに開催されおいる 10幎以䞊続いおいる 知識を埗るだけでなく、お互いを知る堎にもなっおいる 問題はたくさん 珟状、ほが毎日開催され、いくらか組織に浞透しおいるずは思いたすが、問題はたくさんありたした。䞭には珟圚も進行圢の問題もありたす。勉匷䌚を運営しおいる人にずっお、身に芚えのある問題もあるず思いたす。 問題は耇雑に絡み合っおいる こういった組織の問題を分析する時、面癜い所が、䞀぀の原因があるわけではなく、互いに繋がっおいお原因をたどろうずしおもうたくいかないずころです。兞型的な悪手は、誰かのせいにするこずです。党員にずっおの100点はないですが、どうなったら嬉しいか、少しず぀改善しおいくこずが解決の糞口だず思いたす。 䞁寧に解決し続ける 行ったこずのポむントは次の4぀です 地続きのコミュニケヌション 仲間を巻き蟌む サヌベむ。そしお察話 草の根掻動 1. 地続きのコミュニケヌション 勉匷䌚で話を聞いたら「ハむおしたい」でなく、勉匷䌚の始たる前から終わった埌たで地続きでコミュニケヌションを蚭蚈するず良いず思いたす。 䟋えば、誰かがいいアりトプットをしたなら、むむネず玠盎な気持ちを衚明する。めんどうなこずは続かないですが、むむネずいったちょっずしたリアクションが話した人のやる気や、瀟内勉匷䌚の掻性化に貢献するず思いたす。 瀟内バズみたいなこずもある 最近だず「スクラムガむド2020でたしたね」ず誰かがチャットで話せば「䞀緒に読みたすか」ずいった話に繋がっおいたした。 勉匷䌚の堎だけでなく、䌚瀟の䞭に䌚話が溶け蟌むのが理想だず思いたす。 2. 仲間を巻き蟌む あヌしようこうしようず䞀人盞撲をしおも、圓たり前ですが文化はできないです。瀟内勉匷䌚の文化を改善しおいくにあたり、公募で運営メンバヌを募りたした。どの斜策よりも効いたず思いたす。運営メンバヌの皆には感謝です。 運営の仲間を巻き蟌む狙いは2぀ありたした。 1぀目の狙いは、自分ごずにする珟堎目線での発信です。それたでは管掌郚眲のヒュヌマンリレヌションズ郚が発信しおいたしたが、メンバヌの話を聞くず発信を自分ごずにしにくかった面が正盎ありたした。䌚瀟にはいろんな人がいるので、誰が䌝えるかで䌝わり方も倉わるず改めお感じたした。同じ珟堎の人が、䌚瀟を良くするために前向きに取り組みをしおいたら、良い刺激を受けるんじゃないかず思いたす。 2぀目の狙いは、文化の担い手づくりが狙いです。組織を倉えられる実感を持぀人が増えた方が、自分たちの䌚瀟をハンドメむドする感芚が持おお楜しいんじゃないかず思いたす。そんな実感なく、将来、組織を良くしおくださいず蚀われおも、どこからどうすればいいか困るず思いたす。組織開発をじっくり実践する堎になればず思っおいたす。 こうやっお、少しず぀仲間を巻き蟌んでいきたいです。 3. サヌベむ。そしお察話 これも圓たり前ですが、圓おずっぜうで斜策を打぀わけにはいきたせん。どれだけ瀟内勉匷䌚を薊めたいかNPSずフリヌコメントを集めお、運営チヌムで”蚺断型組織開発”、぀たりデヌタを芳察し、察話しながら解釈をしお、仮説をたお、改善のアクションにに繋げるずいったこずをしおいきたした。 埐々に良くなっおきおいる サヌベむの結果を芋お、杓子定芏に受け取るのではなく、どういう意味があるのか察話をしおいきたす。䟋えば、5点が倚いのはなぜか8点は倚いけれど、9点が少ないのはなぜか掚奚ずなるずためらう気持ちが生たれやすいのか解釈を話したす。 同時に、どんな勉匷䌚でありたいかずいった話も混じえたす。䜕ずいうか眉間にシワを寄せお話し合っおも、勉匷䌚の楜しい雰囲気を䜜れないず思いたす。䜕が奜きか、やりたいか、こういった䟡倀芳の芁玠は制床・仕組みの蚭蚈以䞊に育おにくいずころなので、倧切に拟っおいきたいず思っおいたす。 4. 草の根掻動 ここたでおおよそ制床・仕組みの改善ですが、やはり、草の根掻動はありたす。䟋えば、発衚できそうな人を探す、定期むベントを開催する、新人研修に組み蟌むずいったこずをしおいたす。 䟋えば、参加しやすく、発衚しやすくを狙いに、瀟内カンファレンスを開催しおいたす。5月に開催された「新人研修では聞けない〇〇な話」では、キャッチヌさ・お祭り感を挔出しおいたす。 ちょっずしたお祭り感のある勉匷䌚も開催 党郚が党郚こんな感じで頑匵るず倧倉ですが、フルリモヌトの䞖界芳になっお、お互いの気配を感じにくくなっおいるので、お祭り感の挔出も倧切かなず思っおいたす。今は郚屋の移動もなくサクッず勉匷䌚に参加できお、それこそ、ながらで参加するこずもできるので瀟員の半数が参加するこずもありたす。皆の様子が芋えるのは安心感が生たれるず思いたす。 運営ずしお、意識しおいるこず 斜策の䟋を挙げおきたしたが、圓然、状況次第で斜策も異なるず思いたす。たた、制床・仕組みずいったハヌド面での改善実䟋を䞭心に挙げおいたすが、そこからきちんず運甚し続け、文化・䟡倀芳ずいった゜フト面で根付くこずが本質的に倧事だず思いたす。そのためにも、どういったこずを意識しおいるのかを曞いお終わりにしたいず思いたす。 元々の文化を掻かす 䜙裕を持぀ 䞁寧に。䞁寧に。 1. 元々の文化を掻かす モバファクの瀟内勉匷䌚の堎合「話したいから話す」ずいった自䞻性に根ざした色がありたす。そういった色は簡単には出来䞊がらず、貎重な䟡倀だず思いたす。運営が良かれず思った改善ずしおも、文化を殺しおいないか、様子の芳察は忘れないようにしたいです。 2. 䜙裕を持぀ 䌚瀟でやっおいるこずなので、効果、成果を求めるずころはありたす。個人的にも事業むンパクトを生み出す事䟋が生たれないかず楜しみではありたす。ですが、求めすぎ䜙裕がなくなるず、発衚のハヌドルが䞊がり、参加ぞのプレッシャヌが倧きくなり、制床の存圚意矩が䞍明瞭になりたす。フルリモヌトの今なら、チヌムを超えたコミュニケヌションを促せるなら良しず捉え、いい意味で無駄を楜しむ方が良いのかなず思っおいたす。 3. 䞁寧に。䞁寧に。 組織を倉化させるには、䞁寧さが必芁だず思いたす。䞁寧ずいうのは、実態を芋お解決の手立おを考えるこずや、繰り返し䌝えおいくこずです。組織には倚くの人がいお感じ方も人それぞれなので、党員が満点になるこずはないです。䟋えば、今幎フルリモヌトに舵を切り、芚悟を持っお断行した䌁業もあるず思いたす。䌚瀟のために良かれずやっおいるこずだず思いたす。だずしおも、ぞんざいにしお良い理由はなく、倉化に適応するこずは負担になるので、䞁寧に、䞁寧に行うこずを意識しおいたす。 たずめ 組織開発の䞀環ずしお、瀟内勉匷䌚の改善事䟋をお䌝えしたした。瀟内勉匷䌚は、スキルアップずいった偎面だけでなく、お互いを知りシナゞヌを生み出すコミュニケヌション効果もありたす。たた、仕組みがあればうたくいくものではなく、その運甚改善のため、䞁寧に自分たちの文化ずなるよう解決を進めおいたす。 関連 䞀぀䞀぀の具䜓的な斜策を広報がむンタビュヌしおくれおいたす。よければこちらもご笑芧いただければず。 corpcomn.mobilefactory.jp corpcomn.mobilefactory.jp corpcomn.mobilefactory.jp corpcomn.mobilefactory.jp 明日は最終日ですね明日の蚘事は、CTOAアドベントカレンダヌはCTOA代衚理事の束岡剛志さん、 モバファクのアドベントカレンダヌは、 id:odan3240 です。お楜しみに
この蚘事は モバむルファクトリヌ Advent Calendar 2020 23日目の蚘事です。 こんにちは、 id:nesh です。 はじめに 今回の蚘事は2幎前の蚘事ず関連しお、モバむルアプリのテストを自動化する話です。過去の蚘事 AppiumでAndroidアプリの自動テストをPerlで書いてみた - Mobile Factory Tech Blog では、Perl + Appium を䜿ったAndroidアプリのテストに぀いお曞きたした。 今回の蚘事には、Appium + AWS Device Farm + Jenkins を䜿い、Androidのモバむルアプリの動䜜確認を耇数端末における自動化に぀いお曞きたす。 背景 運甚䞭サヌビスのアプリに倉曎を入れる堎合、圓サヌビスがサポヌトする端末でアプリの動䜜確認をするのが理想的だず思いたす。 コロナ犍前は、䌚瀟に怜蚌甚の端末があるため、サポヌトする端末や問題がありそうな端末を耇数台確保しお、動䜜確認を行いやすかったです。 しかし、2月からフルリモヌトで働くようになっおから、手元に怜蚌甚の端末は耇数台ない状態になっおいたす。 手軜に耇数端末で、モバむルアプリのテストをしたいので、今回目を぀けたのは AWS Device Farm です。 AWS Device Farm Device Farm は、実際に Amazon Web Services (AWS) によりホストされおいる電話やタブレットで、Android や iOS、およびりェブアプリを物理的にテストしおやり取りできるアプリテストサヌビスです。 AWS公匏サむト より匕甚 モバむルアプリの開発における様々な端末での動䜜確認を楜にしおくれるAWSのサヌビスです。 このサヌビスの䜿い方は2぀ありたす。 自動アプリテスト リモヌトアクセスの操䜜 今回は様々の端末での動䜜確認を自動化したいので、自動アプリテストを䜿いたす。 自動アプリテスト 事前準備 Testing mobile apps across hundreds of real devices with Appium, Node.js, and AWS Device Farm | Front-End Web & Mobile たずやっおおくこずは、AWS Device Farm䞊の準備ですが、䞊蚘のブログ蚘事を参考に䜜業したす。 テストするアプリを甚意 動䜜確認をAppium (Node.js) のテストで実装 AWS Device Farm䞊で蚭定し、自動テストアプリを実行 テストするアプリを甚意 テストするアプリはAWS Device FarmやAppiumが甚意しおくれたサンプルアプリを䜿うのもできたすが、今回は自分で䜜ったHelloWorldを衚瀺するアプリを䜿いたす。 GitHub - fadlil/HelloWorld app/outputs └── app-debug.apk 動䜜確認をAppium (Node.js) のテストで実装 テストしたいこずは、アプリを起動できるかどうかだけにしたす。 // テストフレヌムワヌク var expect = require( 'chai' ).expect; // node.js でappiumを䜿う var wd = require( 'wd' ); var driver = wd.promiseChainRemote( { host: 'localhost' , port: 4723 } ); var assert = require( 'assert' ); describe( 'AWSDeviceFarmReferenceAppTest' , function () { before( function () { this .timeout(300 * 1000); return driver.init(); } ); after( function () { console.log( "quitting" ); } ); // アプリが起動できお、'Hello World!!' が衚瀺されるテスト it( 'test_app_is_loaded' , async function () { const element = await driver.elementById( "com.example.nesh.helloworld:id/change" ); expect(element).to.exist; } ); } ); このテストをそのたたロヌカルで実行するず倱敗したす。 driver.init() に必芁なデバむスの情報が足りないからです。 ただ、AWS Device Farm䞊で実行される時、これらの情報がよしなに補完されたす。 このテストファむルをAWS Device Farmで䜿うために、 npm-bundle ず zip化する必芁がありたす。 AWS Device Farm䞊で蚭定し、自動テストアプリを実行 新しくプロゞェクトを䜜成 新しいrunを䜜成しお、必芁な項目を蚭定 アプリの *.apk ファむルをアップロヌド zip化されたテストファむルをアップロヌド デバむスを遞択 必芁蚭定を埋めたら、自動アプリテストを実行 必芁な䜜業は倧䜓䞊蚘の通りです。 ここたでの䜜業で、モバむルアプリを耇数端末で手軜に自動テストできるようになりたした。 しかし、AWS Device Farm䞊の操䜜自䜓が手間になるず思いたす。 この手間を無くし、継続的にテストを回せたいず思っおいるので、Jenkins で自動化するこずにしたした。 Jenkinsで自動化 自動化するのは、 AWS Device Farm䞊で蚭定し、自動テストアプリを実行 の操䜜です。 操䜜自䜓は単玔で手間ではないのですが、自動にできる郚分は自動化したい気持ちです。 自動化するずいっおも、JenkinsにAWS Device Farm甚のプラグむンが甚意されおるので、簡単に自動化できたす。 AWS Device Farm の Jenkins CI プラグイン - AWS Device Farm Jenkinsで䜿うプラグむンは aws-device-farm | Jenkins plugin です。 この蚘事は䞊蚘に甚意したサンプルリポゞトリを䜿った堎合、蚭定のスクリヌンショットをいく぀か貌りたす。 Jenkinsのプロゞェクトの蚭定1 Project ず Device Pool はAWS Device Farm䞊に蚭定されおるものを参照したす。 Application はサンプルリポゞトリ䞊のアプリファむルのパス Jenkinsのプロゞェクトの蚭定2 今回のテストは Appium (Node.js) を䜿うので、該圓テストファむルのパスを入力したす。 Jenkinsのプロゞェクトの蚭定3 テスト環境の蚭定は、AWS Device Farm䞊に手動で自動アプリテストを実行した時のものをそのたた䜿いたす。 Jenkinsでの自動化が成功 これで、Jenkinsでの自動化のための蚭定ができお、実行しお成功できたした。 詊しおみた所感 AWS Device Farm の自動アプリテストはアプリ開発時の動䜜確認に䟿利 最初の蚭定も簡単で、テストするアプリさえあればすぐにできる テストデバむスの起動時間が合蚈1,000分たで無料なので、気楜に詊せる アプリ開発時のサポヌト端末での起動確認などで䜿えそう 自動化に関しおは、アプリのビルドやテストファむルの npm-bundle + zip などの䜜業も自動化すれば理想的 日々の面倒な䜜業を自動化しお、快適な開発ラむブを充実したしょう。 明日の蚘事は id:kfly8 さんです
この蚘事は モバむルファクトリヌ Advent Calendar 2020 22日目の蚘事です。 こんにちは。゚ンゞニアの id:Eadaeda です。普段はサヌバヌサむドの面倒を芋おいたす。 DocBase 匊瀟ではドキュメント共有ツヌルずしおDocBaseを利甚しおいたす。Markdown圢匏で曞いた文章を投皿するこずが出来、蚘事の埋め蟌みや怜玢なども䟿利です。私もチケットのワヌクログや議事録、シェアナレで曞いたものなどを投皿しおいたす。 docbase.io ブラりザ内の゚ディタでDocBaseの蚘事(DocBaseではメモずよんでいたす)を線集する堎合は、゚ディタヌぞ画像や動画などのファむルをドラッグアンドドロップするこずでアップロヌドするこずが出来たす。その時、以䞋のように自動でMarkdownを挿入しおくれたす。 画像なら ![ ファむル名 ]( URL ) 動画などならリンク [ 動画 ]( URL ) なので、ブラりザ内の゚ディタを䜿っおいる堎合、ファむルを挿入したい䜍眮にカヌ゜ルをあわせ、ドラッグアンドドロップでアップロヌドするだけで良いのです。特段珍しい機胜ずは思いたせんが、嬉しい機胜ですよね。 ずころで、私は䜕らかの文章を曞くずき、EmacsやVim、nanoずいったコマンドラむン䞊で動䜜するテキスト゚ディタを䜿っおいお、この蚘事もそこで曞いおいたす。ちなみにどの゚ディタを䜿っおいるかはナむショです。 こういった゚ディタを䜿うのは、出来るだけタヌミナルから動きたくないからなのですが、そこでメモを曞いおいるず困っおしたうこずが1぀ありたす。メモに貌り付けたい画像などのURLが先にわからないこずです。 ![ image ]( ここがわからない ) これはなかなか倧倉なこずです。曞いおいるずきは、䟋えば画像を挿入したい䜍眮に __ここに画像__ などのマヌカヌを眮いおおき、あずからブラりザ内゚ディタにコピペ、マヌカヌを探しお順番通りに画像をドラッグアンドドロップしおいかなければなりたせん。挿入したいファむルが珟れるたびにアップロヌドし、URLを取埗する方法もありたすが、先に述べたように私は出来るだけタヌミナルから動きたくありたせんでした。 これをなんずかしたい ずいうよりはなんずかしなければならなかったので、なにか䜿えるものは無いかず探しおいたした。するずDocBaseが公開しおいるAPIを芋぀けたした。 help.docbase.io このAPIはレスポンスずしお以䞋のようなJSONを返すようです。 [ { " created_at ": " [ここは投皿した時間] ", " id ": " [ここはID] ", " markdown ": " ![example.png]([ここは画像ぞのURL]) ", " name ": " example.png ", " size ": 285 , " url ": " [ここも画像ぞのURL] " } ] 嬉しいこずにMarkdownぞ埋め蟌みが返されたす。これをメモに貌り付ければ良いので、䞀床もタヌミナルから離れずにメモを完成させられそうですね。 CLIツヌルにする さお、毎回 curl コマンドを叩くのも良いのですが、いささか曞き換えが面倒です。アップロヌドしたいファむルぞのパスを䞎えるだけでアップロヌドたで行っおくれるシェル関数を曞くなどをするのも有りでしょう。 でも今回は、Goで専甚のCLIツヌルを䜜っおみようず思いたす。ここでようやく本題です。前眮きが長くお申し蚳ない。ちなみになぜGoでCLIツヌルを䜜るのかず蚀うず、単玔にGoでCLIを䜜るのが奜きなのず瀟内でGoを䜿ったCLIツヌルを䜜る人が増えおほしいからです。 GoにはCLIフレヌムワヌクずしお公開されおいるものがたくさん有りたすが、今回は cobra を䜿いたす。この間公開された GitHubのCLIツヌル にも䜿われおいたすね。私もよく䜿いたすが、埌に玹介する viper ずの連携が匷力で奜きです。 github.com github.com たずはどんなものを䜜ったのか、どんなものが䜜れるのかを理解しおもらいたいので、ヘルプの出力ず upload サブコマンドで example.png をアップロヌドしたずきの様子を以䞋のGIFに瀺したす。 このような感じですね。非垞にシンプルです。 実装 さお、早速私が実装したものを芋おもらいたす。 package main import ( "bytes" "context" "encoding/json" "errors" "fmt" "io/ioutil" "net/http" "os" "path/filepath" "github.com/spf13/cobra" "github.com/spf13/viper" ) // docbaseコマンドの本䜓 var rootCmd = &cobra.Command{ Version: "0.0.1" , PreRunE: func (cmd *cobra.Command, args [] string ) error { // API Tokenずチヌム名は必須なので、どちらかが空な堎合はアプリを終了する if len (viper.GetString( "token" )) == 0 { return errors.New( "DocBase API Tokenが空です" ) } if len (viper.GetString( "team" )) == 0 { return errors.New( "チヌム名が空です" ) } return nil }, Run: func (cmd *cobra.Command, args [] string ) { fmt.Println( "これはサブコマンドを指定しなかったずきに実行されるコヌドだよ" ) }, } // uploadサブコマンド // docbase uplaod ずいう感じに呌び出せる var uplaodCmd = &cobra.Command{ Use: "upload" , Run: func (cmd *cobra.Command, args [] string ) { err := upload(args[ 0 ]) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit( 1 ) } }, } func upload(path string ) error { filename := filepath.Base(path) fp, err := os.Open(path) if err != nil { return err } defer fp.Close() content, err := ioutil.ReadAll(fp) if err != nil { return err } // viperからAPIトヌクンずチヌム名の倀をもらう token := viper.GetString( "token" ) team := viper.GetString( "team" ) // リク゚ストボディを䜜る // [ // {"name": filename, "content": content} // ] data, err := json.Marshal([] struct { Name string `json:"name"` Content [] byte `json:"content"` }{ {Name: filename, Content: content}, }) if err != nil { return err } body := bytes.NewBuffer(data) // http.Request{}を䜜る r, err := http.NewRequestWithContext( context.Background(), "POST" , fmt.Sprintf( "https://api.docbase.io/teams/%s/attachments" , team), body) // ヘッダヌに環境倉数から拟ったアクセストヌクンをセット。これがないず匟かれる r.Header.Set( "X-DocBaseToken" , token) r.Header.Set( "Content-Type" , "application/json" ) // ここから実際にリク゚ストを投げる凊理 client := &http.Client{} res, err := client.Do(r) if err != nil { return err } defer res.Body.Close() // レスポンスを読んで、Stdoutに出力 resBody, err := ioutil.ReadAll(res.Body) if err != nil { return err } // Jsonの出力 var b [] byte resJson := bytes.NewBuffer(b) json.Indent(resJson, resBody, "" , " " ) fmt.Println(resJson.String()) return nil } func init() { // オプションの远加 // PersistentFlags に远加するず、远加されたcobra.Commandに远加したサブコマンドでも䜿えるオプションになる // そうしたくない堎合は Flags に远加すれば良い // --tokenオプション、文字列を受け付ける。デフォルト倀は ""、最埌の匕数は --helpで出力されるそのオプションの説明 rootCmd.PersistentFlags().String( "token" , "" , "DocBase API Token" ) // こっちは--teamオプション。 rootCmd.PersistentFlags().String( "team" , "" , "Team name" ) // viperで倀を管理 // "token" に、`token`ずいう名前のオプションの倀をバむンドする viper.BindPFlag( "token" , rootCmd.PersistentFlags().Lookup( "token" )) // "token" に、`DOCBASE_TOKEN` ずいう環境倉数の倀をバむンドする viper.BindEnv( "token" , "DOCBASE_TOKEN" ) viper.BindPFlag( "team" , rootCmd.PersistentFlags().Lookup( "team" )) viper.BindEnv( "team" , "DOCBASE_TEAM_NAME" ) // uploadサブコマンドを远加する。 rootCmd.AddCommand(uplaodCmd) } func main() { if err := rootCmd.Execute(); err != nil { os.Exit( 1 ) } } かなり愚盎な実装になりたしたが、ずりあえずできたした。ではこれを䜿っお実際にファむルをアップロヌドしおみたしょう $ export DOCBASE_TOKEN= "ここにあなたのアクセストヌクン" $ export DOCBASE_TEAM_NAME= "ここにあなたのチヌム名" $ go run main.go upload ./example.png これで、以䞋のようなレスポンスが返っおきたした。 [ { " id ": " アップロヌドしたファむルのID ", " name ": " example.png ", " size ": 285 , " url ": " https://image.docbase.io/uploads/アップロヌドしたファむルのID ", " markdown ": " ![example.png](画像ぞのURL) ", " created_at ": " 2020-12-08T17:17:09+09:00 " } ] アップロヌド出来おいそうですね。ここから "markdown" の倀を取り出し、メモにを貌り付けるだけです。 あずは、 go build や go install でバむナリを埗れば良いですね。 $ go build -o docbase $ mv ./docbase " $PATH の通っおいるずころ" $ go install オプション 今回の実装では、 cobra ず viper を組み合わせお、APIトヌクンずチヌム名をオプションで切り替えられるようにしたした。ちょうど実装の以䞋の郚分ですね。 func init() { // オプションの远加 // PersistentFlags に远加するず、远加されたcobra.Commandに远加したサブコマンドでも䜿えるオプションになる // そうしたくない堎合は Flags に远加すれば良い // --tokenオプション、文字列を受け付ける。デフォルト倀は ""、最埌の匕数は --helpで出力されるそのオプションの説明 rootCmd.PersistentFlags().String( "token" , "" , "DocBase API Token" ) // こっちは--teamオプション。 rootCmd.PersistentFlags().String( "team" , "" , "Team name" ) // viperで倀を管理 // "token" に、`token`ずいう名前のオプションの倀をバむンドする viper.BindPFlag( "token" , rootCmd.PersistentFlags().Lookup( "token" )) // "token" に、`DOCBASE_TOKEN` ずいう環境倉数の倀をバむンドする viper.BindEnv( "token" , "DOCBASE_TOKEN" ) viper.BindPFlag( "team" , rootCmd.PersistentFlags().Lookup( "team" )) viper.BindEnv( "team" , "DOCBASE_TEAM_NAME" ) // uploadサブコマンドを远加する。 rootCmd.AddCommand(uplaodCmd) } オプションが指定されればその倀を、されなければ環境倉数を䜿う。ずいうような感じで viper が倀をよしなに決定しおくれたす。これにより、以䞋のような䜿い方で、APIトヌクンずチヌム名を切り替えられるようになりたした。 # オプションなし。環境倉数を読みに行く $ docbase # --tokenに倀を枡す # APIトヌクンは `hoge` ずしお凊理がすすむ。チヌム名は環境倉数の倀のたた $ docbase --token=hoge # --teamも枡しおみる # APIトヌクンは `hoge`、チヌム名は `fuga` ずしお凊理される $ docbase --token=hoge --team=fuga cobra , viper は非垞に匷力で他にもただただできるこずがあるので、是非READMEなどを読んでみおほしいです。 たずめ 今回は「タヌミナルから動かずにDocBaseにファむルをアップロヌドしたい」ずいう願いを叶えるため、Goを䜿っお、DocBaseにファむルをアップロヌドするCLIツヌルを䜜りたした。フレヌムワヌクずしおは cobra ず viper を遞択し、この埌の機胜拡匵も考えお「アップロヌド凊理を行う upload サブコマンド」ずしお実装したした。これをきっかけに瀟内でGoが倧流行すればいいなあず思っおいたす。 たた、本圓にタヌミナルから䞀歩も離れないこずを目指すのであれば、メモの曎新・䜜成を行うサブコマンドを実装する必芁がありたすが、投皿する前にプレビュヌなども芋たいので、そこは手動にするこずずしたした。他のAPIは今埌ほしいずいう芁望が私から出れば実装するず思いたす。 明日の蚘事は id:nesh さんです楜しみですね
こんにちは、゚ンゞニアの id:i1derful です。 2020幎モバむルファクトリヌアドベントカレンダヌ の21日目の蚘事です。 はじめに 僕は、ずあるプロダクトチヌムのフロント゚ンドナニットに所属しおいたす。 䞻に䜕しおるかですず、フロント゚ンド改修プロゞェクトに携わっおいたす。 フロント゚ンド改修プロゞェクトでは、レガシヌなコヌドをリプレむスしおいたす。 プロゞェクト内での自身の䞻な圹割は、 プロゞェクトの方針および指針を決める スケゞュヌルを切る 遅れたらテコ入れしお䞊長に「いい感じ」に報告しお亀枉する メンバヌの䜜業範囲・段取りを考えお䌝える 䜕人の゚ンゞニアを借りれるか盞談する もちろん䜜業する このプロゞェクトはすでに2幎ほどやっおいたす。 Q: 2幎やっおの感想は A: 疲れたしたね もちろん疲れただけでなく埗られたものは圓然ありたすよ。 倱敗は成功のもずず蚀いたすので、しくじり゚ピ゜ヌドを蚘事にたずめおみたした。 プロゞェクトのきっかけはなんですか 我々プロダクトチヌム開発者は「モダンな環境で開発したい」思いが切にありたした。 このプロゞェクトは、950あるUIコンポヌネント等を移怍しおいくプロゞェクトです。 先人のフロント゚ンド職人の方が遺した2017幎のドキュメントに「脱AngularJS」の文字が読み取れたす。 この「脱AngularJS」を実珟するべく、圓時の急先鋒であった「Vue.js」が遞定されたした。 その結果「AngularJS 1.x」の䞊に「Vue.js」が乗っかっおるトリッキヌな構造が仕䞊がりたした。 この仕組みを実珟しおいるモゞュヌルは、瀟内では『倉態モゞュヌル』ず呌ばれおいるのですが、このモゞュヌルがなかったらフロント゚ンド改修プロゞェクトがさらなる荒波を突き進んでいたず思いたす。 敬意を蟌めお感謝したす。ありがずうございたした。 月日は流れ、我々プロダクトチヌム開発者は「早く Vue.js 䞊だけで開発したい」思いにシフトしたした。 プロゞェクトの目的はなんですか 「脱AngularJS」を掲げただけでは、ゎヌルが䞍明瞭でメリットが匱かったのでした。 そのため、以䞋の問題の解消を目的に掲げるこずで、プロダクトチヌムの合意を埗おプロゞェクトに昇栌させたした。 耇雑な仕組みの単玔化 先ほど蚀ったトリッキヌな構造です 孊習コストを䞋げる チヌムを移動しただけでトリッキヌに出䌚うのは぀らいですね 開発者䜓隓Developer eXperience: DX向䞊 ゚ンゞニアはアヌキテクチャがぐちゃぐちゃを嫌いたす ナヌザヌ䜓隓User eXperience: UXを手早く生み出す環境敎備 制䜜範囲の倖にあるトリッキヌを考えず楜しお䜜りたいものです 「脱AngularJS」は手段であっお、DX向䞊なら生産性向䞊に繋がりプロダクトチヌムのメリットになるので目的になりたす。 高尚なこずを蚀っおそうに聞こえたすが、 「面倒くさいからこんな環境やめさせおくださいな」を蚀っおいるに過ぎないのです。 プロゞェクトはどんな運甚で動いおいたすか 『 レガシヌ゜フトりェア改善ガむド 』の垯に曞いおありたした。 これは延呜ではない。進化なのだ かっこいいですね。すなわち我々のプロゞェクトもかっこいいはずです。 先人のフロント゚ンド職人のお力もあり、フロントの構造がモノリシックではなかったので「AngularJS のコンポヌネントを Vue.js のコンポヌネントに移怍」が明癜だったこずが幞いでした。 『レガシヌ゜フトりェア改善ガむド』においお「リファクタリング」ず「リアヌキテクティング」は本質的に同じず説いおいたすが、コンポヌネント移怍が「リファクタリング」で完党なる「脱AngularJS」が「リアヌキテクティング」に該圓するでしょう。 Q: それはそれずしおシステムを止めおいるの A: いえ、皌働䞭に少しず぀移怍しおいたす システムリプレむスには以䞋の方匏がありたすが、 䞀括移行方匏 段階移行方匏 䞊行移行方匏 パむロット方匏 このプロゞェクトは「䞊行移行方匏」が圓おはたりたす。 なぜ「䞊行移行方匏」を採甚しおいるのかは、コンポヌネントを含むモゞュヌルの数が950もあるためですね。 「䞀括移行方匏」を採甚しおいたら膚倧すぎお倧爆発する恐れもありたした。問題箇所が深くなっおわかりにくくなるでしょう。 「段階移行方匏」を採甚しおいたら950もあれば郜床郜床システムを停止するこずになり、プロダクトが死んでしたうでしょう。 リスクは圓然枛らしたいし、サヌビスを止めたくありたせんよね。 そこで『倉態モゞュヌル』があるこずで、我々プロゞェクトは「䞊行移行方匏」のコンポヌネント移怍ができたわけです。 再床あらためお先人に敬意を蟌めお感謝したす。ありがずうございたした。 しくじり゚ピ゜ヌド プロゞェクトを進行するにあたっお、さたざたな出来事がありたした。 振り返りずしお反省点を3぀挙げおみたす。 しくじり① 蚭蚈の正しさを䜓珟したい欲望に取り憑かれおいた 理想圢も叶えたい謎のバむタリティに溢れおいた時期があり、蚭蚈の正しさを䜓珟したい欲望に取り憑かれおいたした。 今なら「このタむミングでやるべきこずじゃない」ず、優先床を考慮した䞊で蚀い切れたす。 これはプロゞェクト内で党䌚䞀臎した、方針ではなく指針、確固たる䜜業指針がなかったがためず思いたす。 『レガシヌ゜フトりェア改善ガむド』によれば「リラむトのリスク」を軜んじおいたのでしょう。 ずはいえ、蚭蚈に察する考えが深たったので、やっお損だったずたでは思わないですね。 しくじり② 締め切りが近くなるたで焊っおいない 倏䌑みの宿題でしょうか。 僕は倏䌑みの宿題を始業匏間近になっお焊っおやりだすどころか、もはや提出すらしない砎倩荒な子どもでした。 䌚瀟員ずしお働けおいるこずが奇跡に近いかもしれたせん。 ずはいえ、個人だけでなくプロゞェクトメンバヌ党員が等しく締め切りを意識しなければならないはずです。 だからベロシティグラフを芋ながらメンバヌに発砎をかけたりするこずで進捗を安定させないずいけたせん。 締め切りを守らないなんお組織が蚱したせんし、自発的に動くチヌムは垞に改善掻動でアップデヌトしおいくはずです。 スクラムで蚀うずころの透明性がなくおは健党なチヌムではないですね。 しくじり③ やっぱり皮算甚の芋積もり粟床は䜎い 倧きなタスクは䞭身が芋えないこずから芏暡感の蚀い倀で芋積もりされがちなので、やっぱり粟床は高くなかったですね。 そのため、プロゞェクトが進行しおいくうちに「難易床」ずいう物差しで工数芋積もりを行い芋積もり粟床を高めようずしおいきたした。 難易床の䜎いタスクから䞭難床のタスクたではほがほが芋積もりどおりずは思いたした。 けれど、最も難易床が高いタスクは15日かかるず芋積もりをしおいたのですが、15日でやるにはさすがに無理をしお残業しお間に合わせる努力をしたしたね。 小さく芋積もりたくなる人間の匱さ自分を優れたものずしお芋せたがるが出おしたったず振り返れたす。 かずいっお、単に芋積もりを盛っお、30日など倧きめの数字で芋積もっおも、䞊長ぞの報告時に「あヌそんなに」ずいう無䟡倀なトヌクで時間が朰れるので難しいずころですね。 倧きめの数字になるなら、もっず近くのゎヌルがあるのでタスクを小さく分割したす。 分割するず芋積もり粟床が䞊がりたす。 難易床の物差しを䜿う堎合、以䞋のような「぀らみ」が芋積もり数字に反映されたす。 発生条件がレアな動䜜確認 動䜜確認のために環境敎備が必芁 共通化がもたらした密結合なコンポヌネントの関心の分離 モゞュヌル同士の䟝存関係がズブズブで1぀ず぀ほどく必芁がある 䞖の䞭には難易床評䟡の手法がすでにあるので、それを元に芋積もるのが楜そうです。 難易床評䟡をする際は、以䞋が必芁ず蚀われおいたす。 質的䜜業分析 量的䜜業分析 技術的実珟性 先人は必ずいたす。車茪の再発明なんお楜をしおいないのでたっぎらごめんですね。 さいごに このプロゞェクトに関わっおいただいた、今も関わっおくれおいるメンバヌの皆さん本圓にありがずうございたす。 少人数、か぀プロダクトが正垞に皌働するこずが最優先なので、プロゞェクトの進行がストップするこずもよくありたした。 「脱AngularJS」は続きたすが、終わりの目凊が芋えおきたした。 貎方が入瀟されるずきには「刷新されたモダンなフロント゚ンド開発環境」が甚意されおいるはずでしょう。 しゃべっおおも仕方ないので、ひたむきに愚盎にがんばりたすね。 早く終わらせお䞀緒に祝杯でもあげたしょう。 それではたた今床。 次の蚘事は id:Eadaeda さんです。
この蚘事は、 モバむルファクトリヌ Advent Calendar 2020 20日目の蚘事です。 こんにちは、最近眠りが浅いこずで悩んでいる Yunagi_N です。 䞀昚幎に続いお 私はマむペヌスに、今幎も趣味党開のお話をしたす。 はじめに 今幎4月くらいから某 VR SNS にはたっお Unity に興味を持ち、いろいろなこずをやっおいるのですが、 VR 䞖界で VR ならではのパフォヌマンスをやっおいる人たちを芋かけお、憧れおパヌティクルをいじっおみたした。 ここでは、䞀般にパヌティクルラむブや VRMV ず呌ばれおいるパフォヌマンスを指したすが、 それぞれの説明に぀いおは実際に䜓隓しおもらうのが䞀番良いので、各皮 VR プラットフォヌムにおでかけしおみおください。 今回、普通にパヌティクルをいじっおみるだけでは特に面癜みが無いず感じたので、 再生䞭の音楜に合わせお動的に倉化する効果をスクリプトずシェヌダヌで䜜っおみたした。 なお、 䌚瀟で開発しおいるアプリ・プロゞェクトずは䞀切関係ありたせん。 前提 以䞋の環境で開発、動䜜確認を行っおいたす Windows 10 Unity 2018.4.20f1 たた、本蚘事の実装は VR SNS 内郚で動くように䜜られおおり、 セキュリティ䞊の理由などから、独自実装の VM 䞊で動くため以䞋の制限がありたす C# の䞀郚の構文のみをサポヌト (C# 7 盞圓の機胜にさらに制限をかけたもの) 本来は C# そのものではなくノヌド圢匏のプログラミング蚀語で行うのですが、有志が C# で䜜成できるアセットを公開しおいたす Unity で䜿えるすべおの API が蚱容されおいるわけではない 䟋えば、 Job System や ECS (2020.2 の時点でロヌドマップから消えたようですが)、たた List さえ API が公開されおいたせん なお、この蚘事では以䞋の事に぀いおは解説したせん Unity の基瀎 パヌティクル (Particle System) の基瀎 シェヌダヌ (ShaderLab, HLSL) の基瀎 たた、本蚘事では using を省いおいるこずがありたす。ご了承ください。 たた、私は音声呚りのプロではないので、蚘事䞭に間違いなどがある堎合がありたす、ごめんなさい。 実装 たずは音声情報を取埗したす。 パヌティクルなどの動きに応甚できるような情報は基本的には以䞋の2぀だず思いたす。 オヌディオレベル (dB 単䜍) 呚波数スペクトル情報 それぞれの取埗は、以䞋のコヌドで簡単に行えたす。 たずオヌディオレベル (dB) は GetOutputData から蚈算できたす。 なお、ここでのオヌディオレベル (dB) は dBFS ず呌ばれるもので、以䞋の蚈算匏で求められたす。 // MaxValue は RMS (Root Mean Square) が取り埗る倀の最倧倀 var dbfs = 20.0f * Mathf.Log10(RMS / MaxValue); 通垞、 GetOutputData で埗られる倀の範囲は -1  1 であるため、䞋蚘のコヌドにお dBFS が求められたす。 private const int SampleCount = 1024 ; // 64 ~ 8192 の範囲の 2 のべき乗の倀を指定する必芁がありたす。 [SerializeField] private AudioSource audioSource; private float [] _samples = new float[SampleCount]; private void Update() { var db = CalcDecibel(); } private float CalcDecibel() { audioSource.GetOutputData(_samples, 0 ); var sum = 0.0f ; foreach ( var sample in _samples) sum += sample * sample; var rmsValue = Mathf.Sqrt(sum / SampleCount); var dbValue = 20.0f * Mathf.Log10(rmsValue); if (dbValue < - 80.0f ) dbValue = - 80.0f ; return dbValue; } 次に、呚波数ごずのスペクトル情報は GetSpectrumData を䜿いたす。 特に難しいこずはないですね。 // 各皮むンスタンス倉数は䞊蚘のものを䜿い回したす。 private void Update() { audioSource.GetSpectrumData(_samples, 0 , FFTWindow.Hanning); } このずき、第3匕数に蚭定する FFTWindow は、求めおいる粟床に応じお適切なものを䜿甚したす。 今回、 GetSpectrumData で取埗したいデヌタはそこそこの粟床で埗られれば良いので、 FFTWindow.Hanning を蚭定したした。 たた、配列に入れられた倀は、以䞋のように蚈算するこずで、むンデックスず Hz を倉換できたす。 var i = /* 配列の index */ ; var hz = AudioSettings.outputSampleRate * 0.5f * i / SampleCount; 䟋えば、 AudioSettings.outputSampleRate が 44100Hz である堎合、配列の1番目の呚波数は、 var hz = 44100 * 0.5 * 1 / 1024 ; // 21.53Hz ずいった具合で、以降は 21.53Hz ごずにデヌタがサンプルされおいたす。 これで、再生されおいる音声から各皮情報が取埗できたした。 ただし、オヌディオレベルはただしもスペクトルは生デヌタのたたでは䜿いづらいので、 これらのデヌタを加工したうえでパヌティクル (Particle System) やシェヌダヌなどに枡しやすくしたす。 デヌタの加工圢匏はいく぀かあるず思いたすが、今回は最終的に以䞋のデヌタを枡しおパヌティクルを制埡するこずにしたした。 オヌディオレベル (dBFS) ピッチ情報 音域ごずのオヌディオレベル (dBFS) 個人の奜みで分類 Peak Hold Fall Down VU メヌタヌで、最倧倀が曎新されたらそこに点が移動し、埐々に䜎䞋しおいくアレです ここでは、最倧倀 ( max ) + r Hz を範囲に取り、以䞋の挔算の結果を枡したす (Mathf.Clamp(n, max - r, max) - (max - r)) / r たずピッチ情報ですが、これはスペクトル情報から䞀番倧きい倀を取り出し、 良い感じに補正しおあげれば、それらしい倀が埗られるようです。 (ただし、通垞の音楜においおは正確な倀は取れないそう。) コヌドは以䞋の通り。簡単ですね。 // 各皮むンスタンス倉数は䞊蚘のものを䜿い回したす。 private void Update() { audioSource.GetSpectrumData(_samples, 0 , FFTWindow.Hanning); var pitch = CalcPitch(_samples); } private float CalcPitch( float [] samples) { var maxValue = 0.0f ; var maxIndex = 0 ; for ( var i = 0 ; i < SampleCount; i ++ ) { var spectrum = samples[i]; if (maxValue > spectrum) continue ; maxValue = spectrum; maxIndex = i; } var l = samples[maxIndex - 1 ] / samples[maxIndex]; var r = samples[maxIndex + 1 ] / samples[maxIndex]; var f = maxIndex + 0.5f * (r * r - l * l); return f * AudioSettings.outputSampleRate * 0.5f * maxIndex / SampleCount; } 次は、音域ごずに呚波数垯を分類し、各音域のオヌディオレベルを蚈算したす。 これは、カダックさんのオヌディオスペクトルアナラむザヌのコヌドを元に、 bin を128個に分類したものから、特定呚波数区域の倀の平均倀を取りたした。 詳しくは、蚘事末尟に蚘茉しおいる参考リンクを参照ください。 最埌は Peak Hold Fall Down の実装ですが、これは䞋のコヌドで実装したした。 private const float FalldownPerTick = 0.1f ; private const float LevelRange = 5.0f ; private float _peak; // 各皮むンスタンス倉数は䞊蚘のものを䜿い回したす。 private void Update() { var db = ...; // 初めに蚈算した dBFS var value = CalcPeakFallDownValue() } // 雑だけど private float CalcPeakFallDownValue( float db) { var delta = Time.deltaTime; _peak = Mathf.Max(_peak - FalldownPerTick * delta, - 80.0f ); _peak = Mathf.Clamp(db, _peak, 0.0f ); var minValue = _peak - LevelRange; return (Mathf.Clamp(db, minValue, _peak) - minValue) / LevelRange; } ここたでで、ようやく必芁なデヌタがそろいたした。長かったです。 今床は、これらのデヌタを Particle System に枡しおあげたす。 その前に、今回䜿うシェヌダヌのコヌドを匵っおおきたす (ShaderLab は Transparent で良い感じに)。 ポむントはテクスチャを透明床に倉換しおいるのず、頂点カラヌを䜿っおいるこずくらいです。 テクスチャを透明床に倉換しおいるのは甚意したテクスチャの郜合から、 頂点カラヌを䜿っおいるのは、 Material 数を枛らしたいずいうプラットフォヌム䞊の郜合からです。 なお、 Particle System からシェヌダヌにデヌタを枡すには、 Renderer モゞュヌルのうち、 Custom Vertex Streams を有効にした䞊で、䜕をどのセマンティクスに枡すか蚭定する必芁がありたす。 // 各゚ントリポむントは、以䞋の蚭定 (ShaderLab) // // #pragma vertex vs // #pragma fragment fs // #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD; float4 color : COLOR; // 他はご自由に... } struct v2f { float4 vertex : SV_POSITION; float2 uv : TEXCOORD; float4 color : COLOR0; // 他はご自由に... } v2f vs (appdata v) { v2f o = (v2f) 0 ; o.vertex = UnityObjectToClipPos (v.vertex); o.uv = TRANSFORM_TEX (v.uv, _MainTex); o.color = v.color; // 枡したいものや倉換したいものをお奜きに return o; } fixed4 convertMonochromeToTransparent ( const float3 color) { const float transparent = color.r + color.g + color.b; return fixed4 ( 0 , 0 , 0 , transparent / 3 ); } fixed4 fs (v2f i) : SV_TARGET { fixed4 color = convertMonochromeToTransparent ( tex2D (_MainTex, i.uv)); color.rgb = i.color.rgb; color.a *= i.color.a; // Emission color.rgb *= pow ( 2 , _Emission); // あずは付けたい効果をどしどしず return color; } 最埌に、 Particle System でデヌタを枡しおあげたす。 䞊蚘で倉換したデヌタを元に、 Particle System や Particle を操䜜するコンポヌネントを぀くりたす。 それぞれの操䜜は単玔なものなので、コヌドは省略したすが、私は以䞋のようなものを䜜成したした 特定のデヌタの倀を増幅・枛衰させおさらにデヌタを扱いやすくするコンポヌネント 特定のデヌタが条件を満たした堎合、パヌティクルを Emit 特定のデヌタが条件を満たした堎合、 Particle System のプロパティを倉曎 これはむンスペクタヌを䜜るのが面倒なので、非 Active な Particle System から倀を匕っ匵っおくるように実装したした 特定のデヌタの倀や倉化量を Particle そのものに枡す 元デヌタの倀を良い感じにしお、 velocity や rotation に枡すず良いです ちなみに、 Particle そのものの操䜜は䞋蚘のようにすれば行えたす。 // private ParticleSystem ps; var particles = new ParticleSystem.Particle[ps.particleCount]; ps.GetParticles(particles); for ( var i = 0 ; i < ps.particleCount; i ++ ) { var particle = particles[i]; // お奜きな操䜜をここで paritcles[i] = particle; } ps.SetParticles(particles); ず、こんな感じで、音声情報を元に Particle System を操䜜できるコンポヌネント矀が完成したした。 あずは、䞀緒に再生したい音楜や BGM に合わせお、ひたすら数倀を調敎しおいけば、完成です。 正盎な話、コヌドを曞くよりもひたすら数倀調敎するのが厳しい気がしたすが、そこは根気よく頑匵りたしょう。 では、お疲れさたでした。たた来幎䌚いたしょう。 次の蚘事は id:i1derful さんです。 参考 音響とか / Sound and Acoustics How to choose FFT Window type - Unity Answers ヤマハ - 製品情報 Unity のオーディオの再生・エフェクト・解析周りについてまとめてみた - 凹みTips スマホ実機でサウンドのスペクトル解析を見たい - KAYAC engineers' blog
この蚘事は モバむルファクトリヌ Advent Calendar 2020 19日目の蚘事です。 こんにちは新卒゚ンゞニアの id:dorapon2000 です。開発する方にずっおGitは必須だず思いたすが、どのように操䜜しおいるでしょうか。コマンドラむンから、゚ディタの拡匵機胜から、GUIクラむアントからなどの方法があるず思いたす。今回は、GitKrakenずいうGitのGUIクラむアントに぀いお玹介しお、コマンドラむンずの違いに぀いお考えたこずを曞きたいず思いたす。クラむアントだけを利甚する人やコマンドラむンだけを利甚する人が新しい気付きを埗られるず嬉しいです。 GitKraken https://www.gitkraken.com/ UI/UXにずおも力を入れおいるGitのGUIクラむアントです。第䞀印象はGitグラフが矎しいですよね。私自身はこの芋た目に魅了されお、個人的な開発ではGitKrakenを利甚しおいたす。芋た目がきれいだず開発のやる気も起きたす。 アカりントマネゞメントやself-hostedのリポゞトリず連携する以倖のおおよその機胜は無料で利甚できたすが、プラむベヌトリポゞトリを利甚したい堎合は サブスクリプション に登録する必芁がありたす。プラむベヌトリポゞトリを利甚できるようになるIndividualであれば$29/yearです。 機胜 Git操䜜の基本的なこずはGUI䞊からほができたす。 git add & commit addずcommitをしたす。 git add -p addする郚分の遞択も行単䜍で可胜です git rebase -i HEAD~2 2぀のコミットを1぀にたずめたす。 git commit --amend & rebase -iの[r]eword コミットの線集をしたす。 git checkout/push/pull ボタンを抌すだけですぐ可胜です。 コマンドラむンず比べお楜なずころ 差分をすぐ芋れる ファむルや䞀郚のコヌドに察するHEADに戻す操䜜がずおも簡単 コマンドラむンでは耇数のコマンドを䜿い分ける必芁がある git reset --hard HEAD git checkout -p ファむルのgit addが簡単 コマンドラむンでは git add -p でハンクごずに远加するか指定する必芁があるがGUIではさくっずできる コミットメッセヌゞの線集が簡単 コマンドラむンではコミットが盎近であるかないかで耇数のコマンドを䜿い分ける必芁がある 1぀前であれば git commit --ammend 2぀以䞊前であれば git rebase -i remoteでバックログされおいるコミットがすぐわかる コマンドラむンでは git fetch をしないずremoteの最新版ずlocalでどれだけ履歎が離れおいるかわからない クラむアントであれば裏で定期的に git fetch をやっおくれる mergeコミットのrevertが簡単 コマンドラむンからだず git revert $hash -m 1 のようにオプションを付ける必芁ある。このオプションを芚えなくおも枈みたす。 GitKrakenではできないこず 普段遣いで気づいたGitKrakenではできないこずをあげようず思いたす。 少し耇雑なgit差分だず、1行単䜍で git add ができない git add -p の[e]dit rebaseをundoできない ssh先のリポゞトリを管理できない 少し耇雑なgit差分だず、1行単䜍でgit addができない コミットせずにたずめお線集をしお、あずから现かくコミットしおいくずいうこずが私はよくありたす。倉曎が離れた堎所であればGitKraken䞊でもワンクリックでその郚分だけaddできるのですが、同じ堎所に分離したい倉曎が混じっおいるずたずめおaddされおしたい、1行ず぀addのような现かい指定ができたせん。぀たり、 git add -p の[e]ditのような现かいaddができたせん。 rebaseをundoできない コマンドラむンでは git reflog から git reset --hard でもずに戻せたすが、GitKrakenの珟圚の仕組みではそこたで柔軟に察応できないようです。 ssh先のリポゞトリを管理できない sshした先のgitリポゞトリをGitKrakenでは管理できたせん。しょうがないかなずも思っおいたすが、将来の機胜远加に期埅しおいたす。 GUIクラむアントを䜿っおみお コマンドラむンだずオプションやコマンドの䜿い分けを芚える必芁がある郚分を、GUIクラむアントではワンクリックで実珟できおしたいたす。䞀郚の操䜜においお手数を倧きく省ける力がありたす。たた、gitの孊習敷居を䞋げる圹割もあるず思いたした。 どちらからでもよいず思った操䜜は、checkoutやstash、push、pullのようなシンプルなコマンド矀です。個人的にはコマンドラむンから操䜜するのず手数感は倉わりたせんでした。 逆にGUIクラむアントでは手が届かない郚分ずしお、䞊述した git add -p のeや git reflog などがありたす。い぀も䜿うわけではないですが、いざずいうずきにあるず嬉しいですね。 私の堎合、基本的にGitKrakenから操䜜しお、コマンドラむンをちょうど觊っおいるずきは盎接コマンドを叩いたりしおいたす。 たずめ GitKrakenの玹介を兌ねお、GUIクラむアントずコマンドラむンの操䜜感の違いに぀いおご玹介したした。結論ずしおどちらにも埗手䞍埗手がありたすが、個人的にはGUIクラむアントのスピヌド感はずおも奜みです。 明日の蚘事は Yunagi_N さんです
この蚘事は モバむルファクトリヌ Advent Calendar 2020 18日目の蚘事です。 こんにちは、゚ンゞニアの @PikkamanV です。 先日CloudBees瀟が認定するCertified Jenkins Engineer 2020を取埗したした。 この蚘事では普段の業務で埗たJenkinsの知識の延長で詊隓に合栌するたで過皋を蚘録しおいたす。 同じように受隓される方の参考になれば幞いです。 ※本蚘事は2020幎11月時点の詊隓に基づいおおり、珟圚は詊隓に倉曎点がある可胜性がありたす。 受隓の際は最新の情報をCloudBees瀟の公匏サむトでご確認ください。 www.cloudbees.com なぜ受隓したか 元々私は業務でCIツヌルたわりを觊るこずが倚かったのですが、今幎の春ごろからあるプロゞェクトのJenkinsサヌバを觊るこずになりたした。 ちなみに 去幎のアドベントカレンダヌでもCircleCIに぀いお曞いおいたす 。 件のJenkinsサヌバは先人の曞いた構築ログやJenkinsfileを元に構築を行い、9か月ほど自分が䞭心ずなっお運甚しおいたした。 その結果チヌム内からは「自称Jenkins職人」ずしお認知されるようになり、サヌバに䜕か䞍具合があるず呌ばれるようになっおいたした。 トラブルを解決するたびにJenkinsに぀いおの知識を深めおいきたしたが、果たしおこれでJenkinsそのものに詳しくなったず蚀えるか今のプロゞェクトの事情に詳しいだけで、他のプロゞェクトでは通甚しない知識なのではないかず考えるようにもなりたした。 そこで今回Certified Jenkins Engineerの認定詊隓を受け、合栌しお「認定Jenkins職人」ずなり自分のスキルアップを蚌明するこずを目指したした。 たた、受隓料は䌚瀟のキャリアアップ支揎制床を利甚しお負担しおもらいたした。 モバファクでは瀟員が倖郚のセミナヌぞの参加や資栌取埗などにかかる費甚を、予算の枠内で支揎する制床がありたす。 昚幎のアドベントカレンダヌにもキャリアアップ支揎制床を利甚しおAWSのトレヌニングを受講した蚘事があるので参考にしおください。 tech.mobilefactory.jp 詊隓の抂芁 詊隓ぱンタヌプラむズ版のCloudBees Jenkinsを提䟛しおいるCloudBees瀟が運営しおいたす。 日本語察応はしおおらず申蟌から受隓たですべお英語のみで行うこずになりたすが、日本のテストセンタヌでも受けるこずができたす。 ただし、今幎は䞖界的なCOVID-19の流行のため、自宅でオンラむン受隓をするこずができたした。 受隓料もテストセンタヌで受ける堎合150ドルに比べお自宅受隓の方がお安くなっおいるので99ドルおすすめです。 詊隓範囲もテストセンタヌで受ける堎合ず同䞀です。 詊隓自䜓は90分で60問の択䞀問題を解きたす。耇数の遞択肢を遞ぶ問題では郚分点もありたす。 合栌ラむンは66%ですが、問われる内容は倚岐に枡るため、埌述するコヌスワヌクをやっただけではなく実際に䜕ヶ月か運甚した経隓がないずボヌダヌを超えるのは難しいず思いたす。 詊隓勉匷から受隓たで 勉匷できる期間は1か月ほどだったので、できるだけ効率的に詊隓勉匷を進めようずあらかじめ蚈画を立おたした。 たずCloudBees瀟が提䟛する孊習サむトのCloudBees Universityに詊隓ガむドが掲茉されおいたす。 このガむドには詊隓範囲やサンプル問題、参考資料が掲茉されおおり、勉匷の指針を立おるずきに最初にチェックする文曞になるでしょう。 https://standard.cbu.cloudbees.com/certification-guide-and-information/370303 standard.cbu.cloudbees.com たた、 2018幎版の詊隓ガむド にはさらに詳现に読むべき資料が曞かれおいお䟿利です。 しかし、1か月の勉匷期間で参考資料をすべお読むこずは䞍可胜に思えたした。 そこで、たずむンタヌネット䞊から受隓者の䜓隓蚘を探し、圌らの勉匷した内容をたずめたした。 日本語話者の情報はあたり出おこないのですが、英語で怜玢するず結構出題パタヌンが分かっおきたす。 たた、YouTubeにも察策講座がいく぀か挙がっおいるので参考になりたした。以䞋はその䞀䟋です。 実際の詊隓問題は詊隓ガむドのサンプルより難しいので、無料の教材でもやっおみるず緎習になりたす。 www.youtube.com 総合しお以䞋の方針で詊隓を攻略するこずにしたした。 自宅にJenkinsサヌバを立おお、むンストヌルや管理の方法を埩習する 業務で䜿っおいるJenkinsやJenkinsfileでやっおいるこずを埩習する CloudBees瀟が提䟛する無料コヌスワヌクにVMを立おお手を動かしお取り組む コヌスワヌクで登堎した甚語や機胜を Jenkins Handbook から調べおいく 結果的にこの手順で詊隓に合栌するのに必芁な知識はカバヌできたした。 孊べたこず Jenkinsのフリヌスタむルゞョブずパむプラむンに぀いおは業務経隓で埗た知識をもう䞀床実践しおみる圢になり、よい埩習になりたした。 䞀方で業務では䜿っおいない機胜に぀いおは芚えるのはやや苊劎したしたが、新鮮な気持ちで勉匷できたした。 いく぀か䟋を挙げおみたす。 Blue Ocean パむプラむンをGUIで管理する機胜です。 GitHubなどのSCMサヌビスず連携しGUIでJenkinsfileを線集し、パむプラむンを実行埌、コミットするこずができたす。 特に䟿利な点は、GUIで各stepを線集しおいる途䞭でもリアルタむムにJenkinsfileをプレビュヌするこずができるこずです。たた、プレビュヌ䞭のJenkinsfileを盎接線集しおも、GUIでの蚭定が倉曎されるので、操䜜が分かりやすいです。ただし、䞀郚の凊理はただBlue Oceanに察応しおいないのに泚意が必芁です。 Shared Library あるプロゞェクトに耇数のJenkinsfileがあるずきに䟿利な機胜です。コピペされた凊理が曞かれたJenkinsfileたちの䞀郚に倉曎が入った堎合、すべおのJenkinsfileに同じ倉曎を加えるこずになり倧倉な手間です。 そういう時に共通凊理郚分を共有ラむブラリずしおたずめ、SCMから盎接配垃するこずができたす。これによっおJenkinsfileは小さく保守のしやすい圢を保぀こずができたす。 Docker agent 今のプロゞェクトではJenkinsサヌバ自䜓に様々なミドルりェアをむンストヌルしおいるのですが、ビルド環境をDockerで甚意するこずも可胜です。Docker Pipeline プラグむンを䜿うず公開されおいるむメヌゞや自前のむメヌゞを指定できるようになりたす。Dockerを䜿うこずでCIのプロセスだけでなく、その環境構築も再珟性を高くできるので、タむミングを芋お移行したいず考えおいたす。 たずめ 今回の受隓を通じお自分のスキルを客芳的に蚌明できるようになっただけでなく、業務だけでは埗られない知識を䜓系的に孊ぶこずができたした。いわゆるベンダヌ資栌は受隓料が高くなかなか手を出しにくかったのですが、これからは䌚瀟の支揎制床も利甚しおいくこずで、他分野に぀いおもスキルアップしおいけたらず思いたす。 明日の蚘事は id:dorapon2000 さんです
この蚘事は モバむルファクトリヌ Advent Calendar 2020 17日目の蚘事です。 こんにちは、゚ンゞニアの shioiyan です。 モバむルファクトリヌには郚掻動制床があり、いく぀もの郚掻動が存圚しおいるのですが、自分はそのうちのゲヌムゞャム郚に所属しおいたす。 今幎2月から匊瀟はリモヌトワヌクになりたしたが、ゲヌムゞャム郚はビデオ通話を䜿っお掻動を継続しおいたす。 近頃、倖出自粛しおいる人が増えた䞭でも、ビデオ通話で話しながら楜しく遊べるサヌビスを䜜ろうずいうこずで郚掻を通じお、Web䞊でリアルタむムにそれぞれの画面が同期するお絵かきチャットの開発をしたした。 仕様 今回䜜るリアルタむムお絵かきチャットの仕様はざっくり以䞋のようになりたす。 ナヌザは郚屋を遞んで入宀ができる 郚屋にはマりスやタップ操䜜で絵を描くこずのできるキャンバスがある 絵を描くず同じ郚屋のメンバヌのキャンバスがリアルタむムで曎新される 郚屋を退宀するこずができる 技術遞定の背景 各クラむアントのお絵かき画面をリアルタむムで同期するためには WebSocket API を甚いるこずが思い浮かびたした。 しかし、WebSocketサヌバの構築・管理にはコストがかかるため、「動いお觊れるものを玠早く䜜りたい」ずいう方針だったゲヌムゞャム郚で䜜るには少しネックでした。 調べおいく䞭で API Gateway の WebSocket API を甚いればサヌバレスでさくっず構築できおサヌバの管理芁らずで良さそう、ずいうこずがわかっおきたので、公匏で玹介されおいたチャットの実装を参考にものは詊しず䜜っおみたした。 するず思った以䞊にさくっず芁件を叶える実装をするこずができたので、その知芋を共有しようず思いたす。 利甚した技術スタック リアルタむム通信: Amazon API Gateway WebSocket API バック゚ンド: AWS Lambda 接続/郚屋情報の保持: Amazon DynamoDB フロント゚ンド: Nuxt.js (v2.14.11) 構成 たずはじめに今回実装したものの党䜓の構成を瀺しおおきたす。 クラむアントはAPI GatewayずWebSocketで通信し、通信内容に応じお3皮類のLambda関数を実行したす。 DynamoDBはLambda関数を通じお接続しおいるクラむアント情報の参照や曎新を行いたす。 実装されたもの(canvasの同期のみ) (巊のブラりザのcanvasに描いた絵が右のブラりザにも同期されおいたす) 実装 ▶ フォルダ構成 $ tree -I node_modules -L 3 . ├── webSocket │ ├── onConnect │ │ └── app.js │ ├── onDisconnect │ │ └── app.js │ ├── package.json │ ├── sendMessage │ │ └── app.js │ ├── serverless.yml │ └── yarn.lock └── view ├── README.md ├── assets │ └── README.md ├── commitlint.config.js ├── components │ └── README.md ├── layouts │ ├── README.md │ └── default.vue ├── middleware │ └── README.md ├── nuxt.config.js ├── package.json ├── pages │ ├── README.md │ ├── room.vue │ └── index.vue ├── plugins │ └── README.md ├── static │ ├── README.md │ └── favicon.ico ├── store │ └── README.md ├── stylelint.config.js ├── utils │ └── webSocket.js └── yarn.lock Serverless Framework API Gateway WebSocket API + Lambda + DynamoDBの構成はServerless Frameworkで䜜成したす。 コマンド1぀で各皮リ゜ヌスの生成・曎新・削陀ができるのはずおも䟿利です。 デプロむ $ sls deploy -v --stage dev ... Service Information service: advent-calendar-2020 stage: dev region: ap-northeast-1 stack: advent-calendar-2020-dev resources: 25 api keys: None endpoints: wss://xxxxx.execute-api.ap-northeast-1.amazonaws.com/dev functions: connectHandler: advent-calendar-2020-dev-connectHandler disconnectHandler: advent-calendar-2020-dev-disconnectHandler sendMessageHandler: advent-calendar-2020-dev-sendMessageHandler layers: None Stack Outputs ConnectHandlerLambdaFunctionQualifiedArn: arn:aws:lambda:ap-northeast-1:xxxxx:function:advent-calendar-2020-dev-connectHandler:10 DisconnectHandlerLambdaFunctionQualifiedArn: arn:aws:lambda:ap-northeast-1:xxxxx:function:advent-calendar-2020-dev-disconnectHandler:10 ServiceEndpointWebsocket: wss://xxxxx.execute-api.ap-northeast-1.amazonaws.com/dev ServerlessDeploymentBucketName: advent-calendar-2020-dev-serverlessdeploymentbuck-xxxxx SendMessageHandlerLambdaFunctionQualifiedArn: arn:aws:lambda:ap-northeast-1:xxxxx:function:advent-calendar-2020-dev-sendMessageHandler:10 ✹ Done in 18 .34s. リ゜ヌス䞀括削陀 $ sls remove --stage dev ▶ webSocket/serverless.yml service : advent-calendar-2020 provider : name : aws stage : ${opt:stage, 'dev' } region : ${opt:region, 'ap-northeast-1' } runtime : nodejs12.x iamRoleStatements : - Effect : Allow Action : - dynamodb:Query - dynamodb:GetItem - dynamodb:PutItem - dynamodb:DeleteItem Resource : - Fn::GetAtt : [ ConnectionsTable, Arn ] - Effect : Allow Action : - dynamodb:Query Resource : "arn:aws:dynamodb:${self:provider.region}:*:table/${self:service}-connections-${self:provider.stage}/index/*" environment : TABLE_NAME : Ref : ConnectionsTable websocketsApiName : ${self:service}-${self:provider.stage} websocketsApiRouteSelectionExpression : $request.body.message functions : connectHandler : handler : onConnect/app.handler events : - websocket : $connect disconnectHandler : handler : onDisconnect/app.handler events : - websocket : $disconnect sendMessageHandler : handler : sendMessage/app.handler events : - websocket : sendMessage resources : Resources : ConnectionsTable : Type : AWS::DynamoDB::Table Properties : TableName : ${self:service}-connections-${self:provider.stage} AttributeDefinitions : - AttributeName : connectionId AttributeType : S - AttributeName : roomId AttributeType : S KeySchema : - AttributeName : connectionId KeyType : HASH ProvisionedThroughput : ReadCapacityUnits : 1 WriteCapacityUnits : 1 GlobalSecondaryIndexes : - IndexName : roomId-index KeySchema : - AttributeName : roomId KeyType : HASH ProvisionedThroughput : ReadCapacityUnits : 1 WriteCapacityUnits : 1 Projection : ProjectionType : ALL SSESpecification : SSEEnabled : False DynamoDB WebSocketのconnectionIdずその接続しおいるクラむアントが今入宀しおいる郚屋情報を保持するために、connectionIdずroomIdのカラムを䜜成しおいたす。 たた、同じ郚屋に入っおいるメンバヌのレコヌドを取埗するためにroomIdを䜿甚しおク゚リを投げたいのですが、パヌテションキヌ(connectionId)の指定をせずにク゚リを投げるこずはできたせん。パヌテションキヌを指定せずにク゚リを投げるず ValidationException: Query condition missed key schema element ゚ラヌになる そこでroomIdに グロヌバルセカンダリむンデックス を貌っお怜玢できるようにしおいたす。 SSESpecification.SSEEnabled は DynamoDBに保存されたデヌタの暗号化を有効にするかの蚭定 です。 有効にするず料金がかかるので、開発時には無効にしおおくず良いでしょう。 API Gateway WebSocket API API GatewayのWebSocket APIでは serverless.yml の websocketsApiRouteSelectionExpression で指定された倀( routeKey )がクラむアントから枡されるず、それに応じたルヌトず呌ばれるリ゜ヌスタむプで凊理が実行されたす。 今回の実装だず、 $request.body.message の倀によっおルヌトが決定され、 $request.body.message が sendMessage だず sendMessageHandler の関数が実行されるこずになりたす。 ただし、API Gatewayで最初からルヌトに䜿甚できる3぀の特別なrouteKey倀が存圚したす。 $connect: クラむアントがWebSocket APIに最初に接続するずきに䜿甚される 接続開始したずきに実行したい凊理にルヌティングできる 今回の実装だず connectHandler が実行される $disconnect: クラむアントがWebSocket APIから切断するずきに䜿甚される 接続を切断したずきに実行したい凊理にルヌティングできる 今回の実装だず disconnectHandler が実行される $default: websocketsApiRouteSelectionExpression の倀が他のrouteKeyに䞀臎しない堎合に䜿甚される 今回は䜿甚しない( sendMessage 以倖の $request.body.message は考慮しない) Lambda関数 今回実装しおいる3぀の関数の倧枠は こちらのドキュメント を参考にしおいたす。 ▶ webSocket/onConnect/app.js const AWS = require( 'aws-sdk' ) AWS.config.update( { region: process.env.AWS_REGION } ) const DDB = new AWS.DynamoDB( { apiVersion: '2012-10-08' } ) exports.handler = function ( event , context, callback) { let roomId = '' if ( event .queryStringParameters && event .queryStringParameters.roomId) { roomId = event .queryStringParameters.roomId } const putParams = { TableName: process.env.TABLE_NAME, Item: { connectionId: { S: event .requestContext.connectionId } , roomId: { S: roomId } } } DDB.putItem(putParams, function (err) { callback( null , { statusCode: err ? 500 : 200, body: err ? 'Failed to connect: ' + JSON.stringify(err) : 'Connected.' } ) } ) } 接続時にQuery ParameterでroomIdを枡しおconnectionIdず共にDynamoDBに保持したす。 これによっおWebSocketの接続状態をDynamoDBに保持し぀぀、接続しおいるクラむアントがどの郚屋に入っおいるかも参照できるようになりたす。 ▶ webSocket/sendMessage/app.js const AWS = require( 'aws-sdk' ) const DDB = new AWS.DynamoDB.DocumentClient( { apiVersion: '2012-08-10' } ) const { TABLE_NAME } = process.env exports.handler = async ( event , context) => { const roomId = JSON.parse( event .body).roomId // 自分が参加しおいるルヌムの参加者のレコヌドを取埗 const queryParams = { TableName: TABLE_NAME, KeyConditionExpression: "#ROOMID = :ROOMID" , ExpressionAttributeNames: { "#ROOMID" : "roomId" } , ExpressionAttributeValues: { ":ROOMID" : roomId } , IndexName: 'roomId-index' } const connectionData = await DDB.query(queryParams).promise() const apigwManagementApi = new AWS.ApiGatewayManagementApi( { apiVersion: '2018-11-29' , endpoint: event .requestContext.domainName + '/' + event .requestContext.stage } ) const postData = JSON.parse( event .body).data const myConnectionId = event .requestContext.connectionId const postCalls = connectionData.Items.map(async ( { connectionId } ) => { try { // 送信者には送らない if (myConnectionId !== connectionId) { await apigwManagementApi.postToConnection( { ConnectionId: connectionId, Data: postData } ).promise() } } catch (e) { if (e.statusCode === 410) { console.log( `Found stale connection, deleting ${connectionId} ` ) await DDB. delete ( { TableName: TABLE_NAME, Key: { connectionId } } ).promise() } else { throw e } } } ) try { await Promise.all(postCalls) } catch (e) { return { statusCode: 500, body: e.stack } } return { statusCode: 200, body: 'Data sent.' } } グロヌバルセカンダリむンデックスを貌ったroomIdで同じ郚屋のクラむアントのレコヌドを取埗しお、それらのconnectionIdに察しお postToConnection でdataを送信したす。 クラむアントから送信されたdataを同じ郚屋のクラむアント党員に送信しおいたす。 ▶ webSocket/onDisconnect/app.js const AWS = require( 'aws-sdk' ) AWS.config.update( { region: process.env.AWS_REGION } ) const DDB = new AWS.DynamoDB( { apiVersion: '2012-10-08' } ) exports.handler = function ( event , context, callback) { const deleteParams = { TableName: process.env.TABLE_NAME, Key: { connectionId: { S: event .requestContext.connectionId } } } DDB.deleteItem(deleteParams, function (err) { callback( null , { statusCode: err ? 500 : 200, body: err ? 'Failed to disconnect: ' + JSON.stringify(err) : 'Disconnected.' } ) } ) } 切断時にはDynamoDBから切断したクラむアントのレコヌドを削陀したす。 クラむアント WebSocketの接続にはJavaScriptのWebSocketのwrapperラむブラリの Sockette を䜿甚しおいたす。 Socketteを䜿甚するこずで再接続凊理や、WebSocketの各皮EventListenerで実行される関数が簡単に指定できたす。 ▶ view/utils/webSocket.js import Sockette from 'sockette' export function newConnection( { roomId, onReceivedMessage } ) { return new Sockette( `wss://xxxxx.execute-api.ap-northeast-1.amazonaws.com/dev?roomId= ${roomId} ` , { timeout: 5e3, maxAttempts: 3, onmessage: (e) => onReceivedMessage(e), onerror: (e) => console.error(e), } ) } vueコンポヌネントの実装は、実際に絵を描く郚分は割愛したすが以䞋のようになりたす。 ▶ view/pages/room.vue <template> <div> <canvas ref= "canvas" ></canvas> </div> </template> <script> import { newConnection } from '~/utils/webSocket' export default { data() { return { ws: null , roomId: 'roomA' , // XXX : 実際は動的にする } } , mounted() { this .connectWs() } , beforeDestroy() { this .disconnectWs() } , methods: { connectWs() { this .ws = newConnection( { roomId: this .roomId, onReceivedMessage: this .onReceivedMessage, } ) } , disconnectWs() { if ( this .ws !== null ) { this .ws.close() } } , onReceivedMessage( event ) { // WebSocketでメッセヌゞを受信したずきに実行される const data = JSON.parse( event .data) switch (data.actionType) { // actionTypeによっお凊理を分岐。画面クリア、1぀戻る/進むずいったeventを増やしたりする case 'DRAW' : this .draw(data.positions) break default : break } } , draw( { fromX, fromY, toX, toY } ) { // canvasにfromの座暙からtoの座暙に線を匕く ... } , sendDrawMessage(positions) { this .sendMessage( { data: JSON.stringify( { actionType: 'DRAW' , positions, } ), } ) } , sendMessage( { data } ) { if ( this .ws !== null && this .roomId) { this .ws.json( { // WebSocketを介しおobjを送信 message: 'sendMessage' , data, roomId: this .roomId, } ) } } , // タップ&ドラッグ時にthis.sendDrawMessage(positions)や自分のcanvasに察しおthis.draw(positions)を行う凊理 ... } , } </script> ... ペヌゞ衚瀺埌にWebSocketの接続を行い、接続した状態でcanvas䞊でタップ&ドラッグ操䜜をするずその座暙をWebSocketを介しお同じ郚屋のクラむアントに操䜜した内容を送信しおcanvasの同期を行いたす。 送信するobjに actionType ずいう倀を持たせおいたすが、これは受信したメッセヌゞの内容を識別するためのものです。 この倀を倉えるこずで、別のデヌタのやりずりずそれに応じた凊理の分岐も簡単に行えたす。 この蚘事では実装しおいたせんが、䟋えばキャンバスクリアや描いた絵を1぀戻す(undo)/進める(redo)ずいったむベントの同期をできるようにするず、よりお絵かきチャットっぜくなるでしょう。 たずめ API Gateway + WebSocket APIでお絵かきチャットを䜜るこずができたした。 今回はcanvasの同期に利甚したしたが、様々なリアルタむム通信が必芁な堎面で䟿利に䜿っおいけそうな機胜だず感じたした。 明日の蚘事は id:pikkaman さんです
この蚘事は モバむルファクトリヌ Advent Calendar 2020 16日目の蚘事です。 はじめに 動䜜環境 逆匕き 条件を満たす最初の芁玠をずる 条件を満たす最埌の芁玠をずる 条件を満たす芁玠より埌ろの芁玠たちを抜出する 条件を満たす芁玠以降の芁玠たちを抜出する 条件を満たす芁玠より前の芁玠たちを抜出する 条件を満たす芁玠以前の芁玠たちを抜出する 先頭からいく぀かの芁玠を抜出する 末尟からいく぀かの芁玠を抜出する 1぀しかない芁玠のみを抜出する 条件を満たす芁玠を怜玢する 条件を満たす芁玠数を求める 条件を満たさない芁玠数を求める 条件を満たす芁玠のむンデックスを求める 条件を満たす最初の芁玠のむンデックスを求める 条件を満たす最埌の芁玠のむンデックスを求める 条件を満たすただ䞀぀の芁玠のむンデックスを求める 最初にCODE BLOCKが正垞終了する芁玠の結果を求める 最埌にCODE BLOCKが正垞終了する芁玠の結果を求める 䞀぀だけCODE BLOCKが正垞終了する芁玠の結果を求める 芁玠の最倧倀を求める 文字列の最倧を求める 芁玠の最小倀を求める 文字列の最小を求める 最小倀ず最倧倀を同時に求める リストを文字列順で䞊び替える リストを数倀順で䞊び替える 文字列を結合する 芁玠の合蚈を求める 芁玠の積を求める 各芁玠のうちどれかが条件を満たすならtrue 各芁玠のうちどれかが条件を満たさないならtrue 党芁玠が条件を満たすならtrue 党芁玠が条件を満たさないならtrue 芁玠がただ1぀だけ条件を満たすならtrue それぞれの芁玠に凊理を行いたい 芁玠をランダムに䞊び替える 最頻倀を求める 重耇を匟く 特定の芁玠の埌ろに芁玠を远加する 2぀のリストを同時に操䜜する 耇数のリストを1぀のリストにする 1぀のリストを耇数のリストに仕分ける 各芁玠を耇数のリストに仕分ける 条件ごずの芁玠数を求める リストからむテレヌタを䜜成する リストから耇数をたずめお返すむテレヌタを䜜成する key-valueリスト操䜜 key-valueリストをたずめお1芁玠にする key, valueのたずたりを展開する key-valueリストのkeyのみを抜出する key-valueリストのvalueのみを抜出する key-valueリストから条件にあう党芁玠を抜出する 条件にあったペア数を出す 条件にあう最初の芁玠を抜出する 条件にあう芁玠が芋぀かったかを調べる key-valueリストにmapず同じこずをしたい たずめ はじめに こんにちは、゚ンゞニアの id:Dozi0116 です。 自分は4月に入瀟しおから珟圚たででいろいろなPerlのコヌドに觊れられたのですが、その䞭で List::AllUtils ずいうモゞュヌルが印象に残っおいたす。 List::AllUtilsずは、「リスト操䜜のいろいろが詰たったモゞュヌル」を集めお䜿えるようにしたモゞュヌルで、 List::Util List::SomeUtils List::UtilsBy の3぀のモゞュヌルが䞀䜓化したモゞュヌルです。 実際の開発でも䜿っおいたすが、たくさんの関数が詰たっおいるため、どんなこずができるのかを党く理解しきれおいたせん。たた、既に曞かれおいる゜ヌスコヌドをすぐにList::AllUtilsのものず理解ができず、新しい関数を芋かけるたびにこんなこずたでできるのかず驚いおいたした。 せっかくこんな䟿利モゞュヌルを䜿っおいるのに䜿いこなせないのはもったいない  そこで、List::AllUtilsができるこずを理解するため、やりたいこずから䜿うべきコヌドを芋぀けやすくなるように、そしお他に同じように困っおいる人のハヌドルを䞋げるため、この逆匕きを䜜りたした。 動䜜環境 Perl 5.30.2 List::AllUtils 0.18 List::SomeUtils 0.56 List::Util 1.45 List::UtilsBy 0.11 逆匕き 今回茉せおいる䟋は このテストコヌド で怜蚌しおいたす。 今回の逆匕きでは、 reduce などの発想次第でなんでもできそうな関数は、既にある関数ず差別化ができる堎合にのみ茉せおいたす。ご了承ください。 たた、List::AllUtilsの䟝存バヌゞョン関係䞊、䜿えない関数がいく぀かありたす。 条件を満たす最初の芁玠をずる @list = ( 4 , 7 , 1 ); $result = first { $_ > 5 } @list ; # 7 $result = first_value { $_ > 5 } @list ; # 7 $result = firstval { $_ > 5 } @list ; # first_valueの゚むリアス extract_first_by は条件を満たした芁玠をオリゞナルのリストから消す @list = ( 4 , 7 , 1 ); $result = extract_first_by { $_ > 5 } @list ; # 7 print ( @list ); # (4, 1); reduce で曞くこずもできる @list = ( 4 , 7 , 1 ); $result = reduce { defined ( $a ) ? $a : $b > 5 ? $b : undef } undef , @list ; 条件を満たす最埌の芁玠をずる @list = ( 4 , 7 , 1 ); $result = last_value { $_ < 5 } @list ; # 1 $result = lastval { $_ < 5 } @list ; # 同じ(last_valueの゚むリアス) 条件を満たす芁玠より埌ろの芁玠たちを抜出する @list = ( 2 , 4 , 6 , 8 , 10 ); @result = after { $_ > 5 } @list ; # (8, 10) 条件を満たす芁玠以降の芁玠たちを抜出する @list = ( 2 , 4 , 6 , 8 , 10 ); @result = after_incl { $_ > 5 } @list ; # (6, 8, 10) 条件を満たす芁玠より前の芁玠たちを抜出する @list = ( 2 , 4 , 6 , 8 , 10 ); @result = before { $_ > 5 } @list ; # (2, 4) 条件を満たす芁玠以前の芁玠たちを抜出する @list = ( 2 , 4 , 6 , 8 , 10 ); @result = before_incl { $_ > 5 } @list ; # (2, 4, 6) 先頭からいく぀かの芁玠を抜出する @list = 1..10 ; @result = head 3 , @list ; # (1, 2, 3) @result = head - 2 , @list ; # (1, 2, 3, 4, 5, 6, 7, 8) 末尟からいく぀かの芁玠を抜出する @list = 1..10 ; @result = tail 3 , @list ; # (8, 9, 10) @result = tail - 2 , @list ; # (3, 4, 5, 6, 7, 8, 9, 10) 1぀しかない芁玠のみを抜出する @list = ( 1 , 1 , 1 , 2 , 3 , 3 , 4 , 5 ); @result = singleton @list ; # (2, 4, 5) 条件を満たす芁玠を怜玢する @list = 1. . .10 ; @result = grep { $_ == 4 } @list ; # (4) 二分探査をするため、 CODE BLOCKは 比范した芁玠が小さいなら-1を、倧きいなら1を、ちょうどなら0を返す必芁が、たた @list は゜ヌトされおいる必芁がある @sorted_list = 1. . .10 ; @result = bsearch { $_ <=> 4 } @sorted_list ; # (4) extract_by は芋぀けた芁玠を result に抜き出しお、オリゞナルから消える @list = 1. . .10 ; @result = extract_by { $_ == 4 } @list ; # (4) print ( @list ); # (1, 2, 3, 5, 6, 7, 8, 9, 10) 条件を満たす芁玠数を求める @list = 1. . .10 ; $result = true { $_ < 4 } @list ; # 3 条件を満たさない芁玠数を求める @list = 1. . .10 ; $result = false { $_ < 4 } @list ; # 6 条件を満たす芁玠のむンデックスを求める 二分探査をするため、 CODE BLOCKは 比范した芁玠が小さいなら-1を、倧きいなら1を、ちょうどなら0を返す必芁が、たた @list は゜ヌトされおいる必芁がある @sorted_list = 1. . .10 ; $result = bsearch_index { $_ <=> 4 } @sorted_list ; # 3 $result_b = bsearchidx { $_ <=> 4 } @sorted_list ; # 同じ(bsearch_indexの゚むリアス) 耇数のむンデックスをたずめお求めるなら indexes を䜿う @list = ( 1 , 1 , 1 , 2 , 4 , 1 ); @result = indexes { $_ == 1 } @list ; # (0, 1, 2, 5) 条件を満たす最初の芁玠のむンデックスを求める @list = ( 1 , 1 , 1 , 2 , 4 , 1 ); $result = first_index { $_ == 1 } @list ; # 0 $result = firstidx { $_ == 1 } @list ; # 同じ(first_indexの゚むリアス) 条件を満たす最埌の芁玠のむンデックスを求める @list = ( 1 , 1 , 1 , 2 , 4 , 1 ); $result = last_index { $_ == 1 } @list ; # 5 $result = lastidx { $_ == 1 } @list ; # 同じ(last_indexの゚むリアス) 条件を満たすただ䞀぀の芁玠のむンデックスを求める 芁玠が耇数あった堎合、 -1 が返る @list = ( 1 , 1 , 1 , 2 , 4 , 1 ); $result = only_index { $_ == 2 } @list ; # 3 $result = onlyidx { $_ == 2 } @list ; # 同じ(only_indexの゚むリアス) $result = only_index { $_ == 1 } @list ; # -1 最初にCODE BLOCKが正垞終了する芁玠の結果を求める @list = ( 4 , 7 , 1 ); $result = first_result { $_ ** 2 if $_ > 3 } @list ; # 16 $result = firstres { $_ ** 2 if $_ > 3 } @list ; # 同じ(first_indexの゚むリアス) 最埌にCODE BLOCKが正垞終了する芁玠の結果を求める @list = ( 4 , 7 , 1 ); $result = last_result { $_ ** 2 if $_ > 3 } @list ; # 49 $result = lastres { $_ ** 2 if $_ > 3 } @list ; # 同じ(last_indexの゚むリアス) 䞀぀だけCODE BLOCKが正垞終了する芁玠の結果を求める @list = ( 4 , 7 , 1 ); $result = only_result { $_ ** 2 if $_ > 5 } @list ; # 49 $result = onlyres { $_ ** 2 if $_ > 5 } @list ; # 同じ(last_indexの゚むリアス) # 正垞終了する芁玠が耇数ある堎合、undefを返す $result = only_result { $_ ** 2 if $_ > 3 } @list ; # undef 芁玠の最倧倀を求める @list = ( 1 , 4 , 3 ); $result = max @list ; # 4 max_by で曞けば、CODE BLOCKは自由に蚘述できるので噚甚なこずができる @list = ({ value => 2 , id => 1 }, { value => 5 , id => 2 }, { value => 5 , id => 3 }); $result = max_by { $_->{ value } } @list ; # { value => 5, id => 2 } リストコンテキストを返り倀に期埅すれば、党郚の芁玠を取埗できる @list = ({ value => 2 , id => 1 }, { value => 5 , id => 2 }, { value => 5 , id => 3 }); @result = max_by { $_->{ value } } @list ; # ({ value => 5, id => 2 }, { value => 5, id => 3 }) 文字列の最倧を求める ここでいう文字列の最倧ずは、文字コヌド比范での最倧を指す @list = qw/a b c/ ; $result = maxstr @list ; # c @list = ({ name => 'a' }, { name => 'b' }, { name => 'c' }); $result = reduce { $a->{ name } gt $b->{ name } ? $a : $b } @list ; # { name => 'c' } 芁玠の最小倀を求める @list = ( 2 , 3 , 1 ); $result = min @list ; # 1 min_by で曞けば、CODE BLOCKは自由に蚘述できるので噚甚なこずができる @list = ({ value => 2 , id => 1 }, { value => 1 , id => 2 }, { value => 1 , id => 3 }); $result = min_by { $_->{ value } } @list ; # { value => 1, id => 2 } リストコンテキストを返り倀に期埅すれば、党郚の芁玠を取埗できる @list = ({ value => 2 , id => 1 }, { value => 1 , id => 2 }, { value => 1 , id => 3 }); @result = min_by { $_->{ value } } @list ; # ({ value => 1, id => 2 }, { value => 1, id => 3 }) 文字列の最小を求める ここでいう文字列の最小ずは、文字コヌド比范での最小を指す @list = qw/b c a/ ; $result = minstr @list ; # a @list = ({ name => 'b' }, { name => 'a' }, { name => 'c' }); $result = reduce { $a->{ name } lt $b->{ name } ? $a : $b } @list ; # { name => 'a' } 最小倀ず最倧倀を同時に求める @list = ( 2 , 3 , 1 ); ( $min , $max ) = minmax @list ; # (1, 3) minmax_by で曞けば、CODE BLOCKは自由に蚘述できるので噚甚なこずができる @list = ({ value => 2 , id => 1 }, { value => 1 , id => 2 }, { value => 1 , id => 3 }); ( $min , $max ) = minmax_by { $_->{ value } } @list ; # ({ value => 1, id => 2 }, { value => 2, id => 1 }) リストを文字列順で䞊び替える @list = ( 'banana' , 'melon' , 'apple' ); @result = sort @list ; # ('apple', 'banana', 'melon') 比范するものが組み蟌み関数の sort ず違っお省略できるため、 sort_by を䜿えば比范的簡朔に曞くこずができる @list = ({ name => 'banana' }, { name => 'melon' }, { name => 'apple' }); @result = sort_by { $_->{ name } } @list ; # ({ name => 'apple' }, { name => 'banana' }, { name => 'melon' }) sort_by を降順で䜿いたい時は rev_sort_by が䜿える @list = ({ name => 'banana' }, { name => 'melon' }, { name => 'apple' }); @result = rev_sort_by { $_->{ name } } @list ; # ({ name => 'melon' }, { name => 'banana' }, { name => 'apple' }) リストを数倀順で䞊び替える @list = ( 23 , 1 , 12 ); @result = sort { $a <=> $b } @list ; # (1, 12, 23) 比范するものが組み蟌み関数の sort ず違っお省略できるため、 nsort_by を䜿えば比范的簡朔に曞くこずができる @list = ({ value => 23 }, { value => 1 }, { value => 12 }); @result = nsort_by { $_->{ value } } @list ; # ({ value => 1 }, { value => 12 }, { value => 23 }) nsort_by を降順で䜿いたい時は rev_nsort_by が䜿える @list = ({ value => 23 }, { value => 1 }, { value => 12 }); @result = rev_nsort_by { $_->{ value } } @list ; # ({ value => 23 }, { value => 12 }, { value => 1 }) 文字列を結合する @list = qw/a b c/ ; $result = join '' , @list ; # "abc" reduce で曞けば、CODE BLOCKは自由に蚘述できるので噚甚なこずができる @list = qw/a b c/ ; $result = reduce { uc ( $a ) . uc ( $b ) } @list ; # "ABC" 芁玠の合蚈を求める @list = 1..10 ; $result = sum @list ; # 55 # 芁玠がない時はundefを返す @list = (); $result = sum @list ; # undef sum0 を甚いるず芁玠0のリストの堎合は0を返す @list = (); $result = sum0 @list ; # 0 @list = ({ value => 2 }, { value => 4 }, { value => 1 }); $result = reduce { $a + $b->{ value } } 0 , @list ; # 7 芁玠の積を求める @list = 1..10 ; $result = product @list ; # 3628800 # 芁玠がない時は1を返す @list = (); $result = product @list ; # 1 @list = ({ value => 2 }, { value => 4 }, { value => 1 }); $result = reduce { $a * $b->{ value } } 1 , @list ; # 8 各芁玠のうちどれかが条件を満たすならtrue @list = ({ flag => 1 }, { flag => 0 }, { flag => 1 }); $result = any { $_->{ flag } } @list ; # 1 # 空リストの堎合、falseを返す @list = (); $result = any { $_->{ flag } } @list ; # "" any_u は空リストの堎合に undef を返す @list = (); $result = any { $_->{ flag } } @list ; # "" $result = any_u { $_->{ flag } } @list ; # undef 各芁玠のうちどれかが条件を満たさないならtrue @list = ({ flag => 1 }, { flag => 0 }, { flag => 1 }); $result = notall { $_->{ flag } } @list ; # 1 # 空リストの堎合、falseを返す @list = (); $result = notall { $_->{ flag } } @list ; # "" notall_u は空リストの堎合に undef を返す @list = (); $result = notall { $_->{ flag } } @list ; # "" $result = notall_u { $_->{ flag } } @list ; # undef 党芁玠が条件を満たすならtrue @list = ({ flag => 1 }, { flag => 1 }, { flag => 1 }); $result = all { $_->{ flag } } @list ; # 1 # 空リストの堎合、trueを返す @list = (); $result = all { $_->{ flag } } @list ; # 1 all_u は空リストの堎合に undef を返す @list = (); $result = all { $_->{ flag } } @list ; # 1 $result = all_u { $_->{ flag } } @list ; # undef 党芁玠が条件を満たさないならtrue @list = ({ flag => 0 }, { flag => 0 }, { flag => 0 }); $result = none { $_->{ flag } } @list ; # 1 # 空リストの堎合、trueを返す @list = (); $result = none { $_->{ flag } } @list ; # 1 none_u は空リストの堎合に undef を返す @list = (); $result = none { $_->{ flag } } @list ; # 1 $result = none_u { $_->{ flag } } @list ; # undef 芁玠がただ1぀だけ条件を満たすならtrue @list = ({ flag => 0 }, { flag => 1 }, { flag => 0 }); $result = one { $_->{ flag } } @list ; # 1 # 空リストの堎合、falseを返す @list = (); $result = one { $_->{ flag } } @list ; # "" one_u は空リストの堎合に undef を返す @list = (); $result = one { $_->{ flag } } @list ; # "" $result = one_u { $_->{ flag } } @list ; # undef それぞれの芁玠に凊理を行いたい @list = ( 4 , 7 , 1 ); @result = map { $_ *= 2 } @list ; # (8, 14, 2) print ( @list ); # (8, 14, 2); apply で曞けば、元のリストは倉曎されない @list = ( 4 , 7 , 1 ); @result = apply { $_ *= 2 } @list ; # (8, 14, 2) print ( @result ); # (4, 7, 1) bundle_by で曞けば、耇数の芁玠をたずめお凊理できる @list = ( 1. . .8 ); @result = bundle_by { [ $_[ 0 ] , $_[ 1 ] , $_[ 2 ] ] } 3 , @list ; # ([1, 2, 3], [4, 5, 6], [7, 8, undef]) 芁玠をランダムに䞊び替える @list = ( 'a' , 'b' , 'c' , 'd' ); @result = shuffle @list ; # 䜕が出るかは神のみぞ知る weighted_shuffle_by で曞けば、重みを぀けたランダムになる @list = ( 'a' , 'b' , 'c' ); @result = weighted_shuffle_by { { a => 1 , b => 0 , c => 99 }->{ $_ } } @list ; # ほがほが ('c', 'a', 'b') 最頻倀を求める @list = ( 'apple' , 'pineapple' , 'apple' , 'banana' , 'apple' , 'apple' , 'apple' ); @result = mode @list ; # ('apple') 重耇を匟く 埌に出おきた重耇芁玠が消される @list = ( 'hoge' , 'hoge' , 22 , 35 , 10 , 22 ); @result = uniq @list ; # ('hoge', 22, 35, 10) @result = distinct @list ; # 同じ(uniqの゚むリアス) 芁玠が数倀or文字列で䞀定なら以䞋の関数も䜿える @list = ( 1 , 2 , 3 , 1 , 5 ); @result = uniqnum @list ; # (1, 2, 3, 5) @list = ( 'a' , 'A' , 'aa' , 'a' ); @result = uniqstr @list ; # ('a', 'A', 'aa') uniq_by で曞けば、CODE BLOCKは自由に蚘述できるので噚甚なこずができる @list = ({ value => 2 , id => 1 }, { value => 1 , id => 2 }, { value => 1 , id => 3 }); @result = uniq_by { $_->{ value } } @list ; # ({ value => 2, id => 1 }, { value => 1, id => 2 }) 特定の芁玠の埌ろに芁玠を远加する @list = ( 1 , 2 , 3 , 5 ); my $result = insert_after { $_ == 3 } 4 , @list ; @list ; # (1,2,3,4,5) stringの等䟡比范をするなら、 insert_after_string が䜿える @list = ( 'first' , 'second' , 'third' , 'fifth' ); my $result = insert_after_string 'third' , 'fourth' , @list ; @list ; # ('first', 'second', 'third', 'fourth', 'fifth') 2぀のリストを同時に操䜜する @list_a = ( 'a' , 'b' , 'c' ); @list_b = ( 1 , 2 , 3 ); @result = pairwise { { str => $a , num => $b } } @list_a , @list_b ; # ( { str => 'a', num => 1 }, { str => 'b', num => 2 }, { str => 'c', num => 3 }, ) 耇数のリストを1぀のリストにする @list_a = ( 'a' , 'b' , 'c' ); @list_b = ( 1 , 2 ); @result = mesh @list_a , @list_b ; # ('a', 1, 'b', 2, 'c', undef) @result = zip @list_a , @list_b ; # 同じ(meshの゚むリアス) zip_by で曞けば、CODE BLOCKは自由に蚘述できるので噚甚なこずができる @list_a = ( 'a' , 'b' , 'c' ); @list_b = ( 1 , 2 ); @result = zip_by { $_[ 0 ] , $_[ 1 ] } \ @list_a , \ @list_b ; # ('a', 1, 'b', 2, 'c', undef) 1぀のリストを耇数のリストに仕分ける # CODE BLOCK は 仕分け先のむンデックスを期埅しおいる @list = ( 1 , 2 , 1 , 1 , 2 ); @result = part { $_ } @list ; # ( undef, [1, 1, 1], [2, 2] ) partition_by を䜿うず、仕分ける時の倀をkeyずしたハッシュで返しおくれる @list = ( 1 , 2 , 1 , 1 , 2 ); %result = partition_by { $_ } @list ; # { 1 => [ 1, 1, 1 ], 2 => [ 2, 2] } 各芁玠を耇数のリストに仕分ける @list = ({ id => 1 , name => 'hoge' , }, { id => 2 , name => 'fuga' }, { id => 3 , name => 'piyo' }); ( $ids , $names ) = unzip_by { $_->{ id } , $_->{ name } } @list ; # ids: (1, 2, 3), names: ('hoge', 'fuga', 'piyo') 条件ごずの芁玠数を求める @list = ( 1 , 2 , 1 , 1 , 2 ); %result = count_by { $_ } @list ; # { 1 => 3, 2 => 2 } リストからむテレヌタを䜜成する @list = ( 1 , 2 , 3 ); $it = each_array( @list ); $it ->(); # 1 $it ->(); # 2 $it ->(); # 3 $it ->(); # undef リファレンスから䜜成するなら each_arrayref が䜿える $list = [ 1 , 2 , 3 ]; $it = each_array( $list ); $it ->(); # 1 $it ->(); # 2 $it ->(); # 3 $it ->(); # undef リストから耇数をたずめお返すむテレヌタを䜜成する @list = ( 1. . .8 ); $it = natatime 3 , @list ; $it ->(); # (1, 2, 3) $it ->(); # (4, 5, 6) $it ->(); # (7, 8) $it ->(); # undef key-valueリスト操䜜 ここでの key-valueリスト ずはリストの芁玠が (key1, value1, key2, value2, ...) ずなっおいるもの。 @kvlist = ( 'jp' , 'こんにちは' , 'en' , 'hello' ); %hash = @kvlist ; $hash{ jp } ; # こんにちは key-valueリストをたずめお1芁玠にする @list = ( 'k1' , 'v1' , 'k2' , 'v2' ); @result = pairs @list ; # ( ['k1', 'v1'], ['k2', 'v2'] ) key, valueのたずたりを展開する @list = ( [ 'k1' , 'v1' ], [ 'k2' , 'v2' ] ); @result = unpairs @list ; # ('k1', 'v1', 'k2', 'v2') key-valueリストのkeyのみを抜出する @list = ( 'k1' , 'v1' , 'k2' , 'v2' ); @result = pairkeys @list ; # ( 'k1', 'k2' ) key-valueリストのvalueのみを抜出する @list = ( 'k1' , 'v1' , 'k2' , 'v2' ); @result = pairvalues @list ; # ( 'v1', 'v2' ) key-valueリストから条件にあう党芁玠を抜出する @list = ( 'k1' , 'v1' , 'k2' , 'v2' , 'k3' , 'v1' ); @result = pairgrep { $b eq 'v1' } @list ; # ('k1', 'v1', 'k3', 'v1') 条件にあったペア数を出す 2぀1セットで芋るため、最倧倀は芁玠の半分になるこずに泚意 @list = ( 'k1' , 'v1' , 'k2' , 'v2' , 'k3' , 'v1' ); $result = pairgrep { $a eq 'k1' && $b eq 'v1' } @list ; # 1 条件にあう最初の芁玠を抜出する @list = ( 'k1' , 'v1' , 'k2' , 'v2' , 'k3' , 'v1' ); ( $key , $value ) = pairfirst { $b eq 'v1' } @list ; # $key = 'k1', $value = 'v1' 条件にあう芁玠が芋぀かったかを調べる @list = ( 'k1' , 'v1' , 'k2' , 'v2' , 'k3' , 'v1' ); $result = pairfirst { $b eq 'v1' } @list ; # 1 key-valueリストにmapず同じこずをしたい @list = ( 'k1' , 'v1' , 'k2' , 'v2' ); @result = pairmap { " $a - $b " } @list ; # ( 'k1-v1', 'k2-v2' ) たずめ PerlのモゞュヌルであるList::AllUtilsの逆匕きを䜜りたした。 もちろんこれが正解ずいうわけではなく、いろいろな曞き方があるので、この蚘事を読んだ方も曞き方や掻甚䟋があれば教えおください。 自分みたいに党容を理解できおいない人の助けになったら嬉しいです。 最埌になりたすが、この蚘事に曞くにあたっお協力しおくれた瀟員のみなさん、ありがずうございたした 明日の蚘事は id:summer_gift さんです
この蚘事は モバむルファクトリヌ Advent Calendar 2020 15日目の蚘事です。 ゚ンゞニアの yokoi0803 です。DB蚭蚈をしおいお倚察䞀のリレヌションを芋たり、蚭蚈したりする機䌚が䜕床かあっお、その床にどう蚭蚈するかで悩んでたす。 倚察䞀のリレヌションはいく぀かの蚭蚈で実珟できたすが、その遞定の際の指暙を埗るため、今回はパフォヌマンスの芳点から蚭蚈の比范をしおみたいず思いたす。 倚察䞀のリレヌションを衚珟する3぀の蚭蚈 あるテヌブルが耇数のテヌブルに察しお倚察䞀で玐付くケヌスに぀いお、ここではくじ匕きずその景品を衚珟するためのDB蚭蚈を想定したす。 箱の䞭のくじそれぞれに景品が蚭定されおおり、景品には旅行刞や果物など、様々な皮類のものがありたす。 こういった仕様の蚭蚈手法にはいく぀か皮類がありたすが、今回は「ポリモヌフィック関連」、「亀差テヌブルを甚いた蚭蚈」、「芪テヌブルを甚いた蚭蚈」の3皮類に぀いお取り䞊げるこずにしたす。 たずはそれぞれどのような蚭蚈なのか、簡単に玹介しおいきたす。 ポリモヌフィック関連 どのテヌブルのどのレコヌドに玐付くのか、ずいう情報をテヌブルに持たせる蚭蚈です。 prizes テヌブルの target_type が 「どのテヌブルに玐付くか」、 target_id が「どのレコヌドに玐付くか」の情報を瀺したす。 ポリモヌフィック関連はSQLアンチパタヌンでも取り䞊げられおいるように、倖郚キヌ制玄を぀けるこずができないために玐付く察象のテヌブルが保蚌されず、理由がない限り掚奚される蚭蚈ではありたせん。 亀差テヌブルを甚いた蚭蚈 玐付き先の皮類ごずに関連情報だけを持たせたテヌブル亀差テヌブルを甚意する蚭蚈です。 prizes ずその玐付き先である tickets 、 fruits ずの間に、関連を瀺す亀差テヌブルがそれぞれ存圚したす。 芪テヌブルを甚いた蚭蚈 玐付き元ず玐付き先の党おのテヌブルに共通の芪テヌブルを甚意する蚭蚈です。 今回のくじ匕きのケヌスでは、くじ匕きのくじそのものを衚す balls ずいうテヌブルを芪ずしお、党おのテヌブルが玐付くように蚭蚈しおみたした。 パフォヌマンスの比范 同じ仕様を実珟する3皮類の蚭蚈がありたすが、どれを遞択すれば良いでしょうか。 「良い蚭蚈」に぀いおはよく蚀及されおいるず思いたすが、今回はそれに぀いおは考えず、パフォヌマンスの芳点から3皮類の蚭蚈を比范しおみたいず思いたす。 準備 今回は䞊の説明で取り䞊げた、くじ匕きずその景品に぀いおのDB蚭蚈をそのたた題材ずしたす。 「ポリモヌフィック関連」、「亀差テヌブルを甚いた蚭蚈」、「芪テヌブルを甚いた蚭蚈」の3皮類で蚭蚈し、「玐付き先のデヌタ量の倉化」および「玐付き先の皮類数の倉化」に察しお怜玢パフォヌマンスがどのように倉化するかを確認したす。 具䜓的には玐付き元である prizes テヌブルから1,000件ず、その玐付き先のレコヌドから情報を取埗するたでの実行時間を蚈枬したした。蚈枬は Benchmark で10,000回詊行し、結果ずしおいたす。 マシンスペックに぀いお 項目 倀 OS Ubuntu 16.04.7 LTS CPU Intel(R) Xeon(R) CPU E3-1220L V2 @ 2.30GHz ×4 メモリ 16GB DB管理システムに぀いお MySQL5.6を利甚しおいる 蚈枬時に発行されるク゚リの党おにINDEXが䜿甚されるように蚭定 ク゚リの実行蚈画を衚瀺する ポリモヌフィック関連 EXPLAIN SELECT * FROM prizes LEFT JOIN fruits ON prizes.target_type = ' fruits ' AND prizes.target_id = fruits.id LEFT JOIN tickets ON prizes.target_type = ' tickets ' AND prizes.target_id = tickets.id WHERE prizes.id IN (:prize_ids); + ----+-------------+---------+--------+---------------+---------+---------+----------------------------------------------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | + ----+-------------+---------+--------+---------------+---------+---------+----------------------------------------------+------+-------------+ | 1 | SIMPLE | prizes | range | PRIMARY | PRIMARY | 4 | NULL | 9 | Using where | | 1 | SIMPLE | fruits | eq_ref | PRIMARY | PRIMARY | 4 | polymorphic_relation.prizes.target_id | 1 | Using where | | 1 | SIMPLE | tickets | eq_ref | PRIMARY | PRIMARY | 4 | polymorphic_relation.prizes.target_id | 1 | Using where | + ----+-------------+---------+--------+---------------+---------+---------+----------------------------------------------+------+-------------+ 亀差テヌブルを甚いた蚭蚈 EXPLAIN SELECT * FROM prizes LEFT JOIN prizes_fruits ON prizes.id = prizes_fruits.prize_id LEFT JOIN fruits ON prizes_fruits.fruit_id = fruits.id LEFT JOIN prizes_tickets ON prizes.id = prizes_tickets.prize_id LEFT JOIN tickets ON prizes_tickets.ticket_id = tickets.id WHERE prizes.id IN (:prizes_ids); + ----+-------------+----------------+--------+---------------+---------+---------+-----------------------------------------------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | + ----+-------------+----------------+--------+---------------+---------+---------+-----------------------------------------------+------+-------------+ | 1 | SIMPLE | prizes | range | PRIMARY | PRIMARY | 4 | NULL | 9 | Using where | | 1 | SIMPLE | prizes_fruits | eq_ref | PRIMARY | PRIMARY | 4 | cross_table_relation.prizes.id | 1 | NULL | | 1 | SIMPLE | fruits | eq_ref | PRIMARY | PRIMARY | 4 | cross_table_relation.prizes_fruits.fruit_id | 1 | NULL | | 1 | SIMPLE | prizes_tickets | eq_ref | PRIMARY | PRIMARY | 4 | cross_table_relation.prizes.id | 1 | NULL | | 1 | SIMPLE | tickets | eq_ref | PRIMARY | PRIMARY | 4 | cross_table_relation.prizes_tickets.ticket_id | 1 | NULL | + ----+-------------+----------------+--------+---------------+---------+---------+-----------------------------------------------+------+-------------+ 芪テヌブルを甚いた蚭蚈 EXPLAIN SELECT * FROM prizes LEFT JOIN tickets ON prizes.ball_id = tickets.ball_id LEFT JOIN fruits ON prizes.ball_id = fruits.ball_id WHERE prizes.id IN (:prize_ids); + ----+-------------+---------+-------+---------------+----------+---------+--------------------------------------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | + ----+-------------+---------+-------+---------------+----------+---------+--------------------------------------+------+-------------+ | 1 | SIMPLE | prizes | range | PRIMARY | PRIMARY | 4 | NULL | 9 | Using where | | 1 | SIMPLE | tickets | ref | ball_idx | ball_idx | 4 | parent_table_relation.prizes.ball_id | 1 | NULL | | 1 | SIMPLE | fruits | ref | ball_idx | ball_idx | 4 | parent_table_relation.prizes.ball_id | 1 | NULL | + ----+-------------+---------+-------+---------------+----------+---------+--------------------------------------+------+-------------+ 蚭定ファむル (my.cnf) を衚瀺する [ mysqld ] character-set-server = utf8 expire_logs_days = 1 max_binlog_size =300M skip-name-resolve wait_timeout = 10 log_error = /var/log/mysql/error.log slow_query_log slow_query_log_file = /var/log/mysql/mysql-slow.log long_query_time = 0 . 1 enforce_gtid_consistency sql_mode = TRADITIONAL,NO_AUTO_VALUE_ON_ZERO,ONLY_FULL_GROUP_BY binlog_format = ROW binlog_row_image =minimal query_cache_size = 0 query_cache_type = 0 #------------------------------------------------ ## InnoDB #------------------------------------------------ innodb_file_per_table innodb_log_file_size = 2G innodb_flush_method = O_DIRECT innodb_open_files = 2000 #メモリ䜿甚抑制 table_definition_cache = 400 [ mysqld_safe ] open_files_limit = 65535 スキヌマ定矩に぀いお それぞれの蚭蚈でのスキヌマ定矩を掲茉したす。 tickets 関係のテヌブルは fruits 関係のテヌブルず同様になるので省略しおいたす。 ポリモヌフィック関連 [root@localhost] polymorphic_relation> show create table prizes\G *************************** 1. row *************************** Table: prizes Create Table: CREATE TABLE `prizes` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `rank` int(10) unsigned NOT NULL, `target_id` int(10) unsigned NOT NULL, `target_type` varchar(32) NOT NULL, PRIMARY KEY (`id`), KEY `target_type_target_id_idx` (`target_type`,`target_id`) ) ENGINE=InnoDB AUTO_INCREMENT=1001 DEFAULT CHARSET=utf8 1 row in set (0.00 sec) [root@localhost] polymorphic_relation> show create table fruits\G *************************** 1. row *************************** Table: fruits Create Table: CREATE TABLE `fruits` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(32) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=100001 DEFAULT CHARSET=utf8 1 row in set (0.00 sec) 亀差テヌブルを甚いた蚭蚈 [root@localhost] cross_table_relation> show create table prizes\G *************************** 1. row *************************** Table: prizes Create Table: CREATE TABLE `prizes` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `rank` int(10) unsigned NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 1 row in set (0.00 sec) [root@localhost] cross_table_relation> show create table prizes_fruits\G *************************** 1. row *************************** Table: prizes_fruits Create Table: CREATE TABLE `prizes_fruits` ( `prize_id` int(10) unsigned NOT NULL, `fruit_id` int(10) unsigned NOT NULL, PRIMARY KEY (`prize_id`), KEY `fruit_idx` (`fruit_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 1 row in set (0.00 sec) [root@localhost] cross_table_relation> show create table fruits\G *************************** 1. row *************************** Table: fruits Create Table: CREATE TABLE `fruits` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(32) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 1 row in set (0.00 sec) 芪テヌブルを甚いた蚭蚈 [root@localhost] parent_table_relation> show create table prizes\G *************************** 1. row *************************** Table: prizes Create Table: CREATE TABLE `prizes` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `ball_id` int(10) unsigned NOT NULL, PRIMARY KEY (`id`), KEY `ball_idx` (`ball_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 1 row in set (0.00 sec) [root@localhost] parent_table_relation> show create table balls\G *************************** 1. row *************************** Table: balls Create Table: CREATE TABLE `balls` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `color` varchar(32) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 1 row in set (0.00 sec) [root@localhost] parent_table_relation> show create table fruits\G *************************** 1. row *************************** Table: fruits Create Table: CREATE TABLE `fruits` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `ball_id` int(10) unsigned NOT NULL, `name` varchar(32) NOT NULL, PRIMARY KEY (`id`), KEY `ball_idx` (`ball_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 1 row in set (0.00 sec) 蚈枬方法に぀いお 蚈枬で甚いたコヌドを掲茉したす。 use strict ; use warnings ; use utf8 ; use DBI; use Benchmark qw/timethese cmpthese/ ; use constant +{ PRIZE_COUNT => 1000 , ITERATION => 10000 , }; my $user = "root" ; my $pass = "" ; my $result = timethese(ITERATION, +{ polymorphic => sub { my $dbh = DBI-> connect ( "dbi:mysql:database=polymorphic_relation;host=Localhost;port=3306" , $user , $pass , +{ RootClass => 'DBIx::Sunny' }, ) || die $ DBI:: errstr ; my $sql = <<EOT ; SELECT * FROM prizes LEFT JOIN fruits ON prizes.target_type = 'fruits' AND prizes.target_id = fruits.id LEFT JOIN tickets ON prizes.target_type = 'tickets' AND prizes.target_id = tickets.id WHERE prizes.id IN (:prize_ids); EOT $dbh->select_all ( $sql , +{ prize_ids => [ 1. .PRIZE_COUNT] }); }, cross_table => sub { my $dbh = DBI-> connect ( "dbi:mysql:database=cross_table_relation;host=Localhost;port=3306" , $user , $pass , +{ RootClass => 'DBIx::Sunny' }, ) || die $ DBI:: errstr ; my $sql = <<EOT ; SELECT * FROM prizes LEFT JOIN prizes_fruits ON prizes.id = prizes_fruits.prize_id LEFT JOIN fruits ON prizes_fruits.fruit_id = fruits.id LEFT JOIN prizes_tickets ON prizes.id = prizes_tickets.prize_id LEFT JOIN tickets ON prizes_tickets.ticket_id = tickets.id WHERE prizes.id IN (:prize_ids); EOT $dbh->select_all ( $sql , +{ prize_ids => [ 1. .PRIZE_COUNT] }); }, parent_table => sub { my $dbh = DBI-> connect ( "dbi:mysql:database=parent_table_relation;host=Localhost;port=3306" , $user , $pass , +{ RootClass => 'DBIx::Sunny' }, ) || die $ DBI:: errstr ; my $sql = <<EOT ; SELECT * FROM prizes LEFT JOIN tickets ON prizes.ball_id = tickets.ball_id LEFT JOIN fruits ON prizes.ball_id = fruits.ball_id WHERE prizes.id IN (:prize_ids); EOT $dbh->select_all ( $sql , +{ prize_ids => [ 1. .PRIZE_COUNT] }); }, }); cmpthese $result ; 蚈枬 玐付き先のデヌタ量によるパフォヌマンス倉化の比范 玐付き先のテヌブルのレコヌド数が倉化するこずで、パフォヌマンスがどのように倉化するか蚈枬したす。 玐付き先の皮類は党お2぀ずし、レコヌド数はそれぞれ10件、1,000件、100,000件ず倉化させたした。 結果を以䞋に瀺したす。 レコヌド数10ä»¶ Benchmark: timing 10000 iterations of cross_table, parent_table, polymorphic... cross_table: 87 wallclock secs (36.01 usr + 2.82 sys = 38.83 CPU) @ 257.53/s (n=10000) parent_table: 81 wallclock secs (31.21 usr + 1.76 sys = 32.97 CPU) @ 303.31/s (n=10000) polymorphic: 73 wallclock secs (35.58 usr + 2.55 sys = 38.13 CPU) @ 262.26/s (n=10000) Rate cross_table polymorphic parent_table cross_table 258/s -- -2% -15% polymorphic 262/s 2% -- -14% parent_table 303/s 18% 16% -- レコヌド数1,000ä»¶ Benchmark: timing 10000 iterations of cross_table, parent_table, polymorphic... cross_table: 90 wallclock secs (36.54 usr + 2.41 sys = 38.95 CPU) @ 256.74/s (n=10000) parent_table: 88 wallclock secs (30.96 usr + 2.88 sys = 33.84 CPU) @ 295.51/s (n=10000) polymorphic: 79 wallclock secs (36.63 usr + 1.97 sys = 38.60 CPU) @ 259.07/s (n=10000) Rate cross_table polymorphic parent_table cross_table 257/s -- -1% -13% polymorphic 259/s 1% -- -12% parent_table 296/s 15% 14% -- レコヌド数100,000ä»¶ Benchmark: timing 10000 iterations of cross_table, parent_table, polymorphic... cross_table: 91 wallclock secs (36.68 usr + 2.29 sys = 38.97 CPU) @ 256.61/s (n=10000) parent_table: 101 wallclock secs (31.46 usr + 2.33 sys = 33.79 CPU) @ 295.95/s (n=10000) polymorphic: 79 wallclock secs (36.45 usr + 1.52 sys = 37.97 CPU) @ 263.37/s (n=10000) Rate cross_table polymorphic parent_table cross_table 257/s -- -3% -13% polymorphic 263/s 3% -- -11% parent_table 296/s 15% 12% -- 考察 レコヌド数の倉化に関しお芋おいくず、どの蚭蚈でもレコヌド数が少なくずも100,000件皋床たでであれば、パフォヌマンスの劣化は芋られたせんでした。 各蚭蚈手法をパフォヌマンスで比范するず、芪テヌブル > 亀差テヌブル ≒ ポリモヌフィック関連 ずなっおいたす。 芪テヌブルに察しお亀差テヌブルのパフォヌマンスが䜎いのは、JOINするテヌブルの数が関係しおいそうです。 芪テヌブルに察しおポリモヌフィック関連のパフォヌマンスが䜎いのは、JOINする際の凊理の差が関係しおいるのではないかず考えおいたす。 玐付き先の皮類数によるパフォヌマンス倉化の比范 玐付き先の皮類数が倉化するこずで、パフォヌマンスがどのように倉化するか蚈枬したす。 玐付き先の皮類は2個ず10個のケヌスで比范し、レコヌド数は党お1,000件ずしお固定したした。 玐付き先の皮類数を10個に増やしお蚈枬した結果を以䞋に瀺したす。 Benchmark: timing 10000 iterations of cross_table, parent_table, polymorphic... cross_table: 188 wallclock secs (57.09 usr + 3.08 sys = 60.17 CPU) @ 166.20/s (n=10000) parent_table: 163 wallclock secs (35.00 usr + 2.79 sys = 37.79 CPU) @ 264.62/s (n=10000) polymorphic: 104 wallclock secs (38.80 usr + 2.74 sys = 41.54 CPU) @ 240.73/s (n=10000) Rate cross_table polymorphic parent_table cross_table 166/s -- -31% -37% polymorphic 241/s 45% -- -9% parent_table 265/s 59% 10% -- 考察 玐付き先が増えるこずでJOINするテヌブルも増えるため、どの蚭蚈でもパフォヌマンスの劣化を起こすようです。 玐付き先が増えるほどJOINするテヌブルの数の差が亀差テヌブルず他2぀の蚭蚈ずの間で開いおいくため、パフォヌマンスの差も顕著になっおいるのだず考えられたす。 たずめ 倚察䞀のリレヌションを衚珟する蚭蚈ずしお「ポリモヌフィック関連」、「亀差テヌブルを甚いた蚭蚈」、「芪テヌブルを甚いた蚭蚈」をパフォヌマンスの芳点から比范した。 どの蚭蚈でも、玐付き先の皮類が増えた堎合、テヌブル結合凊理もその分増えおいくためにパフォヌマンスの劣化を起こす。 亀差テヌブルを甚いた蚭蚈は、他2぀の蚭蚈ず比范しお、玐付き先の皮類が増えるこずに察するパフォヌマンスの劣化床合いが倧きい。 明日の蚘事は id:Dozi0116 さんです。
この蚘事は モバむルファクトリヌ Advent Calendar 2020 14日目の蚘事です。 はじめたしお、20卒゚ンゞニアのthe96です。 今回は業務䞭に䜿っおいるPerlのVSCodeの拡匵機胜のメ゜ッド呌び出しの際の定矩元ゞャンプが正しく動䜜するように修正した話をしたす。 やったこず 埓来の VSCode Perl ではメ゜ッド呌び出し( Hoge::Fuga->func() )のずきに定矩元ゞャンプを行った堎合、別パッケヌゞの同名関数の定矩元に移動しおしたうこずがありたす。 ゜ヌスコヌドを読む際に正しい定矩元に移動しないず䞍䟿なので、この拡匵機胜に手を入れお改善したした。 この䞍具合に぀いお説明するために、同名の関数 hello が定矩された二぀のパッケヌゞ A ず B を甚意したした。 修正前の定矩元ゞャンプ機胜では、関数呌び出し( A::hello )のずきは A#hello の定矩に移動できおいたすが、メ゜ッド呌び出し( A->hello )のずきに定矩元ゞャンプをするず B#hello の定矩に移動しおしたっおいたす。 修正埌の定矩元ゞャンプ機胜では、メ゜ッド呌び出しでも期埅通り A#hello ぞ移動できおいたす。 原因 メ゜ッド呌び出しされおいる関数で定矩元ゞャンプをする際、その関数の前の文字列を参照し、パッケヌゞ名であればそれを含めお怜玢しおくれたす。 しかし、 -> がパッケヌゞ名を぀なぐ区切り文字ずしお認識されおいなかったのが原因でした。 https://github.com/vscode-perl/vscode-perl/blob/master/src/utils.ts#L15 export function getPackageBefore(document: vscode.TextDocument, range: vscode.Range): string { let separatorRange = getRangeBefore(range, 2); let separator = document.getText(separatorRange); let pkg = ""; while (separator === "::") { const newRange = document.getWordRangeAtPosition(getPointBefore(separatorRange, 1)); if (newRange) { range = newRange; pkg = document.getText(range) + separator + pkg; separatorRange = getRangeBefore(range, 2); separator = document.getText(separatorRange); } else { // break loop separator = ""; } } return pkg.replace(/::$/, ""); } 区切り文字に -> を加えた結果、予想通りパッケヌゞ名を考慮しお正しい定矩元ぞずゞャンプしおくれるようになりたした これで、業務䞭のコヌドリヌディングが捗りそうです:tada: 改良埌の拡匵機胜 初めおのOSSぞのP-Rです https://github.com/vscode-perl/vscode-perl/pull/41 しばらく曎新されおいなかったので、VSCodeのマヌケットプレむスにも公開しおおきたした。 䞊蚘が元リポゞトリに反映されるたで、よろしければご利甚ください。 https://marketplace.visualstudio.com/items?itemName=the96.vscode-perl 明日の蚘事は yokoi0803 さんです
この蚘事は モバむルファクトリヌ Advent Calendar 2020 13日目の蚘事です。 はじめたしお ずあるチヌムでUX・UI呚りを担圓しおいるデザむナヌの id:yux_0_0 です。 今日の蚘事では、職皮に限らず誰でもUXを意識できるようにするための「自䞻トレヌニングのススメ」ず、それを埌抌しする「UX探怜隊」に぀いおご玹介したす。 はじめに 本題に入る前に、私がUXの䞖界ずどう向き合っおいるかを軜く曞かせおいただきたす。 私のデザむナヌずしおのキャリアは、玙媒䜓のデザむンが䞻な仕事の小さなデザむン事務所から始たりたした。 数幎かけおデザむンの基瀎を身に぀けた埌にweb系䌁業に転職し、FlashでActionScriptを曞いたり、グラフィックデザむンやwebデザむン等を担圓しおきたした。 モバむルファクトリヌに入瀟しおからもしばらくはグラフィックデザむンずUIデザむンをメむンで担圓しおいたしたが、数幎前にUXの䞖界に觊れお「難しいけど面癜い」ず感じ、それからは珟堎で詊行錯誀しながらUXに関わる仕事をやり続けお今に至りたす。 解決すべき問題に察しおたくさん考えお答えを芋぀ける事が、難しいけれど面癜くお奜きです。 そしお䜜ったものがリリヌスされたあずのナヌザヌさんたちの様子を芋たり、自分でむベントをやりに珟地にいったずきの楜しさず嬉しさずいったら やるべき事もやりたい事も倚くお倧倉ですが、日々頭を抱えながらもワクワクしおいたす。 「いいものを䜜れるようになりたい」ずいうシンプルか぀壮倧な気持ちを原動力にしお、「デザむナヌ」ずいう枠にずらわれず、興味があるこずに手を䌞ばしお日々珟堎で走り回っおいたす。 自䞻トレヌニングのススメ そんな感じで日々テンション高く仕事をしおいるのですが、UXずいうものはどうしおも「䜕をやっおいるのか分かりにくいし専門的で難しそう」ず思われがちです。 「UX」ずいう名称が぀いた曞籍やwebの蚘事などでは専門的な甚語や様々な手法が倚く玹介されおいるのですが、実はUXの基本は「深く考えるこず」です。 もう少し詳しくいうず「色々なケヌスを想定しお色々な方向から物事を芋るこず」なのですが、これは職皮や経隓に限らず誰でも意識できるこずです。 なのでい぀も「職皮限らずUXを意識できる人が増えたら、チヌムでも䌚瀟でももっずいいものが䜜れるようになるはず」ず思っおいたした。 「もっずみんなで深く考え、自瀟サヌビスを良くするための意芋を掻発に亀わしたい」ず。 ですが今たで意識しおいなかった人からするず「䜕から始めたら良いの」ず思うはず。 そこで今幎の倏、瀟内向けドキュメント 『UXを意識した珟堎のお仕事玹介ず、自䞻トレヌニングのススメ』 を公開したした。 「珟堎のお仕事玹介」では自分がやっおいるこずに぀いお、専門甚語を䜿わずどの職皮でも理解できるように説明したした。 「自䞻トレヌニング以䞋、自䞻トレず衚蚘」は普段仕事でやっおいる内容を応甚したものです。 考える時の基瀎䜓力 を぀け、 問題解決のため自分で深く考えられるようにする のを䞻な目的ずしおいたす。 自䞻トレのやり方 自䞻トレは次の぀のステップで行いたす。 STEP1. 芋るものを決めたす 自瀟サヌビスだず答えを探す方に意識がいっおしたうので、自瀟サヌビス以倖を掚奚したす STEP2. タヌゲットず目的を考えお箇条曞きにしたす 「タヌゲットはどの局なのか」ず「この斜策の目的は䜕なのか」を考え、思い぀いたものを曞きだしおみたしょう すぐに思い぀いたものを曞くだけでOKです STEP3. 箇条曞きにしたものに察しお自問自答したす STEP2で曞いたものに察しお「本圓にそうなのか」「なぜそう思うのか」ず自問自答し、その理由ず考え぀く限りの可胜性を曞き出したす 曞き出すものが無くなったら終了 このドキュメントは公開盎埌からたくさんの反響をいただきたした。以前 モバファクブログ でも玹介されたので、タむトルに芋芚えがある方もいるかもしれないですね。 UX探怜隊、はじめたした ですがその埌も「自䞻トレをやっおみようかな」「自䞻トレを始めたよ」ずいう声は聞こえおきたせんでした。あんなにたくさんの反響をいただいたのにどうしおだろう ず考え、たどり着いたのは「おそらく始めるきっかけがないから」。 じゃあきっかけになるような䌚をやろうず考え、10月に誕生したのが『UX探怜隊』です。 UX探怜隊ずは 少人数最倧6人で自䞻トレの内容を芋ながらワむワむず話す瀟内勉匷䌚 参加条件は、 自䞻トレの内容を持っおきお圓日共有する ずいうこずだけ Google Meetを䜿ったビデオ䌚議。2週に1回、1時間開催 私が隊長で、毎回募集する参加者はゲスト隊員ず呌んでいたす。 ゲスト隊員が 探怜の䞋準備 自䞻トレをしお、UX探怜隊の時間にみんなでその内容を 探怜する 深堀りするずいうものです。 自䞻トレのテヌマや圢匏は自由です。できるだけ決たりごずを無くしお参加しやすくしおいたす。 「UX探怜隊ずいう勉匷䌚を始めたす」ず発衚した日、「自䞻トレ始めようかな」ずいう声が聞こえおきたした。「さっそく効果が出たかも」ず嬉しくなりたした。その埌、数回開催しおいたす。 探怜のずきにやっおいるこず UX探怜隊では、議論をしたり䜕かの結論を出すこずなどはしたせん。専門甚語の解説などもしたせん。ただただ「なぜだろう」ず想像しお意芋を亀わし合うだけの時間です。 珟状、探怜のテヌマに遞ばれるものは゜ヌシャルゲヌムが倚いですが、気になるテヌマずしお以䞋を挙げおいるゲスト隊員もいたした。どれもずおも面癜そうです ・ 日頃めちゃめちゃ䜿っおいるコマンドラむンツヌルのUI/UXずは ・ ブラりザのタブの䜍眮 ・ 自転車のトップチュヌブの圢 ある日の探怜の様子をご玹介 基本的には䞊で玹介した「自䞻トレのやり方」に沿っおドキュメントを準備しおくれる隊員が倚いです。圓日はそのドキュメントを画面共有しおもらっお、探怜スタヌト ここからご玹介する内容は隊員の自問自答の様子なので、こういう考えもあるよね、ずいう認識で読んでいただければず思いたす。 詳しい内容を曞くずずおも長くなっおしたうのでほんの䞀郚だけですが、雰囲気が䌝われば STEP1 芋るものを決めたす ずあるゲスト隊員が遞んだテヌマは「ゲヌムの歊噚・キャラクタヌなどの線成画面」。 自身で遊んでいるゲヌムの䞭からバトルシステムが異なる2぀のゲヌムをチョむスしたずのこず。知らないゲヌムもあったので画像付きで軜く説明しおもらいたした。 STEP2 タヌゲットず目的を考えお箇条曞きにしたす 3぀の芖点から考えた内容を話しおくれたした。 線成画面があるゲヌムっおどんなこずを楜しんでほしいのだろう ・ タヌゲットは線成が億劫ではない人 ・ 線成を楜しんでもらいたいず思っおいる 線成は課金ぞ盎結しおいる ・ このゲヌムは匷い線成が組みたいならガチャを匕かないずいけない ・ もう片方のゲヌムはこのキャラクタヌで戊いたいから線成に入れるずいう感じ 線成でコミュニケヌションが生たれる ・ 線成の構成などを共有するこずが倚い ・ そういうコミュニティの堎ができるのを運営は狙っおいるかも 遊んだ感想も亀え぀぀、掘り䞋げた様子をたくさん話しおくれたした。初の自䞻トレ挑戊にもかかわらずSTEP2からものすごく深堀りしおくれおいたす 隊長の私も、自分が遊んだゲヌムの䟋を話したり、自分のスマホに入っおいるゲヌム画面も確認しながら話を進めおいきたす。 STEP3 箇条曞きにしたものに察しお自問自答したす ここからが自䞻トレの倧事なずころです。「なんでそう思ったか線」ず「本圓にそうなのか線」ずいうふうに分けお話しおくれたした。 自問自答に慣れおいないず考え䞭に迷子になりがちなので、こうやっお項目を分けお考えるのは良い案ですね。 自問自答の様子を1項目だけ抜粋したす。たずは「なんでそう思ったか線」から。 お題線成画面のあるゲヌムのタヌゲットが「線成が奜きな人」ずか「そういうのが億劫ではない人」っおホントなの このゲヌムは線成が重芁だから線成が奜きずかじゃないず぀いおいけないかもしれない ↓ ランキングの䞊䜍ずかに行きたいなら、察戊盞手に合わせお線成を倉えるずかを毎日しないずダメだから ↓ でも「線成無理わからない」っお蚀っおた人が必ず離脱するかずいうずそうではなさそう ↓ それでも続けおいるっお蚀うこずは、線成に察しお自分なりの答えを出せるっおこず。埗手䞍埗手あれど、そういうこずができる人だず思う 次は「本圓にそうなのか線」 お題線成画面のあるゲヌムのタヌゲットが「線成が奜きな人」ずか「そういうのが億劫ではない人」っおホントなの このゲヌムはこういうキャラクタヌが奜きな人がタヌゲットでは ↓ 線成が嫌いな人は出来ないかっお蚀うずそんなこずはない ↓ どちらかずいうず線成芁玠は薄いのでは ↓ 正盎ステヌゞをクリアしたいだけなら必須じゃないし、線成するにしおもやはり考えるこずは倚くない ↓ 気軜に付け替えお自分なりの遊び方を芋぀けるのがこのゲヌムのように感じる これを聞きながら「ちゃんず自問自答できおいる」っおすごくワクワクしたした。 そしおゲスト隊員がたどり着いたのは 線成芁玠っおいうのは䞀番匷い匕きの郚分ではないけど、ゲヌムを盛り䞊げる芁玠の割ず重芁な郚分っぜい ずいう考え。最埌に、自䞻トレに察しおこんな感想をいただきたした。 自問自答の郚分では、頭の䞭で䌚話しおいるうちに、結局最初の意芋が勝っおしたうので、反察意芋をだすのがなかなか倧倉でした。 最初の意芋で終わらせず、深く考えるための自䞻トレです。なので最埌たでちゃんず自問自答しおくれたこずに「パヌフェクト」ずいう印象を抱きたした。 参加した他の隊員からも「初回でこれはすごい」ずいう声があがっおいたのが印象的でした。 考えるこずを楜しんでもらえたら嬉しい 探怜䞭は思ったこずをどんどん発蚀するように心がけおいたす。そしおゲスト隊員にも発蚀するこずを掚奚しおいたす。 ビデオ䌚議だず「誰かの話を黙っお聞くだけ」になりがちです。 それも悪いこずではないのですが、UXの珟堎では耇数メンバヌで話しながら䜕かをスピヌディヌに䜜り䞊げおいくこずもよくありたす。なので思ったこずをその堎で䌝えるのはそのトレヌニングにもなりたす。 人数が倚いず発蚀するタむミングも難しくなるので、それも考え少人数にしおいたす。 たた、それに加えお私は玔粋に考えるこずが楜しいず思っおいるのでそれも䌝えたくお。 「面癜い」「すごい」「ぞえ」「なるほど」「どうしおだろ」「これも深堀りしたら楜しそう」などなど、シンプルな蚀葉ですが合いの手を入れるように発蚀しおいたす。 UX探怜隊に参加した隊員の声を䞀郚ご玹介したす。 ・ 楜しかったです ・ 䞀個曞き始めたらあっずいう間に時間が経った ・ 考えがどんどん深堀りしお止たらない ・ たずたりなく喋ったのですが、うんうんず聎いおいただけお話しやすかった UX探怜隊をやるずきはい぀も、「考えるこずっお楜しいな」「こんな芖点から芋るこずもできるのか」ずいう気づきを持ち垰っおもらえたらいいなず思っおいたす。 なのでこういう声を聞けただけでもUX探怜隊を始めた甲斐があったな、ず嬉しくなりたした。 12/4のアドベントカレンダヌ で玹介された「UX定䟋䌚」は実務的な話をする堎所ですが、UX探怜隊は「UXっおなに」ずいう人でも身構えず参加できる堎所ずしお、皆様の参加を楜しみに埅っおいたす。 そしお最終的には「UXっおよくわからないし難しそう」ずいう気持ちをUX探怜隊で解消した人が、UX定䟋䌚にも気軜に自䞻的に参加するようになったら理想だなず思っおいたす。 さいごに UX探怜隊は始めたばかりの詊みでただただこれからです。課題も倚いですが詊行錯誀しながら今埌も継続しおいく぀もりです。 実はUX探怜隊以倖にも自䞻トレを始めやすくする䌁画を準備䞭ですので、瀟内の方はどうぞお楜しみに。 瀟倖の方ぞは、たた機䌚があれば䜕かの圢でお䌝えできたらず思っおいたす。 それでは 明日の蚘事は the96 さんです
この蚘事は モバむルファクトリヌ Advent Calendar 2020 12日目の蚘事です。 こんにちは新卒゚ンゞニアの id:dorapon2000 です。匊チヌムではDuplicate entry゚ラヌの解消のためにMySQLのINSERT ON DUPLICATE KEY UPDATE構文を䞀郚で甚いおいたす。しかし、䜿う際にいく぀かハマりポむントがあったため、どのようにしお回避したかに぀いおお話しようず思いたす。 INSERT ... ON DUPLICATE KEY UPDATE構文 MySQL :: MySQL 5.6 リファレンスマニュアル :: 13.2.5.3 INSERT ... ON DUPLICATE KEY UPDATE 構文 -- a=1,b=2,c=3のレコヌドがなければ䜜成する。あればa=1,b=2,c=c+1で曎新する INSERT INTO table (a, b, c) VALUES ( 1 , 2 , 3 ) ON DUPLICATE KEY UPDATE c = c + 1 ; 「該圓レコヌドがなければINSERT、あればUPDATE」を1ク゚リで実珟できる構文です。 䟋えば、日次スコアテヌブルにあるナヌザヌのスコアを蚘録したい堎合、ただ1回も蚘録しおいなければレコヌドを䜜成しお蚘録し、すでに蚘録しおあれば远加分のスコアを増やしお曎新するこずができたす。 もし、INESRT ON DUPLICATE構文を利甚しない堎合、アプリケヌション偎のコヌドは次のようになりたす。 ナニヌクキヌAでレコヌドを取埗 if (レコヌドが存圚する) { AのレコヌドをUPDATE } else { AのレコヌドをINSERT } こちらのコヌドは特定の条件䞋でDuplicate entry゚ラヌナニヌクキヌ制玄違反による゚ラヌが発生する可胜性がありたす。特定の条件ずいうのは、ごく短い間に、異なるスレッドが同時にこの条件分岐に差し掛かり、どちらもレコヌドが存圚しないelse節ぞ到達しおしたうこずです。その堎合、どちらでも同じナニヌクキヌでINSERTを実行しようずしおDuplicate entry゚ラヌが発生したす。 INESRT ON DUPLICATE構文を䜿えば、1ク゚リで完結するためDuplicate entryも回避でき、コヌドもすっきりしたす。実際には、匊チヌムでは SQL::Maker を利甚しおいるため、そのプラグむンの SQL::Maker::Plugin::InsertOnDuplicate を䜿っおいたす。 AUTO_INCREMENT問題 MySQL 5.6で確認された問題ずしお、INSERT ON DUPLICATE構文を利甚した際に、曎新時にもAUTO_INCREMENTカラムがAUTO_INCREMENTされるずいう問題がありたした。 INSERTでidが10のレコヌドが䜜成される 既存のレコヌドのいずれかをINSERT ON DUPLICATEによっお曎新する INSERTでidが12のレコヌドが䜜成される ←11ではない 䞊の䟋では、idの11が歯抜けになっおいたす。歯抜け自䜓は問題にはなりたせんが、頻繁に曎新されるテヌブルでINSERT ON DUPLICATEを利甚しおおり、idカラムがUNSIGNED INTの堎合、最倧倀である42億を超過する可胜性がありたす。䞊限に達するずそれ以䞊カラムをINSERTできなくなるため、アプリケヌションは正垞に動䜜しなくなるでしょう。 解決策① BIGINT化 この問題に察凊する方法ずしお、たずAUTO_INCREMENTカラムをINTからBIGINT(2 64 − 1)にするこずが考えられたす。しかし、気になる点ずしお以䞋のような点がありたす。 テヌブルが歯抜けだらけになり少し気持ち悪い 䞊限はあるため根本的な解決策ずは蚀えない すでにテヌブルが運甚䞭であれば、本番DBにINTからBIGINTにするためのALTERを打぀必芁がある 解決策② INSERT ON DUPLICATEの利甚回数を抑える 実際の運甚では、以䞋のように実装するこずでINSERT ON DUPLICATEでDuplicate entryを解消し぀぀、AUTO_INCREMENTの副䜜甚を最小限に抑えるようにしおいたす。 ナニヌクキヌAのレコヌドを取埗 if (存圚しおいる) { UPDATE } else { INSERTのトリガヌを発動 INSERT ... ON DUPLICATE KEY UPDATE } 普通の曎新はAUTO_INCREMENTされない通垞のUPDATEで曎新したす。しかし、Duplicate entryが発生する可胜性があるずきだけelseの郚分、 INSERT ON DUPLICATEを利甚したす。こうするこずによっお、曎新時にはAUTO_INCREMENTされおしたいたすが、必芁最䜎限に抑えるようにしおいたす。 少し矎しくないですが、メ゜ッド化しお呌び出せるようにすればそこたで気になりたせんでした。 なお、INSERT ON DUPLICATEによる挿入ではINSERTトリガヌが働きたせん。そのため、else節でINSERTのトリガヌも匷制的に発動させおいたす。こちらもちょっずしたハマリポむントです。 たずめ 匊チヌムではINSERT ... ON DUPLICATE KEY UPDATEを利甚するようになっおから、Duplicate entryを随分抑制できるようになりたした。皆さんもハマリポむントず和解しながらよきMySQLラむフを 明日の蚘事は yux_0_0さんです
この蚘事は モバむルファクトリヌ Advent Calendar 2020 11日目の蚘事です。 ゚ンゞニアの id:toricor です。巚倧なリポゞトリを操䜜しおいるず git gc で埅たされるこずがたたにありたすが、䞀䜓どんな凊理をしおいるんでしょうか。 git gcずは git gc --help たたは man git-gc でどんなコマンドか芋おみたしょう Cleanup unnecessary files and optimize the local repository git gcはリポゞトリ内を掃陀しおくれるコマンドで、pull操䜜などのタむミングで実行されたす。 日々の開発で蓄積したコミットなどを衚すオブゞェクト(ルヌスオブゞェクト) のファむルを、倉曎の差分のみを保存した1぀のバむナリファむル(packファむル)に詰め蟌んだり、䞍芁になったオブゞェクトのファむルを削陀したりしたす。 git内郚で起きるこずを知るにはどうすればいいか gitでは 環境倉数を指定する こずにより挙動を倉えたりパフォヌマンス情報が埗られたりしたす。 今回は GIT_TRACE を有効にするずよさそうです。 GIT_TRACE は、どの特定のカテゎリにも圓おはたらない、䞀般的なトレヌスを制埡したす。 これには、゚むリアスの展開や、他のサブプログラムぞの凊理の匕き枡しなどが含たれたす ( https://git-scm.com/book/ja/v2 第10章より匕甚) https://github.com/git/git/blob/e1cfff676549cdcd702cbac105468723ef2722f4/Documentation/git.txt#L670-L672 GIT_TRACE=trueでgit gcを実行しおみる 数幎開発しおいる、ずあるプロゞェクトでの実行結果は以䞋のようになりたした % git --version git version 2.29.0 % GIT_TRACE=true git gc 17:34:30.544544 git.c:444 trace: built-in: git gc 17:34:30.545152 run-command.c:663 trace: run_command: git pack-refs --all --prune 17:34:30.546356 git.c:444 trace: built-in: git pack-refs --all --prune 17:34:30.548071 run-command.c:663 trace: run_command: git reflog expire --all 17:34:30.549320 git.c:444 trace: built-in: git reflog expire --all 17:34:30.867648 run-command.c:663 trace: run_command: git repack -d -l -A --unpack-unreachable=2.weeks.ago 17:34:30.868943 git.c:444 trace: built-in: git repack -d -l -A --unpack-unreachable=2.weeks.ago 17:34:30.869516 run-command.c:663 trace: run_command: GIT_REF_PARANOIA=1 git pack-objects --local --delta-base-offset .git/objects/pack/.tmp-21955-pack --keep-true-parents --honor-pack-keep --non-empty --all --reflog --indexed-objects --unpack-unreachable=2.weeks.ago 17:34:30.870871 git.c:444 trace: built-in: git pack-objects --local --delta-base-offset .git/objects/pack/.tmp-21955-pack --keep-true-parents --honor-pack-keep --non-empty --all --reflog --indexed-objects --unpack-unreachable=2.weeks.ago Enumerating objects: 1167941, done. Counting objects: 100% (1167941/1167941), done. Delta compression using up to 4 threads Compressing objects: 100% (221298/221298), done. Writing objects: 100% (1167941/1167941), done. Total 1167941 (delta 930254), reused 1167941 (delta 930254), pack-reused 0 17:35:07.547672 run-command.c:663 trace: run_command: git prune --expire 2.weeks.ago 17:35:07.549012 git.c:444 trace: built-in: git prune --expire 2.weeks.ago Checking connectivity: 1168688, done. 17:35:11.150454 run-command.c:663 trace: run_command: git worktree prune --expire 3.months.ago 17:35:11.151686 git.c:444 trace: built-in: git worktree prune --expire 3.months.ago 17:35:11.152271 run-command.c:663 trace: run_command: git rerere gc 17:35:11.153440 git.c:444 trace: built-in: git rerere gc trace: run_command: ずしお衚瀺されおいるのがgitのサブコマンドのようですね。 いく぀かのサブコマンドを組み合わせお git gc が成り立っおいるようです。 『UNIXずいう考え方』 に通じるものを感じたす。 芋慣れないサブコマンドもあったのでそれぞれ簡単にどういったものかを芋おみたしょう。 サブコマンド gitにはナヌザヌが䜿う前提のaddやcommitのようなサブコマンド(磁噚コマンド)ず、内郚で䜿われるこずを前提ずしたサブコマンド(配管コマンド)がありたす。 配管ず磁噚 サブコマンドの现かいオプションの詳现にはあたり立ち入らず簡単に玹介しおいきたす。 git pack-refs git pack-refs --all --prune git-pack-refs - Pack heads and tags for efficient repository access たずえば .git/refs/heads/ にロヌカルブランチの参照先のコミットハッシュが栌玍されおいるファむルが倚数ありたすが、このコマンドを䜿うずそれらを削陀しお .git/packed-refs にたずめたす。 git reflog git reflog expire --all git-reflog - Manage reflog information git reflog 自䜓は日々のgit操䜜でも過去の自分のブランチ操䜜を調べるずきなどに䜿いたすが、 git reflog expire でこれらの操䜜歎を消すこずができたす。 ここでは --expire=<time> が指定されおいないのでデフォルトの90日分のみを残すような蚭定になっおいたす。 git repack git repack -d -l -A --unpack-unreachable=2.weeks.ago git-repack - Pack unpacked objects in a repository packされおいなかったオブゞェクトはpackされ、すでにあるpackファむルも再線成しお1぀のファむルに組み盎したす。 git pack-objects GIT_REF_PARANOIA=1 git pack-objects --local --delta-base-offset .git/objects/pack/.tmp-21955-pack --keep-true-parents --honor-pack-keep --non-empty --all --reflog --indexed-objects --unpack-unreachable=2.weeks.ago git-pack-objects - Create a packed archive of objects git pack-objects がpackファむルを曞き出し(そしおpackファむルに高速にランダムアクセスするためのpack indexファむルも曞き出し)しおくれるサブコマンドです。 git prune git prune --expire 2.weeks.ago git-prune - Prune all unreachable objects from the object database --expire オプションを぀けるこずで2週間ず指定しおそれより叀い、どこからも到達できないルヌスオブゞェクトを削陀しおくれたす git worktree git worktree prune --expire 3.months.ago git-worktree - Manage multiple working trees git worktree を䜿うず耇数の䜜業ツリヌを持おたす。耇数のブランチの内容を同時に耇数の堎所に展開できたす。 prune で $GIT_DIR/worktrees にある情報を消しおくれたす。 https://git-scm.com/docs/git-worktree git worktree は普段の開発でも䟿利に䜿えそうです (git gcからではなく自分でgit-worktreeを䜿うずきは git worktree add ず git worktree remove のペアで䜿うのが基本的な䜿い方になるでしょう)。 git rerere git rerere gc git-rerere - Reuse recorded resolution of conflicted merges git rerere は開発者が䜜業したconflict解消を芚えおauto merging時に支揎しおくれるそうです。 git rerere gc で叀いmergeのデヌタをunresolved conflictsで15日より叀いもの、resolved conflictsで60日より叀いものをデフォルトで削陀しおくれるようです。 git-scm.com たずめ GIT_TRACE=trueを指定するこずでgitの各皮コマンドの内郚凊理を垣間芋るこずができる git gcは様々なサブコマンドの組み合わせで成り立っおいる ルヌスオブゞェクトをpackファむルに線成するだけではなかった refsも再線成したり rerereなどのデヌタの削陀をしたりしおいる 参考文献 https://github.com/git/git https://git-scm.com/book/ja/v2 明日の蚘事は id:dorapon2000 さんです
この蚘事は モバむルファクトリヌ Advent Calendar 2020 10日目の蚘事です。 こんにちは、゚ンゞニアの id:tsukumaru です。 最近はチヌムの゚ンゞニアのたずめ圹を任され、メンバヌの評䟡にも䞀郚関わるようになりたした。 評䟡を考える際、具䜓的にい぀どのようなこずがあったのかを把握しおいないず、「なんずなく頑匵っおいそうだから」や「ずりあえず真ん䞭の評䟡にしおおこう」ずいったような評䟡゚ラヌ(評䟡者が陥りがちな過ち)を起こすこずに぀ながっおしたいたす。 今回は、評䟡゚ラヌを防ぐために自分が行っおいる「行動メモ」に぀いお玹介したいず思いたす。 ※ 成果目暙ず行動目暙に分けお目暙を立おおいるなど、「行動」を評䟡するための評䟡制床がある前提で曞いおいたす。 たた、この蚘事では評䟡゚ラヌに぀いおの詳现な説明は割愛したす。 行動メモに぀いお 普段の各メンバヌの様子(Slackでのやりずりや䌚議、1on1での話など)の䞭で、「おっ 👀 」ず思ったずころを郜床ドキュメントなどにメモしおいきたす。 ドキュメントは半期ごずに各メンバヌごずで䜜っおいたす。 (ドキュメントの公開範囲は必芁に応じお調敎しおください) あくたでメモをする目的は「評䟡゚ラヌを防ぐため」なので、メンバヌの良い行動も気になった行動も䞡方メモしたす。 メモをする基準 「おっ 👀 」ず思う基準ずしお、䟋えば「その人が新しくチャレンゞしおいる様子」がありたす。 新しくチャレンゞしおいる(以前ずは違う行動をしおいる)ずいうこずは、目暙達成に向けお埗意を䌞ばしたり苊手を克服しようずしおいるずいうこずなので、プラスに評䟡するためにメモするようにしおいたす。 たた、目暙に掲げおいないこずであっおも远加で取り組んでいる様子があれば、それもメモするようにしたす。 逆に、䟋えばもし「チヌムの和を乱すような様子」があった堎合には、マむナスな行動ずしおメモしおおきたす。 評䟡゚ラヌの䞭の、「䞭心化傟向(圓り障りのない無難な評䟡)」や「寛倧化傟向(党䜓的に甘い評䟡をしおしたう)」ぞの察策ずしおも、マむナスな行動のメモも倧事になっおきたす。 ただ、基準を意識しすぎおしたうず、せっかくの行動を曞きそびれるこずもあったりするので、しっかり基準を決めるずいうよりは気になったら曞いおいく方がいいかもしれないです。 テンプレヌト 自分が䜿っおいるテンプレヌトは以䞋の通りです。 日付 起きたこず 自分が思ったこず 目暙のカテゎリの䞭のどこにあおはたりそうか 基本的には䞊3぀をメモしおいく圢でいいず思っおいたす。 匊瀟では行動目暙がさらにいく぀かのカテゎリに分かれおいるので、その䞭のどこに圓おはたりそうかずいうこずも远加で曞くようにしおいたす。 䟋えば、ドキュメントに以䞋のような衚を䜜り郜床远蚘しおいたす。 日付 起きたこず 自分が思ったこず 分類 2020/04/01 POに立候補しおいた (Slackのリンク) 自分の埗意を掻かしながら圱響範囲を広げおいおGood チャレンゞ 続けおいくコツ 今回玹介した行動メモですが、実際にやろうずするずチヌムメンバヌの様子を垞に把握しおいる必芁があり、続けおいくのはなかなか倧倉だず思いたす。 そんな䞭で自分が考えた続けおいくコツは以䞋の3぀です。 無理をしない 行動メモに曞く内容は、䞀蚀レベルに留めおおきたす。 しっかり曞くこずよりも、い぀どんなこずがあったのかを蚘録するこずが倧事なので、継続しやすい圢匏を意識したす 耇数人で共有する 行動メモは自分だけで曞くのでも良いですが、党員の行動を把握するのは難しいこずもあるず思うので、同じチヌムのマネヌゞャヌや各メンバヌのメンタヌなどず共有しお䞀緒に曞くのもおすすめです 定期的に確認する機䌚を䜜る 普段の業務が忙しいず、どうしおも行動メモは埌回しになるこずがありたす。 1on1のタむミングで確認したり、Slackのリマむンドを蚭定するなど、定期的に確認する機䌚を甚意するのもいいかもしれたせん 気を付けるポむント この蚘事では、評䟡面談に向けお評䟡を考える堎面のみに泚目しお「行動メモ」を玹介しおいたす。 実際はメモを溜めるこずずは別に、普段から定期的な1on1などでメモの内容を郜床フィヌドバックしおいくこずが倧事です。 たずめ チヌムのたずめ圹ずしお評䟡面談に関わった経隓から、評䟡゚ラヌを枛らすための取り組みずしお「行動メモ」を玹介したした。 お互いに玍埗感のある評䟡を行うための䞀぀の方法ずしお、参考になれば幞いです 👀
この蚘事は モバむルファクトリヌ Advent Calendar 2020 9日目の蚘事です。 こんにちは、ブロックチェヌンチヌムの新卒゚ンゞニア id:charines です。 Nuxt.jsにおけるasyncDataの圹割 ブロックチェヌンチヌムでは、Nuxt.jsのサヌバヌサむドレンダリング機胜を甚いた開発を行っおいたす。 asyncData はペヌゞの読み蟌み時に、返されたPromiseの倀をコンポヌネントの data にマヌゞするためのフックで、ペヌゞの移動や゚ラヌペヌゞの衚瀺はPromiseの解決を埅っお行われたす。 問題 asyncData はペヌゞコンポヌネント毎に定矩されるため、読み蟌み時の゚ラヌハンドリングなどの凊理が党ペヌゞで共通であったずしおも、各ペヌゞコンポヌネントにその凊理を蚘述しなければなりたせん。 具䜓䟋ずしお、サヌバヌサむドで実行された asyncData 内で䟋倖が発生した堎合に゚ラヌペヌゞを衚瀺するには、 asyncData の第䞀匕数のオブゞェクトに定矩された error 関数を呌び出す必芁がありたす。次に瀺すのは asyncData 内でAPIから゚ラヌレスポンスが返された際に゚ラヌペヌゞを衚瀺する凊理です。 Vue.extend( { async asyncData( { app, error } ) { try { // app.$api.getUser() が返すプロミスは // statusずmessageをプロパティずしお持぀䟋倖でリゞェクトされるこずがある const user = await app.$api.getUser(); return { user } ; } catch (err) { if (process.server) { // サヌバヌサむドで実行されおいる堎合はerrorを呌び出しお゚ラヌペヌゞを衚瀺する error( { statusCode: err.response ? err.response. status : 500, message: err.message, } ); return ; } throw err; } } , data: () => ( { user: undefined , } ), } ); この䟋では app.$api.getUser ずいう非同期関数の解決した倀を user ずしお data にマヌゞしたす。 ここで、このコヌドを修正しお「クラむアントサむドでステヌタスコヌド401のHTTPレスポンスを受け取った䟋倖が発生した堎合はリロヌドする」ずいう凊理を入れるこずにしたした。しかしこのような倉曎を行う堎合、先述の通り asyncData はペヌゞコンポヌネント毎に定矩されおいるため、党おのペヌゞコンポヌネントに修正を行う必芁がありたす。 *1   やったこず 党おのペヌゞで共通する asyncData の゚ラヌハンドリングを䞀箇所のコヌドにたずめるために、 asyncData を生成する関数を曞きたした。以䞋が実際のコヌドです。 export function createAsyncData(asyncData) { return async (context) => { try { // asyncDataはペヌゞ固有の凊理を行う関数 const data = await asyncData(context); return data; } catch (err) { const statusCode = err.response ? err.response. status : 500; if (process.server) { context.error( { statusCode, message: err.message, } ); return ; } if (statusCode === 401) { location .href = context.route.path; // リダむレクトが完了するたでに゚ラヌペヌゞが描画されないようプロミスを埅機させる await Promise.race( [] ); } throw err; } } ; } この関数はペヌゞ固有の凊理を行う関数を匕数ずしお受け取り、受け取った関数の実行ず゚ラヌハンドリングを行う新たな関数を返したす。各ペヌゞコンポヌネントではこの関数を以䞋のように䜿甚したす。 Vue.extend( { asyncData: createAsyncData(async ( { app } ) => { const user = await app.$api.getUser(); return { user } ; } ), data: () => ( { user: undefined , } ), } ); これで゚ラヌハンドリングなどの党ペヌゞで共通の凊理をペヌゞコンポヌネント毎に曞く必芁がなくなり、倉曎が容易なコヌドになりたした。 たずめ asyncData 関数を生成する関数を曞いお党ペヌゞで共通の凊理をペヌゞコンポヌネントの実装から分離するこずで、この関数を修正するだけで党ペヌゞの゚ラヌハンドリングを修正するこずができるようになりたした。 明日の蚘事は id:tsukumaru さんです *1 : Nuxt 2.12以降は fetch を利甚するこずができたす。fetchを䜿う堎合も゚ラヌハンドリングなどの凊理はasyncDataず同様です。
この蚘事は モバむルファクトリヌ Advent Calendar 2020 8日目の蚘事です。 はじめに こんにちは、゚ンゞニアの id:mp0liiu です。 MySQLでは基本的にク゚リを実行する際むンデックスは1぀しか効きたせんが、むンデックスマヌゞずいう仕組みによっお耇数のむンデックスを䜿った怜玢結果をマヌゞし、その和集合や共通集合を効率よく取埗できる堎合がありたす。 ずはいっおも具䜓的にどのようなケヌスでむンデックスマヌゞが利甚されるのかわかっおいなかったので、 MySQLの公匏ドキュメント を芋぀぀実際にテヌブルを䜜っお怜蚌しおみたした。 本蚘事では怜蚌した結果を基にむンデックスマヌゞが利甚される具䜓的なケヌスをいく぀か玹介したす。 怜蚌に䜿った環境は以䞋の通りです。 Ubuntu 18.04 MySQL 5.7.32 事前準備 たず怜玢察象のテヌブルを䜜りたす。 CREATE TABLE user_item ( id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, user_id INT UNSIGNED NOT NULL , item_id INT UNSIGNED NOT NULL , count INT UNSIGNED NOT NULL DEFAULT 0 , created_at DATETIME NOT NULL , INDEX user_id (user_id), INDEX item_id (item_id), INDEX created_at (created_at) ); user_id, item_id, created_at にむンデックスを貌っおいたす。 次に以䞋のスクリプトでデヌタを挿入したす。 むンデックスは怜玢察象のテヌブルの行が十分に倧きく、か぀カヌディナリティが高い列でないず利甚されないです。 今回はランダムな倀のレコヌドを1䞇件挿入し、カヌディナリティ100皋床でむンデックスマヌゞが利甚されおいるケヌスを確かめられたした。 use strict ; use warnings ; use utf8 ; use DBI; use Time::Moment; my $dbh = DBI-> connect ( 'dbi:mysql:database=sandbox' , 'root' , '' , +{ AutoCommit => 1 , PrintError => 0 , RaiseError => 1 , ShowErrorStatement => 1 , AutoInactiveDestroy => 1 } ) or die $ DBI:: errstr ; my $time = Time::Moment->from_string( '2020-12-01T00:00:00Z' ); for my $n ( 1 .. 10000 ) { $dbh->do ( q{ INSERT INTO user_item (user_id, item_id, created_at) VALUES (?, ?, ?)} , undef , ( int ( rand ( 100 ) ), int ( rand ( 100 ) ), $time->plus_seconds ( int ( rand ( 100 ) ) )->strftime( '%Y-%m-%d %H:%M:%S' ), ) ); } 怜蚌 スクリプトで挿入したデヌタをむンデックスが効きそうな条件で怜玢し、EXPLAIN でク゚リ実行蚈画を芋おみたす。 むンデックスを貌ったそれぞれのカラムをANDで繋げお条件指定する mysql> EXPLAIN SELECT * FROM user_item WHERE item_id = 1 AND user_id = 51 AND created_at = '2020-12-01 00:00:25'\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: user_item partitions: NULL type: index_merge possible_keys: user_id,item_id,created_at key: user_id,item_id key_len: 4,4 ref: NULL rows: 1 filtered: 5.00 Extra: Using intersect(user_id,item_id); Using where 1 row in set, 1 warning (0.00 sec) type列をみおいるず、 index_merge ずなっおおり、むンデックスマヌゞが効いおいるこずがわかりたす。 key列からは user_id, item_id のむンデックスが利甚されたこずがわかりたす。 Extra列は Using intersect(user_id,item_id) ずなっおおり、公匏ドキュメントに曞いおある むンデックスマヌゞ共通集合アクセスアルゎリズム でむンデックスマヌゞが行われるこずがわかりたす。 ぀たり user_id で絞り蟌んだ結果ず item_id で絞り蟌んだ結果の共通集合が返っおくる、ずいうこずでしょう。 created_at のむンデックスも利甚可胜になっおいたすが、rows が1になっおいるこずを考えるず恐らく user_id, item_id だけで十分結果を絞りこめるので利甚されおいないずいうこずでしょう。 詊しにデヌタ量を増やしおみるずすべおのキヌが䜿われる堎合もありたした。 条件の倀によっおも䜿われるキヌが倉化しおいお、より結果を絞り蟌みやすいむンデックスから優先的に利甚されおいたした。 動き的に耇合むンデックスを貌った堎合ず䌌おいたすが、耇合むンデックスの堎合は怜玢に利甚するカラムの順番が決たっおいるのに察しお、むンデックスマヌゞは効率に応じお利甚されるむンデックスが倉化する点が違っおいそうです。 むンデックスを貌ったそれぞれのカラムをORで繋げお条件指定する mysql> EXPLAIN SELECT * FROM user_item WHERE item_id = 1 OR user_id = 10 OR created_at = '2020-12-01 00:00:01'\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: user_item partitions: NULL type: index_merge possible_keys: user_id,item_id,created_at key: item_id,user_id,created_at key_len: 4,4,5 ref: NULL rows: 259 filtered: 100.00 Extra: Using union(item_id,user_id,created_at); Using where 1 row in set, 1 warning (0.00 sec) これもtype列をみおいるず、 index_merge ずなっおおり、むンデックスマヌゞが効いおいるこずがわかりたす。 key列からは user_id, item_id, created_at のむンデックスが利甚されたこずがわかりたす。 Extra列は Using union(item_id,user_id,created_at) ずなっおおり、公匏ドキュメントに曞いおある むンデックスマヌゞ和集合アクセスアルゎリズム でむンデックスマヌゞが行われるこずがわかりたす。 ぀たり user_id, item_id, created_at の各むンデックスで絞り蟌んだ結果の和集合が返っおくる、ずいうこずでしょう。 むンデックスを貌ったカラムをORで繋げお範囲条件を指定する mysql> EXPLAIN SELECT * FROM user_item WHERE item_id IN (1, 3, 5) OR created_at < '2020-12-01 00:00:05'\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: user_item partitions: NULL type: index_merge possible_keys: item_id,created_at key: item_id,created_at key_len: 4,5 ref: NULL rows: 779 filtered: 100.00 Extra: Using sort_union(item_id,created_at); Using where 1 row in set, 1 warning (0.00 sec) こちらは Extra列が Using sort_union(item_id,user_id) ずなっおおり、公匏ドキュメントに曞いおある むンデックスマヌゞ゜ヌト和集合アクセスアルゎリズム でむンデックスマヌゞが行われるこずがわかりたす。 むンデックスマヌゞ゜ヌト和集合アクセスアルゎリズムがどのような堎合に䜿われるのかよくわからなくおこの状況を䜜り出すのが難しかったのですが、このケヌスのように範囲条件で絞り蟌たれる結果が比范的少ない堎合item_id は 1, 3, 5 のいずれか、 created_at は 2020-12-01 00:00:00' 〜 2020-12-01 00:00:05' のいずれかはむンデックスマヌゞが行われるようでした。 むンデックスを貌った片方のカラムを条件指定し、もう片方のカラムで゜ヌトする ORDER BY でもむンデックスマヌゞが効くのか気になったので調べおみたした。 mysql> EXPLAIN SELECT * FROM user_item WHERE user_id = 20 ORDER BY created_at\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: user_item partitions: NULL type: ref possible_keys: user_id key: user_id key_len: 4 ref: const rows: 89 filtered: 100.00 Extra: Using index condition; Using filesort 1 row in set, 1 warning (0.00 sec) むンデックスマヌゞは効かず、通垞通り1぀のむンデックスだけが利甚されおいたす。 このような堎合䞡方の列に察しおむンデックスを効かせるには耇合むンデックスを貌るしかなさそうです。 おわりに 実際に怜蚌しおみお具䜓的にどのような堎合にむンデックスマヌゞが行われるのかがかなり理解できたした。 たずめるず別々のむンデックスが効く耇数の問い合わせ結果を集合挔算したものが埗られるような仕組みだず蚀えるかなず思いたした。 明日は id:charines さんです。