ę Ŗå¼ä¼šē¤¾ć‚Øćƒ–ćƒŖćƒ¼ć®ćƒ–ćƒ­ć‚° - TECH PLAY

TECH PLAY

ę Ŗå¼ä¼šē¤¾ć‚Øćƒ–ćƒŖćƒ¼

ę Ŗå¼ä¼šē¤¾ć‚Øćƒ–ćƒŖćƒ¼ ć®ęŠ€č”“ćƒ–ćƒ­ć‚°

å…Ø491ä»¶

目欔 ćÆć˜ć‚ć« ć‚»ćƒƒć‚·ćƒ§ćƒ³ćƒ»ćƒÆćƒ¼ć‚Æć‚·ćƒ§ćƒƒćƒ—ē“¹ä»‹ ä»Šę—„ć‹ć‚‰å§‹ć‚ć‚‹pprof(ymotongpooさん) Goを使ってTDDć‚’ä½“éØ“ć—ć‚ˆć†ļ¼ļ¼(chihiro恕悓) Goć§ä½“ę„Ÿć™ć‚‹Multipath TCP ― Go 1.24 時代の MPTCP Listener ć‚’ē†č§£ć™ć‚‹ļ¼ˆTakeru Hayasakaさん) 0→1č£½å“ć®ęÆŽé€±ćƒŖćƒŖćƒ¼ć‚¹ć‚’ę”Æćˆć‚‹Goćƒ‘ćƒƒć‚±ćƒ¼ć‚øęˆ¦ē•„ā€”ā€”AI時代のPackage by Feature実践(OPTiM äøŠåŽŸć•ć‚“ļ¼‰ ć‚Øćƒ–ćƒŖćƒ¼ć‚Øćƒ³ć‚øćƒ‹ć‚¢ć®ć‚»ćƒƒć‚·ćƒ§ćƒ³ ć‚Øćƒ–ćƒŖćƒ¼ć‚¹ćƒćƒ³ć‚µćƒ¼ćƒ–ćƒ¼ć‚¹ ćƒŽćƒ™ćƒ«ćƒ†ć‚£ ć‚¢ćƒ³ć‚±ćƒ¼ćƒˆ ćƒ•ć‚©ćƒˆćƒ–ćƒ¼ć‚¹ å„ē¤¾ć‚¹ćƒćƒ³ć‚µćƒ¼ćƒ–ćƒ¼ć‚¹ Resilire恕悓 ć‚Ŗćƒ—ćƒ†ć‚£ćƒ ć•ć‚“ ćƒŠćƒ¬ćƒƒć‚øćƒÆćƒ¼ć‚Æć•ć‚“ まとめ éžå…¬å¼ć‚¢ćƒ•ć‚æćƒ¼ć‚¤ćƒ™ćƒ³ćƒˆ Go Bash vol.2 ć®ćŠēŸ„ć‚‰ć› ęœ€å¾Œć« ćÆć˜ć‚ć« åŽ»å¹“ć«å¼•ćē¶šćć€2025 幓 9 月 27 ę—„(土)态28 ę—„(ę—„)ć«é–‹å‚¬ć•ć‚ŒćŸć€ŒGo Conference 2025ć€ć«å‚åŠ ć•ć›ć¦ć„ćŸć ćć¾ć—ćŸć€‚ ä»Šå›žć‚‚å‚åŠ ćƒ¬ćƒćƒ¼ćƒˆćØć—ć¦ć€ä¼šå “ć®ę§˜å­ć‚„ć‚»ćƒƒć‚·ćƒ§ćƒ³ć®ę„Ÿęƒ³ć«ć¤ć„ć¦ćŠå±Šć‘ć—ć¾ć™ļ¼ gocon.jp ć‚»ćƒƒć‚·ćƒ§ćƒ³ćƒ»ćƒÆćƒ¼ć‚Æć‚·ćƒ§ćƒƒćƒ—ē“¹ä»‹ ä»Šę—„ć‹ć‚‰å§‹ć‚ć‚‹pprof(ymotongpooさん) こんにごは、開発1éƒØć®å²©ļØ‘ć§ć™ć€‚ 私は1ę—„ē›®ć®ćƒÆćƒ¼ć‚Æć‚·ćƒ§ćƒƒćƒ—ć€Œä»Šę—„ć‹ć‚‰å§‹ć‚ć‚‹pprofć€ć«å‚åŠ ć—ć¾ć—ćŸć€‚ gocon.jp ymotongpooć•ć‚“ć‚’ć‚¹ćƒ”ćƒ¼ć‚«ćƒ¼ćØć—ć¦ć€Goć®ćƒ—ćƒ­ćƒ•ć‚”ć‚¤ćƒŖćƒ³ć‚°ćƒ„ćƒ¼ćƒ«ć§ć‚ć‚‹pprofć®åŸŗęœ¬ēš„ćŖä½æć„ę–¹ć‹ć‚‰å®Ÿéš›ć«ć‚½ćƒ¼ć‚¹ć‚³ćƒ¼ćƒ‰ć‚’čØˆęø¬ć—ć¦ę”¹å–„ć™ć‚‹ć¾ć§ć‚’ćƒćƒ³ć‚ŗć‚Ŗćƒ³å½¢å¼ć§å®Ÿę–½ć„ćŸć ćć¾ć—ćŸć€‚ å®Ÿę–½å†…å®¹ćÆäø‹čØ˜ć®é€šć‚Šć§ć™ć€‚ ćƒ—ćƒ­ćƒ•ć‚”ć‚¤ćƒ«ćØćÆ pprof ć®åŸŗęœ¬ę©Ÿčƒ½ć®čŖ¬ę˜Ž pprof ć‚’ćƒ—ćƒ­ć‚°ćƒ©ćƒ ć«ēµ„ćæč¾¼ć‚€ pprof ć§ćƒ—ćƒ­ćƒ•ć‚”ć‚¤ćƒ«ć‚’å–å¾—ć™ć‚‹ pprof ć§ćƒ—ćƒ­ćƒ•ć‚”ć‚¤ćƒ«ć‚’åÆč¦–åŒ–ć™ć‚‹ pprof の読み方を理解する pprof ć®ēµęžœć«åŸŗć„ć„ć¦ę”¹å–„ć™ć‚‹ ę”¹å–„ēµęžœć‚’ pprof ć§ē¢ŗčŖć™ć‚‹ ļ¼ˆćƒœćƒ¼ćƒŠć‚¹ļ¼‰å®Ÿéš›ć®é–‹ē™ŗć«ę“»ć‹ć›ć‚‹ē¶™ē¶šēš„ćƒ—ćƒ­ćƒ•ć‚”ć‚¤ćƒ«ć®č§£čŖ¬ Goć«ćŠć‘ć‚‹ćƒ—ćƒ­ćƒ•ć‚”ć‚¤ćƒ«ćØćÆć€ćƒ‘ćƒ•ć‚©ćƒ¼ćƒžćƒ³ć‚¹ćƒ—ćƒ­ćƒ•ć‚”ć‚¤ćƒ«ć®ć“ćØć‚’ęŒ‡ć—ć¾ć™ć€‚ ēµ±čØˆēš„ć«ć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³ć®ćƒ‘ćƒ•ć‚©ćƒ¼ćƒžćƒ³ć‚¹ć«é–¢ć™ć‚‹ęƒ…å ±ć‚’åŽé›†ć—ć€ćƒ—ćƒ­ć‚°ćƒ©ćƒ ć®ćƒœćƒˆćƒ«ćƒćƒƒć‚Æć‚’ē‰¹å®šć™ć‚‹ćŸć‚ć«ä½æē”Øć•ć‚Œć¾ć™ć€‚ pprofはGoć®ęØ™ęŗ–ćƒ©ć‚¤ćƒ–ćƒ©ćƒŖć«å«ć¾ć‚Œć¦ćŠć‚Šć€ćƒ—ćƒ­ćƒ•ć‚”ć‚¤ćƒ«ćƒ‡ćƒ¼ć‚æć®å–å¾—ć‹ć‚‰åÆč¦–åŒ–ć¾ć§ć‚’ē°”å˜ć«č”Œć†ć“ćØćŒć§ćć¾ć™ć€‚ ćƒÆćƒ¼ć‚Æć‚·ćƒ§ćƒƒćƒ—å†…ć§ćÆä»„äø‹ć®ćƒŖćƒć‚øćƒˆćƒŖć®ć‚½ćƒ¼ć‚¹ć‚³ćƒ¼ćƒ‰ć‚’ä½æē”Øć—ć¾ć—ćŸć€‚ github.com é”ŒęćÆcutć‚³ćƒžćƒ³ćƒ‰ć‚’å®Ÿč£…ć—ćŸCLIćƒ„ćƒ¼ćƒ«ć ć£ćŸć®ć§ć€ runtime/pprof ćƒ‘ćƒƒć‚±ćƒ¼ć‚øć‚’ä½æē”Øć—ć¦pprofć‚’čØˆč£…ć—ć¾ć—ćŸć€‚ å®Ÿč£…ćÆć‚·ćƒ³ćƒ—ćƒ«ć§ć€čØˆęø¬ć—ćŸć„ć‚³ćƒ¼ćƒ‰ć«äø‹čØ˜ć®å®Ÿč£…ć‚’čæ½åŠ ć™ć‚‹ć ć‘ć§ć™ć€‚ import ( "os" "runtime/pprof" ) func main() { report, _ := os.Create( "cpu.prof" ) defer report.Close() _ = pprof.StartCPUProfile(report) defer pprof.StopCPUProfile() // ć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³ć®ćƒ”ć‚¤ćƒ³ćƒ­ć‚øćƒƒć‚Æ // ... } ćƒ“ćƒ«ćƒ‰ć—ćŸćƒć‚¤ćƒŠćƒŖć‚’å®Ÿč”Œć™ć‚‹ćØć€ęŒ‡å®šć—ćŸćƒ•ć‚”ć‚¤ćƒ«åļ¼ˆä»Šå›žćÆ cpu.prof ļ¼‰ć§ćƒ—ćƒ­ćƒ•ć‚”ć‚¤ćƒ«ćƒ‡ćƒ¼ć‚æćŒē”Ÿęˆć•ć‚Œć¾ć™ć€‚ ę¬”ć«ć€ē”Ÿęˆć•ć‚ŒćŸćƒ—ćƒ­ćƒ•ć‚”ć‚¤ćƒ«ćƒ‡ćƒ¼ć‚æć‚’pprofćƒ„ćƒ¼ćƒ«ć§č§£ęžć—ć¾ć™ć€‚ go tool pprof -http :9999 cpu.prof -http ć‚Ŗćƒ—ć‚·ćƒ§ćƒ³ć‚’ęŒ‡å®šć™ć‚‹ć“ćØć§ć€Webć‚¤ćƒ³ć‚æćƒ¼ćƒ•ć‚§ćƒ¼ć‚¹ć§ćƒ—ćƒ­ćƒ•ć‚”ć‚¤ćƒ«ćƒ‡ćƒ¼ć‚æć‚’åÆč¦–åŒ–ć§ćć€ęŒ‡å®šć—ćŖć„å “åˆćÆć‚¤ćƒ³ć‚æćƒ©ć‚Æćƒ†ć‚£ćƒ–ćŖCLIć§ćƒ—ćƒ­ćƒ•ć‚”ć‚¤ćƒ«ćƒ‡ćƒ¼ć‚æć‚’č§£ęžć§ćć¾ć™ć€‚ Webć‚¤ćƒ³ć‚æćƒ¼ćƒ•ć‚§ćƒ¼ć‚¹ć§ćÆć€ä»„äø‹ć®ć‚ˆć†ćŖē”»é¢ćŒč”Øē¤ŗć•ć‚Œć¾ć™ć€‚ ē”»åƒć«ć‚ć‚‹ć‚°ćƒ©ćƒ•ćÆFlame GraphというViewć®ę¦‚åæµć§ć€é–¢ę•°ć®å‘¼ć³å‡ŗć—éšŽå±¤ćØå„é–¢ę•°ć®CPUä½æē”ØēŽ‡ć‚’č¦–č¦šēš„ć«č”Øē¾ć—ćŸć‚‚ć®ć§ć™ć€‚ 恓恮Flame Graph ćŒé•·ć„å “åˆćÆćƒŖć‚½ćƒ¼ć‚¹ć‚’å¤šćę¶ˆč²»ć—ć¦ć„ć‚‹ć“ćØć‚’ē¤ŗć—ć¦ćŠć‚Šć€ćƒœćƒˆćƒ«ćƒćƒƒć‚ÆćØćŖć£ć¦ć„ć‚‹åÆčƒ½ę€§ćŒé«˜ć„ć§ć™ć€‚ ć¾ćŸć€Sourceć§ćÆć‚½ćƒ¼ć‚¹ć‚³ćƒ¼ćƒ‰ć®ć©ć“ć§ę™‚é–“ćŒę¶ˆč²»ć•ć‚Œć¦ć„ć‚‹ć‹ć‚’č”Œå˜ä½ć§ē¢ŗčŖć™ć‚‹ć“ćØćŒć§ćć¾ć™ć€‚ ē”»åƒć§ćÆ _, err := f.Read(buf[:]) 恧Cum が510msę¶ˆč²»ć•ć‚Œć¦ć„ć‚‹ć“ćØćŒć‚ć‹ć‚Šć¾ć™ć€‚ 恓恓恧FlatćÆćć®é–¢ę•°ćŒę¶ˆč²»ć—ćŸę™‚é–“ć‚’ęŒ‡ć—ć€CumćÆćć®é–¢ę•°ćØćć®é–¢ę•°ć‹ć‚‰å‘¼ć³å‡ŗć•ć‚Œć‚‹é–¢ę•°ć§ę¶ˆč²»ć—ćŸę™‚é–“ć‚’č”Øć—ć¦ć„ć¾ć™ć€‚ ć¤ć¾ć‚ŠFlatćŒé…ć„å “åˆćÆć‚¢ćƒ«ć‚“ćƒŖć‚ŗćƒ č‡Ŗä½“ć«å•é”ŒćŒć‚ć‚‹åÆčƒ½ę€§ćŒé«˜ćć€CumćŒé…ć„å “åˆćÆå‘¼ć³å‡ŗć—ć¦ć„ć‚‹é–¢ę•°ć«å•é”ŒćŒć‚ć‚‹åÆčƒ½ę€§ćŒé«˜ć„ć§ć™ć€‚ ćŸć ęØ™ęŗ–ćƒ‘ćƒƒć‚±ćƒ¼ć‚øć‚„č‘—åćŖćƒ‘ćƒƒć‚±ćƒ¼ć‚øćÆęœ€é©åŒ–ć•ć‚Œć¦ć„ć‚‹ć“ćØćŒå¤šć„ć®ć§ć€åŸŗęœ¬ēš„ć«ćÆč‡Ŗåˆ†ć®å®Ÿč£…ć‚’ē–‘ć†ć®ćŒč‰Æć„ćØć®ć“ćØć§ć—ćŸć€‚ ć‚ˆć£ć¦ć“ć®č”Œć§ćƒ«ćƒ¼ćƒ—ć”ćØć«Readć‚’å‘¼ć³å‡ŗć—ć¦ć„ć‚‹ć“ćØćŒåŽŸå› ć§ć‚ć‚‹åÆčƒ½ę€§ćŒé«˜ć„ćØć„ć†ć“ćØćŒć‚ć‹ć‚Šć¾ć™ć€‚ ćƒÆćƒ¼ć‚Æć‚·ćƒ§ćƒƒćƒ—å†…ć§ć“ć®ćƒœćƒˆćƒ«ćƒćƒƒć‚Æć‚’č§£ę¶ˆć™ć‚‹ę”¹å–„ć‚’č©¦ćæć€ę”¹å–„å¾Œć®Profilećƒ‡ćƒ¼ć‚æć®å¤‰åŒ–ć‚’ä»„äø‹ć®ć‚³ćƒžćƒ³ćƒ‰ć§ē¢ŗčŖć™ć‚‹ć“ćØć§ć€čØˆęø¬ć«ć‚ˆć£ć¦ćƒ‘ćƒ•ć‚©ćƒ¼ćƒžćƒ³ć‚¹ćŒę”¹å–„ć•ć‚Œć‚‹ć“ćØć‚’ä½“ę„Ÿć§ćć¾ć—ćŸć€‚ $ go tool pprof -http :9999 -base cpu.prof cpu_optimized.prof ęŽØęø¬ć™ć‚‹ćŖčØˆęø¬ć›ć‚ˆć€ć®ćƒćƒŖć‚·ćƒ¼ć«å¾“ć£ć¦ćƒ‘ćƒ•ć‚©ćƒ¼ćƒžćƒ³ć‚¹ę”¹å–„ć‚’č”Œć†ć“ćØć‚’å­¦ć¶ć“ćØćŒć§ćć€ćØć¦ć‚‚ęœ‰ę„ē¾©ćŖćƒÆćƒ¼ć‚Æć‚·ćƒ§ćƒƒćƒ—ć«ćŖć‚Šć¾ć—ćŸć€‚ Goを使ってTDDć‚’ä½“éØ“ć—ć‚ˆć†ļ¼ļ¼(chihiro恕悓) ćƒ‡ćƒŖćƒƒć‚·ćƒ„ć‚­ćƒƒćƒćƒ³ć§ćƒćƒƒć‚Æć‚Øćƒ³ćƒ‰ć‚Øćƒ³ć‚øćƒ‹ć‚¢ć‚’ć—ć¦ć„ć‚‹ē§‹å±±ć§ć™ć€‚ ē§ćÆć“ć‚Œć¾ć§TDDć«ć¤ć„ć¦å°‘ć—å­¦ć‚“ć ć“ćØćÆć‚ć£ćŸć‚‚ć®ć®ć€å®Ÿéš›ć«å–ć‚Šēµ„ć‚“ć ć“ćØćÆćŖć‹ć£ćŸć®ć§ć“ć”ć‚‰ć®ćƒÆćƒ¼ć‚Æć‚·ćƒ§ćƒƒćƒ—ć«å‚åŠ ć—ć¾ć—ćŸć€‚ gocon.jp ćƒÆćƒ¼ć‚Æć‚·ćƒ§ćƒƒćƒ—ćÆäø»ć«äø‹čØ˜ć®ęµć‚Œć§é€²ćæć¾ć—ćŸć€‚ 自己瓹介 TDD(ćƒ†ć‚¹ćƒˆé§†å‹•é–‹ē™ŗ)恮ꦂ要 FizzBuzzć‚’ä¾‹ćØć—ćŸTDDć®ćƒ‡ćƒ¢ ćŠé”Œć«åŸŗć„ć„ć¦TDDć‚’å®Ÿč·µ å…Øä½“ęŒÆć‚Ščæ”ć‚Š ć“ć“ć§ćÆē‰¹ć«ćƒ‡ćƒ¢ć«ć¤ć„ć¦ē“¹ä»‹ć§ćć‚Œć°ćØę€ć„ć¾ć™ć€‚ FizzBuzzć‚’é”ŒęćØć—ć¦ć€äø‹čØ˜ć®ć‚ˆć†ćŖęµć‚Œć§ćƒ‡ćƒ¢ć‚’č”ŒćŖć£ć¦ć„ćŸć ćć¾ć—ćŸć€‚ ā‘  TODOćƒŖć‚¹ćƒˆ(ćƒ†ć‚¹ćƒˆćƒŖć‚¹ćƒˆ)ć‚’ä½œęˆć™ć‚‹ ć¾ćšćÆå®Ÿč£…å‰ć«ć€TODOćƒŖć‚¹ćƒˆć‚’ē®‡ę”ę›øćć§ę•“ē†ć—ć¾ć—ćŸć€‚ ä¾‹ćˆć°ć€ 3ć®å€ę•°ć®å “åˆćÆFizzを返す 5ć®å€ę•°ć®å “åˆćÆBuzzを返す 3と5ć®å€ę•°ć®å “åˆćÆFizzBuzzを返す ć®ć‚ˆć†ć«å…·ä½“ēš„ćŖTODOć‚’ę›øć„ć¦ć„ćć¾ć™ć€‚ ā‘” TODOćƒŖć‚¹ćƒˆć«åŸŗć„ććƒ†ć‚¹ćƒˆć®ä½œęˆ ä½œęˆć—ćŸTODOćƒŖć‚¹ćƒˆć™ć¹ć¦ć«åÆ¾ć—ć¦äø€ę°—ć«ćƒ†ć‚¹ćƒˆć‚’ę›øćć®ć§ćÆćŖćć€ć¾ćšćÆ1ć¤ć®é …ē›®ć«ēµžć£ć¦ćƒ†ć‚¹ćƒˆć‚’ä½œęˆć—ć¾ć—ćŸć€‚ ęœ€åˆćÆć€Œ3ć®å€ę•°ć®å “åˆćÆFizzć‚’čæ”ć™ć€ć®ćƒ†ć‚¹ćƒˆć‚’ä½œęˆć—ć¾ć—ćŸć€‚ func TestFizzBuzz(t *testing.T) { got := FizzBuzz( 3 ) if got != "Fizz" { t.Errorf( "FizzBuzz(%d) = %v, want %v" , 3 , got, "Fizz" ) } } ć“ć®ę®µéšŽć§ćÆć¾ć FizzBuzzć®å®Ÿč£…ć‚’ć—ć¦ć„ćŖć„ćŸć‚ć€ćƒ†ć‚¹ćƒˆćÆå¤±ę•—ć—ć¾ć™ć€‚ ā‘¢ ćƒ†ć‚¹ćƒˆć‚’ęŗ€ćŸć™ć‚ˆć†ć«å®Ÿč£… ę¬”ć«ć€ā‘”ć§ä½œęˆć—ćŸćƒ†ć‚¹ćƒˆć‚’é€šć™ćŸć‚ć®ęœ€ä½Žé™ć®å®Ÿč£…ć‚’č”Œć„ć¾ć™ć€‚ ä¾‹ćˆć°ć€ func FizzBuzz(n int ) string { return "Fizz" } ćØć„ć†ć‚ˆć†ć«ć€ćƒ­ć‚øćƒƒć‚ÆćÆę·±ćč€ƒćˆćšć«ć€ć¾ćšćÆć‚·ćƒ³ćƒ—ćƒ«ć«ćƒ†ć‚¹ćƒˆć‚’é€šć™ć“ćØć‚’å„Ŗå…ˆć—ć¾ć—ćŸć€‚ å®Ÿč£…å¾Œć€ćƒ†ć‚¹ćƒˆć‚’å®Ÿč”Œć™ć‚‹ćØē„”äŗ‹ćƒ†ć‚¹ćƒˆćŒé€šć‚Šć¾ć™ć€‚ ć“ć‚Œć§ćƒ†ć‚¹ćƒˆćŒé€šć£ćŸć®ć§ä»Šåŗ¦ćÆ TODOćƒŖć‚¹ćƒˆć«ć‚ć‚‹ę¬”ć®é …ē›®ć®ćƒ†ć‚¹ćƒˆć‚³ćƒ¼ćƒ‰ć‚’ä½œęˆ ćƒ†ć‚¹ćƒˆć‚’ęŗ€ćŸć™ć‚ˆć†ć«å®Ÿč£… ćØć„ć†å…·åˆć§ē¹°ć‚Ščæ”ć™ć‚ˆć†ć«ē“°ć‹ćå®Ÿč£…ć—ć¦ć„ćć¾ć—ćŸć€‚ å®Ÿč£…é€”äø­ć§č¶³ć‚Šć¦ć„ćŖć‹ć£ćŸTODOć«ę°—ć„ćć“ćØć‚‚ć‚ć‚‹ć®ć§ć€ćć®éƒ½åŗ¦TODOć‚’čæ½åŠ ć—ć¦é€²ć‚ć¦ć„ćć¾ć—ćŸć€‚ ćƒ‡ćƒ¢ć®å¾ŒćÆć€äøŽćˆć‚‰ć‚ŒćŸćŠé”Œć«åÆ¾ć—ć¦ćƒšć‚¢ćƒ—ćƒ­ć§TDDć‚’å®Ÿč·µć—ć¦ć„ćć¾ć—ćŸć€‚ å€‹äŗŗēš„ć«ćÆęœ€åˆć®TODOćƒŖć‚¹ćƒˆć‚’ä½œć‚‹ä½œę„­ćŒé›£ć—ć‹ć£ćŸć§ć™ć€‚ chihiroć•ć‚“ć‹ć‚‰ć®ć”čŖ¬ę˜Žć®äø­ć§ć€Œé›†äø­ć€ćØć„ć†ćƒÆćƒ¼ćƒ‰ćŒå‡ŗć¦ćŸć®ć§ć™ćŒć€ ćƒ‡ćƒ¢ć‚„å®Ÿč·µć‚’ä½“éØ“ć™ć‚‹äø­ć§ć€ŒTDDć‚’č”Œć†ć“ćØć§äø€ć¤ć®ć“ćØć«é›†äø­ć™ć‚‹ć“ćØćŒć§ćć€ä½™čØˆćŖć“ćØć‚’č€ƒćˆćŖćć¦ęøˆć‚€ć€ćØć„ć†ć“ćØć‚’å®Ÿę„Ÿć§ćć¾ć—ćŸļ¼ å…Øä½“ć‚’é€šć—ć¦ć€TDDć®ęµć‚Œć‚’äø€å®šē†č§£ć§ććŸć‹ćŖćØę€ć†ć®ć§ć€ä»Šå¾ŒćÆTDDć®ē†č§£ć‚’ę·±ć‚ćŖćŒć‚‰ę„­å‹™ć§ć‚‚å®Ÿč·µć—ć¦ć„ććŸć„ćØę€ć„ć¾ć—ćŸļ¼ Goć§ä½“ę„Ÿć™ć‚‹Multipath TCP ― Go 1.24 時代の MPTCP Listener ć‚’ē†č§£ć™ć‚‹ļ¼ˆTakeru Hayasakaさん) こんにごは、開発1éƒØć®é»’é«™ć§ć™ć€‚ 私からは、2ę—„ē›®ć®ć‚»ćƒƒć‚·ćƒ§ćƒ³ć€ŒGoć§ä½“ę„Ÿć™ć‚‹Multipath TCP ― Go 1.24 時代の MPTCP Listener ć‚’ē†č§£ć™ć‚‹ć€ć«ć¤ć„ć¦ē“¹ä»‹ć—ć¾ć™ć€‚ gocon.jp こごらでは、Go 1.24 ć‹ć‚‰ćƒ‡ćƒ•ć‚©ćƒ«ćƒˆć§ęœ‰åŠ¹åŒ–ć•ć‚ŒćŸ Multipath TCP(MPTCPļ¼‰ć®ä»•ēµ„ćæćØć€ćć® Go ć«ćŠć‘ć‚‹ę“»ē”Øę–¹ę³•ćŒč§£čŖ¬ć•ć‚Œć¾ć—ćŸć€‚č³‡ę–™ćÆä»„äø‹ć§å…¬é–‹ć•ć‚Œć¦ć„ć¾ć™ć€‚ speakerdeck.com MPTCP ćÆć€ę—¢å­˜ć® TCP を拔張して 1 ć¤ć®é€šäæ”ć‚»ćƒƒć‚·ćƒ§ćƒ³ć®äø‹ć§č¤‡ę•°ć® TCP ć‚³ćƒć‚Æć‚·ćƒ§ćƒ³ļ¼ˆsubflowļ¼‰ć‚’åŒę™‚ć«ę‰±ćˆć‚‹ć‚ˆć†ć«ć—ćŸćƒ—ćƒ­ćƒˆć‚³ćƒ« ć§ć™ć€‚ć“ć‚Œć«ć‚ˆć‚Šć€ä¾‹ćˆć°ć‚¹ćƒžćƒ¼ćƒˆćƒ•ć‚©ćƒ³ć® Wi-Fi と 4G å›žē·šć‚’ęŸć­ć¦ 1 ęœ¬ć®ć‚»ćƒƒć‚·ćƒ§ćƒ³ćØć—ć¦åˆ©ē”Øć—ć€ åøÆåŸŸå¹…ć‚’ć¾ćØć‚ć¦é«˜é€ŸåŒ–ć—ćŸć‚Šć€ē‰‡ę–¹ć®å›žē·šćŒåˆ‡ć‚Œć¦ć‚‚é€šäæ”ć‚’ē¶™ē¶šć—ćŸć‚Šć§ćć‚‹ ć®ćŒå¤§ććŖē‰¹å¾“ć§ć™ć€‚ć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³ć‹ć‚‰č¦‹ć‚‹ćØé€šåøøć® TCP ćØåŒć˜ć‚¤ćƒ³ć‚æćƒ¼ćƒ•ć‚§ćƒ¼ć‚¹ć§ä½æćˆć‚‹ćŸć‚ć€äø‹å±¤ć®ćƒćƒƒćƒˆćƒÆćƒ¼ć‚ÆćŒč¤‡ę•°å›žē·šć‚’ę„č­˜ć—ć¦å‡¦ē†ć‚’č”Œć„ć¾ć™ć€‚ ē™ŗč”Øć§ćÆć¾ćšć€Go 1.21 恧 MPTCP ć‚µćƒćƒ¼ćƒˆćŒå…„ć‚Šć€1.24 からは net.Listen ćŒč‡Ŗå‹•ēš„ć« MPTCP ć‚’ęœ‰åŠ¹åŒ–ć™ć‚‹ć‚ˆć†ć«ćŖć£ćŸēµŒē·ÆćŒē“¹ä»‹ć•ć‚Œć¾ć—ćŸć€‚ę—¢å­˜ć®ć‚³ćƒ¼ćƒ‰ć‚’å¤§ććę›øćę›ćˆć‚‹ć“ćØćŖć MPTCP ć®ę©ęµć‚’å—ć‘ć‚‰ć‚Œć‚‹äø€ę–¹ć§ć€ ListenConfig.SetMultipathTCP(false) ć§ę˜Žē¤ŗēš„ć«ē„”åŠ¹åŒ–ć™ć‚‹ć“ćØć‚‚åÆčƒ½ć§ć‚ć‚Šć€SocketOption ć®äŗ’ę›ę€§ć«å•é”ŒćŒć‚ć‚‹å “åˆć®å›žéæē­–ćØć—ć¦čŖ¬ę˜Žć•ć‚Œć¦ć„ć¾ć—ćŸć€‚ ä»Šå›žć®ć‚¢ćƒƒćƒ—ćƒ‡ćƒ¼ćƒˆć«ć‚ˆć‚Šć€ć‚Æćƒ©ć‚¤ć‚¢ćƒ³ćƒˆćŒ MPTCP ć‚’ä½æć£ć¦ęŽ„ē¶šć—ć¦ććŸå “åˆć§ć‚‚ē‰¹åˆ„ćŖåÆ¾åæœć‚’ć—ćŖćć¦ć‚‚č‡Ŗå‹•ēš„ć«åˆ©ē”Øć§ćć€å•é”ŒćŒć‚ć‚Œć°å¾“ę„é€šć‚Š TCP ć«ćƒ•ć‚©ćƒ¼ćƒ«ćƒćƒƒć‚Æć™ć‚‹ćŸć‚ć€ę—¢å­˜ć®ć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³ć«ć‚‚å°Žå…„ć—ć‚„ć™ć„ć“ćØćŒåˆ†ć‹ć‚Šć¾ć—ćŸć€‚ Go1.24ć®ä»•ę§˜ć«ć¤ć„ć¦ćÆć‚‚ć”ć‚ć‚“ć§ć™ćŒć€åˆć‚ć¦č§¦ć‚ŒćŸMPTCPćƒ—ćƒ­ćƒˆć‚³ćƒ«ć®ę¦‚åæµć‚’ć—ć£ć‹ć‚Šē†č§£ć™ć‚‹ć“ćØćŒć§ćć€å¤§å¤‰å‹‰å¼·ć«ćŖć‚Šć¾ć—ćŸć€‚ 0→1č£½å“ć®ęÆŽé€±ćƒŖćƒŖćƒ¼ć‚¹ć‚’ę”Æćˆć‚‹Goćƒ‘ćƒƒć‚±ćƒ¼ć‚øęˆ¦ē•„ā€”ā€”AI時代のPackage by Feature実践(OPTiM äøŠåŽŸć•ć‚“ļ¼‰ 開発1部の ćć‚‡ćƒ¼ です! åƒ•ć®ę–¹ć‹ć‚‰ćÆOPTiM äøŠåŽŸć•ć‚“ć® 怌0→1č£½å“ć®ęÆŽé€±ćƒŖćƒŖćƒ¼ć‚¹ć‚’ę”Æćˆć‚‹Goćƒ‘ćƒƒć‚±ćƒ¼ć‚øęˆ¦ē•„ā€”ā€”AI時代のPackage by Featureå®Ÿč·µć€ć®ć‚»ćƒƒć‚·ćƒ§ćƒ³ć«ć¤ć„ć¦å…±ęœ‰ć—ć‚ˆć†ćØę€ć„ć¾ć™ć€‚ gocon.jp ć“ć®ć‚»ćƒƒć‚·ćƒ§ćƒ³ć§ćÆä»„äø‹ć®ć‚ˆć†ćŖęµć‚Œć§é€²ć‚“ć§ć„ćć¾ć—ćŸć€‚ Package by Layer ę§‹ęˆć§é–‹ē™ŗć—ćŸéš›ć«ę„Ÿć˜ćŸčŖ²é”Œć®ē“¹ä»‹ 1ć§ę„Ÿć˜ćŸčŖ²é”Œć®č§£ę±ŗē­–ćØćŖć‚‹ć€ę©Ÿčƒ½ć”ćØć«ćƒ¬ć‚¤ćƒ¤ćƒ¼ć‚’åˆ†é›¢ć™ć‚‹ćØć„ć† Package by Feature ę§‹ęˆć«ć¤ć„ć¦ć®ē“¹ä»‹ Package by Feature ę§‹ęˆć§å¾—ć‚‰ć‚ŒćŸåŠ¹ęžœ ćƒ‘ćƒƒć‚±ćƒ¼ć‚øęˆ¦ē•„ć®ęÆ”č¼ƒ Package by Layer ę§‹ęˆļ¼ˆćƒ¬ć‚¤ćƒ¤ćƒ¼ćƒ‰ć‚¢ćƒ¼ć‚­ćƒ†ć‚Æćƒćƒ£ć®ć‚ˆć†ćŖć‚‚ć®ćØć—ć¦čŖč­˜ć—ć¦ć„ć¾ć™ć€‚ļ¼‰ć§é–‹ē™ŗć—ćŸéš›ć«ę„Ÿć˜ćŸčŖ²é”Œć®ē“¹ä»‹ć§ćÆć€é–‹ē™ŗåŠ¹ēŽ‡ćØäæå®ˆę€§ć®ä½Žäø‹ćŒå–ć‚ŠäøŠć’ć‚‰ć‚Œć¦ć„ć¾ć—ćŸć€‚é–‹ē™ŗäŗŗę•°ćŒå¤šććŖć‚‹ć«ć¤ć‚Œē™ŗē”Ÿć™ć‚‹ć‚³ćƒ³ćƒ•ćƒŖć‚Æćƒˆé »åŗ¦ć®å¢—åŠ ć‚„ć€ćƒ—ćƒ­ć‚øć‚§ć‚Æćƒˆę‹”å¤§ć«ä¼“ć†ę©Ÿčƒ½é–“ć®ēµåˆåŗ¦ć®å¢—å¤§ć€‚ć¾ćŸå¢—å¤§ć—ćŸēµęžœć‚³ćƒ¼ćƒ‰ć‚’å¤‰ę›“ć—ć‚ˆć†ćØć—ćŸéš›ć®å½±éŸæēÆ„å›²ć®å¢—åŠ ć«ę‚©ć¾ć•ć‚Œć¦ć„ćŸć‚ˆć†ć§ć—ćŸć€‚ ć“ć‚Œć‚‰ć®čŖ²é”Œć‚’č§£ę±ŗć—ćŸćå–ć‚Šå…„ć‚ŒćŸć®ćŒä»„äø‹ć®ć‚ˆć†ćŖ Package by Feature ćØć„ć†ę§‹ęˆć§ć™ć€‚ Package by Feature ę§‹ęˆć‚’å–ć‚Šå…„ć‚ŒćŸēµęžœä»„äø‹ć®ć‚ˆć†ćŖåŠ¹ęžœć‚’å¾—ć‚‰ć‚ŒćŸć‚ˆć†ć§ć—ćŸć€‚ ć€é–‹ē™ŗåŠ¹ēŽ‡ć€‘ę©Ÿčƒ½åˆ„ć®äø¦č”Œé–‹ē™ŗć«ć‚ˆć‚‹ć‚³ćƒ³ćƒ•ćƒŖć‚Æćƒˆē™ŗē”ŸēŽ‡ć‚’ 1/3 ć«å‰Šęø› ć€é–‹ē™ŗåŠ¹ēŽ‡ć€‘ę©Ÿčƒ½å”ä¼šć®ę˜Žē¢ŗåŒ–ć«ć‚ˆć‚ŠAIć‚³ćƒ¼ćƒ‡ć‚£ćƒ³ć‚°ćØć®č¦Ŗå’Œę€§ćŒå‘äøŠ ć€äæå®ˆę€§ć€‘ę©Ÿčƒ½å˜ä½ć§ć‚¢ćƒ¼ć‚­ćƒ†ć‚Æćƒćƒ£å¤‰ę›“ćƒ»åˆ†å‰²ćŒå®¹ę˜“ ć€äæå®ˆę€§ć€‘ćƒ‡ć‚£ćƒ¬ć‚ÆćƒˆćƒŖę§‹é€ ćØä¾å­˜é–¢äæ‚ć®åÆč¦–åŒ–ć«ć‚ˆć‚ŠčØ­čØˆē†č§£ćŒäæƒé€² ć€äæå®ˆę€§ć€‘ę©Ÿčƒ½åˆ„ćƒ†ć‚¹ćƒˆē’°å¢ƒć®ę§‹ēÆ‰ćƒ»ē®”ē†ćŒåŠ¹ēŽ‡åŒ– speakerdeck.com ē°”å˜ćŖć¾ćØć‚ć«ćŖć‚Šć¾ć™ćŒć€ä»Šå¾Œćƒćƒ¼ćƒ ćŒęˆé•·ć—é–‹ē™ŗč¦ęØ”ćŒę‹”å¤§ć—ćŸéš›ć«å–ć‚Šå…„ć‚Œć¦ćæćŸć„ćØę€ć„ć¾ć—ćŸļ¼ ć‚Øćƒ–ćƒŖćƒ¼ć‚Øćƒ³ć‚øćƒ‹ć‚¢ć®ć‚»ćƒƒć‚·ćƒ§ćƒ³ ć‚Øćƒ–ćƒŖćƒ¼ć‹ć‚‰ćÆć€é–‹ē™ŗ1部 ćƒ‡ćƒŖćƒƒć‚·ćƒ„ć‚­ćƒƒćƒćƒ³AWG ć®ęœ¬äøøć‹ć‚‰ć€ć€Œ10幓もののAPIć‚µćƒ¼ćƒćƒ¼ć«ćŠć‘ć‚‹CI/CDć®ę”¹å–„ć®å„®é—˜ć€ćØć„ć†ć‚æć‚¤ćƒˆćƒ«ć§ć‚¹ćƒćƒ³ć‚µćƒ¼ć‚»ćƒƒć‚·ćƒ§ćƒ³ć‚’ć•ć›ć¦ć„ćŸć ćć¾ć—ćŸć€‚ speakerdeck.com gocon.jp ć‚Øćƒ–ćƒŖćƒ¼ć®ćƒ”ć‚¤ćƒ³ć‚µćƒ¼ćƒ“ć‚¹ć€Œćƒ‡ćƒŖćƒƒć‚·ćƒ„ć‚­ćƒƒćƒćƒ³ć€ć®APIć‚µćƒ¼ćƒćƒ¼ćÆć€Go惻echoć§ę§‹ęˆć•ć‚Œć¦ćŠć‚Šć€ē“„10å¹“ć®ę­“å²ć®äø­ć§č‚„å¤§åŒ–ć—ć¦ć„ćć¤ć‚‚ęŠ€č”“ēš„ćŖč² å‚µć‚’ęŠ±ćˆć¦ć„ć¾ć™ć€‚ä»Šå›žć®ć‚»ćƒƒć‚·ćƒ§ćƒ³ć§ćÆCI/CDに焦点を当て、Goć®ć‚³ćƒ¼ćƒ‰ć‚’äø­åæƒćØć—ćŸCI/CDé«˜é€ŸåŒ–ć®å–ć‚Šēµ„ćæć«ć¤ć„ć¦č§£čŖ¬ć—ć¾ć—ćŸć€‚ čžć„ć¦ć„ćŸć ć„ćŸēš†ę§˜ć€ć‚ć‚ŠćŒćØć†ć”ć–ć„ć¾ć—ćŸļ¼ ć‚Øćƒ–ćƒŖćƒ¼ć‚¹ćƒćƒ³ć‚µćƒ¼ćƒ–ćƒ¼ć‚¹ ć‚Øćƒ–ćƒŖćƒ¼ćÆę˜Øå¹“ć«å¼•ćē¶šćć‚¹ćƒćƒ³ć‚µćƒ¼ćƒ–ćƒ¼ć‚¹ć‚’å‡ŗć•ć›ć¦ć„ćŸć ćć¾ć—ćŸć€‚ å¼Šē¤¾ć‚µćƒ¼ćƒ“ć‚¹ć§ć‚ć‚‹ćƒ‡ćƒŖćƒƒć‚·ćƒ„ć‚­ćƒƒćƒćƒ³ć‚’ć‚¤ćƒ”ćƒ¼ć‚øć—ćŸé»„č‰²åŸŗčŖæć®ćƒ–ćƒ¼ć‚¹ćØćŖć£ć¦ć„ć¾ć™ć€‚ č¶³ć‚’é‹ć‚“ć§ć„ćŸć ć„ćŸēš†ę§˜ć€ęœ¬å½“ć«ć‚ć‚ŠćŒćØć†ć”ć–ć„ć¾ć—ćŸļ¼ ćƒŽćƒ™ćƒ«ćƒ†ć‚£ ä»Šå›žćÆä»„äø‹ć®ć‚ˆć†ćŖćƒŽćƒ™ćƒ«ćƒ†ć‚£ć‚’ē”Øę„ć•ć›ć¦ć„ćŸć ćć¾ć—ćŸć€‚ ćƒ‰ćƒŖćƒƒćƒ—ćƒćƒƒć‚°ć‚³ćƒ¼ćƒ’ćƒ¼ ä¼šē¤¾ćƒ»ć‚µćƒ¼ćƒ“ć‚¹ć®ć‚¹ćƒ†ćƒƒć‚«ćƒ¼ ć‚­ćƒƒćƒćƒ³ć‚°ćƒƒć‚ŗ ć‚­ćƒƒćƒćƒ³ć‚°ćƒƒć‚ŗćÆé‹ć€čØˆé‡ć‚¹ćƒ—ćƒ¼ćƒ³ć€ć¾ćŖęæć€ć—ć‚ƒć‚‚ć˜ć€ćŠē®øć‚’ē”Øę„ć—ć€Xćƒ•ć‚©ćƒ­ćƒ¼ć§å¼•ć‘ć‚‹ćć˜ć§ćƒ—ćƒ¬ć‚¼ćƒ³ćƒˆć•ć›ć¦ć„ćŸć ćć¾ć—ćŸć€‚ ćŸćć•ć‚“ć®ć”å‚åŠ ć€ć‚ć‚ŠćŒćØć†ć”ć–ć„ć¾ć—ćŸļ¼ ć‚¢ćƒ³ć‚±ćƒ¼ćƒˆ å‚åŠ č€…ć®ę–¹ć€…ćŒć‚³ćƒŸćƒ„ćƒ‹ć‚±ćƒ¼ć‚·ćƒ§ćƒ³ć‚’å–ć‚Œć‚‹ć‚ˆć†ćŖćć£ć‹ć‘ć‚’ä½œć‚ŠćŸćć€ć‚¢ćƒ³ć‚±ćƒ¼ćƒˆćƒœćƒ¼ćƒ‰ć‚’ē”Øę„ć•ć›ć¦ć„ćŸć ćć¾ć—ćŸć€‚ćŠé”ŒćÆć€ć€ŒGoę­“ć€ćØć€Œę°—ć«ćŖć‚‹ćƒˆćƒ”ćƒƒć‚Æć€ć§ć™ć€‚ å›žē­”ć„ćŸć ć„ćŸå¤šćć®ēš†ę§˜ć€ć‚ć‚ŠćŒćØć†ć”ć–ć„ć¾ć—ćŸļ¼ęœ€ēµ‚ēµęžœćÆć“ć”ć‚‰ć§ć™ā€¦ļ¼ 0~5幓から、15å¹“ä»„äøŠć®å¤§ćƒ™ćƒ†ćƒ©ćƒ³ć¾ć§ć€ćØć¦ć‚‚å¹…åŗƒć„ę­“ć®Gopherć«ć”å›žē­”ć„ćŸć ć‘ć¦ć„ć¾ć™ļ¼ ć‹ćŖć‚Šćƒćƒ©ć‘ć¾ć—ćŸćŒć€8ęœˆć«ćƒŖćƒŖćƒ¼ć‚¹ć•ć‚ŒćŸGo 1.25态GoとAIć‚³ćƒ¼ćƒ‡ć‚£ćƒ³ć‚°ć€ä»–ē¤¾ć®Goć®ēŸ„č¦‹ćÆē‰¹ć«ę°—ć«ćŖć‚‹ę–¹ćŒå¤šć‹ć£ćŸć‚ˆć†ć§ć™ļ¼ ćƒ•ć‚©ćƒˆćƒ–ćƒ¼ć‚¹ ēš†ć•ć‚“ć«Go Conferenceć‚’ć‚‚ć£ćØę„½ć—ć‚“ć§ć„ćŸć ć‘ć‚‹ć‚ˆć†ć€ä»Šå›žę–°ćŸćŖå–ć‚Šēµ„ćæćØć—ć¦ć€ćƒ•ć‚©ćƒˆćƒ–ćƒ¼ć‚¹ć‚’čØ­ē½®ć•ć›ć¦ć„ćŸć ćć¾ć—ćŸļ¼ ćƒ‘ćƒćƒ«ćÆć€ćƒ‡ćƒŖćƒƒć‚·ćƒ„ć‚­ćƒƒćƒćƒ³ć‚Øćƒ—ćƒ­ćƒ³ć‚’ē€ćŸGopherć€ćƒ‡ćƒŖćƒƒć‚·ćƒ„ć‚­ćƒƒćƒćƒ³ć‚¢ć‚¤ć‚³ćƒ³ć€ć‚Øćƒ–ćƒŖćƒ¼ćƒ­ć‚“ć‚’ć”ē”Øę„ć—ć¾ć—ćŸć€‚ ćƒ–ćƒ¼ć‚¹ć«ć„ć‚‰ć£ć—ć‚ƒć£ćŸå¤šćć®Gopherć®ēš†ę§˜ć«ć€"Gopher"ćØäø€ē·’ć«å†™ēœŸć‚’ę’®ć£ć¦ć„ćŸć ćć¾ć—ćŸļ¼ ę„½ć—ć‚“ć§ć„ćŸć ć‘ć¦ć„ćŸć‚‰å¹øć„ć§ć™ļ¼ å„ē¤¾ć‚¹ćƒćƒ³ć‚µćƒ¼ćƒ–ćƒ¼ć‚¹ ä»–ē¤¾ć•ć‚“ć®ć‚¹ćƒćƒ³ć‚µćƒ¼ćƒ–ćƒ¼ć‚¹ć«ć‚‚ćŸćć•ć‚“čØŖå•ć•ć›ć¦ć„ćŸć ćć¾ć—ćŸļ¼ å„ē¤¾č¶£å‘ć‚’å‡ć‚‰ć—ćŸćƒ–ćƒ¼ć‚¹ć‚„ę§˜ć€…ćŖä¼ē”»ćŒå±•é–‹ć•ć‚Œć€ä¼šå “å…Øä½“ćŒćØć¦ć‚‚č³‘ć‚ć£ć¦ć„ć¾ć—ćŸć€‚ ć„ćć¤ć‹ć”ē“¹ä»‹ć•ć›ć¦ć„ćŸć ćć¾ć™ć€‚ Resilire恕悓 Resilireć•ć‚“ćÆć€č‡Ŗē¤¾ć‚µćƒ¼ćƒ“ć‚¹ć§ęä¾›ć—ć¦ć„ć‚‹ć‚µćƒ—ćƒ©ć‚¤ćƒć‚§ćƒ¼ćƒ³ć®ćƒŖć‚¹ć‚Æē›£č¦–ć«ć¤ć„ć¦ć€ć‚Æć‚¤ć‚ŗć‚’äŗ¤ćˆćŖćŒć‚‰ć‚ć‹ć‚Šć‚„ć™ćč§£čŖ¬ć—ć¦ć„ćŸć ćć¾ć—ćŸļ¼ļ¼ ć‚Øćƒ³ć‚øćƒ‹ć‚¢ć®ć‚¤ćƒ™ćƒ³ćƒˆć ćØć€ćƒŖć‚¹ć‚Æć‚’ć€Œå˜äø€éšœå®³ē‚¹ć€ć«ä¾‹ćˆć‚‹ćØć‚Øćƒ³ć‚øćƒ‹ć‚¢ć«ē†č§£ć—ć¦ć‚‚ć‚‰ćˆć‚‹ć€ćØć„ć†ćŠč©±ćŒå°č±”ēš„ć§ć—ćŸć€‚ć‚µćƒ—ćƒ©ć‚¤ćƒć‚§ćƒ¼ćƒ³ćØć‚·ć‚¹ćƒ†ćƒ čØ­čØˆć«ćÆć€å…±é€šć™ć‚‹éƒØåˆ†ćŒć‚ć‚‹ć“ćØć‚’ē†č§£ć§ćć¾ć—ćŸā€¦ļ¼ ć‚Ŗćƒ—ćƒ†ć‚£ćƒ ć•ć‚“ ć‚Ŗćƒ—ćƒ†ć‚£ćƒ ć•ć‚“ć®ćƒ–ćƒ¼ć‚¹ć§ćÆć€ć‚³ćƒ¼ćƒ‰ć®äø€éƒØć‚’č¦‹ć¦ć©ć®Goć®ć©ć®ćƒ‘ćƒƒć‚±ćƒ¼ć‚øć‹ć‚’å½“ć¦ć‚‹ć€ć€ŒGo Package Guessrć€ć«ćƒćƒ£ćƒ¬ćƒ³ć‚øć—ć¾ć—ćŸļ¼ ęœ€åˆć‚ć‹ć‚‰ćŖćć¦ć‚‚ć€10ē§’å¾Œć‹ć‚‰ćƒ’ćƒ³ćƒˆćŒå‡ŗć‚‹ć®ć§ćŖć‚“ćØć‹ē­”ćˆć‚‹ć“ćØćŒć§ćć¾ć—ćŸć€‚ę™®ę®µä½æć£ć¦ć„ćŖć„ćƒ‘ćƒƒć‚±ćƒ¼ć‚øć‚’ēŸ„ć‚‹č‰Æć„ę©Ÿä¼šć«ćŖć‚Šć¾ć—ćŸļ¼ē„”äŗ‹ćƒŽćƒ™ćƒ«ćƒ†ć‚£ć‚‚ć‚²ćƒƒćƒˆć—ć¾ć—ćŸļ¼ ćƒŠćƒ¬ćƒƒć‚øćƒÆćƒ¼ć‚Æć•ć‚“ ćƒŠćƒ¬ćƒƒć‚øćƒÆćƒ¼ć‚Æć•ć‚“ć§ćÆć€Goć®ć‚³ćƒ¼ćƒ‰ć‚’ćƒ¬ćƒ“ćƒ„ćƒ¼ć™ć‚‹ć€ć€ŒENABLE THIS CODE by your REVIEWć€ć«å‚åŠ ć—ć¾ć—ćŸļ¼ 2旄間で全4å•ć‚ć‚Šć€č‡Ŗåˆ†ćÆ2ę—„ē›®ć®ęœć‚¤ćƒć€ē¬¬3å•ć‚’ćƒ¬ćƒ“ćƒ„ćƒ¼ć•ć›ć¦ć„ćŸć ćć¾ć—ćŸć€‚ć”ć‚ƒć‚“ćØęƒ³å®šć—ć¦ć„ćŸéƒØåˆ†ćÆēŖć£č¾¼ć‚ćŸćæćŸć„ć§ć‚ˆć‹ć£ćŸć§ć™ļ¼ćƒŽćƒ™ćƒ«ćƒ†ć‚£ćÆ K ć®ć‚­ćƒ¼ć‚­ćƒ£ćƒƒćƒ—ć‚’ć„ćŸć ćć¾ć—ćŸļ¼ まとめ Go Conference 2025は、Goć«é–¢ć™ć‚‹ęœ€ę–°ć®ęƒ…å ±ć‚„ę“»ē”Øäŗ‹ä¾‹ć€ć‚¢ć‚¤ćƒ‡ć‚£ć‚¢ćŖć©ć‚’å­¦ć¹ć€Go ć‚³ćƒŸćƒ„ćƒ‹ćƒ†ć‚£ć®ē››ć‚ŠäøŠćŒć‚Šć‚’ę„Ÿć˜ć‚‰ć‚Œć‚‹ć€ćØć¦ć‚‚ē“ ę•µćŖć‚¤ćƒ™ćƒ³ćƒˆć§ć—ćŸć€‚ ä»Šå¾Œć‚‚Goć®ć‚³ćƒŸćƒ„ćƒ‹ćƒ†ć‚£ć€Go ConferencećŒć‚ˆć‚Šäø€å±¤ē™ŗå±•ć—ć¦ć„ćć“ćØć‚’ęœŸå¾…ć—ć¦ć„ć¾ć™ć€‚ ä»Šå›žć®å‚åŠ ćƒ¬ćƒćƒ¼ćƒˆćŒć€Goć‚’å­¦ć³ćŸć„ć€ę“»ē”Øć—ćŸć„ę–¹ć®å‚č€ƒć«ćŖć‚Œć°å¹øć„ć§ć™ć€‚ é‹å–¶ć®ēš†ć•ć‚“ć€ć‚«ćƒ³ćƒ•ć‚”ćƒ¬ćƒ³ć‚¹ć‚’é–‹å‚¬ć—ć¦ć„ćŸć ćć‚ć‚ŠćŒćØć†ć”ć–ć„ć¾ć—ćŸļ¼ļ¼ éžå…¬å¼ć‚¢ćƒ•ć‚æćƒ¼ć‚¤ćƒ™ćƒ³ćƒˆ Go Bash vol.2 ć®ćŠēŸ„ć‚‰ć› Go Conference 2025 ć«ć‚¹ćƒćƒ³ć‚µćƒ¼å‚åŠ ć—ćŸ LayerX态ANDPAD态OPTiM态Resilireć€ć‚Øćƒ–ćƒŖćƒ¼ 恮 Gopher たごが Go Conference 2025 ć«åˆŗęæ€ć‚’å—ć‘ć€ćƒˆćƒ¼ć‚Æć‚„ę„Ÿęƒ³ęˆ¦ć‚’ē¹°ć‚Šåŗƒć’ć€ Beer ć§ćÆćŖć Go ć§ē››ć‚ŠäøŠćŒć‚‹ć‚¤ćƒ™ćƒ³ćƒˆć‚’é–‹å‚¬ć—ć¾ć™ļ¼ https://layerx.connpass.com/event/367057/ layerx.connpass.com Go Conferenceć«å‚åŠ ć•ć‚ŒćŸę–¹ć‚‚ć€ć•ć‚ŒćŖć‹ć£ćŸę–¹ć‚‚ć€ćœć²ć”å‚åŠ ćć ć•ć„ļ¼ ęœ€å¾Œć« ć‚Øćƒ–ćƒŖćƒ¼ć§ćÆć€ćØć‚‚ć«åƒćä»²é–“ć‚’å‹Ÿé›†ć—ć¦ć„ć¾ć™ć€‚ ćƒ†ćƒƒć‚Æćƒ–ćƒ­ć‚°ć‚’čŖ­ć‚“ć§å°‘ć—ć§ć‚‚ć‚Øćƒ–ćƒŖćƒ¼ć«čˆˆå‘³ć‚’ęŒć£ć¦ć„ćŸć ć‘ćŸę–¹ćÆć€ćœć²äø€åŗ¦ć‚«ć‚øćƒ„ć‚¢ćƒ«é¢č«‡ć«ćŠč¶Šć—ćć ć•ć„ļ¼ corp.every.tv ęœ€å¾Œć¾ć§ćŠčŖ­ćæć„ćŸć ćć€ć‚ć‚ŠćŒćØć†ć”ć–ć„ć¾ć—ćŸļ¼
ćÆć˜ć‚ć« こんにごは。開発 2 éƒØć§ćƒŖćƒ†ćƒ¼ćƒ«ćƒćƒ–ć®å°å£²ć‚¢ćƒ—ćƒŖć‚’ę‹…å½“ć—ć¦ć„ć‚‹ę± ć§ć™ć€‚ 2024 幓 11 ꜈态AWS Cognito ć®čŖčØ¼ę©Ÿčƒ½ć«ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ćƒ¬ć‚¹čŖčØ¼ę©Ÿčƒ½ļ¼ˆEmail/SMS OTP ć‚„ćƒ‘ć‚¹ć‚­ćƒ¼ļ¼‰ćŒęØ™ęŗ–ę©Ÿčƒ½ćØć—ć¦čæ½åŠ ć•ć‚Œć¾ć—ćŸć€‚ ć“ć‚Œć¾ć§ćÆ Cognito ć§ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ćƒ¬ć‚¹čŖčØ¼ć‚’å®Ÿē¾ć™ć‚‹ć«ćÆć‚«ć‚¹ć‚æćƒ  Lambda ćƒˆćƒŖć‚¬ćƒ¼ļ¼ˆCUSTOM_AUTH čŖčØ¼ćƒ•ćƒ­ćƒ¼ļ¼‰ć‚’åˆ©ē”Øć—ć¦ OTP čŖčØ¼ć‚’å®Ÿč£…ć™ć‚‹åæ…č¦ćŒć‚ć‚Šć¾ć—ćŸćŒć€ęØ™ęŗ–ę©Ÿčƒ½ćØć—ć¦čæ½åŠ ć•ć‚ŒćŸć“ćØć«ć‚ˆć£ć¦ Lambda ć‚’ä½æć‚ćšć« Cognito のみで OTP ć‚’å®Ÿē¾ć™ć‚‹ć“ćØćŒć§ćć‚‹ć‚ˆć†ć«ćŖć‚Šć¾ć—ćŸć€‚ å°å£²ć‚¢ćƒ—ćƒŖć®é–‹ē™ŗć«ćŠć„ć¦ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ćƒ¬ć‚¹čŖčØ¼ć‚’ę¤œčØŽć™ć‚‹ę©Ÿä¼šćŒć‚ć£ćŸćŸć‚ęœ¬čØ˜äŗ‹ć§ćÆ Cognito ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ćƒ¬ć‚¹čŖčØ¼ć‚’č©¦ć—ć¦ć€čØ­å®šę–¹ę³•ć‚„čŖčØ¼ćƒ•ćƒ­ćƒ¼ćŠć‚ˆć³åæ…č¦ćŖ API ć‚’ē¢ŗčŖć—ć¾ć™ć€‚ Cognito ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ćƒ¬ć‚¹čŖčØ¼ć® 3 ć¤ć®ę–¹å¼ Cognito ć®ęØ™ęŗ–ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ćƒ¬ć‚¹čŖčØ¼ć«ćÆä»„äø‹ć® 3 ć¤ć®ę–¹å¼ćŒć‚ć‚Šć¾ć™ć€‚ Email OTP - ćƒ”ćƒ¼ćƒ«ć§é€äæ”ć•ć‚Œć‚‹ćƒÆćƒ³ć‚æć‚¤ćƒ ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ć§čŖčØ¼ SMS OTP - SMS ć§é€äæ”ć•ć‚Œć‚‹ćƒÆćƒ³ć‚æć‚¤ćƒ ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ć§čŖčØ¼ WebAuthn/ćƒ‘ć‚¹ć‚­ćƒ¼ - ē”Ÿä½“čŖčØ¼ć‚„ćƒćƒ¼ćƒ‰ć‚¦ć‚§ć‚¢ćƒˆćƒ¼ć‚Æćƒ³ć§čŖčØ¼ ä»Šå›žćÆć€ć“ć®äø­ć‹ć‚‰ Email OTP čŖčØ¼ 悒 AWS CLI ć‹ć‚‰č©¦ć—ć€ć‚µć‚¤ćƒ³ć‚¢ćƒƒćƒ—ć‹ć‚‰ćƒˆćƒ¼ć‚Æćƒ³å–å¾—ć¾ć§ć®äø€é€£ć®ęµć‚Œć‚’ē¢ŗčŖć—ć¦ćæć¾ć™ć€‚ äŗ‹å‰ęŗ–å‚™ AWS ć‚³ćƒ³ć‚½ćƒ¼ćƒ«ć§ć®čØ­å®šļ¼ˆćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ćƒ¬ć‚¹čŖčØ¼ć®ć‚¢ć‚Æćƒ†ć‚£ćƒ™ćƒ¼ćƒˆčØ­å®šļ¼‰ 1. User Pool の作成・設定 Cognito User Pool ć‚’ä½œęˆć—ćŸå¾Œć€ć‚³ćƒ³ć‚½ćƒ¼ćƒ«ć§ä»„äø‹ć‚’čØ­å®šć—ć¾ć™ć€‚ ć‚µć‚¤ćƒ³ć‚¤ćƒ³čØ­å®š ć€ŒčŖčØ¼ > ć‚µć‚¤ćƒ³ć‚¤ćƒ³ > éøęŠžćƒ™ćƒ¼ć‚¹ć®ć‚µć‚¤ćƒ³ć‚¤ćƒ³ć®ć‚Ŗćƒ—ć‚·ćƒ§ćƒ³ć€ć‹ć‚‰ē·Øé›†ć«é·ē§» ć€Œä½æē”Øć§ćć‚‹éøęŠžč‚¢ć€ć§ć€Œćƒ”ćƒ¼ćƒ«ćƒ”ćƒƒć‚»ćƒ¼ć‚øć®ćƒÆćƒ³ć‚æć‚¤ćƒ ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ć€ć‚’éøęŠžć—ć¦äæå­˜ ć“ć®čØ­å®šć‚’ć™ć‚‹ć“ćØć«ć‚ˆć‚Šć€Œćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ćŖć—ć®ć‚¹ćƒ†ćƒ¼ć‚æć‚¹ć€ćŒć‚¢ć‚Æćƒ†ć‚£ćƒ–ć«ćŖć‚Šć¾ć™ć€‚ ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ćƒ¬ć‚¹čŖčØ¼ļ¼ˆUSER_AUTH čŖčØ¼ćƒ•ćƒ­ćƒ¼ļ¼‰ć®ęœ‰åŠ¹åŒ– USER_AUTH は 2024 幓 11 ęœˆć«čæ½åŠ ć•ć‚ŒćŸ Cognito ć®ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ćƒ¬ć‚¹čŖčØ¼ćƒ•ćƒ­ćƒ¼ć§ć™ć€‚å¾“ę„ć® CUSTOM_AUTH ć®ć‚ˆć†ćŖć‚«ć‚¹ć‚æćƒ  Lambda ćŒäøč¦ć§ć€ęØ™ęŗ–ę©Ÿčƒ½ćØć—ć¦ Email/SMS OTP ć‚„ćƒ‘ć‚¹ć‚­ćƒ¼čŖčØ¼ć«åÆ¾åæœć—ć¦ć„ć¾ć™ć€‚čŖčØ¼ćƒ•ćƒ­ćƒ¼ć® USER_AUTH ć‚’ęœ‰åŠ¹ć«ć™ć‚‹čØ­å®šć‚’č”Œć„ć¾ć™ć€‚ ć€Œć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³ć‚Æćƒ©ć‚¤ć‚¢ćƒ³ćƒˆ > ć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³ć‚Æćƒ©ć‚¤ć‚¢ćƒ³ćƒˆć«é–¢ć™ć‚‹ęƒ…å ±ć€ć‹ć‚‰ē·Øé›†ć«é·ē§» ć€ŒčŖčØ¼ćƒ•ćƒ­ćƒ¼ć€ć®ć€ŒéøęŠžćƒ™ćƒ¼ć‚¹ć®ć‚µć‚¤ćƒ³ć‚¤ćƒ³: ALLOW_USER_AUTHć€ć«ćƒć‚§ćƒƒć‚Æć‚’ć¤ć‘ć¦äæå­˜ ć‚Ŗćƒ—ć‚·ćƒ§ćƒ³ļ¼šOTP ć®ęœ‰åŠ¹ęœŸé™ć‚’ć‚«ć‚¹ć‚æćƒžć‚¤ć‚ŗļ¼ˆćƒ‡ćƒ•ć‚©ćƒ«ćƒˆ 3 åˆ†ļ¼‰ ć“ć®ćƒ•ćƒ­ćƒ¼ć‚’ęœ‰åŠ¹åŒ–ć™ć‚‹ć“ćØć§ć€ć‚µć‚¤ćƒ³ć‚¢ćƒƒćƒ—ć‹ć‚‰čŖčØ¼ć¾ć§äø€č²«ć—ć¦ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ćƒ¬ć‚¹ć§å®Ÿč£…ć§ćć¾ć™ć€‚ åæ…č¦ćŖęƒ…å ±ć®ē¢ŗčŖ čØ­å®šå®Œäŗ†å¾Œć€ä»„äø‹ć®ęƒ…å ±ć‚’ęŽ§ćˆć¦ćŠćć¾ć™ć€‚ User Pool ID - ä¾‹ļ¼š ap-northeast-1_XXXXXXXXX App Client ID - ä¾‹ļ¼š 1a2b3c4d5e6f7g8h9i0j ćƒ•ćƒ­ćƒ¼ę¦‚č¦ ä»Šå›žćÆ Email OTP čŖčØ¼ć«ćŠć‘ć‚‹ä»„äø‹ć® 2 ć¤ć®ćƒ•ćƒ­ćƒ¼ć‚’ē¢ŗčŖć—ćŸć„ćØę€ć„ć¾ć™ć€‚ A. ć‚µć‚¤ćƒ³ć‚¢ćƒƒćƒ—å¾Œć®č‡Ŗå‹•ć‚µć‚¤ćƒ³ć‚¤ćƒ³ćƒ•ćƒ­ćƒ¼ļ¼ˆåˆå›žē™»éŒ²ę™‚ļ¼‰ ć‚µć‚¤ćƒ³ć‚¢ćƒƒćƒ—ļ¼ˆćƒ¦ćƒ¼ć‚¶ćƒ¼ē™»éŒ²ļ¼‰ → OTP 送俔 OTP 兄力 → Session čæ”å“ Session ć‚’ä½æć£ć¦č‡Ŗå‹•ć‚µć‚¤ćƒ³ć‚¤ćƒ³ → ćƒˆćƒ¼ć‚Æćƒ³å–å¾— B. é€šåøøć®ć‚µć‚¤ćƒ³ć‚¤ćƒ³ćƒ•ćƒ­ćƒ¼ļ¼ˆ2 å›žē›®ä»„é™ļ¼‰ ć‚µć‚¤ćƒ³ć‚¤ćƒ³é–‹å§‹ļ¼ˆOTP ē™ŗč”Œč¦ę±‚ļ¼‰ā†’ OTP 送俔 OTP 兄力 → ćƒˆćƒ¼ć‚Æćƒ³å–å¾— ć‚·ćƒ¼ć‚±ćƒ³ć‚¹å›³ A. ć‚µć‚¤ćƒ³ć‚¢ćƒƒćƒ—å¾Œć®č‡Ŗå‹•ć‚µć‚¤ćƒ³ć‚¤ćƒ³ćƒ•ćƒ­ćƒ¼ åˆå›žē™»éŒ²ę™‚ćÆć€ć‚µć‚¤ćƒ³ć‚¢ćƒƒćƒ—ćØåŒę™‚ć« OTP ćŒé€äæ”ć•ć‚Œć€ćƒ¦ćƒ¼ć‚¶ćƒ¼ćŒčŖčØ¼ć‚³ćƒ¼ćƒ‰ć‚’å…„åŠ›ć™ć‚‹ćØ Session ćŒčæ”å“ć•ć‚Œć¾ć™ć€‚ć“ć® Session ć‚’ä½æć†ć“ćØć§ć€čæ½åŠ ć®čŖčØ¼å‡¦ē†ć‚’ęŒŸć¾ćšć«ćƒˆćƒ¼ć‚Æćƒ³ć‚’å–å¾—ć—ć€č‡Ŗå‹•ēš„ć«ć‚µć‚¤ćƒ³ć‚¤ćƒ³ć¾ć§å®Œäŗ†ć§ćć¾ć™ć€‚ć¤ć¾ć‚Šć€Œćƒ¦ćƒ¼ć‚¶ćƒ¼ē™»éŒ² → ćƒ”ćƒ¼ćƒ«ē¢ŗčŖ → ćć®ć¾ć¾ć‚µć‚¤ćƒ³ć‚¤ćƒ³ć€ćŒäø€ę°—é€šč²«ć§å®Ÿē¾ć•ć‚Œć‚‹ęµć‚Œć§ć™ć€‚ B. é€šåøøć®ć‚µć‚¤ćƒ³ć‚¤ćƒ³ćƒ•ćƒ­ćƒ¼ļ¼ˆ2 å›žē›®ä»„é™ļ¼‰ 2 å›žē›®ä»„é™ć®ć‚µć‚¤ćƒ³ć‚¤ćƒ³ć§ćÆć€ć¾ćš initiate-auth ć‚’å®Ÿč”Œć™ć‚‹ćØ OTP ćŒćƒ”ćƒ¼ćƒ«ć§é€äæ”ć•ć‚Œć‚‹ćØåŒę™‚ć« Session ćŒčæ”å“ć•ć‚Œć¾ć™ć€‚ćƒ¦ćƒ¼ć‚¶ćƒ¼ćÆå—ć‘å–ć£ćŸ OTP ć‚’å…„åŠ›ć—ć€ćć® Session ćØēµ„ćæåˆć‚ć›ć¦ respond-to-auth-challenge ć‚’å‘¼ć³å‡ŗć™ć“ćØć§čŖčØ¼ćŒå®Œäŗ†ć—ć€ćƒˆćƒ¼ć‚Æćƒ³ćŒčæ”å“ć•ć‚Œć¾ć™ć€‚ ć¾ćØć‚ć‚‹ćØć€é€šåøøć‚µć‚¤ćƒ³ć‚¤ćƒ³ćÆć€ŒOTP ē™ŗč”Œļ¼ˆSession ä»˜äøŽļ¼‰ā†’ OTP 兄力 & Session ęē¤ŗ → ćƒˆćƒ¼ć‚Æćƒ³å–å¾—ć€ćØć„ć†ęµć‚Œć«ćŖć‚Šć¾ć™ć€‚ API/CLI ć‚³ćƒžćƒ³ćƒ‰ć¾ćØć‚ Email OTP čŖčØ¼ć§ä½æē”Øć™ć‚‹ API/CLI ć‚³ćƒžćƒ³ćƒ‰ć®äø€č¦§ ćƒ•ć‚§ćƒ¼ć‚ŗ API/CLI ć‚³ćƒžćƒ³ćƒ‰ 用途 ćƒ¬ć‚¹ćƒćƒ³ć‚¹ A. ć‚µć‚¤ćƒ³ć‚¢ćƒƒćƒ—å¾Œć®č‡Ŗå‹•ć‚µć‚¤ćƒ³ć‚¤ćƒ³ćƒ•ćƒ­ćƒ¼ ć‚µć‚¤ćƒ³ć‚¢ćƒƒćƒ— sign-up ćƒ¦ćƒ¼ć‚¶ćƒ¼ē™»éŒ² UserSubć€čŖčØ¼ć‚³ćƒ¼ćƒ‰é€äæ”å…ˆ čŖčØ¼ć‚³ćƒ¼ćƒ‰ē¢ŗčŖ confirm-sign-up ćƒ”ćƒ¼ćƒ«ć‚¢ćƒ‰ćƒ¬ć‚¹ē¢ŗčŖ Session ļ¼ˆč‡Ŗå‹•ć‚µć‚¤ćƒ³ć‚¤ćƒ³ē”Øļ¼‰ č‡Ŗå‹•ć‚µć‚¤ćƒ³ć‚¤ćƒ³ initiate-auth (SESSION) Session ć§ć‚µć‚¤ćƒ³ć‚¤ćƒ³ IdToken态AccessToken态RefreshToken B. é€šåøøć®ć‚µć‚¤ćƒ³ć‚¤ćƒ³ćƒ•ćƒ­ćƒ¼ ć‚µć‚¤ćƒ³ć‚¤ćƒ³é–‹å§‹ initiate-auth (USER_AUTH) OTP 送俔 Session态EMAIL_OTP ćƒćƒ£ćƒ¬ćƒ³ć‚ø čŖčØ¼ć‚³ćƒ¼ćƒ‰ē¢ŗčŖ respond-to-auth-challenge OTP å…„åŠ›ćƒ»ćƒˆćƒ¼ć‚Æćƒ³å–å¾— IdToken态AccessToken态RefreshToken å…±é€šę“ä½œ OTP 再送 (ć‚µć‚¤ćƒ³ć‚¢ćƒƒćƒ—ę™‚) resend-confirmation-code ꖰ恗恄 OTP 送俔 é€äæ”å…ˆęƒ…å ± OTP 再送 (ć‚µć‚¤ćƒ³ć‚¤ćƒ³ę™‚) initiate-auth (USER_AUTH) ꖰ恗恄 OTP 送俔 ꖰ恗恄 Session CLI ć§å®Ÿč”Œć—ć¦ćæć‚‹ (1) ć‚µć‚¤ćƒ³ć‚¢ćƒƒćƒ— äŗ‹å‰ęŗ–å‚™ć®ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ćƒ¬ć‚¹čŖčØ¼ć®čØ­å®šć‚’č”Œć£ć¦ćŠćć“ćØć§ć€ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰äøč¦ć§ sign-up ć‚’å®Ÿč”Œć™ć‚‹ć“ćØćŒć§ćć¾ć™ć€‚ aws cognito-idp sign-up --client-id <APP_CLIENT_ID> --username <email@example.com> --user-attributes Name=email,Value=<email@example.com> ćƒ‘ćƒ©ćƒ”ćƒ¼ć‚æč§£čŖ¬ --client-id : User Pool ć«ē“ć„ćć‚¢ćƒ—ćƒŖć‚Æćƒ©ć‚¤ć‚¢ćƒ³ćƒˆć® ID --username : ć‚µć‚¤ćƒ³ć‚¤ćƒ³ę™‚ć«ä½æē”Øć™ć‚‹ćƒ¦ćƒ¼ć‚¶ćƒ¼åļ¼ˆć“ć“ć§ćÆćƒ”ćƒ¼ćƒ«ć‚¢ćƒ‰ćƒ¬ć‚¹ć‚’ęŒ‡å®šļ¼‰ --user-attributes : ćƒ¦ćƒ¼ć‚¶ćƒ¼å±žę€§ć€‚Email OTP ć‚’ä½æć†å “åˆćÆ Name=email ć‚’åæ…é ˆć§ē™»éŒ² ćƒ¬ć‚¹ćƒćƒ³ć‚¹ä¾‹ { " UserConfirmed ": false , " CodeDeliveryDetails ": { " Destination ": " y***@e*** ", " DeliveryMedium ": " EMAIL ", " AttributeName ": " email " } , " UserSub ": " d7f40a38... ", " Session ": " AYABeLIBKr5g... " } CLI ć®å®Ÿč”Œć«ęˆåŠŸć™ć‚‹ćØčŖčØ¼ć‚³ćƒ¼ćƒ‰ćŒę›øć‹ć‚ŒćŸ Email ćŒå±Šćć¾ć™ć€‚ (2) čŖčØ¼ć‚³ćƒ¼ćƒ‰ć®ē¢ŗčŖļ¼ˆć‚µć‚¤ćƒ³ć‚¢ćƒƒćƒ—ę™‚ļ¼‰ čŖčØ¼ć‚³ćƒ¼ćƒ‰ć®ē¢ŗčŖćÆ confirm-sign-up ć§č”Œć„ć¾ć™ć€‚ aws cognito-idp confirm-sign-up --client-id <APP_CLIENT_ID> --username <email@example.com> --confirmation-code 123456 ćƒ¬ć‚¹ćƒćƒ³ć‚¹ä¾‹ { " Session ": " AYABeLIBKr5g... " } ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ćƒ¬ć‚¹čŖčØ¼ćŒęœ‰åŠ¹ćŖå “åˆć€ confirm-sign-up ć®ćƒ¬ć‚¹ćƒćƒ³ć‚¹ć« Session ćŒå«ć¾ć‚Œć¾ć™ć€‚ć“ć® Session ć‚’ä½æē”Øć—ć¦ę¬”ć®ć‚¹ćƒ†ćƒƒćƒ—ć§ć‚µć‚¤ćƒ³ć‚¢ćƒƒćƒ—ę™‚ć®ć‚·ćƒ¼ćƒ ćƒ¬ć‚¹ćŖč‡Ŗå‹•ć‚µć‚¤ćƒ³ć‚¤ćƒ³ćŒåÆčƒ½ć«ćŖć‚Šć¾ć™ć€‚ (2-補足) čŖčØ¼ć‚³ćƒ¼ćƒ‰å†é€ čŖčØ¼ć‚³ćƒ¼ćƒ‰ć‚’å†é€ć™ć‚‹éš›ć«ćÆ resend-confirmation-code ć‚’åˆ©ē”Øć—ć¾ć™ć€‚ aws cognito-idp resend-confirmation-code --client-id <APP_CLIENT_ID> --username <email@example.com> ćƒ¬ć‚¹ćƒćƒ³ć‚¹ä¾‹ { " CodeDeliveryDetails ": { " Destination ": " y***@e*** ", " DeliveryMedium ": " EMAIL ", " AttributeName ": " email " } } (3) ć‚µć‚¤ćƒ³ć‚¢ćƒƒćƒ—å¾Œć®č‡Ŗå‹•ć‚µć‚¤ćƒ³ć‚¤ćƒ³ confirm-sign-up 恧 Session ćŒčæ”ć•ć‚ŒćŸå “åˆć€ćć® Session を使用して initiate-auth ć‚’č”Œć†ć“ćØć§č‡Ŗå‹•ć‚µć‚¤ćƒ³ć‚¤ćƒ³ćŒåÆčƒ½ć§ć™ć€‚ aws cognito-idp initiate-auth --auth-flow USER_AUTH --client-id <APP_CLIENT_ID> --auth-parameters USERNAME=<email@example.com>,SESSION=<confirm-sign-up-session> ćƒ‘ćƒ©ćƒ”ćƒ¼ć‚æč§£čŖ¬ --auth-flow : ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ćƒ¬ć‚¹čŖčØ¼ćƒ•ćƒ­ćƒ¼ć§ć‚ć‚‹ USER_AUTH ć‚’ęŒ‡å®š --auth-parameters : USERNAME ć«ćƒ”ćƒ¼ćƒ«ć‚¢ćƒ‰ćƒ¬ć‚¹ć‚’ęŒ‡å®šć€‚ć“ć“ć§ćÆćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰äøč¦ć€‚SESSION 恫 confirm-sign-up ć®ćƒ¬ć‚¹ćƒćƒ³ć‚¹ć§čæ”ć£ć¦ććŸ Session ć‚’ćć®ć¾ć¾åˆ©ē”Øć€‚ ęˆåŠŸę™‚ćƒ¬ć‚¹ćƒćƒ³ć‚¹ { " ChallengeParameters ": {} , " AuthenticationResult ": { " AccessToken ": " eyJraWQiO... ", " ExpiresIn ": 3600 , " TokenType ": " Bearer ", " RefreshToken ": " eyJjdHkiO... ", " IdToken ": " eyJraWQiO... " } } (4) é€šåøøć® OTP čŖčØ¼é–‹å§‹ļ¼ˆ2 å›žē›®ä»„é™ć®ć‚µć‚¤ćƒ³ć‚¤ćƒ³ļ¼‰ 2 å›žē›®ä»„é™ć®ć‚µć‚¤ćƒ³ć‚¤ćƒ³ć§ćÆć€ USER_AUTH ćƒ•ćƒ­ćƒ¼ć‚’ä½æē”Øć—ć¦ OTP čŖčØ¼ć‚’é–‹å§‹ć—ć¾ć™ć€‚ aws cognito-idp initiate-auth --auth-flow USER_AUTH --client-id <APP_CLIENT_ID> --auth-parameters USERNAME=<email@example.com> ćƒ‘ćƒ©ćƒ”ćƒ¼ć‚æč§£čŖ¬ --auth-flow : ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ćƒ¬ć‚¹čŖčØ¼ćƒ•ćƒ­ćƒ¼ć§ć‚ć‚‹ USER_AUTH ć‚’ęŒ‡å®š --auth-parameters : USERNAME ć«ćƒ”ćƒ¼ćƒ«ć‚¢ćƒ‰ćƒ¬ć‚¹ć‚’ęŒ‡å®šć€‚ć“ć“ć§ćÆćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰äøč¦ ćƒ¬ć‚¹ćƒćƒ³ć‚¹ä¾‹ { " ChallengeName ": " EMAIL_OTP ", " Session ": " AYABeLnHu7... ", " ChallengeParameters ": { " CODE_DELIVERY_DELIVERY_MEDIUM ": " EMAIL ", " CODE_DELIVERY_DESTINATION ": " y***@e*** " } , " AvailableChallenges ": [ " EMAIL_OTP " ] } (5) čŖčØ¼ć‚³ćƒ¼ćƒ‰ć®ē¢ŗčŖļ¼ˆć‚µć‚¤ćƒ³ć‚¤ćƒ³ę™‚ļ¼‰ čŖčØ¼ć‚³ćƒ¼ćƒ‰ć®ē¢ŗčŖćÆ respond-to-auth-challenge ć‚’åˆ©ē”Øć—ć¾ć™ć€‚ aws cognito-idp respond-to-auth-challenge --client-id <APP_CLIENT_ID> --challenge-name EMAIL_OTP --session "AYABe9eY..." --challenge-responses USERNAME=<email@example.com>,EMAIL_OTP_CODE=123456 ćƒ‘ćƒ©ćƒ”ćƒ¼ć‚æč§£čŖ¬ --challenge-name : ä»Šå›žćÆ EMAIL_OTP ć‚’ęŒ‡å®š --session : initiate-auth ć®ćƒ¬ć‚¹ćƒćƒ³ć‚¹ć§čæ”ć£ć¦ććŸ Session ć‚’ćć®ć¾ć¾åˆ©ē”Ø --challenge-responses : ćƒ¦ćƒ¼ć‚¶ćƒ¼åćØ OTP ć‚’ć‚»ćƒƒćƒˆć§ęø”ć™ ćƒ¬ć‚¹ćƒćƒ³ć‚¹ä¾‹ { " ChallengeParameters ": {} , " AuthenticationResult ": { " AccessToken ": " eyJraWQiOi... ", " ExpiresIn ": 3600 , " TokenType ": " Bearer ", " RefreshToken ": " eyJjdHkiOi... ", " IdToken ": " eyJraWQiOi... " } } (5-補足) OTP ć®å†é€ OTP ćŒå±Šć‹ćŖć„ļ¼ęœŸé™åˆ‡ć‚Œć®å “åˆćÆć€å†åŗ¦ initiate-auth ć‚’å®Ÿč”Œć™ć‚‹ćØę–°ć—ć„ OTP ćŒćƒ”ćƒ¼ćƒ«ć«é€äæ”ć•ć‚Œć¾ć™ć€‚ åˆ¶é™äŗ‹é … MFA ćØć®ä½µē”Øåˆ¶é™ ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ćƒ¬ć‚¹čŖčØ¼ć«ćÆä»„äø‹ć® MFA é–¢é€£ć®åˆ¶é™ćŒć‚ć‚Šć¾ć™ć€‚ ćƒ¦ćƒ¼ć‚¶ćƒ¼ćÆ MFA ć¾ćŸćÆćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ćƒ¬ć‚¹čŖčØ¼ć®ć„ćšć‚Œć‹äø€ę–¹ć®ćæä½æē”ØåÆčƒ½ļ¼ˆäø”ę–¹ć®ä½µē”ØćÆäøåÆļ¼‰ MFA ć‚’ć€Œåæ…é ˆć€ć«čØ­å®šć—ć¦ć„ć‚‹å “åˆ ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ćƒ¬ć‚¹čŖčØ¼ć‚’čØ­å®šć§ććŖć„ MFA ć‚’ć€Œć‚Ŗćƒ—ć‚·ćƒ§ćƒ³ć€ć«čØ­å®šć—ć¦ć„ć‚‹å “åˆ MFA ć‚’čØ­å®šęøˆćæć®ćƒ¦ćƒ¼ć‚¶ćƒ¼ļ¼šćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ćƒ¬ć‚¹čŖčØ¼ć‚’ä½æē”Øć§ććŖć„ MFA ęœŖčØ­å®šć®ćƒ¦ćƒ¼ć‚¶ćƒ¼ļ¼šćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ćƒ¬ć‚¹čŖčØ¼ć‚’ä½æē”Øć§ćć‚‹ ć¤ć¾ć‚Šć€ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ćƒ¬ć‚¹čŖčØ¼ć‚’åˆ©ē”Øć™ć‚‹å “åˆćÆ MFA ć‚’ć€Œć‚Ŗćƒ—ć‚·ćƒ§ćƒ³ć€ć¾ćŸćÆć€ŒćŖć—ć€ć«čØ­å®šć™ć‚‹åæ…č¦ćŒć‚ć‚Šć¾ć™ć€‚ć“ć®åˆ¶é™ćÆ AWS Cognito ć®ä»•ę§˜ć«ć‚ˆć‚‹ć‚‚ć®ć§ć€č©³ē“°ćÆ å…¬å¼ćƒ‰ć‚­ćƒ„ćƒ”ćƒ³ćƒˆ ć‚’å‚ē…§ć—ć¦ćć ć•ć„ć€‚ ę©Ÿčƒ½ćƒ—ćƒ©ćƒ³ 2025 幓 9 ęœˆę™‚ē‚¹ć§ Cognito には Lite, Essential, Pro という 3 ć¤ć®ćƒ—ćƒ©ćƒ³ćŒć‚ć‚Šć¾ć™ć€‚ ćć‚Œćžć‚Œć®ćƒ—ćƒ©ćƒ³ć«ćŠć„ć¦åˆ©ē”Øć§ćć‚‹ę©Ÿčƒ½ćØę–™é‡‘ä½“ē³»ćŒē•°ćŖć‚Šć€Lite < Essential < Pro ć®é †ē•Ŗć§ę©Ÿčƒ½ćŒč±ŠåÆŒć«ćŖć‚Šć€ćć®åˆ†ę–™é‡‘ćÆé«˜ććŖć‚Šć¾ć™ć€‚ Cognito ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ćƒ¬ć‚¹čŖčØ¼ć‚’åˆ©ē”Øć™ć‚‹ć«ćÆ Essential ä»„äøŠć®ćƒ—ćƒ©ćƒ³ćŒåæ…č¦ć«ćŖć‚Šć¾ć™ć€‚ ※ ę©Ÿčƒ½ćƒ—ćƒ©ćƒ³ćŠć‚ˆć³ę–™é‡‘ä½“ē³»ćÆå¤‰ę›“ć•ć‚Œć‚‹ć®ć§ć”ę³Øę„ćć ć•ć„ć€‚ まとめ ęœ¬čØ˜äŗ‹ć§ćÆ 2024 幓 11 ꜈恋悉 Cognito ęØ™ęŗ–ę©Ÿčƒ½ćØć—ć¦åˆ©ē”ØåÆčƒ½ć«ćŖć£ćŸćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ćƒ¬ć‚¹čŖčØ¼(Email OTP)恧 AWS CLI ć‚’ä½æć£ć¦ć‚µć‚¤ćƒ³ć‚¢ćƒƒćƒ—ć‹ć‚‰ć‚µć‚¤ćƒ³ć‚¤ćƒ³ć¾ć§ć®ćƒ•ćƒ­ćƒ¼ć‚’ē¢ŗčŖć—ć¾ć—ćŸć€‚ ć“ć‚Œć«ć‚ˆć‚Šć€å¾“ę„ćÆć‚«ć‚¹ć‚æćƒ  Lambda ćŒåæ…é ˆć ć£ćŸ OTP čŖčØ¼ć‚’ć€Cognito ęØ™ęŗ–ę©Ÿčƒ½ć®ćæć§ć‚·ćƒ³ćƒ—ćƒ«ć«å®Ÿē¾ć§ćć‚‹ć“ćØćŒåˆ†ć‹ć‚Šć¾ć—ćŸć€‚ å®Ÿé‹ē”Øć§ćÆ MFA ćØć®ä½µē”Øåˆ¶é™ 悄 ćƒ—ćƒ©ćƒ³č¦ä»¶ ć«ę³Øę„ć™ć‚‹åæ…č¦ćŒć‚ć‚Šć¾ć™ćŒć€ćƒ¦ćƒ¼ć‚¶ćƒ¼ä½“éØ“ć‚’å‘äøŠć•ć›ćŸć„ć‚±ćƒ¼ć‚¹ē­‰ć§ęœ‰åŠ¹ćŖéøęŠžč‚¢ćØćŖć‚Šć¾ć™ć€‚ ä»Šå›žćÆ Email OTP ć®ćæć‚’ē¢ŗčŖć—ć¾ć—ćŸćŒć€ćƒ‘ć‚¹ć‚­ćƒ¼ļ¼ˆWebAuthnļ¼‰ćØć„ć£ćŸä»–ć®ę–¹å¼ć‚‚ę¤œčØ¼ć—ć¦ćæćŸć„ćØę€ć„ć¾ć™ć€‚ ęœ€å¾Œć¾ć§čŖ­ć‚“ć§ć„ćŸć ćć‚ć‚ŠćŒćØć†ć”ć–ć„ć¾ć—ćŸć€‚å°‘ć—ć§ć‚‚å‚č€ƒć«ćŖć‚Œć°å¹øć„ć§ć™ć€‚ å‚č€ƒćƒŖćƒ³ć‚Æ AWS å…¬å¼ćƒ‰ć‚­ćƒ„ćƒ”ćƒ³ćƒˆ - Passwordless authentication AWS CLI ćƒŖćƒ•ć‚”ćƒ¬ćƒ³ć‚¹ - cognito-idp
ć€å®Ÿč·µć€‘Aurora DSQL悒Terraformć§ę§‹ēÆ‰ć—ć¦å®Ÿé‹ē”ØåŒ–ć¾ć§ čƒŒę™Æ Terraform AWS Provider v6 ć•ć¦ć€ę™®é€šć«ć‚¢ćƒƒćƒ—ć‚°ćƒ¬ćƒ¼ćƒ‰ć™ć‚‹ćØć©ć†ćŖć‚‹ć§ć—ć‚‡ć†ļ¼Ÿ å®Ÿéš›ć«ć‚¢ćƒƒćƒ—ć‚°ćƒ¬ćƒ¼ćƒ‰ć—ć¦ćæć‚‹ćØć€ ć‚½ćƒ¼ć‚¹ć‚³ćƒ¼ćƒ‰ä¾‹ Aurora DSQLの構築 Private DNS endpoint ć®ēˆ†čŖ• VPC Privatelink Endpoint Security Group IAMロール まとめ ęœ€å¾Œć« ć€€ć“ć‚“ć«ć”ćÆć€é–‹ē™ŗęœ¬éƒØ é–‹ē™ŗļ¼’éƒØ RetailHUB NetSuperć‚°ćƒ«ćƒ¼ćƒ—ć«ę‰€å±žć™ć‚‹ćƒ›ćƒ¼ć‚ÆšŸ¦…ć‚¢ć‚¤šŸ‘ļøć§ć™ć€‚ čƒŒę™Æ ć€€ē§ć®ę‰€å±žć™ć‚‹ćƒćƒ¼ćƒ ć§ćÆć€ē¾åœØę–°ćƒ—ćƒ­ćƒ€ć‚Æćƒˆć‚’ćƒ­ćƒ¼ćƒ³ćƒć«å‘ć‘ć¦é‹­ę„é–‹ē™ŗäø­ćŖć®ć§ć™ćŒć€ćć®ćƒ‡ćƒ¼ć‚æćƒ™ćƒ¼ć‚¹ć‚¢ćƒ¼ć‚­ćƒ†ć‚Æćƒćƒ£ć«Aurora DSQLć‚’ęŽ”ē”Øć—ć¾ć—ćŸć€‚ćć®ę§‹ęˆćÆć‚·ćƒ³ćƒ—ćƒ«ć«ć‚·ćƒ³ć‚°ćƒ«ćƒŖćƒ¼ć‚øćƒ§ćƒ³ć‚·ćƒ³ć‚°ćƒ«ć‚Æćƒ©ć‚¹ć‚æćƒ¼ć§ć™ć€‚ć“ć®DSQL悒Terraformē®”ē†ć§ę§‹ēÆ‰ć™ć¹ćčŖæęŸ»ć—ć¦ć„ć‚‹ćØawsćƒ—ćƒ­ćƒć‚¤ćƒ€ć®ćƒćƒ¼ć‚øćƒ§ćƒ³ć‚’ęœ€ä½Žć§ć‚‚v5.100.0ä»„é™ć«ć—ćŖć„ćØć„ć‘ćŖć„ć“ćØćŒć‚ć‹ć‚Šć¾ć—ćŸć€‚ ć€€ćƒćƒ¼ćƒ ć§ä½æć£ć¦ć„ć‚‹Terraformē¾¤ćÆē¾åœØć€awsćƒ—ćƒ­ćƒć‚¤ćƒ€ć®ćƒćƒ¼ć‚øćƒ§ćƒ³ćÆ5.44.0ć§ć—ćŸć€‚ćć“ć§ć“ć®éš›ćØć€å½“ę™‚ęœ€ę–°ć®ćƒćƒ¼ć‚øćƒ§ćƒ³6.6.0ćøć®ć‚¢ćƒƒćƒ—ć‚°ćƒ¬ćƒ¼ćƒ‰ć‚’č©¦ćæć‚‹ć“ćØć«ć—ć¾ć—ćŸć€‚ ć€€ć¾ćŸć€ć“ć®ęœ€ę–°ćƒ—ćƒ­ćƒć‚¤ćƒ€ćƒćƒ¼ć‚øćƒ§ćƒ³ć‚’ä½æć£ć¦ć‚¤ćƒ³ćƒ•ćƒ©ę§‹ēÆ‰ć‚’č”Œć£ćŸéš›ć«gočØ€čŖžć§å®Ÿč£…ć—ćŸAPIć‚µćƒ¼ćƒć‹ć‚‰DSQLęŽ„ē¶šć‚’ē¢ŗē«‹ć•ć›ć‚‹ć¾ć§ć®é–“ć«ć¶ć”å½“ćŸć£ćŸå•é”ŒćØćć®č§£ę±ŗē­–ć‚’ē“¹ä»‹ć—ćŸć„ćØę€ć„ć¾ć™ć€‚ Terraform AWS Provider v6 怀AWSć‚Æćƒ©ć‚¦ćƒ‰ē”Øć®ćƒ”ć‚øćƒ£ćƒ¼ćŖćƒ—ćƒ­ćƒć‚¤ćƒ€ć§ć‚ć‚‹AWS Hashicorpćƒ—ćƒ­ćƒć‚¤ćƒ€ć‚’ćƒćƒ¼ćƒ å†…ć§ćÆę—¢ć«åˆ©ē”Øć—ć¦ć„ć¾ć™ć€‚ćć®ęœ€ę–°major version 6が2025幓6月19ę—„GAć•ć‚Œć¾ć—ćŸć€‚ ć€€å…¬å¼ć®ć‚¢ćƒƒćƒ—ć‚°ćƒ¬ćƒ¼ćƒ‰ć‚¬ć‚¤ćƒ‰ć«ć‚ˆć‚‹ćØå¤šćć®ę›“ę–°ęƒ…å ±ćŒć‚ć‚Šć¾ć™ćŒć€ē‰¹ć«ęˆ‘ć€…ć®ćƒćƒ¼ćƒ ć®ę—¢å­˜ē’°å¢ƒć«ć‚¤ćƒ³ćƒ‘ć‚ÆćƒˆćŒć‚ć‚‹ćØę€ć‚ć‚ŒćŸć®ćÆä»„äø‹ć®ęŠœē²‹ć«ć‚ć‚‹ć‚ˆć†ć«regionć«é–¢ć™ć‚‹å†…å®¹ć§ć—ćŸć€‚ registry.terraform.io Enhanced Region Support Version 6.0.0 of the Terraform AWS Provider adds region to most resources making it significantly easier to manage infrastructure across AWS Regions without requiring multiple provider configurations. ć•ć¦ć€ę™®é€šć«ć‚¢ćƒƒćƒ—ć‚°ćƒ¬ćƒ¼ćƒ‰ć™ć‚‹ćØć©ć†ćŖć‚‹ć§ć—ć‚‡ć†ļ¼Ÿ å…ØćƒŖć‚½ćƒ¼ć‚¹ć«ćƒŖćƒ¼ć‚øćƒ§ćƒ³ć‚’č‡Ŗå‹•ć§åŸ‹ć‚č¾¼ć‚“ć§ćć‚Œć‚‹ å…ØćƒŖć‚½ćƒ¼ć‚¹ć«ćƒŖćƒ¼ć‚øćƒ§ćƒ³ć‚’č‡Ŗå‹•ć§åŸ‹ć‚č¾¼ć‚“ć§ćć‚Œć‚‹ćŒć€providerćƒ–ćƒ­ćƒƒć‚ÆćØé‡č¤‡ć—ć¦ ERROR になる å…ØćƒŖć‚½ćƒ¼ć‚¹ć«ćƒŖćƒ¼ć‚øćƒ§ćƒ³ć‚’åŸ‹ć‚č¾¼ć‚ļ¼ćØć„ć† ERROR になる å…ØćƒŖć‚½ćƒ¼ć‚¹ć«ćƒŖćƒ¼ć‚øćƒ§ćƒ³ć‚’åŸ‹ć‚č¾¼ć‚€ć¹ćļ¼ćØć„ć† WARNING になる å®Ÿéš›ć«ć‚¢ćƒƒćƒ—ć‚°ćƒ¬ćƒ¼ćƒ‰ć—ć¦ćæć‚‹ćØć€ terraform plan -refresh-only ć ć‘å®Ÿč”Œć—ć¦198ć®ćƒŖć‚½ćƒ¼ć‚¹å·®åˆ†ćŒå‡ŗć¾ć—ćŸćŒć€ē “å£Šēš„å¤‰ę›“ć®åÆ¾č±”ćƒŖć‚½ćƒ¼ć‚¹ćÆćŖćć€ć™ć¹ć¦å†…éƒØēš„ćŖē®”ē†ćŒä»˜åŠ ć•ć‚Œć‚‹ä»„äø‹ć®3つだけなのでstateꛓꖰ恠恑恧applyćÆå•é”ŒćŖćå®Ÿč”ŒåÆčƒ½ćØåˆ¤ę–­ć—ćŸć®ć§å®Ÿč”Œć—ć€ē„”äŗ‹ć«ęˆåŠŸć—ć¾ć—ćŸć€‚ region:v6ć®ć‚¢ćƒƒćƒ—ć‚°ćƒ¬ćƒ¼ćƒ‰ć‚¬ć‚¤ćƒ‰ć«ć‚ć‚‹é€šć‚Šć€ćƒŖć‚½ćƒ¼ć‚¹å˜ä½ć§ćƒŖćƒ¼ć‚øćƒ§ćƒ³ć‚’äøŠę›øćčØ­å®šć§ćć‚‹ć‚ˆć†ć«ć»ć¼ć™ć¹ć¦ć®ćƒŖć‚½ćƒ¼ć‚¹ć«čæ½åŠ ć•ć‚ŒćŸå¼•ę•°ć€‚Optional and Computedć®å¼•ę•°ćŖć®ć§čØ­å®šć—ćŖćć¦ć‚‚č‡Ŗå‹•ć§å€¤ćŒå…„ć‚Šstateē®”ē†ć«čæ½åŠ ć•ć‚Œć‚‹ å…¬å¼ć®regionć‚Øćƒ³ćƒćƒ³ć‚¹ bucket_region:上のregionćØåŒć˜ę‰±ć„ Guide恮aws_s3_bucket恫恤恄恦 export:v6.4.0ä»„äøŠć‹ć‚‰čæ½åŠ ć•ć‚ŒćŸoptions引数でACMčØ¼ę˜Žę›øć®ć‚Øć‚Æć‚¹ćƒćƒ¼ćƒˆåÆå¦ć‚’čØ­å®šć™ć‚‹ć‚‚ć®ć€‚ćƒ‡ćƒ•ć‚©ćƒ«ćƒˆdisabledć§å®Ÿē’°å¢ƒć«å½±éŸæćŖć—ć®ćƒ‡ćƒ•ć‚©ćƒ«ćƒˆå€¤ć‚’ē®”ē†åÆ¾č±”ć«ć™ć‚‹ć‚‚ć®ć§ć—ć‹ćŖć„ resource:aws_acm_certificate ć‚½ćƒ¼ć‚¹ć‚³ćƒ¼ćƒ‰ä¾‹ ć€€å®Ÿéš›ć«applyå‰å¾Œć§ć©ć®ć‚ˆć†ćŖtfćƒ•ć‚”ć‚¤ćƒ«ć®é•ć„ćŒå‡ŗćŸć‹ć‚’č¦‹ć¦ćæć‚‹ćØē¢ŗć‹ć«ć‚¹ćƒƒć‚­ćƒŖćØć—ćŸčØ˜čæ°ć«ćŖć£ćŸå°č±”ć§ć™ć€‚ć‚‚ćØć‚‚ćØć€ć‚Øć‚¤ćƒŖć‚¢ć‚¹ć‚’åŸ‹ć‚č¾¼ć‚“ć§ć„ćŸćƒŖć‚½ćƒ¼ć‚¹å…ØéƒØć«äæ®ę­£ćŒåæ…č¦ć§ć—ćŸćŒć€åÆ¾č±”ćƒŖć‚½ćƒ¼ć‚¹ć‚‚å°‘ćŖććć‚‚ćć‚‚å„ćƒŖć‚½ćƒ¼ć‚¹ć«ę‰‹ć‚’åŠ ćˆć¦ć„ćŸć®ć§DRYć®č¦³ē‚¹ć§ć‚‚åŒę§˜ć®äæ®ę­£ć«ćŖć‚‰ć–ć‚‹ć‚’å¾—ćŖć„ć®ć§ćć®ć¾ć¾ę”¹äæ®ć‚’å®Ÿę–½ć—ć¾ć—ćŸć€‚ main.tf(before) providers = { aws . virginia = aws.virginia } main.tf(after)ć€€ćÆć€äøøć€…ćć®č”Œć‚’å‰Šé™¤ć—ćŸēŠ¶ę…‹ provider.tf(before) terraform { required_providers { aws = { source = "hashicorp/aws" version = "5.44.0" configuration_aliases = [ aws.virginia ] } } } provider "aws" { alias = "virginia" region = "us-east-1" profile = var.profile } provider.tf(after) terraform { required_providers { aws = { source = "hashicorp/aws" version = "6.6.0" } random = { source = "hashicorp/random" version = ">= 3.0" } } } # ä»„äø‹ćÆć€äøøć€…å‰Šé™¤ route53/main.tf(before) resource "aws_acm_certificate" "hogehoge" { provider = aws.virginia ... } route53/main.tf(after) resource "aws_acm_certificate" "hogehoge" { region = "us-east-1" ... } Aurora DSQLの構築 ć€€ć•ć¦ć€ęœ¬é”Œć®DSQLē’°å¢ƒę§‹ēÆ‰ć§ć™ćŒęœ€ēµ‚ēš„ć«ćÆä»„äø‹ć®ć‚¢ćƒ¼ć‚­ćƒ†ć‚Æćƒćƒ£ę§‹ęˆć§ę§‹ēÆ‰ć—ć¾ć—ćŸć€‚ ć‚¢ćƒ¼ć‚­ćƒ†ć‚Æćƒćƒ£ę§‹ęˆå›³ 怀DSQLć‚Æćƒ©ć‚¹ć‚æćƒ¼č‡Ŗä½“ćÆćƒŖć‚½ćƒ¼ć‚¹ć®Argumentć‚‚å°‘ćŖććØć¦ć‚‚ć‚·ćƒ³ćƒ—ćƒ«ćŖčØ­å®šć—ć‹ćŖćē°”å˜ć«ä½œęˆćÆć§ćć¾ć—ćŸć€‚ć™ć‚‹ćØć€ćƒ‘ćƒ–ćƒŖćƒƒć‚Æć‚Øćƒ³ćƒ‰ćƒć‚¤ćƒ³ćƒˆćØć„ć†ć‚‚ć®ćŒćƒ‡ćƒ•ć‚©ćƒ«ćƒˆć§ę‰•ć„å‡ŗć•ć‚Œć¦ć„ć¾ć—ćŸć€‚RDSć§ćÆč¦‹ćŖć„ć€ęœ€åˆć‹ć‚‰ćƒ‘ćƒ–ćƒŖćƒƒć‚ÆćƒćƒƒćƒˆćƒÆćƒ¼ć‚Æć«ęŽ„ē¶šć•ć‚ŒćŸć‚Øćƒ³ćƒ‰ćƒć‚¤ćƒ³ćƒˆćŒć‚ć‚Šć€ć‚€ć—ć‚ćƒ—ćƒ©ć‚¤ćƒ™ćƒ¼ćƒˆćƒćƒƒćƒˆćƒÆćƒ¼ć‚Æć®ć‚Øćƒ³ćƒ‰ćƒć‚¤ćƒ³ćƒˆćŒćŖć„ēŠ¶ę…‹ć§ć—ćŸć€‚ć“ć“ć§2ć¤ć®čŖ²é”ŒćŒęµ®ćå½«ć‚Šć«ćŖć‚Šć¾ć—ćŸć€‚ VPC内のECSć‚Æćƒ©ć‚¹ć‚æćƒ¼ć‹ć‚‰ć©ć®ć‚ˆć†ć«ćƒćƒƒćƒˆćƒÆćƒ¼ć‚Æå°Žé€šć‚’ē¢ŗäæć™ć‚‹ć‹ å®Ÿé‹ē”Øć«č€ćˆć‚‹ć‚ˆć†ć«ć‚»ć‚­ćƒ„ćƒŖćƒ†ć‚£ć‚’č€ƒę…®ć—ćŖć‘ć‚Œć°ćŖć‚‰ćŖć„ 怀恓恮2ć¤ć®čŖ²é”Œć‚’č§£ę±ŗć•ć›ć‚‹ćŸć‚ć«ę¬”ć«ęŽ²ć’ć‚‹č©³ē“°čØ­å®šć§ę§‹ēÆ‰ć‚’å®Œęˆć•ć›å®Ÿé‹ē”Øć«ć‚‚č€ćˆć†ć‚‹ć‚»ć‚­ćƒ„ćƒŖćƒ†ć‚£ć‚’ē¢ŗäæć™ć‚‹ć‚ˆć†ć«ć—ć¾ć—ćŸć€‚ ć€€ćŖćŠć€ćƒ‡ćƒ•ć‚©ćƒ«ćƒˆć§ę‰•ć„å‡ŗć•ć‚Œć‚‹Public endpointć«åÆ¾ć—ć¦ćÆę‰•ć„å‡ŗć—č‡Ŗä½“ć‚’ē„”åŠ¹åŒ–ć«ć™ć‚‹čØ­å®šćÆä»Šć®ćØć“ć‚å­˜åœØć—ćŖć„ć®ć§ćć®ć¾ć¾ć«ć—ć¦ć„ć¾ć™ć€‚ē‰¹ć«ćƒćƒƒćƒˆćƒÆćƒ¼ć‚ÆACLć«ć‚ˆć‚‹trafficåˆ¶å¾”ćÆć—ć¦ć„ćŖć„ć®ć§ć‚Æćƒ©ć‚¹ć‚æćƒ¼ćøć®å°Žé€šč‡Ŗä½“ćÆåÆčƒ½ć§ć™ćŒć€DSQLęŽ„ē¶šę™‚ć«ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ć®ä»£ć‚ć‚ŠćØć—ć¦ä½æć†ćƒˆćƒ¼ć‚Æćƒ³ē™ŗč”Œę™‚ć®čŖčØ¼ć«ć‚ˆć‚ŠIAMćƒ­ćƒ¼ćƒ«ęØ©é™äøč¶³ć§Access Deniedć«ćŖć‚‹ć®ć§å•é”ŒćŖć—ćØć—ć¦ć„ć¾ć™ć€‚ć‚‚ć—ć€é‹ē”Øäø­ć«DDosę”»ę’ƒć®ć‚ˆć†ćŖć‚¢ć‚Æć‚»ć‚¹éŽå¤šć«ć‚ˆć‚‹é«˜trafficćŒē™ŗē”Ÿć—ćŸå “åˆćÆćć®ę™‚ć«ACLåˆ¶å¾”ć‚‚ę¤œčØŽć™ć‚‹äŗˆå®šć§ć™ć€‚ Private DNS endpoint ć®ēˆ†čŖ• 怀DSQLć‚Æćƒ©ć‚¹ć‚æćƒ¼ć®č©³ē“°ē”»é¢ć‚’ć‚³ćƒ³ć‚½ćƒ¼ćƒ«äøŠć§ē¢ŗčŖć™ć‚‹ćØć€ PrivateLink ćØć„ć†ę°—ć«ćŖć‚‹é …ē›®ć‚æćƒ–ćŒć‚ć‚Šć¾ć—ćŸć€‚å…¬å¼ćƒ‰ć‚­ćƒ„ćƒ”ćƒ³ćƒˆć«ć‚ˆć‚Œć°VPCć‚Øćƒ³ćƒ‰ćƒć‚¤ćƒ³ćƒˆć‚’ä½æćˆć°ä»–ć®VPC恫恂悋ECSć‚Æćƒ©ć‚¹ć‚æćƒ¼ć«åÆ¾ć—ć¦ć‚ćŸć‹ć‚‚åŒć˜ćƒ—ćƒ©ć‚¤ćƒ™ćƒ¼ćƒˆć‚µćƒ–ćƒćƒƒćƒˆäøŠć®åÆ¾č±”ē‰©ćØć—ć¦ęŽ„ē¶šē¢ŗē«‹åÆčƒ½ć«ćŖć‚‹ä»•ēµ„ćæć§ć“ć‚Œć‚’ä½æć†ć“ćØć«ć—ć¾ć—ćŸć€‚ docs.aws.amazon.com docs.aws.amazon.com VPC Privatelink Endpoint 怀VPCć‚Øćƒ³ćƒ‰ćƒć‚¤ćƒ³ćƒˆćÆDSQLć®å…¬å¼ćƒ‰ć‚­ćƒ„ćƒ”ćƒ³ćƒˆć«ć‚‚ć‚ć‚‹ć‚ˆć†ć«ä»„äø‹ć®ć‚ˆć†ć«Interfaceć§ä½œęˆć—ć¾ć—ćŸć€‚vpc_idは、ECSć‚Æćƒ©ć‚¹ć‚æćƒ¼ć®å­˜åœØć™ć‚‹vpc_idć‚’čØ­å®šć—ć¾ć™ć€‚service_nameは、1ćƒŖćƒ¼ć‚øćƒ§ćƒ³å†…ć®DSQLć‚Æćƒ©ć‚¹ć‚æćƒ¼å…±é€šć§ä½œęˆę™‚ć«č‡Ŗå‹•ć§ē™ŗč”Œć•ć‚Œć‚‹ć‚µćƒ¼ćƒ“ć‚¹åć«ćŖć‚Šć¾ć™ć€‚subnet_idsは、ECSć‚Æćƒ©ć‚¹ć‚æćƒ¼ćŒęŽ„ē¶šć—ć¦ć„ć‚‹ć‚µćƒ–ćƒćƒƒćƒˆļ¼ˆćƒžćƒ«ćƒAZę§‹ęˆļ¼‰ć‚’čØ­å®šć—ć¾ć™ć€‚security_group_idsćÆć€å¾Œčæ°ć®ć‚»ć‚­ćƒ„ćƒŖćƒ†ć‚£ć‚°ćƒ«ćƒ¼ćƒ—ć®čØ­å®šć‚’åę˜ ć•ć›ć‚‹ćŸć‚ć«å¤‰ę•°ć§ć‚»ć‚­ćƒ„ćƒŖćƒ†ć‚£ć‚°ćƒ«ćƒ¼ćƒ—IDを渔しています。private_dns_enabledćÆć€ćƒ‡ćƒ•ć‚©ćƒ«ćƒˆfalseć§ęœ€åˆč©¦ć—ć¦Route53ć§ę‰±ć„ć‚„ć™ć„č‡Ŗē”±ćŖę–‡å­—åˆ—ć§CNAMEć‚’čØ­å®šć—ć¦ćæćŸć®ć§ć™ćŒć€ćć‚Œć ćØćƒćƒƒćƒˆćƒÆćƒ¼ć‚Æå°Žé€šć›ćšć€ć†ć¾ćć„ććć†ć«ćŖć‹ć£ćŸć®ć§č«¦ć‚ć¦trueå€¤ć«ć—ć¦č‡Ŗå‹•ć§ę‰•ć„å‡ŗć•ć‚Œć‚‹ćƒ—ćƒ©ć‚¤ćƒ™ćƒ¼ćƒˆDNSåć‚’ä½æć†ć“ćØć«ć—ć¾ć—ćŸć€‚tags恮Nameå±žę€§ć‚’ä½æć£ć¦VPCć‚Øćƒ³ćƒ‰ćƒć‚¤ćƒ³ćƒˆć®NameéƒØåˆ†ć‚’č‡Ŗē”±ć«ē·Øé›†ć™ć‚‹ć“ćØćŒć§ćć¾ć™ć€‚ resource "aws_vpc_endpoint" "dsql_vpc_endpoint" { vpc_id = aws_vpc.ecs_cluster.id service_name = var.dsql_service_name vpc_endpoint_type = "Interface" subnet_ids = var.private_subnet_ids security_group_ids = [ var.dsql_vpc_endpoint_id ] private_dns_enabled = true tags = { Name = "dsql-endpoint" } } Security Group ć€€ć‚»ć‚­ćƒ„ćƒŖćƒ†ć‚£ć‚°ćƒ«ćƒ¼ćƒ—ćÆć€ä»„äø‹ć®ć‚ˆć†ćŖåŽ³åÆ†ćŖinbound(ingress)ćƒ«ćƒ¼ćƒ«ć«čØ­å®šć—ć¾ć—ćŸć€‚source_security_group_idは、APIć‚µćƒ¼ćƒć‚µćƒ¼ćƒ“ć‚¹ćŒå­˜åœØć™ć‚‹ECSć‚Æćƒ©ć‚¹ć‚æćƒ¼ć‹ć‚‰ć®ćæć‚’čØ±åÆć™ć‚‹čØ­å®šć§ć™ć€‚from_port,to_portćÆć€ē‰¹å®šćƒćƒ¼ćƒˆęŽ„ē¶šć®ćæć‚’čØ±åÆć™ć‚‹čØ­å®šć§ć™ć€‚outbound (egress)ćƒ«ćƒ¼ćƒ«ćÆäøč¦ćŖć®ć§čØ­å®šćŖć—ć§ć™ć€‚ ć€€ćŖćŠć€aws_security_group_rulećƒŖć‚½ćƒ¼ć‚¹čØ˜ę³•ćÆå¤ć„čØ˜ę³•ć§v6.6.0ではaws_vpc_security_group_ingress_rulećƒŖć‚½ćƒ¼ć‚¹čØ˜ę³•ć‚’ęŽØå„Øć—ć¦ć„ć¾ć™ćŒć€referenced_security_group_idć«ćć®ć¾ć¾source_security_group_idと同じIDć‚’čØ­å®šć—ć¦ć†ć¾ćć„ćć‹ć©ć†ć‹ćÆå‹•ä½œē¢ŗčŖć—ć¦ć„ćŖć„ćŸć‚ęŽ”ē”Øć—ć¾ć›ć‚“ć§ć—ćŸć€‚ćŠćć‚‰ćę—§čØ˜ę³•ć®aws_security_groupćƒŖć‚½ćƒ¼ć‚¹å†…ć®ingressćƒ–ćƒ­ćƒƒć‚Æć§č”Øē¾åÆčƒ½ć ć£ćŸć®ć§ćć®ćƒ–ćƒ­ćƒƒć‚Æć§č”Øē¾ć•ć‚ŒćŸćƒŖć‚½ćƒ¼ć‚¹ć®å “åˆćÆęŒ‡å®šć§ććŖć„ć‹ć‚‚ć—ć‚Œć¾ć›ć‚“ć€‚ resource "aws_security_group" "dsql_vpc_endpoint" { name = "dsql-vpc-endpoint" vpc_id = var.vpc_id tags = { Name = "dsql-vpc-endpoint" } } resource "aws_security_group_rule" "dsql_vpc_endpoint_ingress" { type = "ingress" from_port = 5432 to_port = 5432 protocol = "tcp" security_group_id = aws_security_group.dsql_vpc_endpoint.id source_security_group_id = aws_security_group.ecs_cluster.id } IAMロール 怀IAMćƒ­ćƒ¼ćƒ«ćÆć€DSQLć®ä»•ę§˜ć§ć‚ć‚‹ćƒˆćƒ¼ć‚Æćƒ³čŖčØ¼ćƒ»čŖåÆę–¹å¼ć«ćŠć„ć¦åæ…é ˆčØ­å®šćØćŖć‚Šć¾ć™ć€‚ć¾ćšć€ECSć‚Æćƒ©ć‚¹ć‚æćƒ¼ć®ē‰¹å®šć‚æć‚¹ć‚Æć«åÆ¾ć—ć¦ęŽ„ē¶ščØ±åÆćƒćƒŖć‚·ćƒ¼ dsql:DbConnectAdmin ć‚’čæ½åŠ ć—ć¾ć—ćŸć€‚ćć®ć†ćˆć§ć€ćƒžć‚¤ć‚°ćƒ¬ćƒ¼ć‚·ćƒ§ćƒ³ć‚’GitHub Actionsć§č‡Ŗå‹•åŒ–ć™ć‚‹ćŸć‚ć«ćć®OpenID Connectć«åÆ¾ć—ć¦ć‚‚ęŽ„ē¶ščØ±åÆćƒćƒŖć‚·ćƒ¼ć‚’čæ½åŠ ć—ć¾ć—ćŸć€‚ć¾ćŸć€APIć‚µćƒ¼ćƒćÆć€ć‚«ć‚¹ć‚æćƒ ćƒ­ćƒ¼ćƒ«ć‚’ä½æć£ć¦ęŽ„ē¶šć•ć›ć‚‹ćŸć‚ć«ć‚«ć‚¹ć‚æćƒ ćƒ­ćƒ¼ćƒ«ć«åÆ¾ć—ć¦ć®ęŽ„ē¶ščØ±åÆćƒćƒŖć‚·ćƒ¼ dsql:DbConnect ć‚‚čæ½åŠ ć—ć¾ć—ćŸć€‚ # ä»„äø‹ć®ćƒćƒŖć‚·ćƒ¼ć‚’ECSタスクのIAMćƒ­ćƒ¼ćƒ«ć«é–¢é€£ä»˜ć‘ statement { sid = "DSQLConnection" effect = "Allow" actions = [ "dsql:DbConnect" , "dsql:DbConnectAdmin" ] resources = [ var.dsql_arn ] } 怀ꬔ恫态 å…¬å¼ćƒ‰ć‚­ćƒ„ćƒ”ćƒ³ćƒˆ ć«å¾“ć£ć¦å®Ÿéš›ć«DSQL恮postgresćƒ‡ćƒ¼ć‚æćƒ™ćƒ¼ć‚¹ć«adminć§ęŽ„ē¶šć—ć¦ć‚«ć‚¹ć‚æćƒ ćƒ­ćƒ¼ćƒ«ä½œęˆć€IAMćƒ­ćƒ¼ćƒ«ęØ©é™ä»˜äøŽć€ē‰¹å®šć‚¹ć‚­ćƒ¼ćƒžć«CRUDęØ©é™ć‚’ä»˜äøŽć—ć¾ć—ćŸć€‚ CREATE ROLE example WITH LOGIN; AWS IAM GRANT example TO ' [ECSタスクのIAMロールARN] ' ; GRANT USAGE ON SCHEMA myschema TO example; GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA myschema TO example; まとめ ć€€ęœ¬ćƒ–ćƒ­ć‚°ć§ćÆć€å‰åŠć§ćÆć€HashiCorpćŒęä¾›ć™ć‚‹Terraform恮AWS Provider version 6 upgradeć‚’å®Ÿéš›ć«č”Œć£ćŸč©±ć«ć¤ć„ć¦ć§ć—ćŸć€‚ ć€€ć¾ćŸå¾ŒåŠć§ćÆć€Amazon Aurora DSQLć«ć¤ć„ć¦ć®å®Ÿé‹ē”Øć®ćŸć‚ć®ę§‹ēÆ‰ę–¹ę³•ć«ć¤ć„ć¦ē°”å˜ć§ćÆć‚ć‚Šć¾ć™ćŒčŖ¬ę˜Žć•ć›ć¦ć„ćŸć ćć¾ć—ćŸć€‚ ć€€ęˆ‘ć€…ć®ę–°ćƒ—ćƒ­ćƒ€ć‚ÆćƒˆćŒē„”äŗ‹é‹ē”Øé–‹å§‹ć•ć‚ŒćŸå¾Œć®é‹ē”Øå®Ÿč«‡ć«ć¤ć„ć¦ć¾ćŸåˆ„ć®ę©Ÿä¼šć«ćŠč©±ć§ćć‚Œć°ćØę€ć„ć¾ć™ć€‚ ęœ€å¾Œć« ć‚Øćƒ–ćƒŖćƒ¼ć§ćÆć€ćØć‚‚ć«åƒćä»²é–“ć‚’å‹Ÿé›†ć—ć¦ć„ć¾ć™ć€‚ ćƒ†ćƒƒć‚Æćƒ–ćƒ­ć‚°ć‚’čŖ­ć‚“ć§å°‘ć—ć§ć‚‚ć‚Øćƒ–ćƒŖćƒ¼ć«čˆˆå‘³ć‚’ęŒć£ć¦ć„ćŸć ć‘ćŸę–¹ćÆć€ćœć²äø€åŗ¦ć‚«ć‚øćƒ„ć‚¢ćƒ«é¢č«‡ć«ćŠč¶Šć—ćć ć•ć„ļ¼ corp.every.tv
ćÆć˜ć‚ć« こんにごは、2025幓4ęœˆć«ć‚½ćƒ•ćƒˆć‚¦ć‚§ć‚¢ć‚Øćƒ³ć‚øćƒ‹ć‚¢ćØć—ć¦ę–°å’å…„ē¤¾ć—ćŸé»’é«˜ć§ć™ć€‚ę™®ę®µćÆ ćƒ‡ćƒŖćƒƒć‚·ćƒ„ć‚­ćƒƒćƒćƒ³ ć«é–¢ć™ć‚‹é–‹ē™ŗć«ęŗć‚ć£ć¦ć„ć¾ć™ć€‚ ć‚¦ć‚§ćƒ–ē‰ˆć®ćƒ‡ćƒŖćƒƒć‚·ćƒ„ć‚­ćƒƒćƒćƒ³ć«ćŠć„ć¦ć€ć‚Æćƒ©ć‚¤ć‚¢ćƒ³ćƒˆē«Æęœ«ć®åˆ¤å®šćƒ­ć‚øćƒƒć‚Æć‚’č¦‹ē›“ć™ę©Ÿä¼šćŒć‚ć‚Šć¾ć—ćŸć€‚ęœ¬čØ˜äŗ‹ć§ćÆć€ć€Œć©ć®ć‚ˆć†ć«ć‚Æćƒ©ć‚¤ć‚¢ćƒ³ćƒˆć‚’č­˜åˆ„ć—ć€ć©ć†č£œå®Œć—ć¦ć„ććØč‰Æć•ćć†ć‹ć€ć‚’ę•“ē†ć—ć¾ć™ć€‚ ē«Æęœ«ć§å‡¦ē†ć‚’åˆ†ć‘ćŸć„ćƒ¦ćƒ¼ć‚¹ć‚±ćƒ¼ć‚¹ ć‚¹ćƒžćƒ›ćØPCć§č”Øē¤ŗć‚’åˆ‡ć‚Šę›æćˆć‚‹å “é¢ćØć—ć¦ēœŸć£å…ˆć«ę€ć„ęµ®ć‹ć¶ć®ćÆćƒ¬ć‚¹ćƒćƒ³ć‚·ćƒ–ćƒ¬ć‚¤ć‚¢ć‚¦ćƒˆć§ć™ćŒć€ć“ć‚ŒćÆå¤šćć®å “åˆCSSć§å®Œēµć—ć¾ć™ć€‚JavaScript恮 window.innerWidth ć‚’ē”Øć„ć‚‹ć“ćØć§ē”»é¢å¹…ć‚’å–å¾—ć—ć€å‡¦ē†ć‚’åˆ†å²ć•ć›ć‚‹ćØć„ć£ćŸę–¹ę³•ć‚‚ć‚ˆćä½æć‚ć‚Œć¾ć™ć€‚ć—ć‹ć—ć€ćƒ­ć‚°ć®åŽé›†ć‚„ćƒ¦ćƒ¼ć‚¶ćƒ¼čØ“ę±‚å†…å®¹ć®åˆ†å²ć€ć‚¢ćƒ—ćƒŖć‚¹ćƒˆć‚¢ćøć®čŖ˜å°Žļ¼ˆä¾‹: iOS ならApp Store态AndroidならGoogle Play Storeļ¼‰ćŖć©ć§ć€ē«Æęœ«ćƒ»ę©ŸēØ®ć«ć‚ˆć£ć¦å‡¦ē†ć‚’åˆ†ć‘ćŸć„å “é¢ć«ćÆåˆ„ć®ć‚¢ćƒ—ćƒ­ćƒ¼ćƒćŒåæ…č¦ć§ć™ć€‚ äø€čˆ¬ēš„ćŖē«Æęœ«åˆ¤å®šę–¹ę³• 基本はUser-Agent(仄降 UA )ćƒ˜ćƒƒćƒ€ć®å†…å®¹ć«ć‚ˆć£ć¦ć€ć‚Æćƒ©ć‚¤ć‚¢ćƒ³ćƒˆć‚’åˆ¤å®šć—ć¦ć„ć¾ć™ć€‚ćƒ‡ćƒŖćƒƒć‚·ćƒ„ć‚­ćƒƒćƒćƒ³ć§åˆ©ē”Øć—ć¦ć„ć‚‹Nuxt.js恧悂 @nuxtjs/device ćƒ¢ć‚øćƒ„ćƒ¼ćƒ«ćŒē”Øę„ć•ć‚Œć¦ć„ć¾ć™ćŒć€ć“ć‚ŒćÆć‚Æćƒ©ć‚¤ć‚¢ćƒ³ćƒˆć‹ć‚‰é€ć‚‰ć‚Œć‚‹UAć®å†…å®¹ć‹ć‚‰åˆ¤åˆ„ć—ć¦ć„ć¾ć™ć€‚ UAćÆćƒ–ćƒ©ć‚¦ć‚¶ć‚„ćƒ‡ćƒć‚¤ć‚¹ćŒć‚µćƒ¼ćƒćƒ¼ć«é€ć‚‹ć€Œč‡Ŗåˆ†ć®ęƒ…å ±ć‚’å«ć‚“ć ę–‡å­—åˆ—ć€ć§ć€OSćƒ»ćƒ–ćƒ©ć‚¦ć‚¶ćƒ»ćƒćƒ¼ć‚øćƒ§ćƒ³ćŖć©ćŒå«ć¾ć‚Œć¦ćŠć‚Šē«Æęœ«ć‚’åˆ¤åˆ„ć™ć‚‹ę‰‹ćŒć‹ć‚Šć«ćŖć‚Šć¾ć™ć€‚č©³ć—ććÆä»„äø‹ć®ćƒ‰ć‚­ćƒ„ćƒ”ćƒ³ćƒˆć‚’å‚ē…§ć—ć¦ćć ć•ć„ć€‚ developer.mozilla.org User-Agentć®åˆ©ē”Øę–¹ę³• ćƒ•ćƒ­ćƒ³ćƒˆć‚Øćƒ³ćƒ‰ć«ćŠć‘ć‚‹SSRć§ćÆćƒŖć‚Æć‚Øć‚¹ćƒˆćƒ˜ćƒƒćƒ€ć‹ć‚‰å‚ē…§ć—ć€ćƒ–ćƒ©ć‚¦ć‚¶å®Ÿč”Œę™‚ćÆ navigator ć‚Ŗćƒ–ć‚øć‚§ć‚Æćƒˆć‚’čŖ­ć‚€ć ć‘ć§å–å¾—ć§ćć¾ć™ć€‚ developer.mozilla.org ć‚µćƒ¼ćƒć‚µć‚¤ćƒ‰ (SSR時) å—äæ”ć—ćŸHTTPćƒŖć‚Æć‚Øć‚¹ćƒˆć®ćƒ˜ćƒƒćƒ€ć« user-agent ćŒå«ć¾ć‚Œć¦ć„ć‚‹ćŸć‚ć€ćć®ć¾ć¾ę–‡å­—åˆ—ć‚’å–ć‚Šå‡ŗć›ć¾ć™ć€‚ const http = require('http'); http.createServer((req, res) => { const ua = req.headers['user-agent']; res.end(`UA: ${ua}`); }).listen(3000); ć‚Æćƒ©ć‚¤ć‚¢ćƒ³ćƒˆć‚µć‚¤ćƒ‰ ļ¼ˆćƒ–ćƒ©ć‚¦ć‚¶å®Ÿč”Œę™‚ļ¼‰ ćƒ–ćƒ©ć‚¦ć‚¶ē’°å¢ƒć§ćÆ navigator.userAgent ćŒåŒć˜ęƒ…å ±ć‚’čæ”ć™ć®ć§ć€ę”ä»¶åˆ†å²ćŖć©ć«åˆ©ē”Øć§ćć¾ć™ć€‚ const ua = navigator.userAgent; User-Agentの例 ćƒ‡ćƒć‚¤ć‚¹ ćƒ–ćƒ©ć‚¦ć‚¶ User-Agent の一例 iPhone (iOS) Safari Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5 Mobile/15E148 Safari/604.1 iPhone (iOS) Chrome Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/135.0.7049.83 Mobile/15E148 Safari/604.1 iPad (iPadOS13仄前) Safari Mozilla/5.0 (iPad; CPU OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5 Mobile/15E148 Safari/604.1 iPad (iPadOS13仄降) Safari Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Safari/605.1.15 Android (Pixel 7) Chrome Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Mobile Safari/537.36 PC (Windows 11) Chrome Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36 Edg/134.0.0.0 PC (macOS) Chrome Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.3.1 Safari/605.1.15 2025: List of User-Agent strings | DeviceAtlas ć‚ˆć‚ŠęŠœē²‹ Chrome恮iOSå‘ć‘ćƒ“ćƒ«ćƒ‰ćÆApp Storeć®åˆ¶ē“„ć«ć‚ˆć£ć¦WebKitć‚’åˆ©ē”Øć™ć‚‹ćŸć‚ć€UAには CriOS ćØć„ć†ćƒˆćƒ¼ć‚Æćƒ³ćŒå«ć¾ć‚Œć¾ć™ć€‚Windows ē’°å¢ƒć§ć®äø€čˆ¬ēš„ćŖChrome恮UAćÆäøŠčØ˜ć®ć‚ˆć†ć« Windows NT ć‚’å«ć‚€ć“ćØćŒå¤šć„ć§ć™ć€‚ ć“ć“ć§ę³Øę„ć—ćŸć„ć®ćŒć€iPadOS13ä»„é™ć§ćÆć€ŒiPadļ¼ˆćŸć ć—iPad minić‚’é™¤ćļ¼‰ć€ćŒć€macOS Safari ćØåŒäø€ć®UAć‚’é€ć‚‹ć‚ˆć†ć«ćŖć£ć¦ć„ć‚‹ć“ćØć§ć™ć€‚ć“ć‚ŒćÆä»„äø‹ć®ę–‡ēŒ®ć§ę˜ŽčØ€ć•ć‚Œć¦ć„ć¾ć™ć€‚ ā€œWith the exception of iPad mini, Safari on iPad will now send a user-agent string that is identical to Safari on macOS.ā€ New WebKit Features in Safari 13 | WebKit 悈悊 č©³ē“°ćÆå¾Œčæ°ć—ć¾ć™ćŒć€ć“ć®å “åˆć€Œć‚æćƒƒćƒćƒ‘ćƒćƒ«ć‹ć©ć†ć‹ć€ć§iPadとMacć®åŒŗåˆ„ć‚’ć™ć‚‹ć“ćØćŒć§ćć¾ć™ć€‚ User-Agentć«ć‚ˆć‚‹ē«Æęœ«åˆ¤å®šć®äø€ä¾‹ 仄下は、 next-modules/device/src/runtime/generateFlags.ts ć§å®šē¾©ć•ć‚ŒćŸåˆ¤å®šćƒ­ć‚øćƒƒć‚Æć®äø€ć¤ć§ć™ć€‚ function isIos(userAgent: string): boolean { return /iPad|iPhone|iPod/.test(userAgent) } ć“ć®ć‚ˆć†ć«ć€ē«Æęœ«ć‚’č”Øć™ę–‡å­—åˆ—ć®ę­£č¦č”Øē¾ć‚’ē”Øć„ćŸå˜ē“”ćŖćƒ­ć‚øćƒƒć‚Æć§ć‚ć‚‹ć“ćØćŒć‚ć‹ć‚Šć¾ć™ć€‚ć—ć‹ć—ć€ć“ć‚Œć§ćÆ"iPad"ćØć„ć†ę–‡å­—åˆ—ćŒå«ć¾ć‚ŒćŖć„ć€ŒiPadOS13ä»„é™ć€ć§ćÆ false ć«ćŖć£ć¦ć—ć¾ć†å•é”ŒćŒć‚ć‚Šć¾ć™ć€‚ User-Agentåˆ¤å®šć‚’č£œå®Œć™ć‚‹ęŠ€č”“ćƒ»ć‚¢ćƒ—ćƒ­ćƒ¼ćƒ UAåˆ¤å®šć«ć‚ˆć‚‹äø»ćŖå•é”Œē‚¹ UAć«ć‚ˆć‚‹åˆ¤å®šćÆć‚·ćƒ³ćƒ—ćƒ«ć§ć™ćŒć€ę¬”ć®ć‚ˆć†ćŖčŖ²é”ŒćŒć‚ć‚Šć¾ć™ć€‚ å•é”Œē‚¹ čŖ¬ę˜Ž å½±éŸæä¾‹ å½č£…åÆčƒ½ę€§ UA ę–‡å­—åˆ—ćÆé–‹ē™ŗč€…ćƒ„ćƒ¼ćƒ«ćŖć©ć§ē°”å˜ć«å¤‰ę›“åÆčƒ½ å®Ÿéš›ćÆ iOS なのに Android ćØåˆ¤å®šć•ć‚Œć‚‹ ä»•ę§˜å¤‰ę›“ćƒŖć‚¹ć‚Æ iPadOS 13 ä»„é™ć®ć‚ˆć†ć« UA ä»•ę§˜ćŒēŖē„¶å¤‰ć‚ć‚‹ "iPad" ćŒå«ć¾ć‚ŒćŖććŖć‚ŠčŖ¤åˆ¤å®šļ¼ˆå‰čæ°ļ¼‰ ęƒ…å ±éŽå¤šćØćƒ—ćƒ©ć‚¤ćƒć‚·ćƒ¼å•é”Œ UA ę–‡å­—åˆ—ć«ćÆäøč¦ćŖęƒ…å ±ć‚‚å¤šćå«ć¾ć‚Œć‚‹ ćƒ—ćƒ©ć‚¤ćƒć‚·ćƒ¼äæč­·ć®č¦³ē‚¹ć‹ć‚‰åˆ¶é™ć®ę–¹å‘ćø User-Agent Client Hints恫恤恄恦 å¾“ę„ć®User-Agentę–‡å­—åˆ—ćÆć€ęƒ…å ±ćŒéŽå‰°ć§å½č£…ć‚‚å®¹ę˜“ć€ć•ć‚‰ć«ćƒ–ćƒ©ć‚¦ć‚¶ć®ä»•ę§˜å¤‰ę›“ć«ć‚ˆć£ć¦åˆ¤å®šćŒå£Šć‚Œć‚„ć™ć„ćØć„ć†čŖ²é”ŒćŒć‚ć‚Šć¾ć—ćŸć€‚ć“ć‚Œć«åÆ¾ć—ć¦GoogleはPrivacy Sandboxの一環として User-Agent Reduction ć‚’ęę”ˆć—ć€ćć®ä»£ę›æćØć—ć¦ User-Agent Client Hints(UA-CH) ćŒęØ™ęŗ–åŒ–ć•ć‚Œć¦ć„ć¾ć™ć€‚ ćŖćŠć€UA-CHć®ę­£å¼ćŖä»•ę§˜ćÆ User-Agent Client Hints W3C Draft ć§å…¬é–‹ć•ć‚Œć¦ćŠć‚Šć€User-Agentć®ęƒ…å ±é‡å‰Šęø›ćØćƒ—ćƒ©ć‚¤ćƒć‚·ćƒ¼é…ę…®ć®č¦³ē‚¹ć‹ć‚‰ęØ™ęŗ–åŒ–ćŒé€²ć‚ć‚‰ć‚Œć¦ć„ć¾ć™ć€‚č©³ć—ć„čƒŒę™Æć‚„ę¦‚č¦ćÆä»„äø‹ć®ć‚µć‚¤ćƒˆć‚‚å‚č€ƒć«ćŖć‚Šć¾ć™ć€‚ privacysandbox.google.com UA-CHć§ćÆć€åæ…č¦ćŖęƒ…å ±ć ć‘ć‚’ćƒ–ćƒ©ć‚¦ć‚¶ćŒę®µéšŽēš„ć«čæ”ć™ä»•ēµ„ćæć‚’å°Žå…„ć—ć€ćƒ—ćƒ©ć‚¤ćƒć‚·ćƒ¼äæč­·ć‚’č€ƒę…®ć—ć¤ć¤OSć‚„ćƒ–ćƒ©ć‚¦ć‚¶ęƒ…å ±ć‚’å®‰å…Øć«å–å¾—ć§ćć¾ć™ć€‚åŸŗęœ¬ęƒ…å ±ļ¼ˆOSåć‚„ćƒ¢ćƒć‚¤ćƒ«åˆ¤å®šļ¼‰ćÆ navigator.userAgentData ć‹ć‚‰å–å¾—ć—ć¾ć™ć€‚ if ( navigator . userAgentData ) { const { platform , mobile } = navigator . userAgentData ; console . log ( platform , mobile ) ; // 例: "Android", true } č©³ē“°ęƒ…å ±ćÆåæ…č¦ć«åæœć˜ć¦ getHighEntropyValues() ć‚’é€šć˜ć¦ę˜Žē¤ŗēš„ć«ćƒŖć‚Æć‚Øć‚¹ćƒˆć—ć¾ć™ć€‚ if ( navigator . userAgentData ) { navigator . userAgentData . getHighEntropyValues ([ "platformVersion" , "architecture" , "model" , "uaFullVersion" ]) . then ( ua => console . log ( ua )) ; // 例: { platformVersion: "13.0.0", architecture: "arm", model: "Pixel 5", uaFullVersion: "120.0.6099.129" } } UA-CHåˆ©ē”Øć«ć‚ˆć‚‹ćƒ”ćƒŖćƒƒćƒˆ ćØć—ć¦ć€ä»„äø‹ćŒęŒ™ć’ć‚‰ć‚Œć¾ć™ć€‚ ćƒ‡ćƒ•ć‚©ćƒ«ćƒˆć§åŸŗęœ¬ęƒ…å ±ć®ćæć€č©³ē“°ęƒ…å ±ćÆę˜Žē¤ŗēš„č¦ę±‚ćŒåæ…č¦ā†’ć‚Æćƒ©ć‚¤ć‚¢ćƒ³ćƒˆć®ćƒ—ćƒ©ć‚¤ćƒć‚·ćƒ¼äæč­·ć«ć¤ćŖćŒć‚‹ navigator.userAgentData ćŖć©ć§ę­£č¦č”Øē¾ćƒ‘ćƒ¼ć‚¹äøč¦ć€ę‰±ć„ć‚„ć™ć•å‘äøŠ å¾“ę„ć®UAć‹ć‚‰å°‘ć—ćšć¤UA-CHć«ē§»č”ŒåÆčƒ½ć§ć€ę—¢å­˜ć‚³ćƒ¼ćƒ‰ćøć®å½±éŸæćŒå°ć•ć„ 恗恋恗态2025幓9ęœˆē¾åœØć§ćÆChromiumē³»ćƒ–ćƒ©ć‚¦ć‚¶ć‚’äø­åæƒć«åÆ¾åæœćŒé€²ć‚“ć§ć„ć‚‹ć‚‚ć®ć®ć€Safarić‚„äø€éƒØćƒ–ćƒ©ć‚¦ć‚¶ć§ćÆęœŖåÆ¾åæœć®ć¾ć¾ć«ćŖć£ć¦ć„ć¾ć™ć€‚ć—ćŸćŒć£ć¦ć€åˆ¤å®šćƒ­ć‚øćƒƒć‚Æć‚’UA-CHć ć‘ć«ē½®ćę›ćˆć‚‹ć“ćØćÆéæć‘ćŸę–¹ćŒč‰Æć„ć§ć—ć‚‡ć†ć€‚ Feature Detection恫恤恄恦 developer.mozilla.org ć“ć”ć‚‰ć®ćƒ‰ć‚­ćƒ„ćƒ”ćƒ³ćƒˆć§ćÆć€UAę–‡å­—åˆ—ć«ć‚ˆć‚‹åˆ¤å®šć®å•é”Œē‚¹ćØć€ćć®ä»£ę›æćØć—ć¦ć® Feature Detection ć®ęœ‰ē”Øę€§ćŒčŖ¬ę˜Žć•ć‚Œć¦ć„ć¾ć™ć€‚ć“ć‚ŒćÆć€ć€Œćƒ–ćƒ©ć‚¦ć‚¶ļ¼ˆć‚„ē«Æęœ«ļ¼‰ć®åå‰ļ¼ćƒćƒ¼ć‚øćƒ§ćƒ³ć‚’čŖæć¹ć¦åˆ†å²ć™ć‚‹ć€ć®ć§ćÆćŖćć€ć€Œ ę“ä½œäø­ć®ē’°å¢ƒćŒē‰¹å®šć®ę©Ÿčƒ½ć‚’ć‚µćƒćƒ¼ćƒˆć—ć¦ć„ć‚‹ć‹ć©ć†ć‹ ć‚’čŖæć¹ć¦ć€ćć®ēµęžœć«åæœć˜ć¦å‡¦ē†ć‚’å¤‰ćˆć‚‹ć€ę–¹å¼ć§ć™ć€‚ä¾‹ćˆć°ć€ä»„äø‹ć®ć‚ˆć†ćŖå®Ÿč£…ä¾‹ćŒęŒ™ć’ć‚‰ć‚Œć¾ć™ć€‚ // ć‚æćƒƒćƒę“ä½œćŒåÆčƒ½ć‹ć©ć†ć‹ć‚’ē›“ęŽ„åˆ¤å®š const touch = 'ontouchstart' in window || navigator.maxTouchPoints > 0; if (touch) console.log('ć‚æćƒƒćƒUIć‚’ęœ‰åŠ¹åŒ–'); Feature Detection ć§č§£ę±ŗć§ćć‚‹ć“ćØ ćØć—ć¦ć€ä»„äø‹ćŒęŒ™ć’ć‚‰ć‚Œć¾ć™ć€‚ ę©Ÿčƒ½ćŒä½æćˆć‚‹ć‹ē›“ęŽ„ē¢ŗčŖć—ć€čŖ¤åˆ¤å®šćƒŖć‚¹ć‚Æć‚’ęŠ‘åˆ¶ć§ćć‚‹ UAć§ćÆåˆ¤ę–­ć—ć„ć‚‰ć„ęœ€ę–°APIć®åÆ¾åæœåÆå¦ć‚’ę­£ć—ćåˆ¤å®š UAä»•ę§˜å¤‰ę›“ć‚„ćƒŖćƒ€ć‚Æć‚·ćƒ§ćƒ³ć®å½±éŸæć‚’å—ć‘ć«ćć„å …ē‰¢ćŖć‚³ćƒ¼ćƒ‰ć«ć¤ćŖćŒć‚‹ OSć‚„ē«Æęœ«č©³ē“°ć®åˆ¤å®šč‡Ŗä½“ćÆć§ććŖć„ć‚‚ć®ć®ć€UAć«ä¾å­˜ć—ćŖć„ć“ć®ć‚ˆć†ćŖę›øćę–¹ćÆē©ę„µēš„ć«å–ć‚Šå…„ć‚Œć‚‹ć¹ćć ćØę„Ÿć˜ć¾ć—ćŸć€‚ ē«Æęœ«åˆ¤å®šćƒ­ć‚øćƒƒć‚Æć®å®Ÿč£…ä¾‹ ē§ćŒę‹…å½“ć—ćŸå®Ÿč£…ć§ćÆć€ć€ŒiPhoneć®å “åˆć«åˆ†å²ć—ćŸć‚³ćƒ¼ćƒ‰ć‚’ć€iPadć«ć‚‚é©ē”Øć•ć›ćŸć„ć€ćØć„ć†ēŠ¶ę³ćŒć‚ć‚Šć¾ć—ćŸć€‚ę³Øę„ć™ć¹ććƒć‚¤ćƒ³ćƒˆćÆć€ć€ŒiPadä»„å¤–ć®ć‚æćƒ–ćƒ¬ćƒƒćƒˆć‚„AndroidćÆę”ä»¶å¤–ć€ć€Œå‰čæ°ć—ćŸiPadOS13ä»„é™ć®č­˜åˆ„å•é”Œć‚’ę­£ć—ćåÆ¾å‡¦ć™ć‚‹ć€ć“ćØć®2ē‚¹ć§ć‚ć‚Šć€ć“ć‚ŒćÆ å‰čæ° ć—ćŸé–¢ę•°ć®åˆ©ē”Øć ć‘ć§ćÆäøååˆ†ć§ć™ć€‚ ä»Šå›žćÆ UA-CHćŒåˆ©ē”ØåÆčƒ½ćŖć‚‰å„Ŗå…ˆ ć—ć€ä½æćˆćŖć„å “åˆćÆ å¾“ę„ć®UA判定 + ć‚æćƒƒćƒć‚µćƒćƒ¼ćƒˆ + ćƒ—ćƒ©ćƒƒćƒˆćƒ•ć‚©ćƒ¼ćƒ ęƒ…å ± ć‚’ēµ„ćæåˆć‚ć›ć‚‹ć‚¢ćƒ—ćƒ­ćƒ¼ćƒć‚’å–ć‚Šć¾ć—ćŸć€‚ćŖćŠć€Macintoshē³»ć§ć‚æćƒƒćƒćƒ‘ćƒćƒ«ć‚’ć‚µćƒćƒ¼ćƒˆć™ć‚‹PCćŒćŖć„ć“ćØć‚’å‰ęćØć—ć¦ć„ć¾ć™ć€‚ iPhone态Android态iPadć€ćć‚Œä»„å¤–ć‚’åˆ¤å®šć—ćŸć„ćØćć®ć‚³ćƒ¼ćƒ‰ä¾‹ćÆä»„äø‹ć®é€šć‚Šć§ć™ć€‚ export async function detectDevice () { // UA-CHćŒä½æćˆć‚‹å “åˆćÆå„Ŗå…ˆ if ( navigator . userAgentData ) { const { platform , mobile } = navigator . userAgentData ; if ( platform === 'iOS' && mobile ) return 'iphone' ; if ( platform === 'iOS' && ! mobile ) return 'ipad' ; if ( platform === 'Android' ) return mobile ? 'android' : 'other' ; return 'other' ; } // UAćØć‚æćƒƒćƒåÆ¾åæœć‚’ēµ„ćæåˆć‚ć›ćŸåˆ¤å®š const ua = navigator . userAgent ; const isTouch = 'ontouchstart' in window || navigator . maxTouchPoints > 0 ; const isIpad = / iPad / . test ( ua ) || (/ Macintosh / . test ( ua ) && isTouch ) ; if (/ iPhone / . test ( ua )) return 'iphone' ; if (/ Android / . test ( ua )) return / Mobile / . test ( ua ) ? 'android' : 'other' ; if ( isIpad ) return 'ipad' ; return 'other' ; } ęœ€å¾Œć« ä»Šå›žć®č¦‹ē›“ć—ć§ę„Ÿć˜ćŸć®ćÆć€ćƒ©ć‚¤ćƒ–ćƒ©ćƒŖć‚„ćƒ¢ć‚øćƒ„ćƒ¼ćƒ«ć®åˆ¤å®šć‚’ćć®ć¾ć¾ä½æć†ć ć‘ć§ćÆćŖćć€ęœ¬å½“ć«ę­£ć—ć„ć‹ćƒ»ęœ€ę–°ä»•ę§˜ć«čæ½éšć§ćć¦ć„ć‚‹ć‹ć‚’ć”ć‚ƒć‚“ćØē¢ŗčŖć™ć‚‹ć“ćØć®å¤§åˆ‡ć•ć§ć™ć€‚ ć¾ćŸć€UAę–‡å­—åˆ—ć«ä¾å­˜ć—ć™ćŽćŖć„čØ­čØˆć«ćŖć‚‹ć‚ˆć†ć€ę©Ÿčƒ½ć®ęœ‰ē„”ć‚’ē›“ęŽ„č¦‹ć‚‹Feature Detectionć‚’åŸŗęœ¬ćØć™ć‚‹ę–¹é‡ćŒč‰Æć•ćć†ć§ć™ć€‚ UA-CHć®åˆ©ē”Øć‚‚äø€ć¤ć®ę‰‹ę®µć§ćÆć‚ć‚Šć¾ć™ćŒć€iOSē«Æęœ«ć§éžåÆ¾åæœć®ē¾ēŠ¶ć‚’čøć¾ćˆć‚‹ćØć€å®Ÿéš›ć«åˆ©ē”Øć™ć‚‹ćƒ”ćƒŖćƒƒćƒˆćÆć¾ć ä¹ć—ć„ćØć‚‚čØ€ćˆć¾ć™ć€‚å°Žå…„ć™ć‚‹ćŖć‚‰ćƒ•ć‚©ćƒ¼ćƒ«ćƒćƒƒć‚Æć‚’å«ć‚ćŸę®µéšŽēš„ćŖē§»č”Œć‚’å‰ęć«ć€ä»Šå¾Œć®åÆ¾åæœēŠ¶ę³ć‚’č¦‹ćŖćŒć‚‰ę…Žé‡ć«ę¤œčØŽć—ć¦ć„ććŸć„ć§ć™ć€‚ å‚č€ƒę–‡ēŒ® window.innerWidth(MDN Web Docs) @nuxtjs/device User agent (ćƒ¦ćƒ¼ć‚¶ćƒ¼ć‚Øćƒ¼ć‚øć‚§ćƒ³ćƒˆ) - MDN Web Docs ē”ØčŖžé›† | MDN navigator(MDN Web Docs) 2025: List of User-Agent strings | DeviceAtlas New WebKit Features in Safari 13 | WebKit nuxt-modules/device/src/runtime/generateFlags.ts User-Agent Client Hints W3C Draft ćƒ¦ćƒ¼ć‚¶ćƒ¼ ć‚Øćƒ¼ć‚øć‚§ćƒ³ćƒˆć®ęƒ…å ±é‡å‰Šęø›ćØćÆ | Privacy Sandbox ćƒ¦ćƒ¼ć‚¶ćƒ¼ć‚Øćƒ¼ć‚øć‚§ćƒ³ćƒˆę–‡å­—åˆ—ć‚’ē”Øć„ćŸćƒ–ćƒ©ć‚¦ć‚¶ćƒ¼ć®åˆ¤å®š - HTTP | MDN
ćÆć˜ć‚ć« ć“ć‚“ć«ć”ćÆć€‚ćƒ‡ćƒŖćƒƒć‚·ćƒ„ć‚­ćƒƒćƒćƒ³ć§ćƒ‡ćƒ¼ć‚æć‚µć‚¤ć‚Øćƒ³ćƒ†ć‚£ć‚¹ćƒˆć‚’ć—ć¦ć„ć‚‹å¤ęæµć§ć™ć€‚ ćƒ‡ćƒŖćƒƒć‚·ćƒ„AIを始め、瓄1幓ほどAIć‚Øćƒ¼ć‚øć‚§ćƒ³ćƒˆć®é–‹ē™ŗć«å–ć‚Šēµ„ć‚“ć§ćć¾ć—ćŸć€‚ ä»Šå›žćÆć€å€‹äŗŗēš„ć«ę„Ÿć˜ćŸAIć‚Øćƒ¼ć‚øć‚§ćƒ³ćƒˆé–‹ē™ŗć®ć‚¢ćƒ¼ć‚­ćƒ†ć‚Æćƒćƒ£ć«ć¤ć„ć¦č§¦ć‚Œć‚ˆć†ćØę€ć„ć¾ć™ć€‚ ć‚ćć¾ć§å€‹äŗŗēš„ę„č¦‹ćØć„ć†ē‚¹ć«ć”ē•™ę„ćć ć•ć„ć€‚ čƒŒę™Æ AIć‚Øćƒ¼ć‚øć‚§ćƒ³ćƒˆć®ć‚ˆć†ćŖć‚·ć‚¹ćƒ†ćƒ ć§ć‚‚ć€ę˜Žē¢ŗćŖčØ­čØˆę€ęƒ³ć«åŸŗć„ć„ćŸć‚¢ćƒ¼ć‚­ćƒ†ć‚Æćƒćƒ£éøęŠžćŒé‡č¦ć§ć™ć€‚ 恗恋恗态AIć‚Øćƒ¼ć‚øć‚§ćƒ³ćƒˆé–‹ē™ŗć«ćŠć‘ć‚‹ē¢ŗē«‹ć•ć‚ŒćŸć‚¢ćƒ¼ć‚­ćƒ†ć‚Æćƒćƒ£ćƒ‘ć‚æćƒ¼ćƒ³ćÆć€ć¾ć ååˆ†ć«ę•“ē†ć•ć‚Œć¦ć„ćŖć„ēŠ¶ę³ć§ć™ć€‚ äø€čˆ¬ēš„ć«ć‚¢ćƒ¼ć‚­ćƒ†ć‚Æćƒćƒ£ć‚’č€ƒćˆć‚‹ę™‚ć€ć‚ÆćƒŖćƒ¼ćƒ³ć‚¢ćƒ¼ć‚­ćƒ†ć‚Æćƒćƒ£ļ¼ˆćƒ¬ć‚¤ćƒ¤ćƒ¼ćƒ‰ć‚¢ćƒ¼ć‚­ćƒ†ć‚Æćƒćƒ£ļ¼‰ćŖć©ć«ä»£č”Øć•ć‚Œć‚‹ć‚ˆć†ćŖć€ä¾å­˜é–¢äæ‚ć‚’ę˜Žē¢ŗć«ć—ćŸć‚Šć€č²¬å‹™ć‚’åˆ†é›¢ć™ć‚‹ć“ćØćŖć©ćŒćƒ”ć‚¤ćƒ³ćƒˆćƒ”ćƒƒć‚Æć ćØę€ć„ć¾ć™ć€‚ AIć‚Øćƒ¼ć‚øć‚§ćƒ³ćƒˆé–‹ē™ŗć§ć‚‚ć€ć“ć‚Œć‚‰ć®č¦³ē‚¹ćÆé‡č¦ć§ć™ć€‚ 恟恠态AIć‚Øćƒ¼ć‚øć‚§ćƒ³ćƒˆå›ŗęœ‰ć®č¦³ē‚¹ć‚‚ć‚ć‚‹ćØę„Ÿć˜ć¦ćŠć‚Šć€ćć‚Œć‚’č€ƒę…®ć—ćŸć‚¢ćƒ¼ć‚­ćƒ†ć‚Æćƒćƒ£čØ­čØˆćŒåæ…č¦ć ćØę€ć£ć¦ć„ć¾ć™ć€‚ ć‚¢ćƒ¼ć‚­ćƒ†ć‚Æćƒćƒ£ę–‡č„ˆć§ć®AIć‚Øćƒ¼ć‚øć‚§ćƒ³ćƒˆå›ŗęœ‰ć®č¦³ē‚¹ å…ˆć«čæ°ć¹ć¦ćŠććØć€AIć‚Øćƒ¼ć‚øć‚§ćƒ³ćƒˆå›ŗęœ‰ć®č¦³ē‚¹ćÆä»„äø‹ć®2ē‚¹ć ćØč€ƒćˆć¾ć—ćŸć€‚ 評侔しやすいこと å‡¦ē†ć‚’ēµ„ćæę›æćˆć‚„ć™ć„ć“ćØ ćŖćœćć®ć‚ˆć†ć«č€ƒćˆćŸć‹ć«ć¤ć„ć¦ć€ä»„äø‹ć«ć¾ćØć‚ć¦ć„ćć¾ć™ć€‚ ć‚¹ć‚³ćƒ¼ćƒ— ひとえにAIć‚Øćƒ¼ć‚øć‚§ćƒ³ćƒˆćØčØ€ć£ć¦ć‚‚ć€Workflowåž‹ćØAgentåž‹ć«å¤§åˆ„ć•ć‚Œć¾ć™ć€‚ ć“ć‚Œć‚‰ćÆåŒŗåˆ„ć•ć‚Œć‚‹ćØčØ€ć£ć¦ć‚‚ć€äø€å®šć®ć‚°ćƒ©ćƒ‡ćƒ¼ć‚·ćƒ§ćƒ³ć‚’ęŒć¤ę€§č³Ŗć‚‚ć‚ć‚Šć¾ć™ć€‚ www.anthropic.com ä»Šå›žćÆć€Workflowåž‹ć‚’äø­åæƒć«č€ƒćˆć¦ć„ćć¾ć™ć€‚ ć“ć‚ŒćÆäø–ć®äø­ć®AIć‚Øćƒ¼ć‚øć‚§ćƒ³ćƒˆć®äø­åæƒćÆć€ć¾ć Workflowåž‹ć ć‚ć†ćØč€ƒćˆć¦ć„ć‚‹ćŸć‚ć§ć™ć€‚ å€‹äŗŗēš„ć«ćÆLLMć«å…Øć¦ć‚’ä»»ć›ć‚‹ć‚ˆć‚Šć‚‚ć€å®Ÿč£…å“ć§ę‰‹ē¶šēš„ćŖå‡¦ē†ć‚’é§†ä½æć—ćŸę–¹ćŒć‚³ćƒ³ćƒˆćƒ­ćƒ¼ćƒ«ćŒć—ć‚„ć™ćć€ćƒ‡ćƒćƒƒć‚°ć‚‚ć—ć‚„ć™ć„ćØč€ƒćˆć¦ć„ć¾ć™ć€‚ RAGć®ć‚¢ćƒ¼ć‚­ćƒ†ć‚Æćƒćƒ£ Workflowåž‹ć®AIć‚Øćƒ¼ć‚øć‚§ćƒ³ćƒˆć®ä»£č”Øę ¼ćØć„ćˆć°RAG恧恙怂 ćƒ‡ćƒŖćƒƒć‚·ćƒ„AI悂RAGć‚’äø­åæƒćØć—ćŸć‚·ć‚¹ćƒ†ćƒ ę§‹ęˆć«ćŖć£ć¦ć„ć¾ć™ć€‚ Langchainは、RAGć®ę§‹ęˆć‚’ä»„äø‹ć®ć‚ˆć†ć«ć¾ćØć‚ć¦ć„ć¾ć™ć€‚ Query Translation Routing Query Construction Indexing Retrieval Generation https://github.com/langchain-ai/rag-from-scratch ć“ć‚ŒćÆå¤§ććåˆ†ć‘ć‚Œć°ć€ä»„äø‹ć®ć‚ˆć†ć«åˆ†ć‘ć‚‰ć‚Œć¾ć™ć€‚ äŗ‹å‰å‡¦ē† Indexing å‰å‡¦ē† Query Translation Routing Query Construction 検瓢 Retrieval ē”Ÿęˆ Generation äøŠčØ˜ć®ć‚ˆć†ć«ę•“ē†ć™ć‚Œć°ć€ć„ć‚ć‚†ć‚‹FTI(Feature/Training/Inference)Pipelinesć‚¢ćƒ¼ć‚­ćƒ†ć‚Æćƒćƒ£ć®ć‚ˆć†ć«ć‚‚č¦‹ćˆć¦ćć¾ć™ć€‚ www.hopsworks.ai Feature悄TrainingćŒć€äŗ‹å‰å‡¦ē†ć‚„å‰å‡¦ē†ć«ć‚ćŸć‚Šć€InferencećŒę¤œē“¢ļ¼‹ē”Ÿęˆć«ć‚ćŸć‚‹ćØć„ć†č€ƒćˆę–¹ć§ć™ć€‚ ć“ć®ć‚ˆć†ćŖč¦³ē‚¹ć§ę•“ē†ć™ć‚Œć°ć€å¾“ę„ć®MLćƒ‘ć‚¤ćƒ—ćƒ©ć‚¤ćƒ³ćØåŒę§˜ć®ć‚¢ćƒ¼ć‚­ćƒ†ć‚Æćƒćƒ£ćØć—ć¦č½ćØć—č¾¼ć‚€ć“ćØć‚‚ć§ććŖććÆć‚ć‚Šć¾ć›ć‚“ć€‚ ćØćÆć„ćˆć€LLMć®å…„å‡ŗåŠ›ć‚’ę•°ē ē¹‹ćŽēš„ć«å‡¦ē†ć™ć‚‹Workflow型AIć‚Øćƒ¼ć‚øć‚§ćƒ³ćƒˆć®ę–¹ćŒć€å¾“ę„ć®MLćƒ‘ć‚¤ćƒ—ćƒ©ć‚¤ćƒ³ć‚ˆć‚Šč¤‡é›‘ćŖå‡¦ē†ćŒå¤šć„ćØę„Ÿć˜ć¦ć„ć¾ć™ć€‚ 複雑さの要因 ćŖćœč¤‡é›‘ć«ćŖć‚‹ć‹ćÆć€äø€čØ€ć§čØ€ćˆć°ć€Œē²¾åŗ¦ć®å‘äøŠć€å‡¦ē†ć®é«˜é€ŸåŒ–ć€ę±Žē”Øę€§ć®å‘äøŠćŖć©å‹•ę©ŸćŒWorkflowć‚’ć©ć†ēµ„ć‚€ć‹ć«åę˜ ć•ć‚Œć‚‹ć€ć‹ć‚‰ć§ć™ć€‚ ćć—ć¦ć€å¤‰ćˆćŸēµęžœå‘äøŠć—ćŸć‹ć©ć†ć‹ć‚’č©•ä¾”ć™ć‚‹åæ…č¦ć‚‚ć‚ć‚Šć¾ć™ć€‚ ä¾‹ćˆć°ć€RAGć®ē²¾åŗ¦å‘äøŠć‚’ć•ć›ć‚‹ćŸć‚ć«ć€å‰å‡¦ē†ć§å˜ä½“ć®LLMćŒå‡¦ē†ć™ć‚‹ēÆ„å›²ć‚’ē‹­ć‚ć¦ć€č¤‡ę•°ć®LLMć§å‡¦ē†ć™ć‚‹ć‚ˆć†ć«åˆ†ć‘ć‚‹ć“ćØćŒć‚ć‚Šć¾ć™ć€‚ ć¾ćŸć€é«˜é€ŸåŒ–ć™ć‚‹ćŸć‚ć«ć€ä¾å­˜ć—ćŖć„LLMć®ćƒŖć‚Æć‚Øć‚¹ćƒˆć‚’äø¦åˆ—ļ¼ˆäø¦č”Œļ¼‰å‡¦ē†ć—ć¦ć€APIć®å¾…ę©Ÿę™‚é–“ć‚’åŠ¹ēŽ‡ēš„ć«ę‰±ćˆć‚‹ć‚ˆć†ć«ć™ć‚‹ć“ćØć‚‚ć‚ć‚Šć¾ć™ć€‚ ć“ć®ć‚ˆć†ć«ć€LLMć®å‡¦ē†ć§ę‹…å½“ć™ć‚‹ēÆ„å›²ć‚’ē‹­ć‚ć¦äø¦č”Œå‡¦ē†ć™ć‚‹ć“ćØćÆć€ē›®ēš„ē‰¹åŒ–ć®å‡¦ē†ć§ć‚ć‚Œć°åˆē†ēš„ć§ć™ć€‚ ć—ć‹ć—ć€ä»£å„ŸćØć—ć¦ę±Žē”Øę€§ćÆå¤±ć‚ć‚Œć‚‹å“é¢ć‚‚ć‚ć‚Šć¾ć™ć€‚ 恝恓恧态Routingć®ć‚ˆć†ćŖWorkflowć®å‡¦ē†č‡Ŗä½“ć‚’åˆ†å²ć™ć‚‹ä»•ēµ„ćæć‚’ä½œć‚Šć€ćƒŖć‚Æć‚Øć‚¹ćƒˆć•ć‚ŒćŸć‚Æć‚ØćƒŖć«åæœć˜ć¦å‘¼ć³å‡ŗć™Workflowć‚’åˆ‡ć‚Šę›æćˆć‚‹ćØć„ć†ć‚¢ćƒ—ćƒ­ćƒ¼ćƒć‚‚ć‚ć‚Šć¾ć™ć€‚ ä»„äø‹ć®č«–ę–‡ć§ćÆć€åæœē­”ć‚Øćƒ¼ć‚øć‚§ćƒ³ćƒˆļ¼ˆč³Ŗå•åæœē­”ć‚Øćƒ¼ć‚øć‚§ćƒ³ćƒˆć€ęŽØč–¦åæœē­”ć‚Øćƒ¼ć‚øć‚§ćƒ³ćƒˆć€é›‘č«‡åæœē­”ć‚Øćƒ¼ć‚øć‚§ćƒ³ćƒˆļ¼‰ćØć©ć®ć‚Øćƒ¼ć‚øć‚§ćƒ³ćƒˆć§åæœē­”ć‚’ć™ć‚‹ć‹ć‚’ę±ŗć‚ć‚‹čØˆē”»ć‚Øćƒ¼ć‚øć‚§ćƒ³ćƒˆć‹ć‚‰ćŖć‚‹AIć‚Øćƒ¼ć‚øć‚§ćƒ³ćƒˆć‚’ęę”ˆć—ć¦ć„ć¾ć™ć€‚ č¤‡ę•°ć®ć‚Øćƒ¼ć‚øć‚§ćƒ³ćƒˆć‚’ä½æć„åˆ†ć‘ć‚‹ć“ćØć§ć€ćƒ¦ćƒ¼ć‚¶ćƒ¼ćØć‚³ćƒŸćƒ„ćƒ‹ć‚±ćƒ¼ć‚·ćƒ§ćƒ³ć‚’ćØć‚ŠćŖćŒć‚‰ć€é©åˆ‡ćŖć‚¢ć‚¤ćƒ†ćƒ ć‚’ęŽØč–¦ć™ć‚‹ćØć„ć†ē›®ēš„ć‚’é”ęˆć—ć¦ć„ć¾ć™ć€‚ arxiv.org ć“ć®ć‚ˆć†ć«ć€å·„å¤«ć—ć¦ć„ć‘ć°ć„ćć»ć©å‡¦ē†ćŒč¤‡é›‘ć«ćŖć‚‹ć®ćÆéæć‘ć‚‰ć‚Œć¾ć›ć‚“ć€‚ ćć®ä»–ć«ć‚‚ć€ē²¾åŗ¦ć®å‘äøŠć‚„é«˜é€ŸåŒ–ć‚’ē›®ēš„ćØć—ć¦ć€ćƒ—ćƒ­ćƒ³ćƒ—ćƒˆć‚’å¤‰ę›“ć—ćŸć„ć€ę–°ć—ć„ćƒ¢ćƒ‡ćƒ«ć‚’č©¦ć—ć¦ćæćŸć„ć€äø€éƒØćƒ«ćƒ¼ćƒ«ćƒ™ćƒ¼ć‚¹ć«ē½®ćę›ćˆć¦ēµęžœćŒå¤‰ć‚ć‚‹ć‹ē¢ŗčŖć—ćŸć„ć€Workflowć®å‡¦ē†é †åŗć‚’å¤‰ę›“ć—ć¦ćæćŸć„ćŖć©ć€ę§˜ć€…ćŖå¤‰ę›“ē‚¹ćŒå‡ŗć¦ćć¾ć™ć€‚ å®Ÿč£…ć®ć‚¢ćƒ—ćƒ­ćƒ¼ćƒ å†ęŽ²ć§ć™ćŒć€ä»„äø‹ć‚’ē›®ęŒ‡ć—ć¾ć™ć€‚ 評侔しやすいこと å‡¦ē†ć‚’ēµ„ćæę›æćˆć‚„ć™ć„ć“ćØ 1. č©•ä¾”ć—ć‚„ć™ć„å®Ÿč£… č©•ä¾”ć—ć‚„ć™ć„å®Ÿč£…ć®å‰ć«ć€č©•ä¾”é …ē›®ć«ć¤ć„ć¦ę•“ē†ć—ć¾ć™ć€‚ RAGć®å “åˆćÆå¤§ććä»„äø‹ć®ć‚ˆć†ć«åˆ†ć‘ć‚‰ć‚Œć¾ć™ć€‚ ę¤œē“¢ć®č©•ä¾” 検瓢+ē”Ÿęˆć®č©•ä¾” å˜ä½“ć®LLMの評侔 ć“ć“ć§ć€ę¤œē“¢ć®č©•ä¾”ćÆć€å¾“ę„ć®ę¤œē“¢ć‚·ć‚¹ćƒ†ćƒ ć®ć‚ˆć†ćŖč©•ä¾”ęŒ‡ęØ™ć‚’é©ē”Øć™ć‚‹ć“ćØćŒć§ćć¾ć™ć€‚ ć¾ćŸć€ę¤œē“¢+ē”Ÿęˆć®č©•ä¾”ćÆć€LLM as a Judgeć‚’ä½æć£ćŸč©•ä¾”ć‚’å®Ÿč£…ć—ć¦ć„ć¾ć™ć€‚ å…·ä½“ēš„ć«ćÆć€ä»„äø‹ć®ćƒ†ćƒƒć‚Æćƒ–ćƒ­ć‚°ć‚’ć”č¦§ćć ć•ć„ć€‚ tech.every.tv å˜ä½“ć®LLMć®č©•ä¾”ćÆć€ć©ć®ć‚ˆć†ćŖć‚æć‚¹ć‚Æć‹ć«ć‚ˆć£ć¦č©•ä¾”ć®ä»•ę–¹ćŒå¤‰ć‚ć‚‹ćØč€ƒćˆć¦ć„ć¾ć™ć€‚ ćć‚ŒćÆć€å›žē­”ć‚’äŗ‹å‰ć«å®šē¾©ć§ćć‚‹å “åˆćØć§ććŖć„å “åˆć§ć™ć€‚ ć§ććŖć„å “åˆćÆć€ä¾‹ćˆć°č¦ē“„ć™ć‚‹ć‚æć‚¹ć‚Æć§ć‚ć‚Œć°ć€ęœ€ēµ‚ēš„ć«ć§ćć‚‹č¦ē“„ć®å†…å®¹ć‚’ć‚ć‚‰ć‹ć˜ć‚å®šē¾©ć™ć‚‹ć“ćØćÆé›£ć—ć„ć§ć™ć€‚ ćć®ćŸć‚ć€LLM as a Judgeć‚’ä½æć£ć¦ć€å›žē­”ć®ćƒ‹ćƒ„ć‚¢ćƒ³ć‚¹ćØć—ć¦åˆć£ć¦ć„ć‚‹ć‹ć‚’č©•ä¾”ć™ć‚‹ć‚¢ćƒ—ćƒ­ćƒ¼ćƒć«ćŖć‚Šć€ę¤œē“¢+ē”ŸęˆćØä¼¼ćŸć‚ˆć†ćŖč©•ä¾”ć‚’ć™ć‚‹ć“ćØć«ćŖć‚‹ćØę€ć„ć¾ć™ć€‚ ć§ćć‚‹å “åˆćÆć€ä¾‹ćˆć°ē‰¹å®šć®ć‚­ćƒ¼ćƒÆćƒ¼ćƒ‰ć‚’ęŠ½å‡ŗć™ć‚‹ć‚æć‚¹ć‚Æć§ć‚ć‚Œć°ć€ęŠ½å‡ŗć—ćŸć„åÆ¾č±”ć‚’ę­£č§£ćØć—ć¦å®šē¾©ć™ć‚‹ć“ćØć§ę­£ē­”ēŽ‡ć‚’å‡ŗć™ć“ćØćŒć§ćć¾ć™ć€‚ ćƒ‡ćƒŖćƒƒć‚·ćƒ„AIć§ćÆć€ć“ć®ć‚ˆć†ćŖå‰å‡¦ē†ćŒć„ćć¤ć‚‚ć‚ć‚Šć¾ć™ć€‚ ä¾‹ćØć—ć¦ć€čŖæē†ę™‚é–“ć‚’ęŠ½å‡ŗć™ć‚‹å‰å‡¦ē†ć§ćÆć€Œ10åˆ†ä»„å†…ć§ä½œć‚Œć‚‹å‰Æčœę•™ćˆć¦ć€ćØć„ć†ćƒ¦ćƒ¼ć‚¶ć®ć‚Æć‚ØćƒŖć«åÆ¾ć—ć¦ć€čŖæē†ę™‚é–“ć®ć‚«ćƒ©ćƒ åć€äøē­‰å·ć€å€¤ć‚’å‡ŗåŠ›ć—ć¾ć™ć€‚ ć“ć‚Œć‚‰ć®å›žē­”ćÆäø€ę„ć«å®šć¾ć‚Šć€äŗ‹å‰ć«ę­£č§£ć‚’å®šē¾©ć™ć‚‹ć“ćØćŒć§ćć‚‹ćŸć‚ć€å®¹ę˜“ć«č©•ä¾”åÆčƒ½ć§ć™ć€‚ ä»„äø‹ćÆć€čŖæē†ę™‚é–“ć‚’ęŠ½å‡ŗć™ć‚‹å‰å‡¦ē†ć®ć‚³ćƒ¼ćƒ‰ä¾‹ć§ć™ć€‚ import os from enum import Enum from pydantic import BaseModel, Field from langchain_core.messages import MessageLikeRepresentation from langchain_core.prompts import ( ChatPromptTemplate, MessagesPlaceholder, SystemMessagePromptTemplate, ) from langchain_openai import ChatOpenAI os.environ[ "OPENAI_API_KEY" ] = "your_api_key" class CookingTimeColumn ( str , Enum): cooking_time = "cooking_time_min" class CookingTimeOperator ( str , Enum): greater_than = ">" less_than = "<" greater_than_or_equal_to = ">=" less_than_or_equal_to = "<=" class CookingTimeFilter (BaseModel): column: CookingTimeColumn operator: CookingTimeOperator value: float class CookingTimeFilters (BaseModel): filters: list [CookingTimeFilter] def create_cooking_time_filter (messages: list [MessageLikeRepresentation]) -> CookingTimeFilters: system_prompt = f """ ć‚ćŖćŸćÆę–™ē†ć®ēŸ„č­˜ćŒč±ŠåÆŒćŖćƒ¬ć‚·ćƒ”ę¤œē“¢AI恧恙怂 ćƒ¦ćƒ¼ć‚¶ćƒ¼ćŒćƒ¬ć‚·ćƒ”ę¤œē“¢ć®ćŸć‚ć«å…„åŠ›ć—ćŸć‚Æć‚ØćƒŖć‚’č§£čŖ­ć—ć€ćƒ¦ćƒ¼ć‚¶ćŒ**調理時間**ć§ćƒ•ć‚£ćƒ«ć‚æćƒŖćƒ³ć‚°ć—ć¦ę¤œē“¢ć—ćŸć„å “åˆćÆć€ćƒ•ć‚£ćƒ«ć‚æćƒŖćƒ³ć‚°ę”ä»¶ć‚’čæ”ć—ć¦ćć ć•ć„ć€‚ ## å‡ŗåŠ›å½¢å¼ * jsonå½¢å¼ć§å‡ŗåŠ›ć—ć¦ćć ć•ć„ * columnć«ć‚«ćƒ©ćƒ åć€operatorć«äøē­‰å·ć€valueć«ćƒ•ć‚£ćƒ«ć‚æćƒŖćƒ³ć‚°åÆ¾č±”ć‚’å…„ć‚Œć¦ćć ć•ć„ """ prompt = ChatPromptTemplate.from_messages( [ SystemMessagePromptTemplate.from_template(system_prompt), MessagesPlaceholder(variable_name= "messages" ) ] ) llm = ChatOpenAI(model= "gpt-4o-mini" ) chain = ( prompt | llm.with_structured_output( schema=CookingTimeFilters, method= "json_schema" , ) ) return chain.invoke({ "messages" : messages, }) print (create_cooking_time_filter([( "user" , "10åˆ†ä»„å†…ć§ä½œć‚Œć‚‹å‰Æčœę•™ćˆć¦" )])) # CookingTimeFilters( # filters=[ # CookingTimeFilter( # column=<CookingTimeColumn.cooking_time: 'cooking_time_min'>, # operator=<CookingTimeOperator.less_than_or_equal_to: '<='>, # value=10.0 # ) # ] # ) äŗ‹å‰ć«ęœŸå¾…ć™ć‚‹ēµęžœć‚’å®šē¾©ć—ć¦äø€č‡“ć—ć¦ć„ć‚‹ć‹ć©ć†ć‹ć‚’č©•ä¾”ć™ć‚‹ćØć„ć†ć“ćØćÆć€pytestćŖć©ć®ćƒ„ćƒ¼ćƒ«ć‚’ä½æć£ć¦åŒć˜ęž ēµ„ćæćØć—ć¦č©•ä¾”ć™ć‚‹ć“ćØć‚‚åÆčƒ½ć«ćŖć‚Šć¾ć™ć€‚ ć‚‚ć”ć‚ć‚“ć€temperature悒0ć«ć—ć¦ć‚‚åŒć˜ēµęžœćŒå¾—ć‚‰ć‚Œć‚‹ćØćÆé™ć‚‰ćŖć„ćŸć‚ć€åæ…ćšćƒ†ć‚¹ćƒˆćØć—ć¦é€šć‚‹ćØćÆé™ć‚Šć¾ć›ć‚“ć€‚ ć¾ćŸć€ćƒ†ć‚¹ćƒˆć®ćŸć³ć«å°‘ćŖć‹ć‚‰ćšAPIć‚³ć‚¹ćƒˆćŒć‹ć‹ć‚Šć¾ć™ć€‚ ć“ć®å•é”Œć‚’ć‚ć‚‹ēØ‹åŗ¦čØ±å®¹ć—ć¤ć¤ć€pytestć§č©•ä¾”ć§ćć‚‹ć“ćØć‚’ē›®ęŒ‡ć—ć¾ć™ć€‚ ä¾‹ćˆć°ć€CIではLLM関連のpytestć‚’å®Ÿč”Œć—ćŖć„ć‚ˆć†ć«čØ­å®šć™ć‚‹ć“ćØć§ć€APIć‚³ć‚¹ćƒˆć‚’ęŠ‘ćˆć‚‹ć“ćØćŒć§ćć¾ć™ć€‚ 仄下は、pytestć§č©•ä¾”ć§ćć‚‹ć‚ˆć†ć«ć™ć‚‹ćŸć‚ć®ć‚³ćƒ¼ćƒ‰ä¾‹ć§ć™ć€‚ pytestć®čØ­å®šćƒ•ć‚”ć‚¤ćƒ«ć« llm ćƒžćƒ¼ć‚«ćƒ¼ć‚’å®šē¾©ć—ć¾ć™ć€‚ # pyproject.toml [ tool.pytest.ini_options ] markers = [ "llm: LLMč©•ä¾”ē³»å˜ä½“ćƒ†ć‚¹ćƒˆ" , ] llm ćƒžćƒ¼ć‚«ćƒ¼ć‚’ä½æć£ć¦ć€LLMć®č©•ä¾”ć«é–¢ć™ć‚‹å˜ä½“ćƒ†ć‚¹ćƒˆć‚’ę›øćć¾ć™ć€‚ # test.py @ pytest.mark.llm def test_create_cooking_time_filter (): actual = create_cooking_time_filter([( "user" , "10åˆ†ä»„å†…ć§ä½œć‚Œć‚‹å‰Æčœę•™ćˆć¦" )]) expected = CookingTimeFilters( filters=[ CookingTimeFilter( column= "cooking_time_min" , operator= "<=" , value= 10.0 ) ] ) assert actual == expected pytestć®ć‚³ćƒžćƒ³ćƒ‰ćƒ©ć‚¤ćƒ³ć‚Ŗćƒ—ć‚·ćƒ§ćƒ³ć‚’čæ½åŠ ć—ć¾ć™ć€‚ ć“ć‚Œć«ć‚ˆć‚Šć€ćƒ‡ćƒ•ć‚©ćƒ«ćƒˆć§ćÆLLMč©•ä¾”ē³»å˜ä½“ćƒ†ć‚¹ćƒˆćÆskip恕悌态 pytest --run-llm 恧LLMć®č©•ä¾”å«ć‚ćŸćƒ†ć‚¹ćƒˆć‚’å®Ÿč”Œć§ćć¾ć™ć€‚ # conftest.py import pytest from _pytest.config import Config from _pytest.config.argparsing import Parser from _pytest.nodes import Item def pytest_addoption (parser: Parser): """ pytest ć«ę–°ć—ć„ć‚³ćƒžćƒ³ćƒ‰ćƒ©ć‚¤ćƒ³ć‚Ŗćƒ—ć‚·ćƒ§ćƒ³ --run-llm ć‚’čæ½åŠ ć€‚ action="store_true" ć«ć‚ˆć‚Šć€ć‚Ŗćƒ—ć‚·ćƒ§ćƒ³ćŒęŒ‡å®šć•ć‚Œć‚Œć° Trueć€ęŒ‡å®šć•ć‚ŒćŖć‘ć‚Œć° False怂 恓悌恧 pytest --run-llm ć‚’å®Ÿč”Œć§ćć‚‹ć‚ˆć†ć«ćŖć‚‹ć€‚ """ parser.addoption( "--run-llm" , action= "store_true" , default= False , help = "Run LLM evaluation tests" , ) def pytest_collection_modifyitems (config: Config, items: list [Item]): """ åŽé›†ć•ć‚ŒćŸć™ć¹ć¦ć®ćƒ†ć‚¹ćƒˆé–¢ę•° (items) ć‚’čµ°ęŸ»ć™ć‚‹ć€‚ å„ćƒ†ć‚¹ćƒˆé–¢ę•°ćŒ @pytest.mark.llm ć§ćƒžćƒ¼ć‚Æć•ć‚Œć¦ć„ć‚‹ć‹ć‚’åˆ¤å®šć€‚ --run-llm が ä»˜ć„ć¦ć„ćŖć„å “åˆćÆć€ćć®ćƒ†ć‚¹ćƒˆć« skip ćƒžćƒ¼ć‚«ćƒ¼ć‚’å‹•ēš„ć«čæ½åŠ ć€‚ ēµęžœćØć—ć¦ļ¼š - ćƒ‡ćƒ•ć‚©ćƒ«ćƒˆ (pytest) → LLMč©•ä¾”ē³»å˜ä½“ćƒ†ć‚¹ćƒˆćÆ skip - ę˜Žē¤ŗēš„ć«å®Ÿč”Œ (pytest --run-llm) → LLMč©•ä¾”ē³»å˜ä½“ćƒ†ć‚¹ćƒˆć‚‚å®Ÿč”Œ """ run_llm = config.getoption( "--run-llm" ) skip_llm = pytest.mark.skip(reason= "use --run-llm to run LLM tests" ) for item in items: if "llm" in item.keywords and not run_llm: item.add_marker(skip_llm) ć“ć®ć‚ˆć†ć«ć—ć¦ć€č©•ä¾”ćƒ‡ćƒ¼ć‚æć‚»ćƒƒćƒˆć‚’ē”Øę„ć§ćć‚Œć°ć€pytestć®ęž ēµ„ćæć§LLMć®å‡ŗåŠ›ć‚’č©•ä¾”ć§ćć‚‹ćØę€ć„ć¾ć™ć€‚ č©•ä¾”ćƒ‡ćƒ¼ć‚æć‚»ćƒƒćƒˆć‚’ä½œć‚‹ć®ćÆå¤§å¤‰ćŖå“é¢ć‚‚ć‚ć‚Šć¾ć™ćŒć€ä½•ć‚‰ć‹ć®å¤‰ę›“ć«åÆ¾ć—ć¦ęŒ™å‹•ćŒå¤‰ć‚ć£ć¦ć„ćŖć„ć‹ć‚’ć€ć‚¬ćƒ¼ćƒ‰ćƒ¬ćƒ¼ćƒ«ēš„ć«ē¢ŗčŖć§ćć‚‹ćØć„ć†ē‚¹ć§ćÆå¤§ććŖćƒ”ćƒŖćƒƒćƒˆćŒć‚ć‚‹ćØę€ć„ć¾ć™ć€‚ 2. å‡¦ē†ć‚’ēµ„ćæę›æćˆć‚„ć™ć„å®Ÿč£… å‡¦ē†ć‚’ēµ„ćæę›æćˆć‚„ć™ć„å®Ÿč£…ć‚’ć™ć‚‹ćŸć‚ć«ćÆć€ć‚·ćƒ³ćƒ—ćƒ«ć«ä½•ć‚‰ć‹ć®ćƒ•ćƒ¬ćƒ¼ćƒ ćƒÆćƒ¼ć‚Æć«é ¼ć‚‹ę–¹ćŒč‰Æć„ćØč€ƒćˆć¦ć„ć¾ć™ć€‚ ä¾‹ćˆć°ć€LangGraphでは、LLMの処理をNodeと、LLMć®å‡¦ē†åŒå£«ć‚’ć©ć†ē¹‹ćć‹ć‚’Edgeć§ę•“ē†ć•ć‚Œć€å¤§åŸŸēš„ć«ę‰±ć†å¤‰ę•°ć‚’StatećØć—ć¦å®šē¾©ć™ć‚‹ć“ćØć§ć€Workflowć‚’ēµ„ć‚€ć“ćØćŒć§ćć¾ć™ć€‚ from langgraph.graph import StateGraph, START, END from langgraph.graph.state import CompiledStateGraph from typing import Annotated, TypedDict from langchain_core.messages import MessageLikeRepresentation from langgraph.graph.message import add_messages class State (TypedDict): messages: Annotated[ list [MessageLikeRepresentation], add_messages] node1_result: str node2_result: str node3_result: str final_result: str def node1 (state: State) -> dict : return { "node1_result" : "node1_result" } def node2 (state: State) -> dict : return { "node2_result" : "node2_result" } def node3 (state: State) -> dict : return { "node3_result" : "node3_result" } def build_graph_in_series () -> CompiledStateGraph: graph = StateGraph(State) graph.add_node( "node1" , node1) graph.add_node( "node2" , node2) graph.add_node( "node3" , node3) # ē›“åˆ—ć«ē¹‹ćå “åˆ graph.add_edge(START, "node1" ) graph.add_edge( "node1" , "node2" ) graph.add_edge( "node2" , "node3" ) graph.add_edge( "node3" , END) return graph.compile() def build_graph_in_parallel () -> CompiledStateGraph: graph = StateGraph(State) graph.add_node( "node1" , node1) graph.add_node( "node2" , node2) graph.add_node( "node3" , node3) # äø¦č”Œć«ē¹‹ćå “åˆ graph.add_edge(START, "node1" ) graph.add_edge(START, "node2" ) graph.add_edge(START, "node3" ) graph.add_edge( "node1" , END) graph.add_edge( "node2" , END) graph.add_edge( "node3" , END) return graph.compile() å‡¦ē†ć‚’ēµ„ćæę›æćˆć‚„ć™ć„å®Ÿč£…ć‚’ć—ćŸć„ćØć„ć†ćƒ¢ćƒćƒ™ćƒ¼ć‚·ćƒ§ćƒ³ćŒć‚ć‚‹ę™‚ē‚¹ć§ć€č¤‡é›‘åŒ–ć‚’čØ±å®¹ć—ćŸäøŠć§Workflowć‚’ēµ„ć‚€åæ…č¦ćŒå‡ŗć¦ć„ć‚‹ćØć„ć†ć“ćØć ćØę€ć„ć¾ć™ć€‚ Workflowć‚’ēµ„ć‚€ćŸć‚ć®å®Ÿč£…ć‚’č‡Ŗå‰ć§ę›øćć‚ˆć‚Šć‚‚ć€LangGraphć‚’ä½æć†ę–¹ćŒå®¹ę˜“ć«å®Ÿč£…ć§ćć‚‹ćØč€ƒćˆć¦ć„ć¾ć™ć€‚ mermaidć«ć‚ˆć‚‹åÆč¦–åŒ–ć‚‚åÆčƒ½ć§ć€ć©ć‚“ćŖWorkflowć«ćŖć£ćŸć‹ć‚’äø€ē›®ć§č¦‹ć‚‹ć“ćØćŒć§ćć¾ć™ć€‚ from IPython.display import Image, display def visualize_graph (graph: CompiledStateGraph): display(Image(graph.get_graph().draw_mermaid_png())) visualize_graph(build_graph_in_series()) visualize_graph(build_graph_in_parallel()) LangGraphć®ęž ēµ„ćæć§create_cooking_time_filterć‚’å®Ÿč£…ć™ć‚‹ćØä»„äø‹ć®ć‚ˆć†ć«ćŖć‚Šć¾ć™ć€‚ import os from typing import Annotated, TypedDict from langchain_core.messages import MessageLikeRepresentation from langchain_core.prompts import ( ChatPromptTemplate, MessagesPlaceholder, SystemMessagePromptTemplate, ) from langgraph.graph.message import add_messages from langchain_openai import ChatOpenAI os.environ[ "OPENAI_API_KEY" ] = "your_api_key" class State (TypedDict): messages: Annotated[ list [MessageLikeRepresentation], add_messages] cooking_time_filters: list [CookingTimeFilter] def create_cooking_time_filter (state: State) -> dict [ str , CookingTimeFilters]: system_prompt = f """ ć‚ćŖćŸćÆę–™ē†ć®ēŸ„č­˜ćŒč±ŠåÆŒćŖćƒ¬ć‚·ćƒ”ę¤œē“¢AI恧恙怂 ćƒ¦ćƒ¼ć‚¶ćƒ¼ćŒćƒ¬ć‚·ćƒ”ę¤œē“¢ć®ćŸć‚ć«å…„åŠ›ć—ćŸć‚Æć‚ØćƒŖć‚’č§£čŖ­ć—ć€ćƒ¦ćƒ¼ć‚¶ćŒ**調理時間**ć§ćƒ•ć‚£ćƒ«ć‚æćƒŖćƒ³ć‚°ć—ć¦ę¤œē“¢ć—ćŸć„å “åˆćÆć€ćƒ•ć‚£ćƒ«ć‚æćƒŖćƒ³ć‚°ę”ä»¶ć‚’čæ”ć—ć¦ćć ć•ć„ć€‚ ## å‡ŗåŠ›å½¢å¼ * jsonå½¢å¼ć§å‡ŗåŠ›ć—ć¦ćć ć•ć„ * columnć«ć‚«ćƒ©ćƒ åć€operatorć«äøē­‰å·ć€valueć«ćƒ•ć‚£ćƒ«ć‚æćƒŖćƒ³ć‚°åÆ¾č±”ć‚’å…„ć‚Œć¦ćć ć•ć„ """ prompt = ChatPromptTemplate.from_messages( [ SystemMessagePromptTemplate.from_template(system_prompt), MessagesPlaceholder(variable_name= "messages" ) ] ) llm = ChatOpenAI(model= "gpt-4o-mini" ) chain = ( prompt | llm.with_structured_output( schema=CookingTimeFilters, method= "json_schema" , ) ) results = chain.invoke(state[ "messages" ]) return { "cooking_time_filters" : results} ć“ć®ć‚ˆć†ćŖå®Ÿč£…ć§č‰Æć•ćć†ć§ć™ćŒć€ć“ć‚Œć ćØå¼•ę•°ć®StatećÆå¤§åŸŸēš„ć«ę‰±ć†ćŸć‚ć€ćƒ†ć‚¹ćƒˆć‚³ćƒ¼ćƒ‰ć®å…„åŠ›ćØć—ć¦ä½æć„ćŸćć‚ć‚Šć¾ć›ć‚“ć€‚ å‹•ć‹ć™ć“ćØč‡Ŗä½“ćÆåÆčƒ½ć‹ć‚‚ć—ć‚Œć¾ć›ć‚“ćŒć€StatećÆę©Ÿčƒ½é–‹ē™ŗć™ć‚Œć°åæ…ćšå¤‰ę•°ćŒå¢—ćˆć‚‹ćŸć‚ć€ćƒ­ć‚øćƒƒć‚Æć®å‡¦ē†ćØLangGraphć®ćƒÆćƒ¼ć‚Æćƒ•ćƒ­ćƒ¼ćÆåˆ†é›¢ć—ćŸę–¹ćŒč‰Æć„ćØč€ƒćˆć¾ć™ć€‚ äøŠčØ˜ć‚’čøć¾ćˆć¦ä»„äø‹ć®ć‚ˆć†ć«å®šē¾©ć—ć¾ć—ćŸć€‚ ćƒ­ć‚øćƒƒć‚Æć®å‡¦ē†ćÆcreate_cooking_time_filter态LangGraphć®ćƒÆćƒ¼ć‚Æćƒ•ćƒ­ćƒ¼ćÆcreate_cooking_time_filter_graphćØć—ć¦å®šē¾©ć—ć¾ć™ć€‚ ćć—ć¦ć€create_cooking_time_filterのみをpytestć®ęž ēµ„ćæć§ćƒ†ć‚¹ćƒˆć‚³ćƒ¼ćƒ‰ć‚’ę›øćć“ćØć§č©•ä¾”ć—ć¾ć™ć€‚ def create_cooking_time_filter (messages: list [MessageLikeRepresentation]) -> CookingTimeFilters: system_prompt = f """ ć‚ćŖćŸćÆę–™ē†ć®ēŸ„č­˜ćŒč±ŠåÆŒćŖćƒ¬ć‚·ćƒ”ę¤œē“¢AI恧恙怂 ćƒ¦ćƒ¼ć‚¶ćƒ¼ćŒćƒ¬ć‚·ćƒ”ę¤œē“¢ć®ćŸć‚ć«å…„åŠ›ć—ćŸć‚Æć‚ØćƒŖć‚’č§£čŖ­ć—ć€ćƒ¦ćƒ¼ć‚¶ćŒ**調理時間**ć§ćƒ•ć‚£ćƒ«ć‚æćƒŖćƒ³ć‚°ć—ć¦ę¤œē“¢ć—ćŸć„å “åˆćÆć€ćƒ•ć‚£ćƒ«ć‚æćƒŖćƒ³ć‚°ę”ä»¶ć‚’čæ”ć—ć¦ćć ć•ć„ć€‚ ## å‡ŗåŠ›å½¢å¼ * jsonå½¢å¼ć§å‡ŗåŠ›ć—ć¦ćć ć•ć„ * columnć«ć‚«ćƒ©ćƒ åć€operatorć«äøē­‰å·ć€valueć«ćƒ•ć‚£ćƒ«ć‚æćƒŖćƒ³ć‚°åÆ¾č±”ć‚’å…„ć‚Œć¦ćć ć•ć„ """ prompt = ChatPromptTemplate.from_messages( [ SystemMessagePromptTemplate.from_template(system_prompt), MessagesPlaceholder(variable_name= "messages" ) ] ) llm = ChatOpenAI(model= "gpt-4o-mini" ) chain = ( prompt | llm.with_structured_output( schema=CookingTimeFilters, method= "json_schema" , ) ) return chain.invoke({ "messages" : messages, }) def create_cooking_time_filter_graph (state: State) -> dict [ str , CookingTimeFilters]: messages = state[ "messages" ] cooking_time_filters = create_cooking_time_filter(messages) return { "cooking_time_filters" : cooking_time_filters} ćŠć‚ć‚Šć« AIć‚Øćƒ¼ć‚øć‚§ćƒ³ćƒˆé–‹ē™ŗć«ćŠć„ć¦ć€Œē²¾åŗ¦ć®å‘äøŠć€å‡¦ē†ć®é«˜é€ŸåŒ–ć€ę±Žē”Øę€§ć®å‘äøŠćŖć©å‹•ę©ŸćŒWorkflowć‚’ć©ć†ēµ„ć‚€ć‹ć«åę˜ ć•ć‚Œć‚‹ć€ćØć„ć†čŖ²é”Œę„ŸćØć€å¤‰ćˆćŸēµęžœå‘äøŠć—ćŸć‹ć©ć†ć‹ć‚’č©•ä¾”ć™ć‚‹åæ…č¦ę€§ć‚’ęē¤ŗć—ć¾ć—ćŸć€‚ ć“ć®čŖ²é”Œć‚’č§£ę±ŗć™ć‚‹ćŸć‚ć«ć€ä»„äø‹ć®2ē‚¹ć«ē„¦ē‚¹ć‚’å½“ć¦ćŸå®Ÿč£…ć®ć‚¢ćƒ—ćƒ­ćƒ¼ćƒć«ć¤ć„ć¦ć¾ćØć‚ć¾ć—ćŸć€‚ 評侔しやすいこと å‡¦ē†ć‚’ēµ„ćæę›æćˆć‚„ć™ć„ć“ćØ č©•ä¾”ć—ć‚„ć™ć„å®Ÿč£…ć«ć¤ć„ć¦ćÆć€äŗ‹å‰ć«ę­£č§£ć‚’å®šē¾©ć§ćć‚‹ć‚æć‚¹ć‚Æć«ćŠć„ć¦ć€pytestć®ęž ēµ„ćæć‚’ę“»ē”Øć™ć‚‹ć“ćØć§ć€LLMć®å‡ŗåŠ›ć‚’å¾“ę„ć®ć‚½ćƒ•ćƒˆć‚¦ć‚§ć‚¢ćƒ†ć‚¹ćƒˆćØåŒę§˜ć«ę‰±ćˆć‚‹ć“ćØć‚’ē¤ŗć—ć¾ć—ćŸć€‚ ć“ć‚Œć«ć‚ˆć‚Šć€ä½•ć‚‰ć‹ć®å¤‰ę›“ć«ć‚ˆć‚‹å˜ä½“ć®LLMćØć—ć¦č©•ä¾”ć§ćć‚‹ć‚ˆć†ć«ćŖć‚Šć€ę„å›³ć—ćŖć„ęŒ™å‹•å¤‰åŒ–ć‚’ę¤œēŸ„ć™ć‚‹ć“ćØćŒć§ćć¾ć™ć€‚ å‡¦ē†ć‚’ēµ„ćæę›æćˆć‚„ć™ć„å®Ÿč£…ć«ć¤ć„ć¦ćÆć€LangGraphć‚’ę“»ē”Øć—ćŸWorkflowē®”ē†ć«ć‚ˆć‚Šć€č¤‡é›‘ćŖAIć‚Øćƒ¼ć‚øć‚§ćƒ³ćƒˆć®å‡¦ē†ćƒ•ćƒ­ćƒ¼ć‚’ęŸ”č»Ÿć«å¤‰ę›“ć§ćć‚‹ć“ćØć‚’ē¤ŗć—ć¾ć—ćŸć€‚ ćƒ­ć‚øćƒƒć‚ÆéƒØåˆ†ćØćƒÆćƒ¼ć‚Æćƒ•ćƒ­ćƒ¼éƒØåˆ†ć‚’åˆ†é›¢ć™ć‚‹ć“ćØć§ć€ćƒ†ć‚¹ć‚æćƒ“ćƒŖćƒ†ć‚£ć‚’äæć”ćŖćŒć‚‰å‡¦ē†ć®ēµ„ćæę›æćˆć‚’å®Ÿē¾ć§ćć¾ć™ć€‚ AIć‚Øćƒ¼ć‚øć‚§ćƒ³ćƒˆé–‹ē™ŗćÆć€ć¾ć ē™ŗå±•é€”äøŠć®åˆ†é‡Žć§ć‚ć‚Šć€ęœ€é©č§£ćŒč¦‹ćˆć¦ć„ćŖć„éƒØåˆ†ć‚‚å¤šć„ć§ć™ć€‚ ć—ć‹ć—ć€å¾“ę„ć®ć‚½ćƒ•ćƒˆć‚¦ć‚§ć‚¢é–‹ē™ŗć§åŸ¹ć‚ć‚ŒćŸčØ­čØˆåŽŸå‰‡ć‚’é©ē”Øć—ć¤ć¤ć€AIć‚Øćƒ¼ć‚øć‚§ćƒ³ćƒˆå›ŗęœ‰ć®čŖ²é”Œć«åÆ¾åæœć™ć‚‹ć“ćØć§ć€äæå®ˆę€§ćØę‹”å¼µę€§ć‚’å…¼ć­å‚™ćˆćŸć‚·ć‚¹ćƒ†ćƒ ć‚’ę§‹ēÆ‰ć§ćć‚‹ćØč€ƒćˆć¦ć„ć¾ć™ć€‚ ä»Šå¾Œć‚‚ć€AIć‚Øćƒ¼ć‚øć‚§ćƒ³ćƒˆé–‹ē™ŗć®ćƒ™ć‚¹ćƒˆćƒ—ćƒ©ć‚Æćƒ†ć‚£ć‚¹ć‚’ęØ”ē“¢ć—ćŖćŒć‚‰ć€ć‚ˆć‚Šč‰Æć„ć‚¢ćƒ¼ć‚­ćƒ†ć‚Æćƒćƒ£ć®åœØć‚Šę–¹ć‚’čæ½ę±‚ć—ć¦ć„ććŸć„ćØę€ć„ć¾ć™ć€‚
ćÆć˜ć‚ć« ć“ć‚“ć«ć”ćÆļ¼ę Ŗå¼ä¼šē¤¾ć‚Øćƒ–ćƒŖćƒ¼ć§1ćƒ¶ęœˆé–“ iOS ć‚¢ćƒ—ćƒŖć‚Øćƒ³ć‚øćƒ‹ć‚¢ćØć—ć¦ć‚¤ćƒ³ć‚æćƒ¼ćƒ³ć‚’ć—ć¦ć„ć‚‹ē™½äŗ•ć§ć™ć€‚ć“ć®čØ˜äŗ‹ć§ćÆć€ć€Œćƒ˜ćƒ«ć‚·ć‚«ć€ćØć„ć†ćƒ˜ćƒ«ć‚¹ć‚±ć‚¢ć‚¢ćƒ—ćƒŖé–‹ē™ŗć«ćŠć„ć¦å–ć‚Šēµ„ć‚“ć ć€ć€Œć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆę©Ÿčƒ½ć€ć®å®Ÿč£…ć«ć¤ć„ć¦ćŠč©±ć—ć—ćŸć„ćØę€ć„ć¾ć™ć€‚ ćƒ˜ćƒ«ć‚·ć‚«ćØćÆļ¼Ÿ ęœ¬é”Œć«å…„ć‚‹å‰ć«ć€ä»Šå›žé–‹ē™ŗć«å–ć‚Šēµ„ć‚“ć ć€Œćƒ˜ćƒ«ć‚·ć‚«ć€ćØćÆć©ć‚“ćŖć‚¢ćƒ—ćƒŖć§ć—ć‚‡ć†ć‹ļ¼Ÿćƒ˜ćƒ«ć‚·ć‚«ćØćÆć€ćƒ€ć‚¤ć‚Øćƒƒćƒˆć‚„å„åŗ·ć®ćŸć‚ć«ć€ćƒ¦ćƒ¼ć‚¶ćƒ¼ć®é£Ÿäŗ‹ć‚„ä½“é‡ć®čØ˜éŒ²ć‚’ę‰‹åŠ©ć‘ć™ć‚‹ć‚¢ćƒ—ćƒŖć§ć™ć€‚ ćƒ˜ćƒ«ć‚·ć‚« - ćƒ€ć‚¤ć‚Øćƒƒćƒˆļ¼†å„åŗ·ć®ćŸć‚ć®ć‚«ćƒ­ćƒŖćƒ¼ē®”ē† every, Inc. ćƒ˜ćƒ«ć‚¹ć‚±ć‚¢ļ¼ćƒ•ć‚£ćƒƒćƒˆćƒć‚¹ 焔料 ‎「ヘルシカ - ダイエット&健康のためのカロリー管理」をApp Storeで å…·ä½“ēš„ćŖę©Ÿčƒ½ćØć—ć¦ć€ä½“é‡ć®čØ˜éŒ²ć‚„ć€ęœé£Ÿćƒ»ę˜¼é£Ÿćƒ»å¤•é£Ÿćƒ»é–“é£Ÿć®é£Ÿäŗ‹å†…å®¹ć‚’čØ˜éŒ²ć™ć‚‹ć“ćØćŒć§ćć€äø‹ć®ē”»åƒć®ć‚ˆć†ć«ć€ćć®ę—„ć®é£Ÿäŗ‹ć®ę‘‚å–ć‚«ćƒ­ćƒŖćƒ¼ć‚„ć€ē³–č³Ŗć€č„‚č³ŖćŖć©ć®ę „é¤Šē“ ć®ę‘‚å–ēŠ¶ę…‹ć«ć¤ć„ć¦ēŸ„ć‚‹ć“ćØćŒć§ćć¾ć™ļ¼ˆå„ę „é¤Šē“ ć®ęƒ…å ±ćÆćƒ—ćƒ¬ćƒŸć‚¢ćƒ ćƒ¦ćƒ¼ć‚¶ćƒ¼ć®ćæé–²č¦§åÆčƒ½ļ¼‰ć€‚ ć¾ćŸć€ć“ć‚Œć«åŠ ćˆć¦ć€ćƒ—ćƒ¬ćƒŸć‚¢ćƒ ćƒ¦ćƒ¼ć‚¶ćƒ¼ćÆ1é£Ÿåˆ†ć®é£Ÿäŗ‹čØ˜éŒ²ć€ē„”ę–™ćƒ¦ćƒ¼ć‚¶ćƒ¼ćÆ3é£Ÿåˆ†ć®é£Ÿäŗ‹čØ˜éŒ²ć‚’ć™ć‚‹ć“ćØć§ć€ē®”ē†ę „é¤Šå£«ć‹ć‚‰ć®é£Ÿäŗ‹ć®ć‚¢ćƒ‰ćƒć‚¤ć‚¹ć‚’ć‚‚ć‚‰ć†ć“ćØćŒć§ćć€ę—„åøøēš„ć«č‡Ŗåˆ†ć®é£Ÿē”Ÿę“»ć«ć¤ć„ć¦č¦‹ē›“ć™ę‰‹åŠ©ć‘ć‚‚ć—ć¦ćć‚Œć¾ć™ć€‚ čƒŒę™Æ ćć‚“ćŖćƒ˜ćƒ«ć‚·ć‚«ć§ć™ćŒć€ē¾åœØé–‹ē™ŗćƒćƒ¼ćƒ ćÆć€ć€Œćƒ¦ćƒ¼ć‚¶ćƒ¼ć®ē¶™ē¶šēŽ‡å‘äøŠć€ć‚’ē›®ęŒ‡ć—ć€ę–°ę©Ÿčƒ½ć®čæ½åŠ ć‚„ę—¢å­˜ę©Ÿčƒ½ć®ę”¹å–„ć«å–ć‚Šēµ„ć‚“ć§ć„ć¾ć™ć€‚ä»Šå›žć€ćć®ę–½ē­–ć®äø€ć¤ćØć—ć¦ć€ę—„ć€…ć®é£Ÿäŗ‹ć®čØ˜éŒ²ć‚’ć‚ˆć‚Šę—„åøøć«ęŗ¶ć‘č¾¼ć¾ć›ć‚‹ćŸć‚ć® ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆę©Ÿčƒ½ ć‚’é–‹ē™ŗć—ć¾ć—ćŸć€‚ ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆćØćÆļ¼Ÿ ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆćØćÆć€ć‚¹ćƒžćƒ¼ćƒˆćƒ•ć‚©ćƒ³ć®ćƒ›ćƒ¼ćƒ ē”»é¢ć«čæ½åŠ ć§ćć‚‹ć‚³ćƒ³ćƒćƒ¼ćƒćƒ³ćƒˆć®ć“ćØć§ć™ć€‚ć“ć‚Œć«ć‚ˆć‚Šć€ćƒ¦ćƒ¼ć‚¶ćƒ¼ćÆć‚¢ćƒ—ćƒŖć‚’čµ·å‹•ć—ćŖćć¦ć‚‚ć€ä»Šę—„ć®äŗˆå®šćŖć©ć®ćƒ‡ćƒ¼ć‚æć‚’ćƒ›ćƒ¼ćƒ ē”»é¢ć‹ć‚‰ē›“ęŽ„ē¢ŗčŖć§ćć‚‹ć‚ˆć†ć«ćŖć£ćŸć‚Šć€ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆć‚’ć‚æćƒƒćƒ—ć™ć‚‹ć ć‘ć§ć‚¢ćƒ—ćƒŖå†…ć®ē‰¹å®šć®ē”»é¢ć«ē“ ę—©ćē§»å‹•ć§ćć‚‹ć‚ˆć†ć«ćŖć‚Šć¾ć™ć€‚ ä»Šå›žé–‹ē™ŗć—ćŸć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆć®2恤恮要件 ä»Šå›žćƒ˜ćƒ«ć‚·ć‚«ć§é–‹ē™ŗć—ćŸć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆćÆć€é£Ÿäŗ‹čØ˜éŒ²ć‚’ēæ’ę…£åŒ–ć—ć‚„ć™ćć™ć‚‹ćŸć‚ć«ć€ä»„äø‹ć®2ć¤ć®č¦ä»¶ć‚’ēµ„ćæč¾¼ćæć¾ć—ćŸć€‚ é£Ÿäŗ‹čØ˜éŒ²ē”»é¢ćøć®ē›“ęŽ„é·ē§» ļ¼šćƒ›ćƒ¼ćƒ ē”»é¢ć‹ć‚‰ćƒÆćƒ³ć‚æćƒƒćƒ—ć§é£Ÿäŗ‹čØ˜éŒ²ē”»é¢ć«é·ē§»ć—ć€ć™ćć«čØ˜éŒ²ć‚’é–‹å§‹ć§ćć‚‹ ē·ę‘‚å–ć‚«ćƒ­ćƒŖćƒ¼ć®åøøę™‚č”Øē¤ŗ ļ¼šäø€ę—„ć®ē·ę‘‚å–ć‚«ćƒ­ćƒŖćƒ¼ć‚’ćƒ›ćƒ¼ćƒ ē”»é¢ć«åøøć«č”Øē¤ŗć—ć€ćƒ›ćƒ¼ćƒ ē”»é¢ć‚’č¦‹ć‚‹ć ć‘ć§ē¾åœØć®ć‚«ćƒ­ćƒŖćƒ¼ēŠ¶ę³ć‚’ć™ćć«ęŠŠę”ć§ćć‚‹ ć“ć‚Œć‚‰ć®ę©Ÿčƒ½ć«ć‚ˆć‚Šć€ćƒ˜ćƒ«ć‚·ć‚«ć®ćƒ”ć‚¤ćƒ³ę©Ÿčƒ½ć§ć‚ć‚‹ć€Œé£Ÿäŗ‹čØ˜éŒ²ć€ćŒćƒ¦ćƒ¼ć‚¶ćƒ¼ć«ćØć£ć¦ć‚ˆć‚Šę‰‹č»½ć«ćŖć‚Šć€ęŒÆć‚Ščæ”ć‚Šć‚‚ē°”å˜ć«ćŖć‚‹ć“ćØćŒęœŸå¾…ć•ć‚Œć€ć“ć®ä½“éØ“ć‚’é€šć˜ć¦ć€ćƒ¦ćƒ¼ć‚¶ćƒ¼ćŒé£Ÿäŗ‹čØ˜éŒ²ć‚’ć‚ˆć‚Šę—„åøøć®ēæ’ę…£ćØć—ć¦ē¶™ē¶šć—ć‚„ć™ććŖć‚‹ć“ćØć‚’ē›®ęŒ‡ć—ć¦ć„ć¾ć™ć€‚ ä»„äø‹ćŒć€ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆć®å®Ÿéš›ć®ćƒ‡ć‚¶ć‚¤ćƒ³ć§ć™ļ¼ ć§ćÆć€ęœ¬é”Œć§ć‚ć‚‹ćć‚Œćžć‚Œć®ę©Ÿčƒ½ć®å®Ÿč£…ć«ć¤ć„ć¦č¦‹ć¦ć„ćć¾ć—ć‚‡ć†ć€‚ 注: ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆć®åŸŗęœ¬ēš„ćŖå®Ÿč£…ę–¹ę³•ć«ć¤ć„ć¦ćÆć“ć®čØ˜äŗ‹ć§ćÆć‚ć¾ć‚Šč§¦ć‚Œć¾ć›ć‚“ć€‚ä»£ć‚ć‚Šć«ć“ć”ć‚‰ć®čØ˜äŗ‹ć‚’ć”å‚ē…§ćć ć•ć„ć€‚ iOS版デリッシュキッチンにウィジェット機能を追加しました - every Tech Blog é£Ÿäŗ‹čØ˜éŒ²ē”»é¢ćøć®ē›“ęŽ„é·ē§» ć¾ćšć€ć€Œé£Ÿäŗ‹čØ˜éŒ²ē”»é¢ćøć®ē›“ęŽ„é·ē§»ć€ć«ć¤ć„ć¦ć§ć™ć€‚ć“ć®ę©Ÿčƒ½ć‚’å®Ÿč£…ć™ć‚‹ć«ćÆć€ DeepLink ćØć„ć†ä»•ēµ„ćæć‚’åˆ©ē”Øć—ć¾ć™ć€‚č©³ē“°ć«ćÆč§¦ć‚Œć¾ć›ć‚“ćŒć€DeepLinkćÆć€ć‚¦ć‚§ćƒ–ć‚µć‚¤ćƒˆć®URLć®ć‚ˆć†ć«ć€ć‚¢ćƒ—ćƒŖå†…ć®ē‰¹å®šć®ē”»é¢ćøē›“ęŽ„ćƒ¦ćƒ¼ć‚¶ćƒ¼ć‚’čŖ˜å°Žć™ć‚‹ę©Ÿčƒ½ć§ć™ć€‚ å®Ÿč£…ć®ćƒ—ćƒ­ć‚»ć‚¹ ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆć§ DeepLink ć®ę©Ÿčƒ½ć‚’åˆ©ē”Øć™ć‚‹ćŸć‚ć«ćÆć€ä»„äø‹ć®2ć¤ć®ć‚¹ćƒ†ćƒƒćƒ—ćŒåæ…č¦ć§ć™ć€‚ ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆå“ć§ć® Link の設定 : ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆå†…ć§ć€å„é£Ÿäŗ‹ć‚æć‚¤ćƒ—ļ¼ˆęœé£Ÿćƒ»ę˜¼é£Ÿćƒ»å¤•é£Ÿćƒ»é–“é£Ÿļ¼‰ć«åÆ¾åæœć™ć‚‹ URL ć‚’čØ­å®šć—ć€ćć‚Œć‚‰ć® URL を用いて Link という UI ć‚³ćƒ³ćƒćƒ¼ćƒćƒ³ćƒˆć‚’ē”Øę„ć—ć¾ć™ć€‚ć“ć‚Œć«ć‚ˆć‚Šć€ćƒ¦ćƒ¼ć‚¶ćƒ¼ćŒ Link ć‚’ć‚æćƒƒćƒ—ć—ćŸå “åˆć€ćć®ć‚æćƒƒćƒ—ćŒē‰¹å®šć® URL ć‚’å‘¼ć³å‡ŗć™ćƒˆćƒŖć‚¬ćƒ¼ćØćŖć‚Šć¾ć™ć€‚ ć‚¢ćƒ—ćƒŖå“ć§ć® DeepLink ć®ćƒćƒ³ćƒ‰ćƒŖćƒ³ć‚° : ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆ ć‹ć‚‰é€ć‚‰ć‚Œć¦ććŸ DeepLink ć‚’ę¤œēŸ„ć™ć‚‹ćŸć‚ć«ć€ć‚¢ćƒ—ćƒŖć® SceneDelegate ć§ć®å‡¦ē†ć®čØ­å®šć‚’č”Œć„ć¾ć™ć€‚ćƒ˜ćƒ«ć‚·ć‚«ć§ćÆć€NotificationCenter ć‚’ä½æć£ć¦ć€é£Ÿäŗ‹čØ˜éŒ²ē”»é¢ć®ē®”ē†ć®č²¬å‹™ć‚’ęŒć¤ ViewController ć«é€šēŸ„ć‚’é€ć‚‹ć“ćØć§ć€ē›®ēš„ć®ē”»é¢ć‚’é–‹ćć‚ˆć†ć«ć—ć¦ć„ć¾ć™ć€‚ ć¤ć¾ć‚Šć€ä»„äø‹ć®å›³ć®ć‚ˆć†ćŖęµć‚Œć§ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆć‹ć‚‰ć‚¢ćƒ—ćƒŖćøćØé·ē§»ć—ć€é£Ÿäŗ‹čØ˜éŒ²ē”»é¢ć‚’é–‹ćć“ćØć‚’å®Ÿē¾ć—ć¦ć„ć¾ć™ć€‚ ć‚³ćƒ¼ćƒ‰ä¾‹ćÆä»„äø‹ć§ć™ļ¼ˆå®Ÿéš›ć®ć‚³ćƒ¼ćƒ‰ćØćÆē•°ćŖć‚Šē°”ē•„åŒ–ć—ć¦ć„ć¾ć™ļ¼‰ć€‚ ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆ(Link) HStack { Link(destination: URL(string: "https://myapp.com/record?type=morning")!) { // UIć‚’å®šē¾©ć™ć‚‹ć‚³ćƒ¼ćƒ‰ } Link(destination: URL(string: "https://myapp.com/record?type=lunch")!) { // UIć‚’å®šē¾©ć™ć‚‹ć‚³ćƒ¼ćƒ‰ } // å¤•é£Ÿć® Link ... } ć‚¢ćƒ—ćƒŖ (SceneDelegate, NotificaitonCenter) class SceneDelegate: UIResponder, UIWindowSceneDelegate { // ... func scene(_: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) { for urlContext in URLContexts { // 恓恓恮 DeepLink ćÆć‚«ć‚¹ć‚æćƒ ć® enum 恧恂悊态 url ć‚’ę‰±ć„ć‚„ć™ćć—ć¦ć„ć‚‹ć€‚ guard let deepLink = DeepLink(url: urlContext.url) else { continue } switch deepLink { // ćć®ä»–ć® DeepLink ć®ćƒćƒ³ćƒ‰ćƒŖćƒ³ć‚° // ... case .mealRecord(let type): NotificationCenter.default.post( name: .widgetMealRecordEditRequest, object: nil, userInfo: ["mealType": type] ) } } } // ... } ć“ć®ć‚ˆć†ć«ć€Œé£Ÿäŗ‹čØ˜éŒ²ē”»é¢ćøć®ē›“ęŽ„é·ē§»ć€ć®å®Ÿč£…ćÆć€ęÆ”č¼ƒēš„ć‚·ćƒ³ćƒ—ćƒ«ć«ēµ‚ćˆć‚‹ć“ćØćŒć§ćć¾ć—ćŸć€‚ ē·ę‘‚å–ć‚«ćƒ­ćƒŖćƒ¼ć®åøøę™‚č”Øē¤ŗ ę¬”ć«ć€ä»Šå›žć®å®Ÿč£…ć§é¢ē™½ć„éƒØåˆ†ć§ć‚ć‚‹ć€Œ1ę—„ć®ē·ę‘‚å–ć‚«ćƒ­ćƒŖćƒ¼ć®åøøę™‚č”Øē¤ŗć€ć«ć¤ć„ć¦č§£čŖ¬ć—ć¾ć™ć€‚ åˆ„ćƒ—ćƒ­ć‚»ć‚¹ć§å‹•ćć‚¢ćƒ—ćƒŖćØć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆ äø€č¦‹ć€ć€Œę‘‚å–ć‚«ćƒ­ćƒŖćƒ¼ć‚’č”Øē¤ŗć™ć‚‹ć€ćØć„ć†ę©Ÿčƒ½ćÆéžåøøć«ć‚·ćƒ³ćƒ—ćƒ«ć«č¦‹ćˆć€ć‚¢ćƒ—ćƒŖå“ć§å–å¾—ć—ć¦ć„ć‚‹ę‘‚å–ć‚«ćƒ­ćƒŖćƒ¼ć®ćƒ‡ćƒ¼ć‚æć‚’ćć®ć¾ć¾ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆć§åˆ©ē”Øć™ć‚Œć°č‰Æć„ć ć‘ćŖć®ć§ćÆļ¼ŸćØę€ć‚ć‚Œć‚‹ć‹ć‚‚ć—ć‚Œć¾ć›ć‚“ć€‚ć—ć‹ć—ć€ä»„äø‹ć®ē†ē”±ć‹ć‚‰ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆćÆć‚¢ćƒ—ćƒŖć®ćƒ‡ćƒ¼ć‚æć‚’ē›“ęŽ„åˆ©ē”Øć™ć‚‹ć“ćØćÆć§ćć¾ć›ć‚“ć€‚ ć‚¢ćƒ—ćƒŖćØć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆćÆ ćć‚Œćžć‚Œē‹¬ē«‹ćŖē’°å¢ƒć‚’ęŒć¤ å…±ęœ‰ć§ććŖć„ć‚¤ćƒ³ćƒ”ćƒ¢ćƒŖćŖćƒ‡ćƒ¼ć‚æ ć‚¢ćƒ—ćƒŖćØć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆćŒ ćć‚Œćžć‚Œē‹¬ē«‹ćŖē’°å¢ƒć‚’ęŒć¤ ć“ćØćÆć€ä»Šå›žć®å®Ÿč£…ć«ćŠć„ć¦ä½•ćŒå•é”ŒćŖć®ć§ć—ć‚‡ć†ć‹ļ¼Ÿē‹¬ē«‹ćŖē’°å¢ƒć‚’ęŒć¤ćØć„ć†ć“ćØćÆć€ ćƒ©ć‚¤ćƒ•ć‚µć‚¤ć‚Æćƒ«ē®”ē†ć‚„ćƒ—ćƒ­ć‚»ć‚¹ć®ē®”ē†ćŒć‚¢ćƒ—ćƒŖćØć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆć§ē‹¬ē«‹ć—ć¦ć„ć‚‹ ćØć„ć†ć“ćØć§ć™ć€‚ćć—ć¦ć€ćƒ—ćƒ­ć‚»ć‚¹ćÆćć‚Œćžć‚Œć§ē‹¬ē«‹ć—ćŸćƒ”ćƒ¢ćƒŖē©ŗé–“ć‚’ęŒć¤ćŸć‚ć€ ćƒ—ćƒ­ć‚»ć‚¹é–“ć§ē›“ęŽ„ćƒ‡ćƒ¼ć‚æć‚’å…±ęœ‰ć™ć‚‹ć“ćØćÆ ć§ćć¾ć›ć‚“ć€‚ćć®ćŸć‚ć€åˆ„ćƒ—ćƒ­ć‚»ć‚¹äøŠć§å‹•ćć‚¢ćƒ—ćƒŖć®å–å¾—ć—ćŸę‘‚å–ć‚«ćƒ­ćƒŖćƒ¼ć®ćƒ‡ćƒ¼ć‚æć‚’ć€ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆćÆē”Øć„ć‚‹ć“ćØćÆć§ćć¾ć›ć‚“ć€‚ ć¾ćŸć€ć‚¤ćƒ³ćƒ”ćƒ¢ćƒŖćŖćƒ‡ćƒ¼ć‚æć§ć‚ć‚‹ę‘‚å–ć‚«ćƒ­ćƒŖćƒ¼ć®ćƒ‡ćƒ¼ć‚æćÆć€ć‚¢ćƒ—ćƒŖć‚’é–‰ć˜ć¦ć—ć¾ć†ćØę¶ˆćˆć¦ć—ć¾ć†ę®ē™ŗę€§ć®ćƒ‡ćƒ¼ć‚æć§ć‚‚ć‚ć‚‹ćŸć‚ć€ćć‚‚ćć‚‚åŒć˜ćƒ—ćƒ­ć‚»ć‚¹äøŠć§å‹•ć„ć¦ć„ćŸćØć—ć¦ć‚‚ć€ć‚¢ćƒ—ćƒŖć‚’é–‰ć˜ć‚‹ćØę‘‚å–ć‚«ćƒ­ćƒŖćƒ¼ć®ćƒ‡ćƒ¼ć‚æćŒå¤±ć‚ć‚Œć€ćć®ćƒ‡ćƒ¼ć‚æć‚’ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆć«č”Øē¤ŗć§ććŖććŖć£ć¦ć—ć¾ć„ć¾ć™ć€‚ čŖčØ¼ć‚’åæ…č¦ćØć™ć‚‹ćƒ‡ćƒ¼ć‚æå–å¾—ćƒ—ćƒ­ć‚»ć‚¹ ć§ćÆć€ć©ć†ć™ć‚Œć°ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆć§ę‘‚å–ć‚«ćƒ­ćƒŖćƒ¼ć®ćƒ‡ćƒ¼ć‚æć‚’å–å¾—ć§ćć‚‹ć§ć—ć‚‡ć†ć‹ļ¼Ÿä»Šåŗ¦ćÆć€ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆćŒē›“ęŽ„APIć‚’å‘¼ć³å‡ŗć—ć¦ćƒ‡ćƒ¼ć‚æć‚’å–å¾—ć™ć‚Œć°č‰Æć„ć®ć§ćÆļ¼Ÿ ćØę€ć†ć‹ć‚‚ć—ć‚Œć¾ć›ć‚“ć€‚ć—ć‹ć—ć€ć“ć‚Œć«ć‚‚å¤§ććŖčŖ²é”ŒćŒć‚ć‚Šć¾ć™ć€‚ č”Øē¤ŗć—ćŸć„ćƒ‡ćƒ¼ć‚æć«ē€ē›®ć—ć¦ćæć¾ć—ć‚‡ć†ć€‚ä»Šå›žč”Øē¤ŗć—ćŸć„ę‘‚å–ć‚«ćƒ­ćƒŖćƒ¼ć®ćƒ‡ćƒ¼ć‚æćÆć€ćƒ¦ćƒ¼ć‚¶ćƒ¼ćŒå€‹åˆ„ć§čØ˜éŒ²ć—ć¦ć„ć‚‹ å€‹äŗŗęƒ…å ± ć§ć™ć€‚ćć®ćŸć‚ć€ćƒ—ćƒ©ć‚¤ćƒć‚·ćƒ¼ć®č¦³ē‚¹ć‹ć‚‰ć€API ć§ćƒ‡ćƒ¼ć‚æć‚’å–å¾—ć™ć‚‹ć«ćÆć€ćƒ¦ćƒ¼ć‚¶ćƒ¼ćŒćć®ćƒ‡ćƒ¼ć‚æć«ć‚¢ć‚Æć‚»ć‚¹ć—ć¦č‰Æć„ć‹ć‚’ē¢ŗčŖć™ć‚‹ čŖčØ¼ć®ćƒ—ćƒ­ć‚»ć‚¹ ćŒåæ…č¦ćØćŖć‚Šć¾ć™ć€‚ć—ć‹ć—ć€čŖčØ¼ć«åæ…č¦ćŖćƒ‡ćƒ¼ć‚æćÆć€ē¾åœØć‚¢ćƒ—ćƒŖć«ć‚ˆć£ć¦ē®”ē†ć•ć‚Œć¦ć„ć‚‹ćŸć‚ć€ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆćÆčŖčØ¼ć«åæ…č¦ćŖćƒ‡ćƒ¼ć‚æć«ć‚¢ć‚Æć‚»ć‚¹ćŒć§ććšć€ę‘‚å–ć‚«ćƒ­ćƒŖćƒ¼ć®ćƒ‡ćƒ¼ć‚æć‚’å–å¾—ć™ć‚‹ć“ćØćŒć§ćć¾ć›ć‚“ļ¼ˆäø‹å›³ć‚’å‚ē…§ļ¼‰ć€‚ć§ćÆć€ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆćÆć©ć®ć‚ˆć†ć«ę‘‚å–ć‚«ćƒ­ćƒŖćƒ¼ć®ćƒ‡ćƒ¼ć‚æć‚’å–å¾—ć™ć‚Œć°č‰Æć„ć®ć§ć—ć‚‡ć†ć‹ļ¼Ÿ č§£ę±ŗę³•ā‘ ļ¼šKeychain ć«ć‚ˆć‚‹ćƒ‡ćƒ¼ć‚æć®ē›“ęŽ„å…±ęœ‰ å˜ē“”ćŖč§£ę±ŗę–¹ę³•ćØć—ć¦ćÆć€Apple ć‹ć‚‰ęä¾›ć•ć‚Œć¦ć„ć‚‹ Keychain ćØć„ć†ćƒ‡ćƒ¼ć‚æćƒ™ćƒ¼ć‚¹ć‚’é€šć˜ć¦ć€ć‚¢ćƒ—ćƒŖćŒå–å¾—ć—ćŸę‘‚å–ć‚«ćƒ­ćƒŖćƒ¼ć®ćƒ‡ćƒ¼ć‚æć‚’ćć®ć¾ć¾ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆć«å…±ęœ‰ć™ć‚‹ ę–¹ę³•ćŒć‚ć‚Šć¾ć™ļ¼ˆäø‹å›³ć‚’å‚ē…§ļ¼‰ć€‚Keychain ć§ćÆć€ćƒ‡ćƒ¼ć‚æćÆć™ć¹ć¦ęš—å·åŒ–ć•ć‚Œć¦ć‹ć‚‰äæå­˜ć•ć‚Œć‚‹ćŸć‚ć€å€‹äŗŗęƒ…å ±ć®äæå­˜ć‚‚å•é”Œć‚ć‚Šć¾ć›ć‚“ć€‚ć—ć‹ć—ć€ć“ć®ę–¹ę³•ć«ćÆå•é”Œē‚¹ćŒć‚ć‚Šć¾ć™ć€‚ć“ć®ę–¹ę³•ć§ćÆć€ ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆćÆćƒ‡ćƒ¼ć‚æć®čŖ­ćæå–ć‚Šć®ćæć‚’č”Œć„ć€API ć®å‘¼ć³å‡ŗć—ćÆå…Øć¦ć‚¢ćƒ—ćƒŖå“ ć§č”Œć‚ć‚Œć¾ć™ć€‚ćć®ćŸć‚ć€ćƒ‡ćƒ¼ć‚æć®ę›“ę–°ćŒć‚¢ćƒ—ćƒŖć«ä¾å­˜ć—ć€ć‚¢ćƒ—ćƒŖć§ć®ę›“ę–°ćŒčµ·ććŖć‘ć‚Œć°ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆć®ćƒ‡ćƒ¼ć‚æćÆåøøć«å¤ć„ć¾ć¾ć«ćŖć£ć¦ć—ć¾ć„ć¾ć™ć€‚ę›“ę–°ćŒć‚ć¾ć‚Šåæ…č¦ć®ćŖć„ćƒ‡ćƒ¼ć‚æć§ć‚ć‚Œć°ć“ć‚ŒćÆć‚ć¾ć‚Šå•é”Œć«ćÆćŖć‚‰ćŖć„ć‹ć‚‚ć—ć‚Œć¾ć›ć‚“ćŒć€ćƒ˜ćƒ«ć‚·ć‚«ć®å “åˆć€ć‚«ćƒ­ćƒŖćƒ¼ć®ćƒ‡ćƒ¼ć‚æćÆę—„ä»˜ćŒå¤‰ć‚ć£ćŸć‚æć‚¤ćƒŸćƒ³ć‚°ć§ę›“ę–°ć•ć‚Œć‚‹åæ…č¦ćŒć‚ć‚Šć¾ć™ć€‚ćć®ćŸć‚ć€ę›“ę–°ć®ć‚æć‚¤ćƒŸćƒ³ć‚°ćŒć‚¢ćƒ—ćƒŖć«ä¾å­˜ć—ć€å¤ć„ćƒ‡ćƒ¼ć‚æćŒč”Øē¤ŗć•ć‚Œć‚‹åÆčƒ½ę€§ć‚‚ć‚ć‚‹ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆćÆć€ćƒ˜ćƒ«ć‚·ć‚«ć®ćƒ¦ćƒ¼ć‚¶ćƒ¼ä½“éØ“ćØć—ć¦ęœ›ć¾ć—ć„ć‚‚ć®ć§ćÆć‚ć‚Šć¾ć›ć‚“ć€‚ ę³Øļ¼šå›³ć§ćÆć€ć‚¢ćƒ—ćƒŖćØ Keychain ćØć®čŖčØ¼ćƒ‡ćƒ¼ć‚æć®å—ć‘ęø”ć—ć®ćƒ•ćƒ­ćƒ¼ć‚’ēœē•„ć—ć¦ć„ć¾ć™ćŒć€ć“ć®å “åˆć§ć‚‚ć‚¢ćƒ—ćƒŖćØ Keychain é–“ć§čŖčØ¼ćƒ‡ćƒ¼ć‚æć®å—ć‘ęø”ć—ćÆč”Œć‚ć‚Œć¦ć„ć¾ć™ć€‚ č§£ę±ŗę³•ā‘”ļ¼šKeychain ć«ć‚ˆć‚‹čŖčØ¼é–¢é€£ćƒ‡ćƒ¼ć‚æć®å…±ęœ‰ ćć“ć§ć€ä»Šå›žęŽ”ē”Øć—ćŸč§£ę±ŗę‰‹ę³•ćÆć€ Keychain ć‚’é€šć˜ć¦čŖčØ¼ć«åæ…č¦ćŖćƒ‡ćƒ¼ć‚æć‚’å…±ęœ‰ć—ć€ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆć‹ć‚‰čŖčØ¼ä»˜ćć® API ć‚’ē›“ęŽ„å‘¼ć³å‡ŗć™ ę‰‹ę³•ć§ć™ļ¼ˆäø‹å›³ć‚’å‚ē…§ļ¼‰ć€‚ć“ć®ę‰‹ę³•ć‚’ē”Øć„ć‚‹ć“ćØć§ć€å…ˆć»ć©ć®ćƒ‡ćƒ¼ć‚æć®ę›“ę–°ć‚æć‚¤ćƒŸćƒ³ć‚°ćŒć‚¢ćƒ—ćƒŖć«ä¾å­˜ć™ć‚‹ćØć„ć†å•é”Œē‚¹ć‚’č§£ę±ŗć—ć¤ć¤ć€ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆć§åøøć«ęœ€ę–°ćƒ‡ćƒ¼ć‚æć‚’å–å¾—ć™ć‚‹ć“ćØć‚‚åÆčƒ½ć«ćŖć‚Šć¾ć™ć€‚ č§£ę±ŗę³•ā‘”ć®å®Ÿč£… では、Keychain ć‚’åˆ©ē”Øć—ćŸć‚¢ćƒ—ćƒŖćƒ»ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆé–“ć®ćƒ‡ćƒ¼ć‚æå…±ęœ‰ć®å®Ÿč£…ę–¹ę³•ć«ć¤ć„ć¦ćæć¦ć„ćć¾ć—ć‚‡ć†ć€‚Keychain ć‚’é€šć˜ć¦ćƒ‡ćƒ¼ć‚æć‚’å…±ęœ‰ć™ć‚‹ćŸć‚ć«ćÆć€ Keychain Sharing ćØć„ć†ę©Ÿčƒ½ć‚’ä½æē”Øć—ć¾ć™ć€‚ Keychain Sharing とは? Keychain Sharing ćÆć€ćć®åć®é€šć‚Šć€ č¤‡ę•°ć®ć‚¢ćƒ—ćƒŖé–“ć«ćŠć„ć¦ Keychain ć®ć‚¢ć‚¤ćƒ†ćƒ ć‚’å…±ęœ‰ć™ć‚‹ę–¹ę³• ć®ć“ćØć§ć™ć€‚å‰ęćØć—ć¦ć€ćƒ‡ćƒ•ć‚©ćƒ«ćƒˆć§ćÆć€å„ć‚¢ćƒ—ćƒŖć”ćØć«äø€ć¤ć® Keychain ć‚’ē®”ē†ć™ć‚‹é ˜åŸŸ (Keychain Group) ćŒäøŽćˆć‚‰ć‚Œć¦ćŠć‚Šć€ä»–ć®ć‚¢ćƒ—ćƒŖćÆåŽŸå‰‡ćć®é ˜åŸŸć®ćƒ‡ćƒ¼ć‚æć‚’č¦‹ć‚‹ć“ćØćÆć§ćć¾ć›ć‚“ć€‚ć—ć‹ć—ć€Keychain Sharing ć‚’ē”Øć„ć¦č¤‡ę•°ć‚¢ćƒ—ćƒŖé–“ć§å…±ęœ‰åÆčƒ½ćŖ Keychain Group ć‚’ä½œęˆć™ć‚‹ć“ćØć«ć‚ˆć‚Šć€č¤‡ę•°ć®ć‚¢ćƒ—ćƒŖć§å®‰å…Øć«ćƒ‡ćƒ¼ć‚æć®å…±ęœ‰ćŒć§ćć‚‹ć‚ˆć†ć«ćŖć‚Šć¾ć™ć€‚ Keychain Sharing の設定 Keychain Sharing ć‚’ä½æē”Øć™ć‚‹ć«ćÆć€ć‚¢ćƒ—ćƒŖćƒ»ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆäø”ę–¹ć«ćŠć„ć¦ć€Keychain Sharing ć®čØ­å®šć‚’č”Œć†åæ…č¦ćŒć‚ć‚Šć¾ć™ć€‚ä»„äø‹ć®ć‚¹ćƒ†ćƒƒćƒ—ć§čØ­å®šć‚’č”Œć„ć¾ć—ć‚‡ć†ć€‚ ć‚æćƒ¼ć‚²ćƒƒćƒˆć®ćƒŖć‚¹ćƒˆć‹ć‚‰ć‚¢ćƒ—ćƒŖļ¼ˆć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆļ¼‰ć®ć‚æćƒ¼ć‚²ćƒƒćƒˆć‚’éøęŠžć—ć€ā€Signing & Capabilitiesā€ ć‚æćƒ–ć‚’ć‚ÆćƒŖćƒƒć‚Æć—ć¾ć™ć€‚ 左上の ā€œ+ Capabilityā€ (2025/09/11 ē¾åœØ) ć‚’ć‚æćƒƒćƒ—ć—ć€ā€Keychain Sharingā€ ć‚’éøęŠžć€čæ½åŠ ć—ć¾ć™ć€‚ ā€Signing & Capabilitiesā€ ć‚æćƒ–å†…ć«ć€Keychain Sharing ć®ć‚»ć‚Æć‚·ćƒ§ćƒ³ćŒå‡ŗē¾ć—ć€Keychain Groups ćŒčØ­å®šć§ćć‚‹ć‚ˆć†ć«ćŖć‚Šć¾ć™ć€‚ Keychain Groups 恮 + ćƒœć‚æćƒ³ć‚’ć‚æćƒƒćƒ—ć—ć€å…±ęœ‰ć—ćŸć„ Keychain Group åć‚’å…„åŠ›ć—ć¾ć™ć€‚(ę—¢å­˜ć® Group ć§ć‚‚ć€ę–°č¦ć§ä½œęˆć™ć‚‹ Group ć§ć‚‚ć©ć”ć‚‰ć§ć‚‚å…±ęœ‰åÆčƒ½ć§ć™ć€‚) ć“ć®čØ­å®šć‚’ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆć€ć‚¢ćƒ—ćƒŖć®äø”ę–¹ć§č”Œć„ć€åŒäø€ć® Keychain Group ć‚’čØ­å®šć™ć‚‹ć“ćØć§ć€ć‚¢ćƒ—ćƒŖćƒ»ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆé–“ć§ć® Keychain ć®ćƒ‡ćƒ¼ć‚æć®å…±ęœ‰ćŒåÆčƒ½ć«ćŖć‚Šć¾ć™ć€‚ Keychain Group ć‹ć‚‰ć®å–å¾— Keychain Sharing ć®čØ­å®šćŒå®Œäŗ†ć—ćŸć‚‰ć€å®Ÿéš›ć« Keychain ć‹ć‚‰ćƒ‡ćƒ¼ć‚æć‚’å–å¾—ć™ć‚‹å‡¦ē†ć‚’å®Ÿč£…ć—ć¾ć™ć€‚OSSć®ćƒ©ć‚¤ćƒ–ćƒ©ćƒŖć‚‚å­˜åœØć—ć¾ć™ćŒć€ć“ć“ć§ćÆćƒć‚¤ćƒ†ć‚£ćƒ–ć§ć®å®Ÿč£…ę–¹ę³•ćØć—ć¦ć€ćƒ‡ćƒ¼ć‚æć®ć€Œčæ½åŠ ć€ć‚’äø€ä¾‹ć«č§£čŖ¬ć—ć¾ć™ć€‚ ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ć‚’äæå­˜ć™ć‚‹ć‚±ćƒ¼ć‚¹ć‚’č€ƒćˆć¾ć—ć‚‡ć†ć€‚Keychain ćøć®ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰äæå­˜ćÆć€Apple ćŒęä¾›ć™ć‚‹ SecItemAdd ćƒ”ć‚½ćƒƒćƒ‰ć«ć‚ˆć£ć¦å®Ÿē¾ć§ćć¾ć™ć€‚å…·ä½“ēš„ć«ćÆć€ä»„äø‹ć®ć‚³ćƒ¼ćƒ‰ć®ć‚ˆć†ć«ć€ć¾ćšäæå­˜ć™ć‚‹ćƒ‡ćƒ¼ć‚æć®å±žę€§ć‚’å®šē¾©ć™ć‚‹ć‚Æć‚ØćƒŖć‚’ä½œęˆć—ć€ć“ć®ć‚Æć‚ØćƒŖć‚’ä½æć£ć¦å…ˆć»ć©ć®ćƒ”ć‚½ćƒƒćƒ‰ć‚’å‘¼ć³å‡ŗć™ć“ćØć«ć‚ˆć£ć¦ć€Keychain ć«ć‚¢ć‚¤ćƒ†ćƒ ć‚’čæ½åŠ ć§ćć¾ć™ć€‚ć¾ćŸć€Keychain Sharing ć§ęŒ‡å®šć—ćŸ Keychain Group ć‚’ć‚Æć‚ØćƒŖć«čØ­å®šć™ć‚‹ć“ćØć§ć€ć‚¢ćƒ—ćƒŖćØć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆć§å…±ęœ‰ć•ć‚Œć¦ć„ć‚‹ Keychain Group ćøć®ę›øćč¾¼ćæćŒåÆčƒ½ć«ćŖć‚Šć¾ć™ć€‚ import Foundation import Security func savePasswordToKeychain (service : String , account : String , data : Data , accessGroup : String ) { // äæå­˜ć™ć‚‹ćƒ‡ćƒ¼ć‚æć®å±žę€§ć‚’å®šē¾©ć™ć‚‹ć‚Æć‚ØćƒŖ let query : [ String : Any ] = [ kSecClass as String : kSecClassGenericPassword , // äæå­˜ć™ć‚‹ćƒ‡ćƒ¼ć‚æć®ēØ®é”žć‚’ęŒ‡å®š(ä»Šå›žćÆćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰) kSecAttrAccount as String : account , // ćƒ¦ćƒ¼ć‚¶ćƒ¼ć‚’åŒŗåˆ„ć™ć‚‹ćŸć‚ć®ć‚¢ć‚«ć‚¦ćƒ³ćƒˆå kSecAttrAccessGroup as String : accessGroup , // äæå­˜å…ˆć®ć‚¢ć‚Æć‚»ć‚¹ć‚°ćƒ«ćƒ¼ćƒ— kSecValueData as String : data // å®Ÿéš›ć«äæå­˜ć™ć‚‹ćƒ‡ćƒ¼ć‚æ ] // ć‚Æć‚ØćƒŖć‚’ä½æē”Øć—ć¦Keychainć«ć‚¢ć‚¤ćƒ†ćƒ ć‚’čæ½åŠ  let status = SecItemAdd(query as CFDictionary , nil ) switch status { // ć‚Øćƒ©ćƒ¼ćƒćƒ³ćƒ‰ćƒŖćƒ³ć‚°ćŖć©ć‚’čØ˜čæ° } } ć“ć‚ŒćØåŒę§˜ć«ć€ć€Œć‚¢ć‚¤ćƒ†ćƒ ć®å‰Šé™¤ć€ć€ć€Œć‚¢ć‚¤ćƒ†ćƒ ć®ę›“ę–°ć€ć€ć€Œć‚¢ć‚¤ćƒ†ćƒ ć®ę¤œē“¢ć€ć«ć¤ć„ć¦ć‚‚å®Ÿč£…ć™ć‚‹ć“ćØć§ć€Keychain å†…ć®ćƒ‡ćƒ¼ć‚æć®ē®”ē†ćŒć§ćć‚‹ć‚ˆć†ć«ćŖć‚Šć¾ć™ć€‚ ć“ć‚Œć‚‰ć®čØ­å®šć‚„å®Ÿč£…ć‚’ć™ć¹ć¦ēµ‚ćˆć‚‹ć“ćØć§ć€čŖčØ¼é–¢é€£ć®ćƒ‡ćƒ¼ć‚æć‚’ć‚¢ćƒ—ćƒŖćØć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆć§å…±ęœ‰ć§ćć€ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆć‹ć‚‰ć§ć‚‚ę‘‚å–ć‚«ćƒ­ćƒŖćƒ¼ćƒ‡ćƒ¼ć‚æć‚’å–å¾—ć§ćć‚‹ć‚ˆć†ć«ćŖć£ćŸćŸć‚ć€ć‚ˆć†ć‚„ćć€Œ1ę—„ć®ē·ę‘‚å–ć‚«ćƒ­ćƒŖćƒ¼ć®åøøę™‚č”Øē¤ŗć€ćØć„ć†č¦ä»¶ć‚’ęŗ€ćŸć™ć“ćØćŒć§ćć¾ć—ćŸļ¼ まとめ ć“ć®čØ˜äŗ‹ć§ćÆć€1ćƒ¶ęœˆé–“ć®ć‚¤ćƒ³ć‚æćƒ¼ćƒ³ć§č”Œć£ćŸć€ćƒ˜ćƒ«ć‚¹ć‚±ć‚¢ć‚¢ćƒ—ćƒŖć€Œćƒ˜ćƒ«ć‚·ć‚«ć€ć«ćŠć‘ć‚‹ć€Œćƒ¦ćƒ¼ć‚¶ćƒ¼ć®ē¶™ē¶šēŽ‡å‘äøŠć€ć«å‘ć‘ćŸę–½ē­–ć§ć‚ć‚‹ć€Œć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆę©Ÿčƒ½ć®čæ½åŠ ć€ć«ć¤ć„ć¦ć¾ćØć‚ć¾ć—ćŸć€‚ ä»Šå›žćÆć€ć€Œé£Ÿäŗ‹čØ˜éŒ²ē”»é¢ćøć®ē›“ęŽ„é·ē§»ć€ćØć€Œē·ę‘‚å–ć‚«ćƒ­ćƒŖćƒ¼ć®åøøę™‚č”Øē¤ŗć€ćØć„ć†2ć¤ć®č¦ä»¶ć‚’ęŗ€ćŸć™ćŸć‚ć«ć€ä»„äø‹ć®ę©Ÿčƒ½ć‚’ē”Øć„ć¾ć—ćŸļ¼š DeepLink ļ¼šć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆć‹ć‚‰ć‚¢ćƒ—ćƒŖå†…ć®ē‰¹å®šē”»é¢ćøć®ē›“ęŽ„é·ē§»ć‚’å®Ÿē¾ Keychain Sharing ļ¼šć‚¢ćƒ—ćƒŖćØć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆé–“ć§ć®å®‰å…ØćŖćƒ‡ćƒ¼ć‚æå…±ęœ‰ć‚’å®Ÿē¾ ē‰¹ć«ć€Œē·ę‘‚å–ć‚«ćƒ­ćƒŖćƒ¼ć®åøøę™‚č”Øē¤ŗć€ć§ćÆć€ć‚¢ćƒ—ćƒŖćØć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆćŒåˆ„ćƒ—ćƒ­ć‚»ć‚¹ć§å‹•ä½œć™ć‚‹ćØć„ć†åˆ¶ē“„ć‚„ć€čŖčØ¼ćŒåæ…č¦ćŖå€‹äŗŗćƒ‡ćƒ¼ć‚æć®å–å¾—ćØć„ć†čŖ²é”Œć«ē›“é¢ć—ć¾ć—ćŸćŒć€ć“ć‚Œć‚‰ć®čŖ²é”Œć‚’ Keychain Sharing ć‚’ę“»ē”Øć—ćŸčŖčØ¼ćƒ‡ćƒ¼ć‚æć®å…±ęœ‰ć«ć‚ˆć£ć¦č§£ę±ŗć—ć€ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆć‹ć‚‰ē›“ęŽ„ API ć‚’å‘¼ć³å‡ŗć™ć“ćØć§ć€åøøć«ęœ€ę–°ć®ćƒ‡ćƒ¼ć‚æć‚’č”Øē¤ŗć§ćć‚‹ć‚ˆć†ć«ćŖć‚Šć¾ć—ćŸć€‚ ć‚Øćƒ–ćƒŖćƒ¼ć§ć®1ćƒ¶ęœˆé–“ć®ć‚¤ćƒ³ć‚æćƒ¼ćƒ³ć§ćÆć€ć‚¦ć‚£ć‚øć‚§ćƒƒćƒˆę©Ÿčƒ½ć‚’ć‚¼ćƒ­ć‹ć‚‰čØ­čØˆćƒ»å®Ÿč£…ć—ć€ęœŸé–“å†…ć«ē„”äŗ‹ćƒŖćƒŖćƒ¼ć‚¹ć¾ć§å®Œäŗ†ć™ć‚‹ć“ćØćŒć§ćć¾ć—ćŸć€‚ę–°ę©Ÿčƒ½ć®å®Ÿč£…ć€ćć—ć¦å®Ÿéš›ć®ćƒ¦ćƒ¼ć‚¶ćƒ¼ć«å±Šć‘ć‚‹ć¾ć§ć®äø€é€£ć®ęµć‚Œć‚’ēµŒéØ“ć§ćć‚‹ć‚¤ćƒ³ć‚æćƒ¼ćƒ³ć‚·ćƒƒćƒ—ćÆč²“é‡ćŖę©Ÿä¼šć ćØę€ć„ć¾ć™ć€‚ę–°ć—ć„ęŠ€č”“ć«ćƒćƒ£ćƒ¬ćƒ³ć‚øć—ćŖćŒć‚‰ć€å®Ÿéš›ć®ćƒ—ćƒ­ćƒ€ć‚Æćƒˆć«å½±éŸæć‚’äøŽćˆć‚‹é–‹ē™ŗć«ęŗć‚ć‚ŠćŸć„ę–¹ćÆć€ćœć²ć‚Øćƒ–ćƒŖćƒ¼ć®ć‚¤ćƒ³ć‚æćƒ¼ćƒ³ć«åæœå‹Ÿć—ć¦ćæć¦ćć ć•ć„ļ¼ å‚č€ƒč³‡ę–™
ćÆć˜ć‚ć« ć“ć‚“ć«ć”ćÆć€‚é–‹ē™ŗęœ¬éƒØé–‹ē™ŗ1éƒØćƒ‡ćƒŖćƒƒć‚·ćƒ„ć‚­ćƒƒćƒćƒ³MS1ć«ę‰€å±žć—ć¦ć„ć‚‹ ę±ŸļØ‘ 恧恙怂 ä»Šå›žćÆć€User Matchingļ¼ˆä»„äø‹ć€UMļ¼‰äŗ‹ę„­ć«ćŠć„ć¦ć€AIć‚½ćƒ•ćƒˆć‚¦ć‚§ć‚¢ć‚Øćƒ³ć‚øćƒ‹ć‚¢ć§ć‚ć‚‹ Devin ć‚’å°Žå…„ć—ć€LPļ¼ˆćƒ©ćƒ³ćƒ‡ć‚£ćƒ³ć‚°ćƒšćƒ¼ć‚øļ¼‰ć®é‹ē”Øć‚æć‚¹ć‚Æć‚’č‡Ŗå‹•åŒ–ć—ćŸäŗ‹ä¾‹ć«ć¤ć„ć¦ē“¹ä»‹ć—ć¾ć™ć€‚ å‰ęēŸ„č­˜ Devinとは Devinは、Cognition AIē¤¾ćŒé–‹ē™ŗć—ćŸAIć‚½ćƒ•ćƒˆć‚¦ć‚§ć‚¢ć‚Øćƒ³ć‚øćƒ‹ć‚¢ć§ć™ć€‚ć‚³ćƒ¼ćƒ‰ć®ē†č§£ć€å®Ÿč£…ć€ćƒ‡ćƒćƒƒć‚°ć€ćƒ†ć‚¹ćƒˆć¾ć§ć‚’č‡Ŗå¾‹ēš„ć«č”Œć†ć“ćØćŒć§ćć€é–‹ē™ŗč€…ć®ä½œę„­ć‚’å¤§å¹…ć«åŠ¹ēŽ‡åŒ–ć—ć¾ć™ć€‚GitHubćØć®é€£ęŗć«ć‚ˆć‚Šć€Issueの内容を理解してPull Requestć‚’ä½œęˆć™ć‚‹ć“ćØć‚‚åÆčƒ½ć§ć™ć€‚ UMとは ćƒ‡ćƒŖćƒƒć‚·ćƒ„ć‚­ćƒƒćƒćƒ³ć‚„ćƒˆćƒ¢ćƒ‹ćƒ†ć§ćÆćƒ—ćƒ¬ć‚¼ćƒ³ćƒˆć‚­ćƒ£ćƒ³ćƒšćƒ¼ćƒ³ćŒå®šęœŸēš„ć«č”Œć‚ć‚Œć¦ćŠć‚Šć€ćƒ¦ćƒ¼ć‚¶ćƒ¼ę§˜ćÆć„ćć¤ć‹ć®č³Ŗå•ć«LPēµŒē”±ć§å›žē­”ć™ć‚‹ć“ćØć§åæœå‹Ÿć§ćć‚‹ć‚ˆć†ć«ćŖć£ć¦ć„ć¾ć™ć€‚ ć“ć®äø­ć§ć€ć‚Øćƒ³ć‚øćƒ‹ć‚¢ćÆć€LPä½œęˆćƒ»å¤‰ę›“ć€ETLå‡¦ē†ć®å®Ÿč£…ćŖć©ć‚’ę‹…å½“ć—ć¦ć„ć¾ć™ć€‚ äŗ‹ę„­ć®ē‰¹ę€§äøŠć€ć‚Æćƒ©ć‚¤ć‚¢ćƒ³ćƒˆę§˜ć®č¦ęœ›ć«åæœć˜ćŸLPć‚’å€‹åˆ„ć§ä½œęˆć—ć¦ć„ć‚‹ćŸć‚ć€LPć®ę–°č¦ä½œęˆć‚„CVRę”¹å–„ćŖć©ć®ćŸć‚ć®ę—¢å­˜ć®LPć®äæ®ę­£ćŒé »ē¹ć«ē™ŗē”Ÿć—ć¾ć™ć€‚ Devinć®å°Žå…„ē›®ēš„ å…·ä½“ēš„ć«ć€UMäŗ‹ę„­ć«ćŠć‘ć‚‹LPć®é‹ē”Øć‚æć‚¹ć‚ÆćÆć€ē”»åƒć®å·®ć—ę›æćˆć‚„ę–‡čØ€äæ®ę­£ć€čØ­å•ć®čæ½åŠ ćƒ»å‰Šé™¤ćØć„ć£ćŸć‚‚ć®ćŒå¤šćć‚’å ć‚ć¦ć„ć¾ć—ćŸć€‚ ć“ć‚Œć‚‰ćÆå˜ē“”ćŖå®šåž‹ä½œę„­ć§ćÆćŖćć€ć‚Æćƒ©ć‚¤ć‚¢ćƒ³ćƒˆę§˜ć”ćØć®č¦ęœ›ć«åæœć˜ć¦å€‹åˆ„ć‚«ć‚¹ć‚æćƒžć‚¤ć‚ŗćŒåæ…č¦ćØćŖć‚‹ä½œę„­ć§ć™ć€‚ äø€ć¤äø€ć¤ć®é›£ę˜“åŗ¦ćÆé«˜ććŖć„ć‚‚ć®ć®ć€ę•°ćŒå¤šćć€ć‹ć¤ćć‚Œćžć‚Œå¾®ē“°ćŖć‚«ć‚¹ć‚æćƒžć‚¤ć‚ŗćŒåæ…č¦ć§ć€ć‚Øćƒ³ć‚øćƒ‹ć‚¢ć®č²“é‡ćŖćƒŖć‚½ćƒ¼ć‚¹ć‚’å‰²ć„ć¦ć—ć¾ć£ć¦ć„ć‚‹ćØć„ć†čŖ²é”ŒćŒć‚ć‚Šć¾ć—ćŸć€‚ ćć“ć§ē§ćŸć”ćÆć€ć“ć‚Œć‚‰ć®LPé‹ē”Øć‚æć‚¹ć‚Æć‚’č‡Ŗå‹•åŒ–ć—ć€é–‹ē™ŗå·„ę•°ć®å‰Šęø›ć€ć‚Øćƒ³ć‚øćƒ‹ć‚¢ćŒć‚ˆć‚Šć‚ÆćƒŖć‚Øć‚¤ćƒ†ć‚£ćƒ–ćŖę„­å‹™ćøé›†äø­ć§ćć‚‹ē’°å¢ƒć®ę§‹ēÆ‰ć‚’ē›®ęŒ‡ć™ćŸć‚ć€Devinć®å°Žå…„ć‚’ę±ŗå®šć—ć¾ć—ćŸć€‚ LPé‹ē”Øć‚æć‚¹ć‚Æć®č‡Ŗå‹•åŒ– Asanać‹ć‚‰å®Ÿč£…ć¾ć§ć®ęµć‚Œ å¼Šē¤¾ć§ćÆć€ć‚æć‚¹ć‚Æē®”ē†ćƒ„ćƒ¼ćƒ«ćØć—ć¦ Asana ć‚’ęŽ”ē”Øć—ć¦ć„ć¾ć™ć€‚ LPé‹ē”Øć‚æć‚¹ć‚ÆćÆć€ćƒ“ć‚øćƒć‚¹č·ć®äŗŗćŒć‚Æćƒ©ć‚¤ć‚¢ćƒ³ćƒˆč¦ęœ›ć‚’čøć¾ćˆć¦ć€Asanać«ćƒć‚±ćƒƒćƒˆć‚’ć™ć‚‹ć“ćØć§ć‚Øćƒ³ć‚øćƒ‹ć‚¢ć«é–‹ē™ŗä¾é ¼ćŒęø”ć£ć¦ćć¾ć™ć€‚ 恝恓恧态Asanać‹ć‚‰å®Ÿč£…ć¾ć§ć‚’č‡Ŗå‹•ć§č”Œć†ć‚ˆć†ćŖćƒ•ćƒ­ćƒ¼ć‚’ę§‹ēÆ‰ć—ć¾ć—ćŸć€‚ å…·ä½“ēš„ćŖęµć‚ŒćÆä»„äø‹ć®ć‚ˆć†ć«ćŖć‚Šć¾ć™ć€‚ ćƒˆćƒŖć‚¬ćƒ¼ ļ¼šä¾é ¼č€…ćŒAsanaćƒć‚±ćƒƒćƒˆć®ć‚«ć‚¹ć‚æćƒ ćƒ•ć‚£ćƒ¼ćƒ«ćƒ‰ć‚’ Devin実蔌 に変曓します。 Webhook発火 ļ¼šćƒ•ć‚£ćƒ¼ćƒ«ćƒ‰å¤‰ę›“ć‚’ćƒˆćƒŖć‚¬ćƒ¼ć«Asana WebhookćŒē™ŗē«ć—ć€API GatewayēµŒē”±ć§Lambda関数を呼び出します。 Issue作成 :LambdaがGitHub APIć‚’å©ćć€LPć®ćƒŖćƒć‚øćƒˆćƒŖć«Issueć‚’č‡Ŗå‹•ä½œęˆć—ć¾ć™ć€‚ å®Ÿč£…čØˆē”»ć®ē«‹ę”ˆ :IssuećŒä½œęˆć•ć‚Œć‚‹ćØć€č‡Ŗå‹•ć§ devin-plan ćƒ©ćƒ™ćƒ«ćŒä»˜äøŽć•ć‚Œć¾ć™ć€‚ć“ć‚Œć‚’ćƒˆćƒŖć‚¬ćƒ¼ć«GitHub Actions恧Devin APIć‚’å©ćć“ćØć§DevinćŒčµ·å‹•ć—ć€Asanać®ä¾é ¼å†…å®¹ć‚’å…ƒć«å®Ÿč£…čØˆē”»ć‚’ē·“ć‚Šć€Issueć®ć‚³ćƒ”ćƒ³ćƒˆćØć—ć¦ęŠ•ēØæć—ć¾ć™ć€‚ čØˆē”»ć®ćƒ¬ćƒ“ćƒ„ćƒ¼ ļ¼šć‚Øćƒ³ć‚øćƒ‹ć‚¢ćŒDevinć®ä½œęˆć—ćŸå®Ÿč£…čØˆē”»ć‚’ē¢ŗčŖć—ć¾ć™ć€‚ć‚‚ć—äæ®ę­£ćŒåæ…č¦ćŖå “åˆćÆć€Issueć«ć‚³ćƒ”ćƒ³ćƒˆć‚’ęŠ•ēØæć—ć¾ć™ć€‚ å®Ÿč£…é–‹å§‹ ļ¼ščØˆē”»ć®ē¢ŗčŖćŒēµ‚ć‚ć£ćŸć‚‰ć€ć‚Øćƒ³ć‚øćƒ‹ć‚¢ćŒIssue恫 devin ćƒ©ćƒ™ćƒ«ć‚’ä»˜äøŽć—ć¾ć™ć€‚ć“ć‚Œć‚‚ devin-plan ćƒ©ćƒ™ćƒ«ćØåŒę§˜ć«GitHub ActionsēµŒē”±ć§DevinćŒčµ·å‹•ć—ć€å®Ÿč£…ć‚’é–‹å§‹ć—ć¾ć™ć€‚ Pull Request作成 :DevinćŒå®Ÿč£…ć‚’å®Œäŗ†ć™ć‚‹ćØć€č‡Ŗå‹•ć§PRć‚’ä½œęˆć—ć¾ć™ć€‚ PRć®ćƒ¬ćƒ“ćƒ„ćƒ¼ćØäæ®ę­£ ļ¼šć‚Øćƒ³ć‚øćƒ‹ć‚¢ćÆē”Ÿęˆć•ć‚ŒćŸć‚³ćƒ¼ćƒ‰ć‚’ćƒ¬ćƒ“ćƒ„ćƒ¼ć—ć¾ć™ć€‚äæ®ę­£ćŒåæ…č¦ćŖå “åˆćÆć€PR上で @DevinAI ćØćƒ”ćƒ³ć‚·ćƒ§ćƒ³ć™ć‚‹ć“ćØć§ć€Devinć«äæ®ę­£ć‚’ä¾é ¼ć§ćć¾ć™ć€‚ ć“ć®ćƒ•ćƒ­ćƒ¼ć«ć‚ˆć‚Šć€ć‚Øćƒ³ć‚øćƒ‹ć‚¢ćÆć€ŒčØˆē”»ć®ćƒ¬ćƒ“ćƒ„ćƒ¼ć€ćØć€Œęœ€ēµ‚ēš„ćŖć‚³ćƒ¼ćƒ‰ć®ćƒ¬ćƒ“ćƒ„ćƒ¼ć€ć‚’ć™ć‚‹ć ć‘ć§č‰ÆććŖć‚Šć¾ć™ć€‚ å®Ÿč£…ć®ćƒć‚¤ćƒ³ćƒˆ 1. ę®µéšŽēš„ćŖå®Ÿč£…ć‚¢ćƒ—ćƒ­ćƒ¼ćƒ Devin恫Asana恋悉PRä½œęˆć¾ć§ć‚’å…Øč‡Ŗå‹•ć§ć‚„ć£ć¦ć‚‚ć‚‰ć†ć®ć§ćÆćŖćć€å®Ÿč£…čØˆē”»ć‚’ć‚Øćƒ³ć‚øćƒ‹ć‚¢ćŒćƒ¬ćƒ“ćƒ„ćƒ¼ć™ć‚‹ć‚¹ćƒ†ćƒƒćƒ—ć‚’čØ­ć‘ć¾ć—ćŸć€‚ć“ć‚Œć«ćÆä»„äø‹ć®ę„å›³ćŒć‚ć‚Šć¾ć™ļ¼š å…Øćē•°ćŖć‚‹čØˆē”»ć‚’ē«‹ć¦ć¦å®Ÿč£…ć‚’č”ŒćŖć£ć¦ć—ć¾ć£ćŸę™‚ć®ę‰‹ęˆ»ć‚ŠćƒŖć‚¹ć‚Æć‚’å›žéæ Devin恮1ć‚»ćƒƒć‚·ćƒ§ćƒ³ć®ACU(DevinćŒć‚æć‚¹ć‚Æć‚’å®Ÿč”Œć™ć‚‹ć®ć«åæ…č¦ćŖčØˆē®—ćƒŖć‚½ćƒ¼ć‚¹ć®å˜ä½ļ¼‰ć‚’å‰Šęø›ć—ć€ć‚³ć‚¹ćƒˆęœ€é©åŒ– 2. ćƒ—ćƒ­ćƒ³ćƒ—ćƒˆć‚Øćƒ³ć‚øćƒ‹ć‚¢ćƒŖćƒ³ć‚° devin-planć§å®Ÿč£…čØˆē”»ć‚’ē«‹ć¦ć‚‹ćØćć«ć€čØˆē”»ć‚’Issueć®ć‚³ćƒ”ćƒ³ćƒˆć«ęŠ•ēØæć›ćšć«ć€å®Ÿč£…ć¾ć§č”ŒćŖć£ć¦ć—ć¾ć†ć“ćØćŒå¤šć€…ć‚ć‚Šć¾ć—ćŸć€‚ćć“ć§ć€ä»„äø‹ć®ć‚ˆć†ć«Devinć«č”Œć†ć‚æć‚¹ć‚Æć®ć‚“ćƒ¼ćƒ«ć‚’ę˜Žē¢ŗć«ć—ćŸćØć“ć‚ć€å‹•ä½œćŒå®‰å®šć™ć‚‹ć‚ˆć†ć«ćŖć‚Šć¾ć—ćŸć€‚ **Issueć«ć‚³ćƒ”ćƒ³ćƒˆćØć—ć¦ęŠ•ēØæć™ć‚‹ć®ćæć§å®Ÿč£…ćÆč”Œć‚ćŖć„ć§ćć ć•ć„ć€‚** ć‚³ćƒ”ćƒ³ćƒˆćØć—ć¦ęŠ•ēØæć—ćŸć‚‰ć‚“ćƒ¼ćƒ«ć§ć™ć€‚ ęŠ•ēØæć—ćŸę™‚ē‚¹ć§ć€sleepć—ć¦ćć ć•ć„ć€‚ 仄下のIssueć®å†…å®¹ć‚’ē¢ŗčŖć—ć€čØˆē”»ć‚’ē«‹ć¦ć¦ć€Issueć«ć‚³ćƒ”ćƒ³ćƒˆćØć—ć¦ęŠ•ēØæć—ć¦ćć ć•ć„ć€‚ Issue ć‚æć‚¤ćƒˆćƒ«: (高-3)DK_xxx_é–‹ē™ŗä¾é ¼ Issue ęœ¬ę–‡: --- ## šŸ“‹ タスク詳瓰 xxx --- Issue URL: https://github.com/xxx/xxx/issues/xxx čØˆē”»ć«ćÆä»„äø‹ć‚’å«ć‚ć¦ćć ć•ć„: 1. å®Ÿč£…ć®ę¦‚č¦ 2. åæ…č¦ćŖå¤‰ę›“ćƒ•ć‚”ć‚¤ćƒ«ć®ćƒŖć‚¹ćƒˆ 3. å®Ÿč£…ę‰‹é † 4. č€ƒę…®äŗ‹é …ć‚„ćƒŖć‚¹ć‚Æ ć‚·ć‚¹ćƒ†ćƒ ć‚¢ćƒ¼ć‚­ćƒ†ć‚Æćƒćƒ£ ć“ć®č‡Ŗå‹•åŒ–ćƒ•ćƒ­ćƒ¼ćÆć€Asana Webhook态AWS态GitHub APIを連携させて構築しています。 å…·ä½“ēš„ćŖć‚¢ćƒ¼ć‚­ćƒ†ć‚Æćƒćƒ£ćÆä»„äø‹ć®ć‚ˆć†ć«ćŖć£ć¦ć„ć¾ć™ć€‚ AWSę§‹ęˆć®č©³ē“° API Gateway Asana恮WebhookćƒŖć‚Æć‚Øć‚¹ćƒˆć‚’å—äæ”ć™ć‚‹RESTć‚Øćƒ³ćƒ‰ćƒć‚¤ćƒ³ćƒˆćØć—ć¦ę©Ÿčƒ½ć—ć¾ć™ć€‚ Lambda (å—äæ”ćƒ»ę¤œčØ¼) Webhookć®čŖčØ¼ć‚„SQSćøć®č»¢é€ć‚’å®Ÿč”Œć—ć¾ć™ļ¼š HMACē½²åę¤œčØ¼ : Asanać‹ć‚‰ć®ę­£å½“ćŖćƒŖć‚Æć‚Øć‚¹ćƒˆć‹ć‚’ę¤œčØ¼ User-Agent検証 : ęƒ³å®šå¤–ć®ć‚Æćƒ©ć‚¤ć‚¢ćƒ³ćƒˆć‹ć‚‰ć®ć‚¢ć‚Æć‚»ć‚¹ć‚’ęŽ’é™¤ SQSへの転送 : å‡¦ē†å†…å®¹ć‚’éžåŒęœŸć‚­ćƒ„ćƒ¼ć«é€äæ” å³åŗ§ć«200ćƒ¬ć‚¹ćƒćƒ³ć‚¹ : Webhookć‚æć‚¤ćƒ ć‚¢ć‚¦ćƒˆć‚’å›žéæ SQS FIFO Queue 1つ目のLambdać‹ć‚‰å‡¦ē†å†…å®¹ć‚’å—ć‘å–ć‚Šć€2つ目のLambdać‚’čµ·å‹•ć™ć‚‹äø­ē¶™å½¹ćØć—ć¦ę©Ÿčƒ½ć—ć¾ć™ć€‚ Lambda (å‡¦ē†å®Ÿč”Œ) SQSć‹ć‚‰å—ć‘å–ć£ćŸćƒ”ćƒƒć‚»ćƒ¼ć‚øć‚’åŸŗć«ć€å®Ÿéš›ć®ćƒ“ć‚øćƒć‚¹ćƒ­ć‚øćƒƒć‚Æć‚’å®Ÿč”Œć—ć¾ć™ļ¼š ć‚«ć‚¹ć‚æćƒ ćƒ•ć‚£ćƒ¼ćƒ«ćƒ‰ē¢ŗčŖ : Asanać‚æć‚¹ć‚Æć®ē‰¹å®šćƒ•ć‚£ćƒ¼ćƒ«ćƒ‰å€¤ć‚’ćƒć‚§ćƒƒć‚Æ Issueå†…å®¹ęˆåž‹ : GitHub Issueē”Øć®ćƒ•ć‚©ćƒ¼ćƒžćƒƒćƒˆć«å¤‰ę› GitHub API呼び出し : RESTful APIć«ć‚ˆć‚‹Issueä½œęˆćØćƒ©ćƒ™ćƒ«ä»˜ć‘ å¤±ę•—ć‹ć‚‰å­¦ć‚“ć ęŠ€č”“ēš„å·„å¤« åˆęœŸå®Ÿč£…ć§ć®å¤§å¤±ę•— ęœ€åˆć®å®Ÿč£…ć§ćÆć€1恤恮Lambda内でAsana Webhookć®å—äæ”ć‹ć‚‰GitHub Issueä½œęˆć¾ć§å…Øć¦å‡¦ē†ć—ć‚ˆć†ćØć—ć¾ć—ćŸć€‚ć—ć‹ć—ć€ć“ć‚ŒćŒå¤§ććŖå•é”Œć‚’å¼•ćčµ·ć“ć—ć¾ć—ćŸć€‚ 問锌: Webhook ć‚æć‚¤ćƒ ć‚¢ć‚¦ćƒˆ Asana恮Webhookは10ē§’ä»„å†…ć«ćƒ¬ć‚¹ćƒćƒ³ć‚¹ć‚’čæ”ć•ćŖć„ćØå¤±ę•—ę‰±ć„ć«ćŖć‚Šć¾ć™ć€‚å¤±ę•—ć™ć‚‹ćØć€Asanaは24Ꙃ間恮間1ę™‚é–“ć”ćØć«ćƒŖćƒˆćƒ©ć‚¤ć‚’ē¹°ć‚Ščæ”ć—ć¾ć™ć€‚ å•é”ŒćÆć€Lambdaでの全処理に10ē§’ä»„äøŠć‹ć‹ć£ć¦ć—ć¾ć£ćŸć“ćØć§ć—ćŸć€‚å®Ÿéš›ć«ćÆGitHub Issueć®ä½œęˆćÆęˆåŠŸć—ć¦ć„ć‚‹ć®ć«ć€Asanać«ćÆå¤±ę•—ćØć—ć¦čŖč­˜ć•ć‚Œć€Webhookć®ćƒćƒƒć‚Æć‚Ŗćƒ•ćŒē™ŗē”Ÿć—ć¦ć—ć¾ć„ć¾ć—ćŸć€‚ ćć®ēµęžœć€ćƒ†ć‚¹ćƒˆē”Øć‚æć‚¹ć‚ÆćŒę·±å¤œć«ē“„200個のIssuećØć—ć¦č‡Ŗå‹•ē”Ÿęˆć•ć‚Œć¾ć—ćŸć€‚ć•ć‚‰ć«ć€ćć‚Œć‚‰ćŒ devin-plan ćƒ©ćƒ™ćƒ«ć«ååæœć—ć¦å¤§é‡ć®DevinćŒčµ·å‹•ć—ć€60ACUļ¼ˆē“„2äø‡å††ļ¼‰åˆ†ć®ć‚³ć‚¹ćƒˆć‚’ē„”é§„ć«ę¶ˆč²»ć—ć¦ć—ć¾ć„ć¾ć—ćŸć€‚ 解決策: éžåŒęœŸå‡¦ē†ć«ć‚ˆć‚‹åˆ†é›¢ ć“ć‚Œć‚‰ć®å¤±ę•—ć‚’å—ć‘ć¦ć€Lambda→SQS→Lambdaという2ę®µéšŽć®éžåŒęœŸå‡¦ē†ę§‹ęˆć«å¤‰ę›“ć—ć¾ć—ćŸć€‚ļ¼ˆč©³ē“°ćÆäøŠčØ˜ć€ŒAWSę§‹ęˆć®č©³ē“°ć€ć‚’å‚ē…§ļ¼‰ ć“ć®ę§‹ęˆå¤‰ę›“ć«ć‚ˆć‚Šć€Webhookć®ć‚æć‚¤ćƒ ć‚¢ć‚¦ćƒˆå•é”ŒćÆå®Œå…Øć«č§£ę±ŗć•ć‚Œć€ē¾åœØćÆå®‰å®šć—ć¦ēØ¼åƒć—ć¦ć„ć¾ć™ć€‚ ēµęžœćØęˆęžœ å°Žå…„åŠ¹ęžœ ć“ć”ć‚‰ć®é‹ē”Øćƒ•ćƒ­ćƒ¼ćÆć€ē¾åœØęœ¬ē•Ŗé‹ē”Øć‚’é–‹å§‹ć—ćŸć°ć‹ć‚Šć®ćƒ•ć‚§ćƒ¼ć‚ŗć«ćŖć‚Šć¾ć™ć€‚ ē¾åœØćÆć€ćƒ“ć‚øćƒć‚¹č·ć‹ć‚‰ä¾é ¼ćŒę„ćŸAsanać®ć‚æć‚¹ć‚ÆčŖ¬ę˜Žć‚’ć‚Øćƒ³ć‚øćƒ‹ć‚¢ćŒå¾®äæ®ę­£ć—ć¦é‹ē”Øć—ć¦ć„ć¾ć™ć€‚ ē¾ę™‚ē‚¹ć§ć‚‚ć€å¤§é‡ć®LPć‚æć‚¹ć‚Æć‚’äø¦åˆ—ć§å®Ÿč£…ć™ć‚‹ć“ćØćŒć§ćć‚‹ć‚ˆć†ć«ćŖć‚Šć€ć‚Øćƒ³ć‚øćƒ‹ć‚¢ć®č² ę‹…ćŒč»½ęø›ć•ć‚Œć¦ćć¦ć„ć¾ć™ć€‚ ä»Šå¾Œć®å±•ęœ› ä»Šå¾Œć€ä»„äø‹ć®ć‚ˆć†ćŖåÆ¾åæœć‚’é€²ć‚ć‚‹ć“ćØć§ć€ćƒ“ć‚øćƒć‚¹č·ć‹ć‚‰ć®ęŒ‡ē¤ŗć§ć‚‚å®‰å®šć—ć¦LPé‹ē”Øć‚æć‚¹ć‚Æć‚’č”ŒćŖć£ć¦ć„ć‘ć‚‹ć‚ˆć†ć«ćŖć‚Œć°ć€ć‚Øćƒ³ć‚øćƒ‹ć‚¢ć®ä»‹å…„ćŒć•ć‚‰ć«å°‘ćŖććŖć‚‹ćØč€ƒćˆć‚‰ć‚Œć¾ć™ć€‚ Asanaćøć®čØ˜č¼‰ćƒ•ć‚©ćƒ¼ćƒžćƒƒćƒˆć®ę•“å‚™ ļ¼šćƒ†ćƒ³ćƒ—ćƒ¬ćƒ¼ćƒˆåŒ–ć«ć‚ˆć‚‹ä¾é ¼å“č³Ŗć®å‘äøŠ Devin恮knowledge強化 ļ¼šć†ć¾ćå®Ÿč£…ć§ććŖć„ćƒ‘ć‚æćƒ¼ćƒ³ć®ćƒ‰ć‚­ćƒ„ćƒ”ćƒ³ćƒˆåŒ– ć¾ćŸć€å¼Šē¤¾ć§ćÆē¾åœØć€ć‚³ćƒ¼ćƒ‰ćƒ¬ćƒ“ćƒ„ćƒ¼AIćƒ„ćƒ¼ćƒ«ć® CodeRabbit と greptile ć®ę¤œčØ¼ć‚’č”ŒćŖć£ć¦ć„ć¾ć™ć€‚ ä»Šå¾Œć€Devinć«ć‚ˆć‚‹å®Ÿč£…ćØć“ć‚Œć‚‰ć®ć‚³ćƒ¼ćƒ‰ćƒ¬ćƒ“ćƒ„ćƒ¼AIć‚’ēµ„ćæåˆć‚ć›ć‚‹ć“ćØć§ć€ć‚Øćƒ³ć‚øćƒ‹ć‚¢ć®č² ę‹…ć‚’ć•ć‚‰ć«č»½ęø›ć§ćć‚‹ćØęœŸå¾…ć—ć¦ć„ć¾ć™ć€‚ まとめ ä»Šå›žćÆć€Devinを擻用してLPé‹ē”Øć‚æć‚¹ć‚Æć‚’č‡Ŗå‹•åŒ–ć—ćŸäŗ‹ä¾‹ć‚’ć”ē“¹ä»‹ć—ć¾ć—ćŸć€‚ AIć«å®Ÿč£…ć‚’ä»»ć›ć‚‹å “åˆćÆć€ć‚æć‚¹ć‚Æć®ē²’åŗ¦ćŒéžåøøć«å¤§åˆ‡ć§ć‚ć‚Šć€ä»Šå›žć®LP運用のタスクはDevinć«äøŽćˆć‚‹ć‚æć‚¹ć‚Æć®ē²’åŗ¦ćØć—ć¦é©åˆ‡ć ćØę€ć„ć¾ć—ćŸć€‚ć¾ćŸć€č‡Ŗå‹•åŒ–ć‚’é€²ć‚ć‚‹äø­ć§ć€ä»„äø‹ć®ć‚ˆć†ćŖå­¦ć³ćŒć‚ć‚Šć¾ć—ćŸļ¼š ę˜Žē¢ŗćŖęŒ‡ē¤ŗć®é‡č¦ę€§ ļ¼ščŖ°ćŒč¦‹ć¦ć‚‚åŒę§˜ć®č§£é‡ˆćŒć§ćć‚‹ćƒ—ćƒ­ćƒ³ćƒ—ćƒˆć®čØ­čØˆ é©åˆ‡ćŖäŗŗé–“ć®ä»‹å…„ ļ¼šå…Øč‡Ŗå‹•åŒ–ć§ćÆćŖćć€čØˆē”»ćƒ¬ćƒ“ćƒ„ćƒ¼ćØć‚³ćƒ¼ćƒ‰ćƒ¬ćƒ“ćƒ„ćƒ¼ćØć„ć†é‡č¦ćŖćƒć‚¤ćƒ³ćƒˆć§ć‚Øćƒ³ć‚øćƒ‹ć‚¢ćŒä»‹å…„ć™ć‚‹ä»•ēµ„ćæ ćƒŠćƒ¬ćƒƒć‚øć®č“„ē© ļ¼šć‚ˆćć‚ć‚‹ćƒ‘ć‚æćƒ¼ćƒ³ć‚„å¤±ę•—äŗ‹ä¾‹ć€ćƒ‰ćƒ”ć‚¤ćƒ³ēŸ„č­˜ć‚’ćƒ‰ć‚­ćƒ„ćƒ”ćƒ³ćƒˆåŒ–ć—ć€Devinć®ē²¾åŗ¦å‘äøŠć«ę“»ē”Ø ć“ć®čØ˜äŗ‹ćŒć€ēš†ć•ć‚“ć®ćƒćƒ¼ćƒ ć§ć®AIę“»ē”Øć‚„ę„­å‹™ę”¹å–„ć®ćƒ’ćƒ³ćƒˆć«ćŖć‚Œć°å¬‰ć—ć„ć§ć™ć€‚ ęœ€å¾Œć¾ć§ćŠčŖ­ćæć„ćŸć ćć‚ć‚ŠćŒćØć†ć”ć–ć„ć¾ć—ćŸć€‚
ćÆć˜ć‚ć« ć“ć‚“ć«ć”ćÆć€ćƒŖćƒ†ćƒ¼ćƒ«ćƒćƒ–é–‹ē™ŗéƒØć§ćƒćƒƒć‚Æć‚Øćƒ³ćƒ‰ć‚Øćƒ³ć‚øćƒ‹ć‚¢ć‚’ć—ć¦ć„ć‚‹ćƒ›ć‚·ćØčØ€ć„ć¾ć™ć€‚ ē¾åœØć€å°å£²ć‚¢ćƒ—ćƒŖć®é–‹ē™ŗć§Laravel11ć‚’åˆ©ē”Øć—ć¦ć‚µćƒ¼ćƒ“ć‚¹é–‹ē™ŗć‚’č”Œć£ć¦ć„ć¾ć™ć€‚ ä»Šå›žćÆē¾åœØć‚µćƒ¼ćƒ“ć‚¹ęä¾›ć—ć¦ć„ć‚‹ē’°å¢ƒć‚’ćƒžćƒ«ćƒćƒ†ćƒŠćƒ³ćƒˆåŒ–ć—ćŸćŠč©±ć‚’ć—ć‚ˆć†ćØę€ć„ć¾ć™ć€‚ ę—¢å­˜ć®å˜äø€ćƒ†ćƒŠćƒ³ćƒˆå‰ęć®Laravelć®ć‚µćƒ¼ćƒ“ć‚¹ć‚’ć€ć‚³ćƒ³ć‚·ćƒ„ćƒ¼ćƒžå‘ć‘ć‚¢ćƒ—ćƒŖćØä¼ę„­å‘ć‘ē®”ē†ē”»é¢ć‚’ē¶­ęŒć—ćŸć¾ć¾ćƒžćƒ«ćƒćƒ†ćƒŠćƒ³ćƒˆåŒ–ć—ć¦ć„ć¾ć™ć€‚ ē¾åœØć®ę§‹ęˆć‚’ćŖć‚‹ć¹ćē¶­ęŒć—ć¤ć¤ć€ęœ€å°ę”¹äæ®ćƒ»ę€§čƒ½ćƒ»é‹ē”Øć®č¦³ē‚¹ć§2ę–¹å¼ć‚’ęÆ”č¼ƒć—ć¾ć—ćŸć€‚ ęÆ”č¼ƒć—ćŸēµęžœćØć—ć¦ć€ćƒ†ćƒŠćƒ³ćƒˆć”ćØć«å®Ÿč”Œē’°å¢ƒć‚’åˆ†é›¢ć™ć‚‹ę–¹å¼ć‚’ęŽ”ē”Øć—ć¦ć„ć¾ć™ć€‚ ęœ¬čØ˜äŗ‹ć§ćÆć€ćć®åˆ¤ę–­åŸŗęŗ–ćØLaravelå“ć®å®Ÿč£…ćƒć‚¤ćƒ³ćƒˆć‚’å°‘ć—ć‚µćƒ³ćƒ—ćƒ«ć‚³ćƒ¼ćƒ‰ć‚‚å…„ć‚Œć¦ćŠč©±ć§ćć‚Œć°ćØę€ć„ć¾ć™ć€‚ 要件恫恤恄恦 ćŸć ć€ćƒžćƒ«ćƒćƒ†ćƒŠćƒ³ćƒˆåŒ–ćØć„ć£ć¦ć‚‚ę§˜ć€…ćŖę–¹ę³•ćŒć‚ć‚‹ćØę€ć„ć¾ć™ćŒć€ ä»Šå›žę¤œčØŽć—ćŸćƒžćƒ«ćƒćƒ†ćƒŠćƒ³ćƒˆćÆä»„äø‹ć®č¦ä»¶ć§č”Œć„ć¾ć—ćŸć€‚ APIć‚µćƒ¼ćƒćƒ¼ćÆLaravel态Octane+Swoole态Auroraを使用。 ćƒŖćƒŖćƒ¼ć‚¹ęøˆćæć®ć‚µćƒ¼ćƒ“ć‚¹ć«ć€ć‚³ćƒ³ć‚·ćƒ„ćƒ¼ćƒžå‘ć‘ć‚¢ćƒ—ćƒŖćØä¼ę„­å‘ć‘ē®”ē†ē”»é¢ć®äŗŒē³»ēµ±ćŒć‚ć‚Šć€ćƒ†ćƒŠćƒ³ćƒˆļ¼ˆä¼ę„­ļ¼‰ć”ćØć«ćƒ‡ćƒ¼ć‚æåˆ†é›¢ć—ćŸć„ć€‚ ē¾ę™‚ē‚¹ć®ć‚³ćƒ¼ćƒ‰ćÆå˜äø€ćƒ†ćƒŠćƒ³ćƒˆå‰ęć€‚ęœ€å°ć®å¤‰ę›“ć§å®‰å…Øć«åˆ†é›¢ć—ć¤ć¤ć€ćŖć‚‹ć¹ććƒ‘ćƒ•ć‚©ćƒ¼ćƒžćƒ³ć‚¹åŠ£åŒ–ć›ćšć«Octane/Swooleć®ć‚³ćƒć‚Æć‚·ćƒ§ćƒ³å†åˆ©ē”ØćƒŖć‚¹ć‚Æć‚‚éæć‘ćŸć„ć€‚ ć¾ćŸć€ęœ¬ę„ćÆć‚¤ćƒ³ćƒ•ćƒ©ę§‹ęˆć‚‚éžåøøć«é‡č¦ć§ć™ćŒč©±ćŒåŗƒćŒć‚Šć™ćŽć‚‹ćŸć‚ć€ć‚µćƒ¼ćƒćƒ¼å“ć®ćæć«ćƒ•ć‚©ćƒ¼ć‚«ć‚¹ć—ć¾ć™ć€‚ Octane/Swoole恫恤恄恦 Octane/Swooleć‚’ä½æē”Øć™ć‚‹ćØćƒŖć‚Æć‚Øć‚¹ćƒˆć”ćØć«ćƒ—ćƒ­ć‚»ć‚¹ć‚’åˆęœŸåŒ–ć›ćšå†åˆ©ē”Øć™ć‚‹ć“ćØć§å‡¦ē†ćŒé«˜é€ŸåŒ–ć™ć‚‹ć“ćØćŒć§ćć¾ć™ć€‚ ćć®ćŸć‚ć€åŒć˜DBęŽ„ē¶šå…ˆć§ć‚ć‚Œć°ćƒ”ćƒŖćƒƒćƒˆć‚’å¤§ććå—ć‘ć‚‰ć‚Œć¾ć™ćŒć€ä»Šå›žć®ę§˜ćŖęŽ„ē¶šå…ˆć®åˆ‡ć‚Šę›æćˆć‚’č”Œć†ć‚ˆć†ćŖå‡¦ē†ćŒć‚ć‚‹ćØć€ ä»„å‰ć®ēŠ¶ę…‹ćŒę®‹ć£ćŸć‚‚ć®ćŒćć®ć¾ć¾ä½æē”Øć•ć‚Œć‚‹ć“ćØć§ćƒć‚°ć‚’å¼•ćčµ·ć“ć™å “åˆćŒć‚ć‚Šć¾ć™ć€‚ ćć®ćŸć‚ć€ęŽ„ē¶š/åˆ‡ę–­ć®å‡¦ē†åÆ¾ē­–ć‚’č”Œć†åæ…č¦ćŒć‚ć‚Šć¾ć™ć€‚ å…±é€ščØ­čØˆć®ćƒć‚¤ćƒ³ćƒˆ ćƒ†ćƒŠćƒ³ćƒˆåˆ¤å®šļ¼šHostļ¼ˆć‚µćƒ–ćƒ‰ćƒ”ć‚¤ćƒ³ļ¼‰ć€X-Tenant-Id惘惃惀态URLćƒ‘ć‚¹ć®ć„ćšć‚Œć‹ć§ē‰¹å®š → ä»Šå›žćÆć‚µćƒ–ćƒ‰ćƒ”ć‚¤ćƒ³ć§åˆ¤å®š ćƒ‡ćƒ¼ć‚æćƒ™ćƒ¼ć‚¹ļ¼šćƒ†ćƒŠćƒ³ćƒˆęÆŽć«ćƒ‡ćƒ¼ć‚æćƒ™ćƒ¼ć‚¹ć‚’åˆ†é›¢ć—ć€ćƒ†ćƒ¼ćƒ–ćƒ«å†…ć§å„ćƒ†ćƒŠćƒ³ćƒˆćƒ‡ćƒ¼ć‚æćŒę··åœØć—ćŖć„ę–¹å¼ć«ć™ć‚‹ ć‚»ćƒƒć‚·ćƒ§ćƒ³č”ēŖå›žéæļ¼šsession.cookieåć€session.domainć‚’ćƒ†ćƒŠćƒ³ćƒˆåˆ„ć«ć™ć‚‹ Swoole/Octaneļ¼šęŽ„ē¶šć®å†åˆ©ē”Øć«ę³Øę„ć€‚purge/disconnectć‚’ć€Œåæ…ćšć€č”Œć„ć€ć‚Æćƒ­ć‚¹ćƒŖć‚Æć‚Øć‚¹ćƒˆę±šęŸ“ć‚’é˜²ć 2ćƒ‘ć‚æćƒ¼ćƒ³ć®ę–¹å¼ć«ć¤ć„ć¦ č¦ä»¶ć€å…±é€ščØ­čØˆć®ćƒć‚¤ćƒ³ćƒˆć‚’čøć¾ćˆć€2ćƒ‘ć‚æćƒ¼ćƒ³ć®ę–¹å¼ć‚’ę¤œčØŽć—ć¾ć—ćŸć€‚ ćć‚Œćžć‚Œć«ćƒ”ćƒŖćƒƒćƒˆć€ćƒ‡ćƒ”ćƒŖćƒƒćƒˆćŒć‚ć‚‹ćŸć‚ć€ćć‚Œćžć‚Œč©³ē“°ć«č¦‹ć¦ć„ććŸć„ćØę€ć„ć¾ć™ć€‚ ā‘  Middlewareć§ęŽ„ē¶šå…ˆåˆ¤å®šļ¼‹mysqlć‚’ę›øćę›ćˆ ćƒŖć‚Æć‚Øć‚¹ćƒˆć”ćØć«database.connections.mysqlć‚’ę›øćę›ćˆć‚‹ę–¹å¼ ā‘” Ecspressoć®čØ­å®šć‚’ćƒ†ćƒŠćƒ³ćƒˆć”ćØć«åˆ†ć‘ć€ć‚æć‚¹ć‚Æļ¼ˆå®Ÿč”Œē’°å¢ƒļ¼‰ć‚’ćƒ†ćƒŠćƒ³ćƒˆåˆ†é›¢ć™ć‚‹ę–¹å¼ ä»„äø‹ć«ć‚µćƒ³ćƒ—ćƒ«ć‚³ćƒ¼ćƒ‰ć‚’čØ˜č¼‰ć—ć¦ć„ć¾ć™ćŒć€ę¤œčØŽäø­ć«å®Ÿč£…ć—ćŸę¤œčØ¼ć‚³ćƒ¼ćƒ‰ć®ćŸć‚ć€ å‹•ä½œäæčØ¼ć§ćć¦ćŠć‚Šć¾ć›ć‚“ć®ć§å‚č€ƒēØ‹åŗ¦ć«ć”č¦§ć„ćŸć ć‘ć‚‹ćØå¹øć„ć§ć™ć€‚ ā‘  Middlewareć§ęŽ„ē¶šå…ˆåˆ¤å®šļ¼‹ęŽ„ē¶ščØ­å®š(mysql)ć‚’ę›øćę›ćˆć‚‹ę–¹å¼ļ¼ˆęœ€å°ę”¹äæ®ļ¼‰ ć€å…Øä½“ć®ęµć‚Œć€‘ Middlewareć§ćƒ†ćƒŠćƒ³ćƒˆć‚’åˆ¤å®šć™ć‚‹ć€‚ļ¼ˆćƒ†ćƒŠćƒ³ćƒˆåˆ¤å®šćÆē®”ē†DBć®åˆ¤å®šē”Øćƒ†ćƒ¼ćƒ–ćƒ«ć‚’ē”Øę„ć™ć‚‹ć€‚ļ¼‰ č©²å½“ćƒ†ćƒŠćƒ³ćƒˆć®DBęŽ„ē¶šęƒ…å ±ć‚’ config(['database.connections.mysql' => …]) ć§äøŠę›øć DB::purge('mysql') → API処理 → DB::disconnect('mysql')(Octane/Swooleć§č¶…é‡č¦ļ¼‰ ć‚µćƒ³ćƒ—ćƒ«ļ¼šćƒ†ćƒŠćƒ³ćƒˆč§£ę±ŗ & ęŽ„ē¶šåˆ‡ę›æ Middleware <?php // app/Http/Middleware/SwitchTenantConnection.php namespace App\Http\Middleware; use Closure ; use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\DB; class SwitchTenantConnection { public function handle ( $ request , Closure $ next ) { $ tenant = $ this -> resolveTenant ( $ request ) ; // 例: ć‚µćƒ–ćƒ‰ćƒ”ć‚¤ćƒ³ć‚„ćƒ˜ćƒƒćƒ€ć‹ć‚‰åˆ¤å®š abort_unless ( $ tenant , 400 , 'Tenant not specified' ) ; // 例: ćƒ†ćƒŠćƒ³ćƒˆē®”ē†DBć‹ć‚‰ęŽ„ē¶šęƒ…å ±ć‚’å–ć‚‹ $ conn = $ this -> lookupTenantConnection ( $ tenant ) ; // ę—¢å­˜ć® 'mysql' ć‚’äøŠę›øć Config :: set ( 'database.connections.mysql' , array_merge ( config ( 'database.connections.mysql' ) , [ 'host' => $ conn [ 'host' ] , 'database' => $ conn [ 'database' ] , 'username' => $ conn [ 'username' ] , 'password' => $ conn [ 'password' ] , ] )) ; // Swoole/Octane: ć‚³ćƒć‚Æć‚·ćƒ§ćƒ³å†åˆ©ē”Øé˜²ę­¢ DB :: purge ( 'mysql' ) ; // ć‚»ćƒƒć‚·ćƒ§ćƒ³å/ć‚­ćƒ£ćƒƒć‚·ćƒ„prefixć‚‚éš”é›¢ļ¼ˆä»»ę„ļ¼‰ config ([ 'session.cookie' => 'sess_' .$ tenant ]) ; config ([ 'cache.prefix' => 'cache_' .$ tenant ]) ; try { return $ next ( $ request ) ; } finally { // åæµć®ćŸć‚åˆ‡ę–­ļ¼ˆé‡č¦ļ¼‰ DB :: disconnect ( 'mysql' ) ; } } private function resolveTenant ( $ request ) : ? string { // ä¾‹ļ¼š ć‚µćƒ–ćƒ‰ćƒ”ć‚¤ćƒ³ from Host $ host = $ request -> getHost () ; // tenant1.example.com $ parts = explode ( '.' , $ host ) ; return $ parts [ 0 ] ?? null ; } private function lookupTenantConnection ( string $ tenant ) : array { // å®Ÿč£…ä¾‹: tenant_master ć‹ć‚‰å–å¾— or ć‚­ćƒ£ćƒƒć‚·ćƒ„ // ć“ć“ć§ćÆē›“ęŽ„čæ”å“ć—ć¦ć„ć¾ć™ćŒć€ē®”ē†ē”ØDBć«ęŽ„ē¶šć—ć¦čæ”å“ć€čØ­å®šćƒ•ć‚”ć‚¤ćƒ«ć‚’åˆ©ē”Øć—ćŸčæ”å“ć‚‚åÆć€‚ return [ 'host' => 'aurora.cluster-xxxx.ap-northeast-1.rds.amazonaws.com' , 'database' => "sample_ { $ tenant } " , 'username' => 'app_user' , 'password' => '***' , ] ; } } ピリット åŒć˜ć‚æć‚¹ć‚Æå†…ć§č¤‡ę•°ć®ćƒ†ćƒŠćƒ³ćƒˆć‚’å‡¦ē†ć§ćć‚‹ćŸć‚ć€ć‚µćƒ¼ćƒćƒ¼ć‚³ć‚¹ćƒˆć‚‚ęŠ‘ćˆć‚„ć™ć„ć€‚ ę—¢å­˜ć®ćƒŖćƒć‚øćƒˆćƒŖ/ć‚µćƒ¼ćƒ“ć‚¹å±¤ć‚’ć»ć¼å¤‰ę›“ć›ćšć«ćƒ†ćƒŠćƒ³ćƒˆć”ćØć«DBåˆ‡ć‚Šę›æćˆćŒć§ćć‚‹ć®ć§äø€ē•Ŗä½Žć‚³ć‚¹ćƒˆć§å®Ÿē¾åÆčƒ½ć€‚ ć‚µćƒ¼ćƒćƒ¼ć‚¢ć‚Æć‚»ć‚¹ć®č¦‹č¾¼ćæćŒäŗˆęø¬ć—ć‚„ć™ćć€ēŖē™ŗēš„ćŖć‚¢ć‚Æć‚»ć‚¹å¢—ćŒčµ·ćć«ćć„å “åˆć‚‚ćƒ”ćƒŖćƒƒćƒˆćŒå¤§ćććŖć‚‹ć€‚ ćƒ‡ćƒ”ćƒŖćƒƒćƒˆļ¼ˆé‡č¦ļ¼‰ å„ćƒŖć‚Æć‚Øć‚¹ćƒˆć§åæ…ćšć€Œpurge/disconnectć€ćŒåæ…č¦ļ¼ˆOctane/Swooleć§ęŽ„ē¶šćŒćƒÆćƒ¼ć‚«ćƒ¼ć«ę®‹ć‚‹ćŸć‚ļ¼‰ ć‚³ćƒ³ć‚·ćƒ„ćƒ¼ćƒžå“ć‚¢ć‚Æć‚»ć‚¹ćŒå¤šć„ćØęŽ„ē¶šć®å¼µć‚Šē›“ć—ć‚³ć‚¹ćƒˆćŒåŠ¹ć„ć¦ćć‚‹ļ¼ˆćƒ‘ćƒ•ć‚©ćƒ¼ćƒžćƒ³ć‚¹åŠ£åŒ–ć®ćƒŖć‚¹ć‚Æļ¼‰ ć‚µćƒ¼ćƒć‚¢ć‚Æć‚»ć‚¹å¢—ć®č€ƒę…®ćŒä»–ć®ę–¹ę³•ć‚ˆć‚Šć‚‚ć‚ˆć‚Šę…Žé‡ć«åÆ¾åæœć™ć‚‹åæ…č¦ćŒć‚ć‚‹ć€‚ ā‘” ć‚æć‚¹ć‚Æļ¼ˆå®Ÿč”Œē’°å¢ƒļ¼‰ć‚’ćƒ†ćƒŠćƒ³ćƒˆåˆ†é›¢ļ¼ˆčØ­å®šć ć‘ć§å®Œēµļ¼‰ Ecspressoć®ć‚æć‚¹ć‚Æå®šē¾©ć‚’åˆ†é›¢ć•ć›ć‚‹åÆ¾åæœć‚’č”Œć†ć ć‘ć§ć€Laravelč¦–ē‚¹ć ćØć‚³ćƒ¼ćƒ‰ę”¹äæ®ć‚¼ćƒ­ć§åÆ¾åæœåÆčƒ½ć€‚ ecspresso.yml region: ap-northeast-1 cluster: cluster_name service: tenant-xxxxxx-server service_definition: ecs-service-def.jsonnet task_definition: ecs-task-def.jsonnet timeout: "30m0s" plugins: - name: tfstate config: url: s3://tfstate-xxxxx.com/tenant.tfstate escpressoć®čØ­å®šćƒ•ć‚”ć‚¤ćƒ«ć«ä»„äø‹ć®å®šē¾©ć‚’ćƒ†ćƒŠćƒ³ćƒˆć”ćØć«ć™ć‚‹ć“ćØć§ć€ DBć®ęŽ„ē¶šćŖć©ć‚’ćć‚Œćžć‚Œć«åˆ†ć‘ć€ć‚æć‚¹ć‚Æć‚’ćƒ†ćƒŠćƒ³ćƒˆåˆ†é›¢ć—ć¾ć™ć€‚ Laravelå“ćÆć€Œecspressoć®čØ­å®šćƒ•ć‚”ć‚¤ćƒ«é•ć„ć€ć ć‘ environments.libsonnet { "name": "APP_NAME", "value": ā€tenantA" }, { "name": "DB_DATABASE", "value": "tenantA_db" }, { "name": "DB_USERNAME", "value": "tenantA" }, ćƒ†ćƒŠćƒ³ćƒˆęÆŽć«DBć®ęŽ„ē¶šå…ˆć‚’å¤‰ćˆć€åæ…č¦ć«åæœć˜ć¦ID悄PASSćŖć©ć‚‚å„ćƒ†ćƒŠćƒ³ćƒˆć”ćØć«å¤‰ę›“ć™ć‚‹ć€‚ DBć®ćƒ‘ć‚¹ćƒÆćƒ¼ćƒ‰ćŖć©ćÆSeacretManagerćŖć©ć‚’åˆ©ē”Øć—ć¦čØ­å®šć™ć‚‹ć€‚ ピリット ć‚³ćƒ¼ćƒ‰ę”¹äæ®ćŖć—ć§åˆ†é›¢ć™ć‚‹ć“ćØćŒć§ćć‚‹ć€‚ ę€§čƒ½/安全性/é‹ē”ØćŒå˜ē‹¬ć‚µćƒ¼ćƒ“ć‚¹ćØåŒē­‰ć§ę˜Žē¢ŗļ¼ˆOctaneć®ęŽ„ē¶šå†åˆ©ē”Øć‚‚ę°—ć«ć—ćŖćć¦č‰Æć„ļ¼‰ ć‚¢ć‚Æć‚»ć‚¹å¢—ćŖć©ćÆć“ć®ę–¹å¼ć§ć‚‚DBč‡Ŗä½“ć®ćƒŖć‚¹ć‚ÆćÆć‚ć‚‹ćŒć€ä»–ć®ę–¹å¼ć‚ˆć‚Šć‚‚č€ƒę…®ē‚¹ć‚’å°‘ćŖćć§ćć‚‹ć€‚ ćƒ‡ćƒ”ćƒŖćƒƒćƒˆ ć‚æć‚¹ć‚Æåˆ†ć®ć‚µćƒ¼ćƒćƒ¼ć‚³ć‚¹ćƒˆę‡øåæµćŒć‚ć‚‹ć€‚ļ¼ˆćŸć ć—å®Ÿå‹™ć§ćÆā€œę€ć£ćŸć»ć©å¢—ćˆćŖć„ā€ć‚±ćƒ¼ć‚¹ćŒå¤šć„ļ¼‰ ćƒ†ćƒŠćƒ³ćƒˆćŒå¤§é‡ć«ć‚ć‚Šć€ć²ćØć¤ć²ćØć¤ćŒéžåøøć«å°‘ćŖć„č² č·ć§č”Œćˆć‚‹å “åˆćÆć“ć®ćƒ‡ćƒ”ćƒŖćƒƒćƒˆćÆå¤§ćććŖć‚‹ć€‚ čØ­å®šćŒćƒ†ćƒŠćƒ³ćƒˆåˆ†å¢—ćˆć‚‹ć®ć§ē®”ē†ćŒē…©é›‘ć«ćŖć‚‹åÆčƒ½ę€§ć‚ć‚Šć€‚ ęŽ”ē”Øć—ćŸēµč«–ć«ć¤ć„ć¦ äøŠčØ˜å†…å®¹ć‚’čøć¾ćˆć€ęœ€ēµ‚ēš„ć«ęŽ”ē”Øć—ćŸć®ćÆę–¹å¼ā‘”ć§ć—ćŸć€‚ ć‚³ćƒ¼ćƒ‰ć®äæ®ę­£ćŒäøč¦ć§ä»Šć¾ć§é€šć‚Šć®å‡¦ē†ć‚’ćć®ć¾ć¾ä½æē”Øć§ćć‚‹ć€‚ åˆ‡ć‚Šę›æćˆå‡¦ē†ć«ć‚ˆć‚‹ćƒ‘ćƒ•ć‚©ćƒ¼ćƒžćƒ³ć‚¹åŠ£åŒ–ć®ę¤œčØ¼ć€åÆ¾ē­–ćŒäøč¦ć§ćƒžćƒ«ćƒćƒ†ćƒŠćƒ³ćƒˆåŒ–ć§ćć‚‹ć€‚ ecspressoć‚’ęŽ”ē”Øć—ć¦ćŠć‚Šć€ć‚æć‚¹ć‚Æå®šē¾©ć®åˆ†é›¢ć‚’ć—ć‚„ć™ćć€čØ­å®šć‚‚ćƒ†ćƒŠćƒ³ćƒˆć”ćØć«ē®”ē†ć—ć‚„ć™ć„ć€‚ čØ­å®šćÆå°‘ć—å·„å¤«ć™ć‚‹ć“ćØć§ć€ćƒ†ćƒŠćƒ³ćƒˆćŒå¢—ćˆćŸéš›ć®čØ­å®šå†…å®¹ć‚‚ęœ€å°é™ć«ć§ćć‚‹ć€‚ ļ¼‘ē•Ŗć®ę‡øåæµć ć£ćŸć‚µćƒ¼ćƒćƒ¼ć‚³ć‚¹ćƒˆå¢—ć‚‚ę–¹å¼ā‘ ćØć®å·®åˆ†ć‚‚ćć“ć¾ć§ę°—ć«ćŖć‚‹ćƒ¬ćƒ™ćƒ«ć§ćŖć‹ć£ćŸć“ćØć€‚ ęœ€åˆćÆę–¹å¼ā‘ ć®ć¤ć‚‚ć‚Šć§ć—ćŸćŒć€ć‚µćƒ¼ćƒćƒ¼ć‚³ć‚¹ćƒˆå¢—ć®ę‡øåæµćŒč©³ē“°ć«ćæć¦ćæć‚‹ćØå®ŸćÆćć“ć¾ć§ę°—ć«ćŖć‚‰ćŖć„ćƒ¬ćƒ™ćƒ«ć ć£ćŸē‚¹ć€ ā‘ ć®åÆ¾åæœćÆę”¹äæ®ć‚³ć‚¹ćƒˆć‚‚ćć†ć§ć™ćŒć€ę¤œčØ¼ć‚³ć‚¹ćƒˆć‚‚ć‚ˆć‚ŠäøŠä¹—ć›ć•ć‚Œć‚‹ē‚¹ć‚„ć€ē‰¹ć«åˆęœŸę®µéšŽć§ćÆā‘”ć®ćƒ”ćƒŖćƒƒćƒˆéƒØåˆ†ćŒäø€ē•Ŗé©ć—ć¦ć„ć‚‹ēŠ¶ę…‹ć ć£ćŸć®ć‚‚ć‚ć‚Šć¾ć—ćŸć€‚ まとめ ć“ć‚Œć¾ć§ć®å†…å®¹ć‚’č”Øć«ć¾ćØć‚ć¦ćæć¾ć—ćŸć€‚ ćƒ‘ć‚æćƒ¼ćƒ³ 内容 ピリット ćƒ‡ćƒ”ćƒŖćƒƒćƒˆ å‘ć„ć¦ć„ć‚‹ę”ä»¶ ā‘  Middlewareć§ęŽ„ē¶šå…ˆåˆ¤å®šļ¼‹ mysql ć‚’ę›øćę›ćˆ ćƒŖć‚Æć‚Øć‚¹ćƒˆć”ćØć« database.connections.mysql ć‚’ę›øćę›ćˆć€ DB::purge() / disconnect() 恙悋 ć‚³ćƒ¼ćƒ‰å¤‰ę›“ćŒęœ€å° ć€‚ę—¢å­˜ćƒŖćƒć‚øćƒˆćƒŖćøć®å½±éŸæćŒå°ć•ć„ Octane/Swooleでは éƒ½åŗ¦åˆ‡ę–­ćŒåæ…é ˆ ć€‚ć‚³ćƒ³ć‚·ćƒ„ćƒ¼ćƒžå“ć‚¢ć‚Æć‚»ć‚¹å¢—ć§ ę€§čƒ½ę‡øåæµ å¤§é‡ć®ę—¢å­˜ć‚³ćƒ¼ćƒ‰ć‚’ć»ć¼č§¦ć‚ŒćŖć„ć§åˆ‡ć‚Šę›æćˆćŸć„ć€å°‘ę•°ćƒ†ćƒŠćƒ³ćƒˆ/ä½Žč² č·ć€Octane/Swooleć‚’ęŽ”ē”Øć—ćŖć„ę–¹ę³•ć‚‚ć‚ć‚Š ā‘” ć‚æć‚¹ć‚Æļ¼ˆå®Ÿč”Œē’°å¢ƒļ¼‰ć‚’ćƒ†ćƒŠćƒ³ćƒˆåˆ†é›¢ Ecspresso/ECS恧 ćƒ†ćƒŠćƒ³ćƒˆć”ćØć«ć‚æć‚¹ć‚Æć‚’åˆ†ć‘ 态 .env 悄 Secret ć§ęŽ„ē¶šå…ˆć‚’åˆ†é›¢ ć‚¢ćƒ—ćƒŖę”¹äæ®ć»ć¼äøč¦ 怂 ę€§čƒ½/å®‰å…Øę€§ćŒę˜Žē¢ŗ ć‚³ć‚¹ćƒˆę‡øåæµ ļ¼ˆćØćÆć„ćˆå®Ÿå‹™ć§ćÆęƒ³å®šć»ć©å¢—ćˆćŖć„ć“ćØć‚‚å¤šć„ļ¼‰ ćƒ†ćƒŠćƒ³ćƒˆę•°ćŒé™å®šēš„ć€ę˜Žē¢ŗćŖåˆ†é›¢ćƒ»é‹ē”Øć®ć‚ć‹ć‚Šć‚„ć™ć•ć‚’å„Ŗå…ˆ ä»Šå›žćÆć€ć‚³ćƒ¼ćƒ‰ę”¹äæ®ć»ć¼ć‚¼ćƒ­ćƒ»ęŒ™å‹•ćŒę˜Žē¢ŗćŖ ā‘”ļ¼ˆćƒ†ćƒŠćƒ³ćƒˆć”ćØć«å®Ÿč”Œē’°å¢ƒć‚’åˆ†é›¢ļ¼‰ć‚’ęŽ”ē”Øć—ć¦ć„ć¾ć™ćŒć€ ļ¼‘ćƒ†ćƒŠćƒ³ćƒˆęÆŽć®č¦ęØ”ćÆå°ć•ć„ćŒć€å¤§é‡ć®ćƒ†ćƒŠćƒ³ćƒˆć‚’ęŒćč¦ä»¶ćŖć‚‰ ā‘  ć‚‚ęœ‰åŠ›ć§ć‚ć‚‹ćØę€ć„ć¾ć—ćŸć€‚ ä½†ć—ć€ā‘ ćÆē¢ŗå®ŸćŖåˆ‡ę›æåˆ¶å¾”ć€ćƒ‡ćƒ¼ć‚æę¼ę“©ć®åÆ¾ē­–ć€ę¤œčØ¼ć‚’ć—ć£ć‹ć‚Šč”ŒćŖć£ćŸäøŠćØć„ć†å‰ęćŒć‚ć‚‹ćŸć‚ć€ę¤œčØ¼ć‚³ć‚¹ćƒˆćŒå¤šćć‹ć‹ć£ć¦ć—ć¾ć†åÆčƒ½ę€§ć‚‚å¤§ćć„ć‹ćŖćØć‚‚ę€ć„ć¾ć—ćŸć€‚ ćŠć‚ć‚Šć« 恄恋恌恧恗恟恧恗悇恆恋怂 ęœ€åˆćÆćƒžćƒ«ćƒćƒ†ćƒŠćƒ³ćƒˆåŒ–ć™ć‚‹ć«ćÆå‡¦ē†å†…ć§åˆ†å²ć•ć›DBć®ęŽ„ē¶šć‚’åˆ‡ć‚Šę›æćˆć‚‹ę–¹å¼ć§č‰Æć„ć‹ćØę€ć£ć¦ć„ćŸć®ć§ć™ćŒć€ ćć‚Œćžć‚Œę¤œčØ¼ć—ć¦ćæć‚‹ćØć€ę§˜ć€…ćŖćƒ”ćƒŖćƒƒćƒˆćƒ‡ćƒ”ćƒŖćƒƒćƒˆćŒć‚ć‚Šä½æć„åˆ†ć‘ćŒéžåøøć«é‡č¦ć§ć‚ć‚‹ć“ćØćŒć‚ć‹ć‚Šć¾ć—ćŸć€‚ ć‚¤ćƒ³ćƒ•ćƒ©č²»ē”Øé¢ć®ę‡øåæµć‚‚ēµå±€ć‚±ćƒ¼ć‚¹ć«ć‚ˆć‚‹ćŸć‚ć€ć‚æć‚¹ć‚Æåˆ†é›¢ćŒć™ćć«é«˜ć‚³ć‚¹ćƒˆć«ć‚‚äø€ę¦‚ć«ćÆē¹‹ćŒć‚‰ćšę¤œčØŽćŒåæ…č¦ćŖć“ćØćŒć‚ć‹ć‚Šć¾ć—ćŸć€‚ ä»Šå›žćÆLaravelć§ćŠč©±ćÆć—ć¦ć„ć¾ć™ćŒć€č€ƒćˆę–¹ćÆä»–ć®ē’°å¢ƒć§ć‚‚ę“»ē”Øć§ćć‚‹ć®ć§ćÆćŖć„ć‹ćØę€ć£ć¦ć„ć¾ć™ć€‚ ęœ¬čØ˜äŗ‹ćŒć€ć“ć‚Œć‹ć‚‰ćƒžćƒ«ćƒćƒ†ćƒŠćƒ³ćƒˆåŒ–ć‚’ę¤œčØŽć™ć‚‹äøŠć§å°‘ć—ć§ć‚‚å‚č€ƒć«ćŖć‚Œć°å¹øć„ć§ć™ć€‚ ęœ€å¾Œć¾ć§ćŠčŖ­ćæć„ćŸć ćć‚ć‚ŠćŒćØć†ć”ć–ć„ć¾ć—ćŸć€‚
ćÆć˜ć‚ć« Android é–‹ē™ŗć‚Øćƒ³ć‚øćƒ‹ć‚¢ć‚’ę‹…å½“ć—ć¦ć„ć‚‹å²”ē”°ć§ć™ć€‚ ä»Šå›žćÆćƒ¦ćƒ¼ć‚¶ćƒ¼ć‚Øćƒ³ć‚²ćƒ¼ć‚øćƒ”ćƒ³ćƒˆć‚’é«˜ć‚ć‚‹å¼·åŠ›ćŖćƒ„ćƒ¼ćƒ«ć§ć‚ć‚‹ć€ Engage SDK ć«ć¤ć„ć¦å°Žå…„ć®ćƒ”ćƒŖćƒƒćƒˆć«ć¤ć„ć¦č§£čŖ¬ć§ćć‚Œć°ćØę€ć„ć¾ć™ć€‚ Engage SDK とは Engage SDK は、Google が 2024 幓 7 ęœˆć«ć€Œ Introducing Collections, a new on-device surface for your content ć€ćØć„ć†ć‚æć‚¤ćƒˆćƒ«ć§ē™ŗč”Øć—ć¾ć—ćŸć€‚ 恓恮 SDK ć‚’å°Žå…„ć™ć‚‹ć“ćØć§ć€ć‚¢ćƒ—ćƒŖć®ć‚³ćƒ³ćƒ†ćƒ³ćƒ„ć‚’ Android ć‚·ć‚¹ćƒ†ćƒ ć®č¤‡ę•°ć®å “ę‰€ć«č”Øē¤ŗć—ć€ćƒ¦ćƒ¼ć‚¶ćƒ¼ć‚Øćƒ³ć‚²ćƒ¼ć‚øćƒ”ćƒ³ćƒˆć‚’å‘äøŠć•ć›ć‚‹ć“ćØćŒć§ćć¾ć™ć€‚ Engage SDK ć®åŸŗęœ¬ę¦‚åæµ Engage SDK ć§ćÆć€ć‚³ćƒ³ćƒ†ćƒ³ćƒ„ć‚’ć‚«ćƒ†ć‚“ćƒŖćƒ¼åˆ„ć«åˆ†é”žć—ć€ćć‚Œćžć‚Œć«åÆ¾åæœć™ć‚‹ Cluster态 Entity ć‚’å®šē¾©ć—ć¦ć„ć¾ć™ć€‚ é–‹ē™ŗč€…ćÆć€ć“ć‚Œć‚‰ć«ę²æć£ćŸęƒ…å ±ć‚’ Google ć«é€äæ”ć™ć‚‹ć“ćØć§ć€é©åˆ‡ćŖå “ę‰€ć«ć‚³ćƒ³ćƒ†ćƒ³ćƒ„ćŒč”Øē¤ŗć•ć‚Œć‚‹ć‚ˆć†ć«ćŖć‚Šć¾ć™ć€‚ Engage SDK では、仄下の 9 ć¤ć®ć‚«ćƒ†ć‚“ćƒŖćƒ¼ćŒē”Øę„ć•ć‚Œć¦ć„ć¾ć™ć€‚ Watch: å‹•ē”» Listen: 音愽 Read: čØ˜äŗ‹ Shopping: 買い物 Food: é£Ÿäŗ‹ Social: ć‚½ćƒ¼ć‚·ćƒ£ćƒ« Travel: ę—…č”Œ Health & Fitness: 偄康 & ćƒ•ć‚£ćƒƒćƒˆćƒć‚¹ Other Verticals: ćć®ä»– 例として、Watch ć‚«ćƒ†ć‚“ćƒŖćƒ¼ć‚’č¦‹ć¦ćæć¾ć—ć‚‡ć†ć€‚ Watch ć‚«ćƒ†ć‚“ćƒŖćƒ¼ć«ćÆć€Recommendation态Continuation态Featured 恮 3 恤恮 Cluster ć‚æć‚¤ćƒ—ćŒå«ć¾ć‚Œć¦ć„ć¾ć™ć€‚ 恓恓恧恄恆 Cluster ćØćÆć€ćƒ‡ćƒ™ćƒ­ćƒƒćƒ‘ćƒ¼ćŒęä¾›ć™ć‚‹ć‚³ćƒ³ćƒ†ćƒ³ćƒ„ć‚’ć¾ćØć‚ć¦č”Øē¤ŗć™ć‚‹ćŸć‚ć® UIļ¼ˆćƒ¦ćƒ¼ć‚¶ćƒ¼ć‚¤ćƒ³ć‚æćƒ¼ćƒ•ć‚§ćƒ¼ć‚¹ļ¼‰ć®å˜ä½ć§ć™ć€‚ Engage SDK 全体で、Recommendation态Continuation态Featured 恮 3 恤恮 Cluster ć‚æć‚¤ćƒ—ćŒå®šē¾©ć•ć‚Œć¦ć„ć¾ć™ć€‚ Watch ć‚«ćƒ†ć‚“ćƒŖćƒ¼ć«ćŠć‘ć‚‹ć€å„ Cluster ć®å½¹å‰²ćÆä»„äø‹ć®é€šć‚Šć§ć™ć€‚ Cluster čŖ¬ę˜Ž Recommendation å€‹ć€…ć®é–‹ē™ŗč€…ćƒ‘ćƒ¼ćƒˆćƒŠćƒ¼ć‹ć‚‰ć®ć‚³ćƒ³ćƒ†ćƒ³ćƒ„ć‚’č¦–č“ć™ć‚‹ćŸć‚ć®ćƒ‘ćƒ¼ć‚½ćƒŠćƒ©ć‚¤ć‚ŗć•ć‚ŒćŸęę”ˆć‚’č”Øē¤ŗć—ć¾ć™ć€‚ Continuation č¤‡ę•°ć®é–‹ē™ŗč€…ćƒ‘ćƒ¼ćƒˆćƒŠćƒ¼ć‹ć‚‰ć®ęœŖå®Œęˆć®å‹•ē”»ć‚„ć€é–¢é€£ć™ć‚‹ę–°ć—ććƒŖćƒŖćƒ¼ć‚¹ć•ć‚ŒćŸć‚Øćƒ”ć‚½ćƒ¼ćƒ‰ć‚’1恤恮UIć‚°ćƒ«ćƒ¼ćƒ—ć«č”Øē¤ŗć—ć¾ć™ć€‚ Featured č¤‡ę•°ć®é–‹ē™ŗč€…ćƒ‘ćƒ¼ćƒˆćƒŠćƒ¼ć‹ć‚‰ć®ć‚Øćƒ³ćƒ†ć‚£ćƒ†ć‚£ć®ć‚»ćƒ¬ć‚Æć‚·ćƒ§ćƒ³ć‚’1恤恮UIć‚°ćƒ«ćƒ¼ćƒ—ć«č”Øē¤ŗć—ć¾ć™ć€‚ Cluster についての詳瓰は、 Engage SDK Cluster publishing guidelines ć«č©³ć—ćčØ˜č¼‰ć•ć‚Œć¦ć„ć¾ć™ć®ć§ć€ä½µć›ć¦ć”ē¢ŗčŖćć ć•ć„ć€‚ ć¾ćŸć€Watch ć‚«ćƒ†ć‚“ćƒŖćƒ¼ć§ćÆć€Cluster å†…ć®å˜äø€ć®ć‚¢ć‚¤ćƒ†ćƒ ć‚’č”Øć™ć‚Ŗćƒ–ć‚øć‚§ć‚Æćƒˆć§ć‚ć‚‹ć€Entity が 6 ēØ®é”žå®šē¾©ć•ć‚Œć¦ć„ć¾ć™ć€‚ Entity čŖ¬ę˜Ž MovieEntity ļ¼ˆę˜ ē”»ļ¼‰ ę˜ ē”»ć‚³ćƒ³ćƒ†ćƒ³ćƒ„ć‚’č”Øć™ć‚Øćƒ³ćƒ†ć‚£ćƒ†ć‚£ć§ć™ć€‚ć‚æć‚¤ćƒˆćƒ«ć€ćƒć‚¹ć‚æćƒ¼ē”»åƒć€å†ē”Ÿę™‚é–“ćŖć©ć®ęƒ…å ±ć‚’å«ćæć¾ć™ć€‚ TvShowEntity ļ¼ˆćƒ†ćƒ¬ćƒ“ē•Ŗēµ„ļ¼‰ ćƒ†ćƒ¬ćƒ“ē•Ŗēµ„ć‚·ćƒŖćƒ¼ć‚ŗå…Øä½“ć‚’č”Øć™ć‚Øćƒ³ćƒ†ć‚£ćƒ†ć‚£ć§ć™ć€‚ē•Ŗēµ„åć‚„ć‚øćƒ£ćƒ³ćƒ«ć€ć‚·ćƒ¼ć‚ŗćƒ³ęƒ…å ±ćøć®ćƒŖćƒ³ć‚ÆćŖć©ćŒå«ć¾ć‚Œć¾ć™ć€‚ TvSeasonEntity ļ¼ˆćƒ†ćƒ¬ćƒ“ć‚·ćƒ¼ć‚ŗćƒ³ļ¼‰ ē‰¹å®šć®ćƒ†ćƒ¬ćƒ“ē•Ŗēµ„ć®ć‚·ćƒ¼ć‚ŗćƒ³ć‚’č”Øć™ć‚Øćƒ³ćƒ†ć‚£ćƒ†ć‚£ć§ć™ć€‚ć‚·ćƒ¼ć‚ŗćƒ³ē•Ŗå·ć‚„ć‚Øćƒ”ć‚½ćƒ¼ćƒ‰ćƒŖć‚¹ćƒˆćøć®ćƒŖćƒ³ć‚Æć‚’ęŒć”ć¾ć™ć€‚ TvEpisodeEntity ļ¼ˆćƒ†ćƒ¬ćƒ“ć‚Øćƒ”ć‚½ćƒ¼ćƒ‰ļ¼‰ ćƒ†ćƒ¬ćƒ“ē•Ŗēµ„ć®å€‹ć€…ć®ć‚Øćƒ”ć‚½ćƒ¼ćƒ‰ć‚’č”Øć™ć‚Øćƒ³ćƒ†ć‚£ćƒ†ć‚£ć§ć™ć€‚ć‚Øćƒ”ć‚½ćƒ¼ćƒ‰ē•Ŗå·ć€ć‚æć‚¤ćƒˆćƒ«ć€ć‚ć‚‰ć™ć˜ćŖć©ćŒå«ć¾ć‚Œć¾ć™ć€‚ LiveStreamingVideoEntity ļ¼ˆćƒ©ć‚¤ćƒ–é…äæ”å‹•ē”»ļ¼‰ ćƒ©ć‚¤ćƒ–ć‚¹ćƒˆćƒŖćƒ¼ćƒŸćƒ³ć‚°äø­ć®å‹•ē”»ć‚³ćƒ³ćƒ†ćƒ³ćƒ„ć‚’č”Øć™ć‚Øćƒ³ćƒ†ć‚£ćƒ†ć‚£ć§ć™ć€‚ćƒ©ć‚¤ćƒ–ć®é–‹å§‹ćƒ»ēµ‚äŗ†ę™‚åˆ»ć‚„ē¾åœØć®č¦–č“č€…ę•°ćŖć©ć®ęƒ…å ±ć‚’å«ćæć¾ć™ć€‚ VideoClipEntity ļ¼ˆćƒ“ćƒ‡ć‚Ŗć‚ÆćƒŖćƒƒćƒ—ļ¼‰ ēŸ­ć„å‹•ē”»ć‚ÆćƒŖćƒƒćƒ—ć‚’č”Øć™ć‚Øćƒ³ćƒ†ć‚£ćƒ†ć‚£ć§ć™ć€‚ćƒŸćƒ„ćƒ¼ć‚øćƒƒć‚Æćƒ“ćƒ‡ć‚Ŗć‚„ćƒ‹ćƒ„ćƒ¼ć‚¹ć‚ÆćƒŖćƒƒćƒ—ć€ćƒć‚¤ćƒ©ć‚¤ćƒˆć‚·ćƒ¼ćƒ³ćŖć©ć«åˆ©ē”Øć•ć‚Œć¾ć™ć€‚ å‹•ē”»č¦–č“ć‚¢ćƒ—ćƒŖć§ć‚ć‚Œć°ć€ć‚¢ćƒ—ćƒŖå†…ć®ć‚³ćƒ³ćƒ†ćƒ³ćƒ„ć‚’ć“ć‚Œć‚‰ć® Entity ć«å¤‰ę›ć—ć€Cluster ćøē™»éŒ²ćƒ»å…¬é–‹ć™ć‚‹ćØć„ć£ćŸå®Ÿč£…ć®ęµć‚Œć«ćŖć‚Šć¾ć™ć€‚ å…·ä½“ēš„ćŖå®Ÿč£…ę–¹ę³• 仄下は Google ćŒå…¬é–‹ć—ć¦ć„ć‚‹ć‚µćƒ³ćƒ—ćƒ«ć‚³ćƒ¼ćƒ‰ć®äø­ć‹ć‚‰ęŠœē²‹ć—ćŸć€ FeaturedCluster 恫 MovieEntity ć‚’čæ½åŠ ć—ć€å…¬é–‹ć—ć¦ć„ć‚‹ constructFeaturedClusterRequest ćƒ”ć‚½ćƒƒćƒ‰ć§ć™ć€‚ suspend fun constructFeaturedClusterRequest(): PublishFeaturedClusterRequest { // ćŠć™ć™ć‚ćØć—ć¦č”Øē¤ŗć™ć‚‹ę˜ ē”»ć®ćƒŖć‚¹ćƒˆć‚’å–å¾— val featuredList = movieDao.loadMovieIsCurrentlyWatching(false) // FeaturedClusterć‚’ę§‹ēÆ‰ć™ć‚‹ćŸć‚ć®Builderć‚’åˆęœŸåŒ–ć—ć¾ć™ć€‚ val featuredCluster = FeaturedCluster.Builder() // å–å¾—ć—ćŸę˜ ē”»ć®ćƒŖć‚¹ćƒˆ featuredList 悒 featuredCluster ćøčæ½åŠ ć™ć‚‹ for (item in featuredList) { featuredCluster.addEntity(ItemToEntityConverter.convertMovie(item)) } // PublishFeaturedClusterRequest(Featuredć‚Æćƒ©ć‚¹ć‚æå…¬é–‹ćƒŖć‚Æć‚Øć‚¹ćƒˆļ¼‰ć‚’ę§‹ēÆ‰ return PublishFeaturedClusterRequest.Builder() .setFeaturedCluster(featuredCluster.build()) .build() } github.com ä»–ć‚«ćƒ†ć‚“ćƒŖćƒ¼ć§ć‚‚ć€å®Ÿč£…ć®ęµć‚ŒćÆåŒć˜ć«ćŖć‚Šć¾ć™ć€‚ 詳瓰については、 Engage SDK Codelab  |  Android Developers ćŒē”Øę„ć•ć‚Œć¦ć„ć¾ć™ć®ć§ć€ć“ć”ć‚‰ć‚’ć”å‚ē…§ćć ć•ć„ć€‚ Engage SDK ć‚’å°Žå…„ć™ć‚‹ćƒ”ćƒŖćƒƒćƒˆ č”Øē¤ŗć•ć‚Œć‚‹å “ę‰€ Engage SDK ć‚’å°Žå…„ć™ć‚‹ć“ćØć§ć€ć‚¢ćƒ—ćƒŖć®ć‚³ćƒ³ćƒ†ćƒ³ćƒ„ćÆä»„äø‹ć®å “ę‰€ć«č”Øē¤ŗć•ć‚Œć‚‹ć‚ˆć†ć«ćŖć‚Šć¾ć™ć€‚ 蔨示 ć‚¢ć‚Æć‚»ć‚¹ę–¹ę³•ćƒ»åÆ¾č±” 機能 特従 å‚™č€ƒ ć‚¹ćƒžćƒ¼ćƒˆćƒ•ć‚©ćƒ³ć®ć‚³ćƒ¬ć‚Æć‚·ćƒ§ćƒ³ Play Store ć®ć‚¢ć‚¤ć‚³ćƒ³ć‚’é•·ęŠ¼ć— > ć‚³ćƒ¬ć‚Æć‚·ćƒ§ćƒ³ ćƒ‘ćƒ¼ć‚½ćƒŠćƒ©ć‚¤ć‚ŗć•ć‚ŒćŸćŠć™ć™ć‚ć‚³ćƒ³ćƒ†ćƒ³ćƒ„ć‚’č”Øē¤ŗ ćƒ¦ćƒ¼ć‚¶ćƒ¼ć®čˆˆå‘³ć«åŸŗć„ć„ćŸå‹•ēš„ćŖć‚³ćƒ³ćƒ†ćƒ³ćƒ„ęŽØč–¦ ē±³å›½ć‚’äø­åæƒć«å±•é–‹äø­ ć‚æćƒ–ćƒ¬ćƒƒćƒˆć®ć‚Øćƒ³ć‚æćƒ¼ćƒ†ć‚¤ćƒ”ćƒ³ćƒˆć‚¹ćƒšćƒ¼ć‚¹ Android ć‚æćƒ–ćƒ¬ćƒƒćƒˆć®ćƒ›ćƒ¼ćƒ ē”»é¢å·¦ē«Æļ¼ˆäø€éƒØćƒ”ćƒ¼ć‚«ćƒ¼ć®ē«Æęœ«ć§ćÆéžę­č¼‰ć€ć¾ćŸćÆē•°ćŖć‚‹ę©Ÿčƒ½ć®å “åˆćŒć‚ć‚Šć¾ć™ļ¼‰ć€‚ ć‚Øćƒ³ć‚æćƒ¼ćƒ†ć‚¤ćƒ”ćƒ³ćƒˆć‚³ćƒ³ćƒ†ćƒ³ćƒ„ć®ęŽØå„Øč”Øē¤ŗ å¤§ē”»é¢ć‚’ę“»ē”Øć—ćŸćƒŖćƒƒćƒćŖć‚³ćƒ³ćƒ†ćƒ³ćƒ„č”Øē¤ŗ Google Play ć‚¹ćƒˆć‚¢ ć‚¢ćƒ—ćƒŖć®č©³ē“°ćƒšćƒ¼ć‚øćŖć© é–¢é€£ć‚³ćƒ³ćƒ†ćƒ³ćƒ„ć®č”Øē¤ŗ ć‚¢ćƒ—ćƒŖē™ŗč¦‹ę™‚ć®čæ½åŠ ć‚³ćƒ³ćƒ†ćƒ³ćƒ„ęä¾› å°Žå…„ćƒ”ćƒŖćƒƒćƒˆ Engage SDK ć‚’ć‚¢ćƒ—ćƒŖć«å°Žå…„ć™ć‚‹ć“ćØćÆć€é–‹ē™ŗč€…ć«ćØć£ć¦å¤šćć®ęˆ¦ē•„ēš„åˆ©ē‚¹ć‚’ć‚‚ćŸć‚‰ć—ć¾ć™ć€‚ ćć®ćƒ”ćƒŖćƒƒćƒˆćÆć€ćƒ¦ćƒ¼ć‚¶ćƒ¼ć‚Øćƒ³ć‚²ćƒ¼ć‚øćƒ”ćƒ³ćƒˆć®å‘äøŠć‹ć‚‰é–‹ē™ŗč² ę‹…ć®č»½ęø›ć¾ć§å¤šå²ć«ć‚ćŸć‚Šć¾ć™ć€‚ 1. ćƒ¦ćƒ¼ć‚¶ćƒ¼ć‚Øćƒ³ć‚²ćƒ¼ć‚øćƒ”ćƒ³ćƒˆć®é£›čŗēš„å‘äøŠ ćƒ¦ćƒ¼ć‚¶ćƒ¼ćŒć‚¢ćƒ—ćƒŖć‚’ē›“ęŽ„é–‹ć‹ćŖćć¦ć‚‚ć€ć‚¹ćƒžćƒ¼ćƒˆćƒ•ć‚©ćƒ³ć®ćƒ›ćƒ¼ćƒ ē”»é¢ćŖć©ć‹ć‚‰ē›“ęŽ„ć‚³ćƒ³ćƒ†ćƒ³ćƒ„ć«ć‚¢ć‚Æć‚»ć‚¹ć§ćć‚‹ć‚ˆć†ć«ćŖć‚Šć¾ć™ć€‚ Google ć®ę©Ÿę¢°å­¦ēæ’ć‚’ę“»ē”Øć—ćŸćƒ‘ćƒ¼ć‚½ćƒŠćƒ©ć‚¤ć‚ŗę©Ÿčƒ½ć«ć‚ˆć‚Šć€å€‹ć€…ć®ćƒ¦ćƒ¼ć‚¶ćƒ¼ć®čˆˆå‘³é–¢åæƒć«åŸŗć„ć„ćŸć‚³ćƒ³ćƒ†ćƒ³ćƒ„ćŒęŽØč–¦ć•ć‚Œć‚‹ćŸć‚ć€ć‚ÆćƒŖćƒƒć‚ÆēŽ‡ć®å‘äøŠćŒęœŸå¾…ć§ćć€ć‚¢ć‚Æćƒ†ć‚£ćƒ–ēŽ‡ć®ē¶­ęŒćƒ»å‘äøŠć«å¤§ććč²¢ēŒ®ć™ć‚‹åÆčƒ½ę€§ćŒć‚ć‚Šć¾ć™ć€‚ 2. Googlećƒ—ćƒ©ćƒƒćƒˆćƒ•ć‚©ćƒ¼ćƒ äøŠć§ć®ć‚¢ćƒ—ćƒŖć®åÆč¦–ę€§å‘äøŠ ć‚¢ćƒ—ćƒŖå†…ć®ć‚³ćƒ³ćƒ†ćƒ³ćƒ„ćŒ Google Play ć‚¹ćƒˆć‚¢ć‚„ć‚Øćƒ³ć‚æćƒ¼ćƒ†ć‚¤ćƒ”ćƒ³ćƒˆć‚¹ćƒšćƒ¼ć‚¹ćŖć©ć€č¤‡ę•°ć® Google ć®ć‚µćƒ¼ćƒ“ć‚¹ļ¼ˆć‚µćƒ¼ćƒ•ć‚§ć‚¹ļ¼‰ć«č”Øē¤ŗć•ć‚Œć‚‹ćŸć‚ć€ćƒ¦ćƒ¼ć‚¶ćƒ¼ćøć®éœ²å‡ŗćŒå¢—åŠ ć—ć¾ć™ć€‚ ć“ć‚Œć«ć‚ˆć‚Šć€ę—¢å­˜ćƒ¦ćƒ¼ć‚¶ćƒ¼ć®ęŗ€č¶³åŗ¦ć‚’é«˜ć‚ć‚‹ć ć‘ć§ćŖćć€ć“ć‚Œć¾ć§ć‚¢ćƒ—ćƒŖć‚’ēŸ„ć‚‰ćŖć‹ć£ćŸę½œåœØēš„ćŖę–°č¦ćƒ¦ćƒ¼ć‚¶ćƒ¼ć‚’ē²å¾—ć™ć‚‹ēµ¶å„½ć®ę©Ÿä¼šćŒē”Ÿć¾ć‚Œć¾ć™ć€‚ 3. é–‹ē™ŗč² ę‹…ć®č»½ęø›ćØć‚¹ćƒ ćƒ¼ć‚ŗćŖēµ±åˆ ęŠ€č”“ēš„ćŖč¦³ē‚¹ć‹ć‚‰ć‚‚ć€å°Žå…„ć®ćƒćƒ¼ćƒ‰ćƒ«ćÆä½ŽćčØ­čØˆć•ć‚Œć¦ć„ć¾ć™ć€‚ SDK ćÆéžåøøć«č»½é‡ć§ć€å¹³å‡ēš„ćŖć‚¢ćƒ—ćƒŖć® APK ć‚µć‚¤ć‚ŗå¢—åŠ ćÆåœ§ēø®ēŠ¶ę…‹ć§ 50KB ęœŖęŗ€ć«ęŠ‘ćˆć‚‰ć‚Œć‚‹ćØę˜ŽčØ˜ć•ć‚Œć¦ć„ć¾ć™ć€‚ ć¾ćŸć€ę—¢å­˜ć®ć‚¢ćƒ—ćƒŖę§‹é€ ć‚’å¤§ććå¤‰ę›“ć™ć‚‹ć“ćØćŖćēµ±åˆć§ćć‚‹ćŸć‚ć€é–‹ē™ŗęœŸé–“ćÆē“„ 1 é€±é–“ēØ‹åŗ¦ćØēŸ­ęœŸé–“ć§å®Œäŗ†ć—ć¾ć™ć€‚ ć“ć‚Œć«ć‚ˆć‚Šć€å°‘ćŖć„ćƒŖć‚½ćƒ¼ć‚¹ć§å¤§ććŖåŠ¹ęžœć‚’ē‹™ć†ć“ćØćŒåÆčƒ½ć§ć™ć€‚ 4. ćƒ‡ćƒ¼ć‚æć«åŸŗć„ć„ćŸē¶™ē¶šēš„ćŖę”¹å–„ ćƒ‡ćƒ¼ć‚æé§†å‹•åž‹ć®ć‚¢ćƒ—ćƒ­ćƒ¼ćƒć«ć‚ˆć‚Šć€ęä¾›ć™ć‚‹ć‚³ćƒ³ćƒ†ćƒ³ćƒ„ć‚’ē¶™ē¶šēš„ć«ęœ€é©åŒ–ć§ćć¾ć™ć€‚ Google ć®é«˜åŗ¦ćŖę©Ÿę¢°å­¦ēæ’ćŒćƒ¦ćƒ¼ć‚¶ćƒ¼ć®č”Œå‹•ć‚’ćƒŖć‚¢ćƒ«ć‚æć‚¤ćƒ ć§åˆ†ęžć—ć€ęœ€ć‚‚åŠ¹ęžœēš„ćŖć‚³ćƒ³ćƒ†ćƒ³ćƒ„ęŽØč–¦ć‚’č‡Ŗå‹•ć§č”Œć„ć¾ć™ć€‚ é–‹ē™ŗč€…ćÆćƒ‘ćƒ•ć‚©ćƒ¼ćƒžćƒ³ć‚¹ć«é–¢ć™ć‚‹åˆ†ęžēµęžœć‚„ę”¹å–„ęę”ˆć‚’ę“»ē”Øć—ć€åøøć«ćƒ¦ćƒ¼ć‚¶ćƒ¼ä½“éØ“ć‚’å‘äøŠćŒęœŸå¾…ć§ćć¾ć™ć€‚ ä»Šå¾Œć®å±•ęœ› Engage SDK ćÆē¾åœØć€ē±³å›½ć‚’äø­åæƒć«ć€ę—„ęœ¬ć‚’å«ć‚€ć„ćć¤ć‹ć®å›½ć§å±•é–‹ć•ć‚Œć¦ć„ć¾ć™ć€‚ē¾åœØć®čŖēŸ„åŗ¦ćÆć¾ć é«˜ććŖć„ēŠ¶ę…‹ć§ć™ćŒć€ä»Šå¾Œéœ²å‡ŗćŒå¢—ćˆć‚‹ćØč€ƒćˆć‚‰ć‚Œć¾ć™ć€‚ ć‚¢ćƒ—ćƒŖé–‹ē™ŗč€…ćÆć€ć“ć®ę–°ć—ć„ćƒ—ćƒ©ćƒƒćƒˆćƒ•ć‚©ćƒ¼ćƒ ć‚’ę—©ęœŸć«ę“»ē”Øć™ć‚‹ć“ćØć§ć€ē«¶åˆä»–ē¤¾ć«å…ˆé§†ć‘ć¦ćƒ¦ćƒ¼ć‚¶ćƒ¼ćØć®ęŽ„ē‚¹ć‚’å¢—ć‚„ć—ć€ć‚¢ćƒ—ćƒŖć®ęˆé•·ćØęˆåŠŸć«ć¤ćŖć’ć‚‰ć‚Œć‚‹ć§ć—ć‚‡ć†ć€‚ まとめ Engage SDK は、Android ć‚¢ćƒ—ćƒŖé–‹ē™ŗč€…ć«ćØć£ć¦ē”»ęœŸēš„ćŖćƒ„ćƒ¼ćƒ«ć§ć™ć€‚ å¾“ę„ć‚¢ćƒ—ćƒŖå†…ć«é–‰ć˜ć¦ć„ćŸć‚³ćƒ³ćƒ†ćƒ³ćƒ„ć‚’ Google Play ć‚¹ćƒˆć‚¢ć‚„ć‚¹ćƒžćƒ¼ćƒˆćƒ•ć‚©ćƒ³ć®ć‚³ćƒ¬ć‚Æć‚·ćƒ§ćƒ³ćŖć©ć€Android ć‚·ć‚¹ćƒ†ćƒ ć®č¤‡ę•°ć®å “ę‰€ć«å±•é–‹ć™ć‚‹ć“ćØć§ć€ćƒ¦ćƒ¼ć‚¶ćƒ¼ć‚Øćƒ³ć‚²ćƒ¼ć‚øćƒ”ćƒ³ćƒˆć®å¤§å¹…ćŖå‘äøŠć‚’å®Ÿē¾ć—ć¾ć™ć€‚ å‚č€ƒč³‡ę–™ developer.android.com developer.android.com developer.android.com android-developers.googleblog.com
ćÆć˜ć‚ć« 開発1éƒØć§ćƒ‡ćƒŖćƒƒć‚·ćƒ„ć‚­ćƒƒćƒćƒ³ć®ćƒ—ćƒ¬ćƒŸć‚¢ćƒ ę©Ÿčƒ½ć®é–‹ē™ŗć‚’ę‹…å½“ć—ć¦ć„ć‚‹å²©ļØ‘ć§ć™ć€‚ ē§ćÆå…„ē¤¾ć—ć¦åˆć‚ć¦Webćƒ•ćƒ¬ćƒ¼ćƒ ćƒÆćƒ¼ć‚Æć® labstack/echo ć«č§¦ć‚Œć¾ć—ćŸć€‚ ä½æć£ć¦ć„ćäø­ć§ć€Œä¾æåˆ©ć ć‘ć©ć“ć‚Œć©ć†ć‚„ć£ć¦å‹•ć„ć¦ć„ć‚‹ć‚“ć ć‚ć†ļ¼Ÿć€ćØę€ć†ć“ćØćŒå¢—ćˆć¦ćć¾ć—ćŸć€‚ ćć“ć‚’ę„č­˜ć—ćŖćć¦ć„ć„ć®ćŒćƒ•ćƒ¬ćƒ¼ćƒ ćƒÆćƒ¼ć‚Æć®č‰Æć„ćØć“ć‚ćŖć‚“ć ćØćÆę€ć„ć¾ć™ćŒć€ę°—ć«ćŖć‚‹ć®ć§å†…éƒØå®Ÿč£…ć‚’č¦—ć„ć¦ćæć‚ˆć†ćØę€ć„ć¾ć™ć€‚ 要瓄 ć“ć“ć‹ć‚‰å…ˆćÆé †ę¬”å®Ÿč£…ć‚’čæ½ć£ć¦ć„ćć®ć§ęœ€åˆć«ēµč«–ć‚’čØ˜č¼‰ć—ć¦ćŠćć¾ć™ć€‚ Echoć®ćƒćƒ³ćƒ‰ćƒ©é–¢ę•°ćŒ echo.Context ć‚’å¼•ę•°ć«å–ć‚‹ć®ćÆć€ćƒ•ćƒ¬ćƒ¼ćƒ ćƒÆćƒ¼ć‚Æć® HandlerFunc åž‹ć§ćć®ć‚ˆć†ć«å®šē¾©ć•ć‚Œć¦ć„ć‚‹ćŸć‚ć€‚ echo.Context は、HTTPćƒŖć‚Æć‚Øć‚¹ćƒˆć‚’å—ć‘å–ć£ćŸéš›ć«DI恕悌恟Echo恮 ServeHTTP ćƒ”ć‚½ćƒƒćƒ‰å†…ć§ć‚Ŗćƒ–ć‚øć‚§ć‚Æćƒˆćƒ—ćƒ¼ćƒ«ć‹ć‚‰å–å¾—ćƒ»åˆęœŸåŒ–ć•ć‚Œć‚‹ć€‚ ćć—ć¦ć€ćƒ«ćƒ¼ćƒ†ć‚£ćƒ³ć‚°ć«ć‚ˆć£ć¦ćƒŖć‚Æć‚Øć‚¹ćƒˆć«åÆ¾åæœć™ć‚‹ćƒćƒ³ćƒ‰ćƒ©ćŒē‰¹å®šć•ć‚Œć€ć“ć® echo.Context ćŒå¼•ę•°ćØć—ć¦ęø”ć•ć‚Œć¦å®Ÿč”Œć•ć‚Œć‚‹ć€‚ echo.ContextćÆć©ć“ć‹ć‚‰ćć¦ć„ć‚‹ć®ć‹ č¦‹å‡ŗć—ć«čØ˜č¼‰ć®é€šć‚Šć§ć™ćŒć€ä»Šå›žćÆecho.Contextć£ć¦ć©ć“ć‹ć‚‰å‘¼ć°ć‚Œć¦ć‚‹ć‚“ć ć‚ć†ć€ćØć„ć†ē–‘å•ć‚’č§£ę¶ˆć—ćŸć„ćØę€ć„ć¾ć™ć€‚ ć“ć‚ŒćŒć©ć†ć„ć†å•ć„ćŖć®ć‹ćÆć€ć‚³ćƒ¼ćƒ‰ć‚’čŖ­ć‚“ć§ć‚‚ć‚‰ć£ćŸę–¹ćŒę—©ć„ćØę€ć„ć¾ć™ć€‚ 仄下はEchoć‚’ä½æē”Øć—ćŸęœ€å°ć®Webć‚µćƒ¼ćƒćƒ¼ć®ć‚³ćƒ¼ćƒ‰ć§ć™ć€‚ package main import ( "net/http" "github.com/labstack/echo/v4" ) // Handler func hello(c echo.Context) error { return c.String(http.StatusOK, "Hello, World!" ) } func main() { // Echo instance e := echo.New() // Routes e.GET( "/" , hello) // Start server e.Start( ":1323" ) } ć“ć‚ŒćÆć€ćƒ«ćƒ¼ćƒˆćƒ‘ć‚¹ / 恫GETćƒŖć‚Æć‚Øć‚¹ćƒˆć‚’å‡¦ē†ć™ć‚‹ćƒćƒ³ćƒ‰ćƒ©ć‚’čæ½åŠ ć—ć€ćć®ćƒćƒ³ćƒ‰ćƒ©ćÆ"Hello, World!"ćƒ¬ć‚¹ćƒćƒ³ć‚¹ć‚’čæ”ć™ć‚ˆć†ćŖć‚·ćƒ³ćƒ—ćƒ«ćŖWebć‚µćƒ¼ćƒćƒ¼ć§ć™ć€‚ echo.New() 恧echoć®ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ć‚’ä½œęˆć—ć¦ć„ć¾ć™ć€‚ ć“ć“ć§ć€ćƒćƒ³ćƒ‰ćƒ©é–¢ę•°ć®å¼•ę•°ćØć—ć¦echo.Contextć‚’å—ć‘å–ć£ć¦ć„ć¾ć™ćŒć€ć“ć‚ŒćÆć©ć“ć‹ć‚‰ćć¦ć„ć‚‹ć®ć§ć—ć‚‡ć†ć‹ļ¼Ÿ ćƒćƒ³ćƒ‰ćƒ©é–¢ę•°ć®å¼•ę•°ć«echo.Contextć‚’čØ­å®šć—ćŖć‘ć‚Œć°ćŖć‚‰ćŖć„ē†ē”± ć¾ćšćÆćƒćƒ³ćƒ‰ćƒ©é–¢ę•°ć‚’ē™»éŒ²ć—ć¦ć„ć‚‹éƒØåˆ†ć®å†…éƒØå®Ÿč£…ć‚’č©³ć—ćč¦‹ć¦ć„ćć¾ć™ć€‚ e.GET() ćƒ”ć‚½ćƒƒćƒ‰ć®å®Ÿč£…ć‚’č¦‹ć‚‹ćØć€ē¬¬2引数として HandlerFunc åž‹ć‚’ęœŸå¾…ć—ć¦ć„ć‚‹ć“ćØćŒć‚ć‹ć‚Šć¾ć™ć€‚ļ¼ˆā€»1) // github.com/labstack/echo/v4@v4.13.4/echo.go func (e *Echo) GET(path string , h HandlerFunc, m ...MiddlewareFunc) *Route { return e.Add(http.MethodGet, pth, h, m...) } HandlerFunc ć®åž‹å®šē¾©ć«ć‚ˆć‚Šć€ē™»éŒ²ć™ć‚‹ć™ć¹ć¦ć®ćƒćƒ³ćƒ‰ćƒ©é–¢ę•°ćÆä»„äø‹ć®ć‚·ć‚°ćƒćƒćƒ£ć«å¾“ć†åæ…č¦ćŒć‚ć‚Šć¾ć™ć€‚ å¼•ę•°ļ¼šecho.Contextć‚’å—ć‘å–ć‚‹ ęˆ»ć‚Šå€¤ļ¼šerrorを返す // github.com/labstack/echo/v4@v4.13.4/echo.go // HandlerFunc defines a function to serve HTTP requests. type HandlerFunc func (c Context) error ć‚ˆć£ć¦ć“ć®åž‹å®šē¾©ć«å¾“ć‚ćŖć„ćƒćƒ³ćƒ‰ćƒ©ć‚’ē™»éŒ²ć—ć‚ˆć†ćØć™ć‚‹ćØć€äø‹čØ˜ć®ć‚ˆć†ć«ć‚Øćƒ©ćƒ¼ćŒå‡ŗć¾ć™ć€‚ ä»„äøŠć«ć‚ˆć‚Šć€echoćƒ•ćƒ¬ćƒ¼ćƒ ćƒÆćƒ¼ć‚Æć‚’ä½æē”Øć—ćŸćƒćƒ³ćƒ‰ćƒ©é–¢ę•°ć«ćÆåæ…ćšecho.Contextć‚’å¼•ę•°ćØć—ć¦å®šē¾©ć—ćŖć‘ć‚Œć°ćŖć‚‰ćŖć„ć“ćØćŒć‚ć‹ć‚Šć¾ć—ćŸć€‚ ではどこからecho.ContextćŒćć¦ć„ć‚‹ć®ć‹ ć“ć“ć‹ć‚‰ćŒęœ¬é”Œć§ć™ć€‚ GETćƒ”ć‚½ćƒƒćƒ‰ćŒå‘¼ć°ć‚ŒćŸę™‚ć«ć€ć©ć®ć‚ˆć†ć«ćƒćƒ³ćƒ‰ćƒ©ć«echo.ContextćŒęø”ć•ć‚Œć‚‹ć®ć‹ć«ć¤ć„ć¦č¦‹ć¦ć„ćć¾ć™ć€‚ ć‚Æćƒ©ć‚¤ć‚¢ćƒ³ćƒˆć‹ć‚‰HTTPćƒŖć‚Æć‚Øć‚¹ćƒˆćŒćć‚‹ćØć€Goć®ęØ™ęŗ–ćƒ©ć‚¤ćƒ–ćƒ©ćƒŖć§ć‚ć‚‹http.ServerがServeHTTPćƒ”ć‚½ćƒƒćƒ‰ć‚’å‘¼ć³å‡ŗć—ć¾ć™ć€‚ net/http 恓恓恧Goć®ęØ™ęŗ–ćƒ©ć‚¤ćƒ–ćƒ©ćƒŖć§ć‚ć‚‹ net/http ćƒ‘ćƒƒć‚±ćƒ¼ć‚øć«ć¤ć„ć¦ē°”å˜ć«č§¦ć‚Œć¦ćŠćć¾ć™ć€‚ net/http ćƒ‘ćƒƒć‚±ćƒ¼ć‚øć®Handlerć‚¤ćƒ³ć‚æćƒ¼ćƒ•ć‚§ćƒ¼ć‚¹ćÆć€ ServeHTTP ćƒ”ć‚½ćƒƒćƒ‰ć‚’1ć¤ć ć‘ęŒć”ć¾ć™ć€‚ type Handler interface { ServeHTTP(ResponseWriter, *Request) } Web ć‚µćƒ¼ćƒćŒćƒŖć‚Æć‚Øć‚¹ćƒˆć‚’å—ć‘å–ć‚‹ćØ ServeHTTP ćŒå„ćƒŖć‚Æć‚Øć‚¹ćƒˆć”ćØć«å®Ÿč”Œć•ć‚Œć€ćƒ¬ć‚¹ćƒćƒ³ć‚¹ć®å‡ŗåŠ›å…ˆć§ć‚ć‚‹ http.ResponseWriter ć«ćƒ¬ć‚¹ćƒćƒ³ć‚¹å†…å®¹ćŒę›øćč¾¼ć¾ć‚Œć¾ć™ć€‚ ęØ™ęŗ–ćƒ‘ćƒƒć‚±ćƒ¼ć‚øå†…ć§ćÆåŸŗåŗ•ć‚³ćƒ³ćƒ†ć‚­ć‚¹ćƒˆć‚’å®šē¾©ć—ć€ęŽ„ē¶šć”ćØć®ć‚³ćƒ³ćƒ†ć‚­ć‚¹ćƒˆć‚’ēµ„ćæē«‹ć¦ć¦ć„ć‚‹ć“ćØćŒć‚ć‹ć‚Šć¾ć™ć€‚ func (srv *Server) Serve(l net.Listener) error { // ēœē•„ baseCtx := context.Background() if srv.BaseContext != nil { baseCtx = srv.BaseContext(origListener) if baseCtx == nil { panic ( "BaseContext returned a nil context" ) } } // ēœē•„ ctx := context.WithValue(baseCtx, ServerContextKey, srv) for { // ēœē•„ connCtx := ctx if cc := srv.ConnContext; cc != nil { connCtx = cc(connCtx, rw) if connCtx == nil { panic ( "ConnContext returned nil" ) } } // ēœē•„ go c.serve(connCtx) } Echo恮ServeHTTP echo.New() ć§ä½œęˆć•ć‚Œć‚‹Echoć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ćÆć€å…ˆć»ć©čŖ¬ę˜Žć—ćŸ http.Handler ć‚¤ćƒ³ć‚æćƒ¼ćƒ•ć‚§ćƒ¼ć‚¹ć‚’å®Ÿč£…ć—ć¦ć„ć¾ć™ć€‚ ć¤ć¾ć‚ŠEcho悂 ServeHTTP ćƒ”ć‚½ćƒƒćƒ‰ć‚’ęŒć£ć¦ć„ć¾ć™ć€‚ Echoć®ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ä½œęˆę™‚ć« http.Server 恮Handlerćƒ•ć‚£ćƒ¼ćƒ«ćƒ‰ć«ä¾å­˜ę€§ę³Øå…„ļ¼ˆDIļ¼‰ć—ć¦ć„ć‚‹ćŸć‚ć€ćƒŖć‚Æć‚Øć‚¹ćƒˆć‚’å—ć‘å–ć‚‹ćØEchoć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ć® ServeHTTP ćŒå‘¼ć°ć‚Œć‚‹ć®ć§ć™ć€‚ // github.com/labstack/echo/v4@v4.13.4/echo.go func New() (e *Echo) { e = &Echo{ Server: new (http.Server), // ćć®ä»–ć®åˆęœŸåŒ– } // ēœē•„ e.Server.Handler = e // ēœē•„ return } ではEcho恮ServeHTTPćƒ”ć‚½ćƒƒćƒ‰ć‚’ć‚‚ć†å°‘ć—č©³ć—ććæć¦ć„ćć¾ć™ć€‚ // github.com/labstack/echo/v4@v4.13.4/echo.go // ServeHTTP implements `http.Handler` interface, which serves HTTP requests. func (e *Echo) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Acquire context c := e.pool.Get().(*context) c.Reset(r, w) var h HandlerFunc if e.premiddleware == nil { e.findRouter(r.Host).Find(r.Method, GetPath(r), c) h = c.Handler() h = applyMiddleware(h, e.middleware...) } else { h = func (c Context) error { e.findRouter(r.Host).Find(r.Method, GetPath(r), c) h := c.Handler() h = applyMiddleware(h, e.middleware...) return h(c) } h = applyMiddleware(h, e.premiddleware...) } // Execute chain if err := h(c); err != nil { e.HTTPErrorHandler(err, c) } // Release context e.pool.Put(c) } まず e.pool.Get().(*context) ć§ćƒ—ćƒ¼ćƒ«ļ¼ˆā€»2)からcontextć‚’å–å¾—ć—ć€Resetćƒ”ć‚½ćƒƒćƒ‰ć§åˆęœŸåŒ–ć—ć¾ć™ć€‚ 恓恓恧态Echo恮contextę§‹é€ ä½“ćÆä»„äø‹ć®ć‚ˆć†ćŖćƒŖć‚Æć‚Øć‚¹ćƒˆć€ćƒ¬ć‚¹ćƒćƒ³ć‚¹ć«é–¢ć™ć‚‹ęƒ…å ±ć‚’ęŒć”ć¾ć™ć€‚ // github.com/labstack/echo/v4@v4.13.4/context.go type context struct { logger Logger request *http.Request response *Response query url.Values echo *Echo store Map lock sync.RWMutex // following fields are set by Router handler HandlerFunc // path is route path that Router matched. It is empty string where there is no route match. // Route registered with RouteNotFound is considered as a match and path therefore is not empty. path string // Usually echo.Echo is sizing pvalues but there could be user created middlewares that decide to // overwrite parameter by calling SetParamNames + SetParamValues. // When echo.Echo allocated that slice it length/capacity is tied to echo.Echo.maxParam value. // // It is important that pvalues size is always equal or bigger to pnames length. pvalues [] string // pnames length is tied to param count for the matched route pnames [] string } ꬔ恫 e.findRouter(r.Host).Find(r.Method, GetPath(r), c) ć«ć‚ˆć‚ŠHTTPćƒŖć‚Æć‚Øć‚¹ćƒˆć‹ć‚‰å–å¾—ć—ćŸHTTPćƒ”ć‚½ćƒƒćƒ‰ćØćƒ‘ć‚¹ć‹ć‚‰ęŽ¢ē“¢ć‚’č”Œć„ć¾ć™ć€‚ļ¼ˆā€»3) č©³ē“°ćŖęŽ¢ē“¢ć‚¢ćƒ«ć‚“ćƒŖć‚ŗćƒ ćÆå‰²ę„›ć—ć¾ć™ćŒć€ćƒ«ćƒ¼ćƒ†ć‚£ćƒ³ć‚°ćÆć€Œé™ēš„ćƒ‘ć‚¹ > ćƒ‘ćƒ©ćƒ”ćƒ¼ć‚æä»˜ććƒ‘ć‚¹ > ćƒÆć‚¤ćƒ«ćƒ‰ć‚«ćƒ¼ćƒ‰ćƒ‘ć‚¹ć€ć®é †ć«ćƒ„ćƒŖćƒ¼ć®ćƒŽćƒ¼ćƒ‰ć‚’ęŽ¢ē“¢ć—ć¾ć™ć€‚ ćć—ć¦ćƒ«ćƒ¼ćƒˆćŒč¦‹ć¤ć‹ć£ćŸå “åˆć€ä»„äø‹ć®ęƒ…å ±ćŒcontextć«čØ­å®šć•ć‚Œć¾ć™ć€‚ ćƒćƒ³ćƒ‰ćƒ©é–¢ę•°ļ¼ˆ ctx.handler ) ćƒ«ćƒ¼ćƒˆćƒ‘ć‚¹ļ¼ˆ ctx.path ) ćƒ‘ćƒ©ćƒ”ćƒ¼ć‚æåć®é…åˆ—ļ¼ˆ ctx.pnames ) // github.com/labstack/echo/v4@v4.13.4/router.go func (r *Router) Find(method, path string , c Context) { // ēœē•„ var rPath string var rPNames [] string if matchedRouteMethod != nil { rPath = matchedRouteMethod.ppath rPNames = matchedRouteMethod.pnames ctx.handler = matchedRouteMethod.handler } else { // ēœē•„ } ctx.path = rPath ctx.pnames = rPNames } ć“ć†ć—ć¦å¾—ć‚‰ć‚ŒćŸćƒćƒ³ćƒ‰ćƒ©é–¢ę•°ć«echo.Contextć‚’å¼•ę•°ćØć—ć¦ęø”ć™ć“ćØć§å‡¦ē†ćŒå®Ÿč”Œć•ć‚Œć¾ć™ć€‚ļ¼ˆā€»4) ć‚ˆć£ć¦å…ˆć»ć©å®šē¾©ć—ćŸćƒ«ćƒ¼ćƒˆćƒ‘ć‚¹ć®helloé–¢ę•°ćÆć“ć“ć§å®Ÿč”Œć•ć‚Œć¦ć„ć‚‹ć®ć§ć™ć€‚ // github.com/labstack/echo/v4@v4.13.4/echo.go // ServeHTTP implements `http.Handler` interface, which serves HTTP requests. func (e *Echo) ServeHTTP(w http.ResponseWriter, r *http.Request) { // ēœē•„ // Execute chain if err := h(c); err != nil { e.HTTPErrorHandler(err, c) } // Release context e.pool.Put(c) } あとは e.HTTPErrorHandler(err, c) ć«ć‚ˆć‚‹ć‚Øćƒ©ćƒ¼ćƒćƒ³ćƒ‰ćƒŖćƒ³ć‚°ć‚’ēµŒć¦ć€contextć‚’ćƒ—ćƒ¼ćƒ«ļ¼ˆā€»1)へPUTć™ć‚Œć°å‡¦ē†ćÆå®Œäŗ†ć§ć™ć€‚ まとめ ć¾ćØć‚ćÆä»„äø‹ć®é€šć‚Šć§ć™ć€‚ Echoć®ćƒćƒ³ćƒ‰ćƒ©é–¢ę•°ćŒ echo.Context ć‚’å¼•ę•°ć«å–ć‚‹ć®ćÆć€ćƒ•ćƒ¬ćƒ¼ćƒ ćƒÆćƒ¼ć‚Æć® HandlerFunc åž‹ć§ćć®ć‚ˆć†ć«å®šē¾©ć•ć‚Œć¦ć„ć‚‹ćŸć‚ć€‚ echo.Context は、HTTPćƒŖć‚Æć‚Øć‚¹ćƒˆć‚’å—ć‘å–ć£ćŸéš›ć«DI恕悌恟Echo恮 ServeHTTP ćƒ”ć‚½ćƒƒćƒ‰å†…ć§ć‚Ŗćƒ–ć‚øć‚§ć‚Æćƒˆćƒ—ćƒ¼ćƒ«ć‹ć‚‰å–å¾—ćƒ»åˆęœŸåŒ–ć•ć‚Œć‚‹ć€‚ ćć—ć¦ć€ćƒ«ćƒ¼ćƒ†ć‚£ćƒ³ć‚°ć«ć‚ˆć£ć¦ćƒŖć‚Æć‚Øć‚¹ćƒˆć«åÆ¾åæœć™ć‚‹ćƒćƒ³ćƒ‰ćƒ©ćŒē‰¹å®šć•ć‚Œć€ć“ć® echo.Context ćŒå¼•ę•°ćØć—ć¦ęø”ć•ć‚Œć¦å®Ÿč”Œć•ć‚Œć‚‹ć€‚ ć»ć‚“ć®äø€éƒØć§ć™ćŒć€Echoć®äø­ć‚’č¦—ćć“ćØć§HTTPćƒŖć‚Æć‚Øć‚¹ćƒˆćŒć©ć®ęµć‚Œć§å‡¦ē†ć•ć‚Œć¦ć„ć‚‹ć®ć‹å¤§ęž ęŠŠę”ć™ć‚‹ć“ćØćŒć§ćć¾ć—ćŸć€‚ 調べているうごにmiddlewareć§ä½•ć‚’ć—ć¦ć„ć‚‹ć®ć‹ćŒę°—ć«ćŖć£ć¦ććŸć®ć§ć€ę¬”ćÆćć“ć‚’čŖæć¹ć¦ćæć‚ˆć†ćØę€ć„ć¾ć™ć€‚ ęœ€å¾Œć¾ć§čŖ­ć‚“ć§ć„ćŸć ćć‚ć‚ŠćŒćØć†ć”ć–ć„ć¾ć—ćŸļ¼ č„šę³Ø ※1) ä»Šå›žćÆGETćƒ”ć‚½ćƒƒćƒ‰ć‚’ä½æć£ć¦ć„ć¾ć™ćŒć€ćć®ä»–ć®HTTPćƒ”ć‚½ćƒƒćƒ‰ć§ć‚‚åŒć˜ē™»éŒ²å‡¦ē†ć‚’č”ŒćŖć£ć¦ć„ć¾ć™ć€‚ ※2) sync package - sync - Go Packages ※3ļ¼‰ćƒŸćƒ‰ćƒ«ć‚¦ć‚§ć‚¢ć®å‡¦ē†ćÆä»Šå›žć®ć‚¹ć‚³ćƒ¼ćƒ—å¤–ćŖć®ć§ēœē•„ć—ć¦ć„ć¾ć™ć€‚ ※4ļ¼‰å®Ÿéš›ć«ćÆć€ćƒćƒ³ćƒ‰ćƒ©é–¢ę•°ćÆćƒŸćƒ‰ćƒ«ć‚¦ć‚§ć‚¢ć§ćƒ©ćƒƒćƒ—ć•ć‚Œć¦ćŠć‚ŠćƒŸćƒ‰ćƒ«ć‚¦ć‚§ć‚¢ć®ćƒć‚§ćƒ¼ćƒ³å‡¦ē†ć®å®Ÿč”Œå¾Œć«ćƒćƒ³ćƒ‰ćƒ©é–¢ę•°ćŒå®Ÿč”Œć•ć‚Œć¾ć™ćŒć€ä»Šå›žć®čØ˜äŗ‹ć®ć‚¹ć‚³ćƒ¼ćƒ—å¤–ćŖć®ć§ēœē•„ć—ć¦ć„ć¾ć™ć€‚
ćÆć˜ć‚ć« ć“ć®åŗ¦ć€ę Ŗå¼ä¼šē¤¾ć‚Øćƒ–ćƒŖćƒ¼ćÆć€2025 幓 9 月 27 ę—„(土)态28 ę—„(ę—„)ć«é–‹å‚¬ć•ć‚Œć‚‹ć€ŒGo Conference 2025ć€ć«ć€Platinum "Go"ld ć‚¹ćƒćƒ³ć‚µćƒ¼ćØć—ć¦ę˜Øå¹“ć«ē¶šćå”č³›ć™ć‚‹ć“ćØć«ćŖć‚Šć¾ć—ćŸļ¼ Go Conferenceとは? gocon.jp ćƒ—ćƒ­ć‚°ćƒ©ćƒŸćƒ³ć‚°čØ€čŖž ā€Goā€ćƒ¦ćƒ¼ć‚¶ćƒ¼ć®ćŸć‚ć®ć‚«ćƒ³ćƒ•ć‚”ćƒ¬ćƒ³ć‚¹ć§ć™ć€‚ä»Šå¹“ćÆćŖć‚“ćØ2ę—„é–“é–‹å‚¬ć§ć€ć‚»ćƒƒć‚·ćƒ§ćƒ³ćÆć‚‚ć”ć‚ć‚“ć€ćƒÆćƒ¼ć‚Æć‚·ćƒ§ćƒƒćƒ—ć‚„ę‡‡č¦Ŗä¼šćŖć©å¤šę•°ć®ć‚³ćƒ³ćƒ†ćƒ³ćƒ„ćŒē”Øę„ć•ć‚Œć¦ć„ć¾ć™ļ¼ ä»Šå¹“ć®é–‹å‚¬ę¦‚č¦ćÆä»„äø‹ć®ćØćŠć‚Šć§ć™ć€‚ 開催旄時 Day 1: 2025幓9月27ę—„ļ¼ˆåœŸļ¼‰ Day 2: 2025幓9月28ę—„ļ¼ˆę—„ļ¼‰ 開催堓所 ę±äŗ¬éƒ½ęø‹č°·åŒŗå®‡ē”°å·ē”ŗ40番1号 Abema Towers 10F / 11F ć‚³ćƒ³ćƒ†ćƒ³ćƒ„ ćƒ»ć‚¹ćƒćƒ³ć‚µćƒ¼ć‚»ćƒƒć‚·ćƒ§ćƒ³ ćƒ»å…¬å‹Ÿć‚»ćƒƒć‚·ćƒ§ćƒ³ļ¼ˆLong / Short) 惻LT ćƒ»ćƒÆćƒ¼ć‚Æć‚·ćƒ§ćƒƒćƒ— ćƒ»ę‡‡č¦Ŗä¼š ć‚»ćƒƒć‚·ćƒ§ćƒ³ć‚„ćƒÆćƒ¼ć‚Æć‚·ćƒ§ćƒƒćƒ—ćÆć€čØ€čŖžä½æē”Øć‹ć‚‰å®Ÿå‹™ć«čæ‘ć„č©±ć¾ć§ę§˜ć€…ćŖå†…å®¹ćŒč¦‹ć‚‰ć‚Œć¾ć™ć€‚2025幓8月に Go 1.25 ćŒćƒŖćƒŖćƒ¼ć‚¹ć•ć‚ŒćŸć“ćØć‚‚ć‚ć‚Šć€ćć‚Œć«é–¢ć™ć‚‹ć‚»ćƒƒć‚·ćƒ§ćƒ³ć‚‚ę³Øē›®ć—ćŸć„ćØć“ć‚ć§ć™ļ¼ē­†č€…å€‹äŗŗćØć—ć¦ćÆć€ć‚øć‚§ćƒćƒŖć‚Æć‚¹é–¢é€£ć®ć‚»ćƒƒć‚·ćƒ§ćƒ³ćŒć„ćć¤ć‹ć‚ć‚‹ć®ć§ę°—ć«ćŖć£ć¦ć„ć¾ć™ļ¼ ć‚¤ćƒ™ćƒ³ćƒˆå½“ę—„ć«ć¤ć„ć¦ Day 2 恮 28ę—„ļ¼ˆę—„ļ¼‰18:00~18:20ć«ć€å¼Šē¤¾ć®ęœ¬äøøć‹ć‚‰ć‚¹ćƒćƒ³ć‚µćƒ¼ć‚»ćƒƒć‚·ćƒ§ćƒ³ć‚’ć•ć›ć¦ć„ćŸć ćć¾ć™ļ¼ć€Œ 10幓もののAPIć‚µćƒ¼ćƒćƒ¼ć«ćŠć‘ć‚‹CI/CDć®ę”¹å–„ć®å„®é—˜ ć€ćØć„ć†ć‚æć‚¤ćƒˆćƒ«ć§ć€Goとecho恧꧋ꈐ恕悌恟APIć‚µćƒ¼ćƒćƒ¼ć®CI/CDé«˜é€ŸåŒ–ć«ć¤ć„ć¦ćŠč©±ć™ć‚‹äŗˆå®šć§ć™ć€‚ćœć²ćŠč¶Šć—ćć ć•ć„ļ¼ gocon.jp ć‚Øćƒ–ćƒŖćƒ¼ć®ćƒ–ćƒ¼ć‚¹ć§ćÆć€Xćƒ•ć‚©ćƒ­ćƒ¼ć§ćƒŽćƒ™ćƒ«ćƒ†ć‚£ć‚„ć‚­ćƒƒćƒćƒ³ć‚°ćƒƒć‚ŗćŖć©ćŒå½“ćŸć‚‹ćć˜å¼•ćć€ć‚¢ćƒ³ć‚±ćƒ¼ćƒˆćƒœćƒ¼ćƒ‰ć€ćƒ•ć‚©ćƒˆćƒ–ćƒ¼ć‚¹ć‚’čØ­ē½®ć—ć¾ć™ć€‚ Go Conference 2025 ć§ēš†ć•ć‚“ćØč‰Æć„å­¦ć³ćƒ»ę€ć„å‡ŗć‚’ä½œć‚Œć‚‹ć“ćØć‚’ę„½ć—ćæć«ć—ć¦ć„ć¾ć™ļ¼ ćƒŽćƒ™ćƒ«ćƒ†ć‚£ éžå…¬å¼ć‚¢ćƒ•ć‚æćƒ¼ć‚¤ćƒ™ćƒ³ćƒˆć®ć”ę”ˆå†… Go BASH Vol.2 LayerX态ANDPAD态OPTiM态Resilireć€ć‚Øćƒ–ćƒŖćƒ¼ć®5ē¤¾åˆåŒć§ć€éžå…¬å¼ć‚¢ćƒ•ć‚æćƒ¼ć‚¤ćƒ™ćƒ³ćƒˆ Go BASH Vol.2 を開催します! å¼Šē¤¾ć‹ć‚‰ćÆęœ¬äøøć€hondćŒē™»å£‡ć—ć¾ć™ļ¼ć‚»ćƒƒć‚·ćƒ§ćƒ³ć‚„ę„Ÿęƒ³ęˆ¦ćŖć©ć®ć‚³ćƒ³ćƒ†ćƒ³ćƒ„ć‚’ē”Øę„ć—ć¦ćŠć‚Šć¾ć™ć®ć§ć€ćæć‚“ćŖć§Goć§ē››ć‚ŠäøŠćŒć‚Šć¾ć—ć‚‡ć†ļ¼ 開催旄時 2025幓10月6ę—„ļ¼ˆęœˆļ¼‰ 開催堓所 ę±äŗ¬éƒ½ęøÆåŒŗęµ·å²ø1丁目2番20号 ę±ē•™ćƒ“ćƒ«ćƒ‡ć‚£ćƒ³ć‚°18F OPTiM TOKYOļ¼ˆę±äŗ¬ęœ¬ē¤¾ļ¼‰ 開催形態 ć‚Ŗćƒ•ćƒ©ć‚¤ćƒ³ / ć‚Ŗćƒ³ćƒ©ć‚¤ćƒ³ ć‚³ćƒ³ćƒ†ćƒ³ćƒ„ ćƒ»å„ē¤¾ć®Goć«é–¢ć™ć‚‹ć‚»ćƒƒć‚·ćƒ§ćƒ³ 惻Go Conference 2025 ę„Ÿęƒ³ęˆ¦ ・交流会 ćŠē”³ć—č¾¼ćæćÆä»„äø‹ć§č”Œć£ć¦ćŠć‚Šć¾ć™ć€‚ćæćŖć•ć‚“ć®ć”å‚åŠ ć‚’ćŠå¾…ć”ć—ć¦ć„ć¾ć™ļ¼ layerx.connpass.com ęœ€å¾Œć¾ć§ćŠčŖ­ćæć„ćŸć ćć€ć‚ć‚ŠćŒćØć†ć”ć–ć„ć¾ć—ćŸļ¼
目欔 ćÆć˜ć‚ć« å­¦ēæ’ć®čƒŒę™ÆćØē’°å¢ƒ ćƒ‡ć‚£ćƒ¬ć‚ÆćƒˆćƒŖę§‹é€  Cursor rules å­¦ēæ’é–‹å§‹ę™‚ć®ćƒ—ćƒ­ćƒ³ćƒ—ćƒˆ å­¦ēæ’ć®ęµć‚Œ ć‚¹ćƒ©ć‚¤ć‚¹ć®å†…éƒØę§‹é€ ć«ć¤ć„ć¦ ć‚¹ćƒ©ć‚¤ć‚¹ć®å®¹é‡ę‹”å¼µć‚¢ćƒ«ć‚“ćƒŖć‚ŗćƒ ć«ć¤ć„ć¦ čØˆē®—é‡ćØćƒ‘ćƒ•ć‚©ćƒ¼ćƒžćƒ³ć‚¹ć®č¦ē‚¹ ć‚¹ćƒ©ć‚¤ć‚¹ćŒčµ·ć“ć™ćƒ”ćƒ¢ćƒŖćƒŖćƒ¼ć‚Æ ę–‡å­—åˆ—ć«ćŠć‘ć‚‹ćƒ”ćƒ¢ćƒŖćƒŖćƒ¼ć‚Æ å¤šę¬”å…ƒć‚¹ćƒ©ć‚¤ć‚¹ć®ćƒ«ćƒ¼ćƒ—å‡¦ē† ćƒć‚¹ćƒˆć—ćŸę§‹é€ ä½“ć®ćƒ«ćƒ¼ćƒ—å‡¦ē† ćƒ”ćƒ¢ćƒŖćƒ—ćƒ¼ćƒ«ć®ę“»ē”Ø ꉀꄟ ćŠć‚ć‚Šć« å‚č€ƒ ćÆć˜ć‚ć« こんにごは。 é–‹ē™ŗęœ¬éƒØé–‹ē™ŗ1éƒØćƒˆćƒ¢ćƒ‹ćƒ†é–‹ē™ŗéƒØę‰€å±žć®åŗ„åø( @ktanonymous )恧恙怂 ęœ€čæ‘ć€AI ć‚’åˆ©ē”Øć—ć¦ Go ć®ć‚¹ćƒ©ć‚¤ć‚¹ć‚„ćƒ”ćƒ¢ćƒŖć€ćƒ‘ćƒ•ć‚©ćƒ¼ćƒžćƒ³ć‚¹ć«é–¢ć™ć‚‹å†…å®¹ć‚’å­¦ēæ’ć—ć¦ćæć‚‹ę©Ÿä¼šćŒć‚ć‚Šć¾ć—ćŸć€‚ ä»Šå›žćÆćć®éš›ć®ä½“éØ“ć«ć¤ć„ć¦ć¾ćØć‚ć¦ćæćŸć„ćØę€ć„ć¾ć™ć€‚ ćŖćŠć€AI ć®å‡ŗåŠ›ć«é–¢ć—ć¦ćÆå‡ŗåŠ›å†…å®¹ć‚’ćć®ć¾ć¾ä½æē”Øć—ć¦ć„ć‚‹ćŸć‚äø€éƒØčŖ¤ć£ć¦ć„ć‚‹ē®‡ę‰€ć‚‚ć‚ć‚Šć¾ć™ćŒć€ é£½ćć¾ć§ć‚‚åÆ¾č©±ć®ćƒ­ć‚°ćØć—ć¦åŸŗęœ¬ēš„ć«ćÆę”¹å¤‰ć›ćšć«čØ˜č¼‰ć—ć¦ć„ć¾ć™ć€‚ čØ˜äŗ‹ęœ¬ę–‡ćØ AI ć®å‡ŗåŠ›ć§å†…å®¹ćŒē•°ćŖć‚‹å “åˆćÆčØ˜äŗ‹ęœ¬ę–‡ć®å†…å®¹ć‚’å„Ŗå…ˆć—ć¦ćć ć•ć„ć€‚ ć¾ćŸć€cursor ć®åÆ¾č©±ć‚’é€šć—ć¦å­¦ć°ćŖć‹ć£ćŸć“ćØć«ć¤ć„ć¦ćÆč§¦ć‚ŒćŖć„ć®ć§ć€ 怌xxć«ćÆā—Æā—Æć‚‚ä½æćˆć‚‹ć‚ˆć€ćØę€ć†ć“ćØć‚‚ć‚ć‚‹ć‹ć‚‚ć—ć‚Œć¾ć›ć‚“ćŒć”äŗ†ę‰æćć ć•ć„ć€‚ å­¦ēæ’ć®čƒŒę™ÆćØē’°å¢ƒ ē›®ēš„ : å®Œå…Øć« AI äø»å°Žć«ć‚ˆć‚‹å­¦ēæ’ä½“éØ“ć‚’ę¤œčØ¼ć™ć‚‹ åˆ©ē”Øćƒ¢ćƒ‡ćƒ« : claude-4-sonnet-thinking AI ć‚Øćƒ‡ć‚£ć‚æ : Cursor ćƒ‡ć‚£ćƒ¬ć‚ÆćƒˆćƒŖę§‹é€  /training ā”œā”€ā”€ .cursor │ └── rules │ └── project_description.mdc └── go_slice ā”œā”€ā”€ compose.yml ā”œā”€ā”€ docker │ └── app ā”œā”€ā”€ examples # ć‚µćƒ³ćƒ—ćƒ«ć‚³ćƒ¼ćƒ‰ ā”œā”€ā”€ README.md └── src └── go.mod Cursor rules ※ å­¦ēæ’ć‚’é€²ć‚ćŖćŒć‚‰ę›“ę–°ć—ćŸéƒØåˆ†ć‚‚ć‚ć‚Šć¾ć™ --- description: globs: alwaysApply: true --- # ćƒ—ćƒ­ć‚øć‚§ć‚Æćƒˆę§‹ęˆć«ć¤ć„ć¦ - ć“ć®ćƒ—ćƒ­ć‚øć‚§ć‚ÆćƒˆćÆćƒ¦ćƒ¼ć‚¶ćƒ¼ćŒå­¦ēæ’ć®ćŸć‚ć«åˆ©ē”Øć—ć¾ć™ć€‚ ## ćƒ‡ć‚£ćƒ¬ć‚ÆćƒˆćƒŖę§‹ęˆć«ć¤ć„ć¦ - å­¦ēæ’ć™ć‚‹é ˜åŸŸć”ćØć«ćƒ‡ć‚£ćƒ¬ć‚ÆćƒˆćƒŖć‚’ä½œęˆć—ć¾ć™ć€‚ - å­¦ēæ’ē’°å¢ƒćÆć‚³ćƒ³ćƒ†ćƒŠē’°å¢ƒć§å®Ÿč”Œć—ć¾ć™ć€‚ - ćƒ‡ć‚£ćƒ¬ć‚ÆćƒˆćƒŖå†…ć®ćƒˆćƒƒćƒ—ćƒ¬ćƒ™ćƒ«ć« ` compose.yml ` 悒态 ` docker ` ćƒ‡ć‚£ćƒ¬ć‚ÆćƒˆćƒŖć«å½¹å‰²ć”ćØć®ćƒ‡ć‚£ćƒ¬ć‚ÆćƒˆćƒŖć‚’ä½œęˆć—ć¦ ` Dockerfile ` ć‚’ä½œęˆć—ć¦ćć ć•ć„ć€‚ - 例 ```bash - react/ - compose.yml - docker - app - Dockerfile - db - Dockerfile - src ``` - å‚č€ƒęƒ…å ±ć«é–¢ć—ć¦ćÆć€webę¤œē“¢ćŖć©ć§ęœ€ę–°ć®å…¬å¼ćƒ‰ć‚­ćƒ„ćƒ”ćƒ³ćƒˆć‚’å‚ē…§ć—ć¦ćć ć•ć„ć€‚ ## 学習の進め方について - å­¦ēæ’å†…å®¹ć®ęœ¬č³Ŗć‚’čŗ«ć«ć¤ć‘ć‚‹ćŸć‚ć«ć‚µćƒ³ćƒ—ćƒ«ć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³ć‚’ä½œęˆć—ć¾ć™ - å­¦ēæ’ćÆē’°å¢ƒę§‹ēÆ‰ć‹ć‚‰å§‹ć‚ć¾ć™ - å­¦ēæ’å†…å®¹ć‚’å®šē€ć•ć›ć‚‹ćŸć‚ć«ć€å®Ÿč”Œć™ć‚‹ć¹ćć‚³ćƒžćƒ³ćƒ‰ć‚„å®Ÿč£…ć™ć‚‹ć¹ćć‚³ćƒ¼ćƒ‰ć‚’ć‚Æć‚¤ć‚ŗå½¢å¼ć«ć—ć¦ćć ć•ć„ - ć‚Æć‚¤ć‚ŗć®å›žē­”ć‚„ē¢ŗčŖäŗ‹é …ć«åÆ¾ć™ć‚‹čæ”ē­”ć‚’å¾…ćŸćšć«ę¬”ć®č©±ć‚’é€²ć‚ćŖć„ć§ćć ć•ć„ć€‚ - å­¦ēæ’ć®ēµŒéŽć‚’ē¢ŗčŖć§ćć‚‹ć‚ˆć†ć«ć€å„ć‚¹ćƒ†ćƒƒćƒ—ć§ä½œęˆć—ćŸć‚µćƒ³ćƒ—ćƒ«ć‚³ćƒ¼ćƒ‰ćÆå‰Šé™¤ć—ćŸć‚ŠäøŠę›øćć—ćŸć‚Šć—ćŖć„ć§ćć ć•ć„ć€‚ - å­¦ēæ’ćƒ†ćƒ¼ćƒžćÆä¼šč©±ć®ćŸć³ć«åæ…ćšę€ć„å‡ŗć—ć¦åæ˜ć‚ŒćŖć„ć‚ˆć†ć«ć—ć¦ćć ć•ć„ć€‚ å­¦ēæ’é–‹å§‹ę™‚ć®ćƒ—ćƒ­ćƒ³ćƒ—ćƒˆ ## 学習内容 goć®ć‚¹ćƒ©ć‚¤ć‚¹ćÆé…åˆ—ć®ę ¹ęœ¬ēš„ćŖä»•ēµ„ćæć«ć¤ć„ć¦ć€ē“°ć‹ć„å‹•ä½œä»•ę§˜ć‚„ę™‚é–“ļ¼ē©ŗé–“čØˆē®—é‡ćŖć©ć®č¦³ē‚¹ć‹ć‚‰ę·±ęŽ˜ć‚ŠćŸć„ć§ć™ ## 制限 äøŠčØ˜ć®å­¦ēæ’å†…å®¹ćÆä¼šč©±ć®ęœ€åˆć«åæ…ćšę€ć„å‡ŗć—ć¦åæ˜ć‚ŒćŖć„ć‚ˆć†ć«ć—ć¦ćć ć•ć„ć€‚ å­¦ēæ’ć®ęµć‚Œ å­¦ēæ’ćÆä»„äø‹ć®ć‚ˆć†ćŖć‚¹ćƒ†ćƒƒćƒ—ć§é€²ćæć¾ć—ćŸć€‚ åŸŗęœ¬ēš„ć«ćÆć€å„ć‚¹ćƒ†ćƒƒćƒ—ć§ć€Œē‹™ć„ć®ęē¤ŗ -> ć‚µćƒ³ćƒ—ćƒ«ć‚³ćƒ¼ćƒ‰å®Ÿč£… -> クイズ -> č§£ē­”ćƒ»č§£čŖ¬ć€ć‚’ē¹°ć‚Ščæ”ć—ć¦ć„ć¾ć™ć€‚ ć¾ćŸć€ć‚Øćƒ©ćƒ¼ćŖć©ć§å­¦ēæ’č‡Ŗä½“ćŒč©°ć¾ć‚‹ć“ćØćÆćŖćć€ é€”äø­ć§ę°—ć«ćŖć£ćŸå†…å®¹ć«é–¢ć—ć¦ćÆčæ½åŠ ć§ē¢ŗčŖć™ć‚‹ć“ćØć‚‚ć§ćć¾ć—ćŸć€‚ ē’°å¢ƒę§‹ēÆ‰ ć‚¹ćƒ©ć‚¤ć‚¹å†…éƒØę§‹é€  čØˆē®—é‡ćØćƒ‘ćƒ•ć‚©ćƒ¼ćƒžćƒ³ć‚¹ ćƒ”ćƒ¢ćƒŖćƒŖćƒ¼ć‚Æ ę–‡å­—åˆ—ć®å‚ē…§å•é”Œ ę·±ć„ćƒć‚¹ćƒˆęœ€é©åŒ– ćƒ”ćƒ¢ćƒŖćƒ—ćƒ¼ćƒ«ę“»ē”Ø ä»„é™ć§å®Ÿéš›ć«å­¦ć‚“ć å†…å®¹ć«ć¤ć„ć¦ę•“ē†ć—ć¤ć¤ć€ å­¦ēæ’ć®ēµŒéŽć‚’å…±ęœ‰ć—ć€ęœ€å¾Œć«ę‰€ę„Ÿć«ć¤ć„ć¦ć¾ćØć‚ćŸć„ćØę€ć„ć¾ć™ć€‚ ćŖćŠć€å®Ÿéš›ć®ä½œę„­ćÆ cursor ćŒęœ€åˆć«ä½œć£ć¦ćć‚ŒćŸä»„äø‹ć®é–‹ē™ŗē’°å¢ƒć§č”Œć„ć¾ć—ćŸć€‚ é–‹ē™ŗē’°å¢ƒ compose.yml services : go-slice-study : build : context : . dockerfile : ./docker/app/Dockerfile container_name : go-slice-study volumes : - ./src:/app/src - ./examples:/app/examples working_dir : /app stdin_open : true tty : true command : [ "sh" , "-c" , "tail -f /dev/null" ] docker/app/Dockerfile # ęœ€ę–°ć®Goć®å…¬å¼ć‚¤ćƒ”ćƒ¼ć‚øć‚’ä½æē”Ø FROM golang:1.22-alpine # åæ…č¦ćŖćƒ„ćƒ¼ćƒ«ć‚’ć‚¤ćƒ³ć‚¹ćƒˆćƒ¼ćƒ« RUN apk add --no-cache \ git \ bash \ curl \ build-base # ä½œę„­ćƒ‡ć‚£ćƒ¬ć‚ÆćƒˆćƒŖć‚’čØ­å®š WORKDIR /app # Go modulesć‚’ęœ‰åŠ¹åŒ– ENV GO111MODULE=on # å­¦ēæ’ē”Øć®åŸŗęœ¬ē’°å¢ƒć«ćÆäøč¦ćŖćƒ„ćƒ¼ćƒ«ć‚’ć‚¹ć‚­ćƒƒćƒ— # RUN go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest # ć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³ē”Øć®ćƒ‡ć‚£ćƒ¬ć‚ÆćƒˆćƒŖć‚’ä½œęˆ RUN mkdir -p /app/src /app/examples # ćƒ‡ćƒ•ć‚©ćƒ«ćƒˆć®ć‚·ć‚§ćƒ«ć‚’bashに設定 RUN echo ' export PS1="\[\033[01;32m\]go-slice-study\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]$ " ' >> ~/.bashrc CMD [ " bash " ] go.mod module go-slice-study go 1.22 require ( github.com/stretchr/testify v1.8.4 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) ć‚¹ćƒ©ć‚¤ć‚¹ć®å†…éƒØę§‹é€ ć«ć¤ć„ć¦ ć‚µćƒ³ćƒ—ćƒ«1ļ¼ˆå®Ÿč£…ļ¼‹å®Ÿč”Œēµęžœļ¼‰ å®Ÿč£… package main import ( "fmt" "unsafe" ) // ć‚¹ćƒ©ć‚¤ć‚¹ć®å†…éƒØę§‹é€ ć‚’č”Øē¾ć™ć‚‹ę§‹é€ ä½“ // runtime/slice.goć§å®šē¾©ć•ć‚Œć¦ć„ć‚‹ć‚‚ć®ćØåŒć˜ type SliceHeader struct { Data uintptr // é…åˆ—ćøć®å…ˆé ­č¦ē“ ć®ćƒć‚¤ćƒ³ć‚æ Len int // ć‚¹ćƒ©ć‚¤ć‚¹ć®é•·ć• Cap int // ć‚¹ćƒ©ć‚¤ć‚¹ć®å®¹é‡ } func main() { fmt.Println( "=== Goć‚¹ćƒ©ć‚¤ć‚¹ć®å†…éƒØę§‹é€ å­¦ēæ’ === \n " ) // 1. ć‚¹ćƒ©ć‚¤ć‚¹ćƒŖćƒ†ćƒ©ćƒ«ć®ä½œęˆ fmt.Println( "1. ć‚¹ćƒ©ć‚¤ć‚¹ćƒŖćƒ†ćƒ©ćƒ«ć®ä½œęˆ" ) s1 := [] int { 1 , 2 , 3 , 4 , 5 } printSliceInfo( "s1" , s1) // 2. makeć‚’ä½æć£ćŸä½œęˆ fmt.Println( " \n 2. makeć‚’ä½æć£ćŸä½œęˆ" ) s2 := make ([] int , 3 , 5 ) // len=3, cap=5 printSliceInfo( "s2" , s2) // 3. ć‚¹ćƒ©ć‚¤ć‚·ćƒ³ć‚°ę“ä½œ fmt.Println( " \n 3. ć‚¹ćƒ©ć‚¤ć‚·ćƒ³ć‚°ę“ä½œ" ) s3 := s1[ 1 : 4 ] // ć‚¤ćƒ³ćƒ‡ćƒƒć‚Æć‚¹1恋悉3まで printSliceInfo( "s3 (s1[1:4])" , s3) // s1とs3ćŒåŒć˜é…åˆ—ć‚’å‚ē…§ć—ć¦ć„ć‚‹ć“ćØć‚’ē¢ŗčŖ fmt.Printf( "s1とs3ćŒåŒć˜é…åˆ—ć‚’å‚ē…§: %t \n " , unsafe.Pointer (&s1[ 1 ]) == unsafe.Pointer (&s3[ 0 ])) // 4. appendę“ä½œć«ć‚ˆć‚‹å®¹é‡ę‹”å¼µ fmt.Println( " \n 4. appendę“ä½œć«ć‚ˆć‚‹å®¹é‡ę‹”å¼µ" ) s4 := [] int { 1 , 2 , 3 } printSliceInfo( "s4 (before append)" , s4) s4 = append (s4, 4 , 5 , 6 , 7 ) printSliceInfo( "s4 (after append)" , s4) // 5. å®¹é‡ćŒč¶³ć‚ŠćŖć„å “åˆć®å†å‰²ć‚Šå½“ć¦ fmt.Println( " \n 5. å®¹é‡ćŒč¶³ć‚ŠćŖć„å “åˆć®å†å‰²ć‚Šå½“ć¦" ) s5 := make ([] int , 3 , 3 ) // ęŗ€ęÆć®ēŠ¶ę…‹ s5[ 0 ], s5[ 1 ], s5[ 2 ] = 10 , 20 , 30 printSliceInfo( "s5 (ęŗ€ęÆēŠ¶ę…‹)" , s5) oldPtr := unsafe.Pointer (&s5[ 0 ]) s5 = append (s5, 40 ) // å†å‰²ć‚Šå½“ć¦ćŒē™ŗē”Ÿ newPtr := unsafe.Pointer (&s5[ 0 ]) printSliceInfo( "s5 (append後)" , s5) fmt.Printf( "é…åˆ—ćŒå†å‰²ć‚Šå½“ć¦ć•ć‚ŒćŸ: %t \n " , oldPtr != newPtr) } // ć‚¹ćƒ©ć‚¤ć‚¹ć®č©³ē“°ęƒ…å ±ć‚’å‡ŗåŠ›ć™ć‚‹é–¢ę•° func printSliceInfo(name string , s [] int ) { if len (s) == 0 { fmt.Printf( "%s: [], len=%d, cap=%d, ptr=nil \n " , name, len (s), cap (s)) return } header := (*SliceHeader)( unsafe.Pointer (&s)) fmt.Printf( "%s: %v, len=%d, cap=%d, ptr=0x%x \n " , name, s, len (s), cap (s), header.Data) } å®Ÿč”Œēµęžœ === Goć‚¹ćƒ©ć‚¤ć‚¹ć®å†…éƒØę§‹é€ å­¦ēæ’ === 1. ć‚¹ćƒ©ć‚¤ć‚¹ćƒŖćƒ†ćƒ©ćƒ«ć®ä½œęˆ s1: [1 2 3 4 5], len=5, cap=5, ptr=0x400007e030 2. makeć‚’ä½æć£ćŸä½œęˆ s2: [0 0 0], len=3, cap=5, ptr=0x400007e060 3. ć‚¹ćƒ©ć‚¤ć‚·ćƒ³ć‚°ę“ä½œ s3 (s1[1:4]): [2 3 4], len=3, cap=4, ptr=0x400007e038 s1とs3ćŒåŒć˜é…åˆ—ć‚’å‚ē…§: true 4. appendę“ä½œć«ć‚ˆć‚‹å®¹é‡ę‹”å¼µ s4 (before append): [1 2 3], len=3, cap=3, ptr=0x4000014078 s4 (after append): [1 2 3 4 5 6 7], len=7, cap=8, ptr=0x400010c000 5. å®¹é‡ćŒč¶³ć‚ŠćŖć„å “åˆć®å†å‰²ć‚Šå½“ć¦ s5 (ęŗ€ęÆēŠ¶ę…‹): [10 20 30], len=3, cap=3, ptr=0x4000014090 s5 (append後): [10 20 30 40], len=4, cap=6, ptr=0x400007e090 é…åˆ—ćŒå†å‰²ć‚Šå½“ć¦ć•ć‚ŒćŸ: true クイズ1 ## **šŸŽÆ クイズ第1問** ć‚µćƒ³ćƒ—ćƒ«1ć®å®Ÿč”Œēµęžœć‚’č¦‹ć¦ć€ä»„äø‹ć®č³Ŗå•ć«ē­”ćˆć¦ćć ć•ć„ļ¼š **問锌**: ` s3 := s1[1:4] ` ć®ę“ä½œć§ć€ ` s3 ` ć®ćƒć‚¤ćƒ³ć‚æå€¤ćŒ ` 0x40000b2038 ` ć«ćŖć‚Šć€ ` s1 ` ć®ćƒć‚¤ćƒ³ć‚æå€¤ ` 0x40000b2030 ` とは8ćƒć‚¤ćƒˆļ¼ˆ0x8ļ¼‰ć®å·®ćŒć‚ć‚Šć¾ć™ć€‚ **なぜ8ćƒć‚¤ćƒˆć®å·®ćŒć‚ć‚‹ć®ć§ć—ć‚‡ć†ć‹ļ¼Ÿ** ć“ć®å·®ć®ę„å‘³ćØć€ćć‚ŒćŒć‚¹ćƒ©ć‚¤ć‚¹ć®ä»•ēµ„ćæćØé…åˆ—ć®č¦ē“ åž‹ć«ć©ć†é–¢äæ‚ć—ć¦ć„ć‚‹ć‹čŖ¬ę˜Žć—ć¦ćć ć•ć„ć€‚ **ćƒ’ćƒ³ćƒˆ**: - ` s1 ` ć®åž‹ćÆ ` []int ` 恧恙 - ` s3 ` は ` s1[1:4] ` ć€ć¤ć¾ć‚Š ` s1 ` ć®ć‚¤ćƒ³ćƒ‡ćƒƒć‚Æć‚¹1ć‹ć‚‰å§‹ć¾ć‚Šć¾ć™ - 64ćƒ“ćƒƒćƒˆć‚·ć‚¹ćƒ†ćƒ ć§ć® ` int ` ć®ć‚µć‚¤ć‚ŗć‚’č€ƒćˆć¦ćæć¦ćć ć•ć„ ć‚ćŖćŸć®å›žē­”ć‚’ćŠčžć‹ć›ćć ć•ć„ć€‚ ć¾ćšć€Œé…åˆ—å‚ē…§ćØć—ć¦ć®ć‚¹ćƒ©ć‚¤ć‚¹ć€ć‚’ä½“ę„Ÿć™ć‚‹ćŸć‚ć€ ć‚¹ćƒ©ć‚¤ć‚·ćƒ³ć‚°å¾Œć®ćƒć‚¤ćƒ³ć‚æå·®åˆ†ćØåž‹ć‚µć‚¤ć‚ŗć®é–¢äæ‚ć‚’ę¤œčØ¼ć—ć¾ć—ćŸć€‚ Go ć®ć‚¹ćƒ©ć‚¤ć‚¹ćÆć€Œé…åˆ—ć‚’ęŒ‡ć™å‚ē…§ć€ć®ę§‹é€ ä½“ć§ć‚ć‚Šć€ä»„äø‹ć® 3 č¦ē“ ć‹ć‚‰ę§‹ęˆć•ć‚Œć¾ć™ 1 2 怂 ļ¼ˆéƒØåˆ†ļ¼‰é…åˆ—å…ˆé ­ć‚’ęŒ‡ć™ćƒć‚¤ćƒ³ć‚æļ¼ˆptr) é•·ć•ļ¼ˆlen) å®¹é‡ļ¼ˆcap) type slice struct { array unsafe.Pointer len int cap int } ä¾‹ćˆć°ć€ s := []int64{1, 2} ćØć„ć†ć‚¹ćƒ©ć‚¤ć‚¹ćŒć‚ć‚‹å “åˆć€ s[0] と s[1] ć®ćƒć‚¤ćƒ³ć‚æå€¤ć®å·®åˆ†ćÆ 64 bit = 8 Byte ćØćŖć‚Šć¾ć™ć€‚ ć‚µćƒ³ćƒ—ćƒ«2ļ¼ˆćƒ‡ćƒ¼ć‚æåž‹ćØćƒ”ćƒ¢ćƒŖć®é–¢äæ‚ę€§ć‚’čæ½åŠ ę¤œčØ¼ļ¼‰ļ¼ˆå®Ÿč£…ļ¼‹å®Ÿč”Œēµęžœļ¼‰ å®Ÿč£… package main import ( "fmt" "unsafe" ) func main() { fmt.Println( "=== 64ćƒ“ćƒƒćƒˆć‚·ć‚¹ćƒ†ćƒ ć§ć®intć®ć‚µć‚¤ć‚ŗćØćƒ”ćƒ¢ćƒŖćƒ¬ć‚¤ć‚¢ć‚¦ćƒˆ === \n " ) // 1. ćƒ‡ćƒ¼ć‚æåž‹ć®ć‚µć‚¤ć‚ŗć‚’ē¢ŗčŖ fmt.Println( "1. ćƒ‡ćƒ¼ć‚æåž‹ć®ć‚µć‚¤ć‚ŗ" ) fmt.Printf( "int のサイズ: %d 惐悤惈 \n " , unsafe.Sizeof( int ( 0 ))) fmt.Printf( "int32 のサイズ: %d 惐悤惈 \n " , unsafe.Sizeof( int32 ( 0 ))) fmt.Printf( "int64 のサイズ: %d 惐悤惈 \n " , unsafe.Sizeof( int64 ( 0 ))) fmt.Printf( "byte のサイズ: %d 惐悤惈 \n " , unsafe.Sizeof( byte ( 0 ))) // 2. é…åˆ—å†…ć§ć®č¦ē“ ć®é…ē½®ć‚’ē¢ŗčŖ fmt.Println( " \n 2. é…åˆ—å†…ć§ć®č¦ē“ ć®é…ē½®" ) arr := [ 5 ] int { 10 , 20 , 30 , 40 , 50 } fmt.Println( "å„č¦ē“ ć®ćƒ”ćƒ¢ćƒŖć‚¢ćƒ‰ćƒ¬ć‚¹:" ) for i := 0 ; i < len (arr); i++ { fmt.Printf( "arr[%d] = %d, ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x%x \n " , i, arr[i], uintptr ( unsafe.Pointer (&arr[i]))) } // 3. ć‚¢ćƒ‰ćƒ¬ć‚¹ć®å·®ć‚’čØˆē®— fmt.Println( " \n 3. éš£ęŽ„ć™ć‚‹č¦ē“ é–“ć®ć‚¢ćƒ‰ćƒ¬ć‚¹å·®:" ) for i := 0 ; i < len (arr)- 1 ; i++ { addr1 := uintptr ( unsafe.Pointer (&arr[i])) addr2 := uintptr ( unsafe.Pointer (&arr[i+ 1 ])) diff := addr2 - addr1 fmt.Printf( "arr[%d]とarr[%d]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹å·®: %d 惐悤惈 \n " , i, i+ 1 , diff) } // 4. ć‚¹ćƒ©ć‚¤ć‚¹ć§ć®å®Ÿéš›ć®ę¤œčØ¼ fmt.Println( " \n 4. ć‚¹ćƒ©ć‚¤ć‚¹ć§ć®å®Ÿéš›ć®ę¤œčØ¼" ) s1 := [] int { 1 , 2 , 3 , 4 , 5 } s3 := s1[ 1 : 4 ] fmt.Printf( "s1ć®ćƒć‚¤ćƒ³ć‚æ: 0x%x (要瓠[0]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹) \n " , uintptr ( unsafe.Pointer (&s1[ 0 ]))) fmt.Printf( "s1[1]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x%x \n " , uintptr ( unsafe.Pointer (&s1[ 1 ]))) fmt.Printf( "s3ć®ćƒć‚¤ćƒ³ć‚æ: 0x%x (要瓠[0]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹) \n " , uintptr ( unsafe.Pointer (&s3[ 0 ]))) addr_s1_0 := uintptr ( unsafe.Pointer (&s1[ 0 ])) addr_s1_1 := uintptr ( unsafe.Pointer (&s1[ 1 ])) addr_s3_0 := uintptr ( unsafe.Pointer (&s3[ 0 ])) fmt.Printf( " \n s1[0]とs1[1]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹å·®: %d 惐悤惈 \n " , addr_s1_1-addr_s1_0) fmt.Printf( "s1[1]とs3[0]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹: åŒć˜ć‹? %t \n " , addr_s1_1 == addr_s3_0) // 5. ē•°ćŖć‚‹ćƒ‡ćƒ¼ć‚æåž‹ć§ć®ęÆ”č¼ƒ fmt.Println( " \n 5. ē•°ćŖć‚‹ćƒ‡ćƒ¼ć‚æåž‹ć§ć®ęÆ”č¼ƒ" ) // byteé…åˆ—ć®å “åˆ byteArr := [ 5 ] byte { 1 , 2 , 3 , 4 , 5 } fmt.Println( "byteé…åˆ—ć§ć®éš£ęŽ„č¦ē“ ć®ć‚¢ćƒ‰ćƒ¬ć‚¹å·®:" ) for i := 0 ; i < len (byteArr)- 1 ; i++ { addr1 := uintptr ( unsafe.Pointer (&byteArr[i])) addr2 := uintptr ( unsafe.Pointer (&byteArr[i+ 1 ])) diff := addr2 - addr1 fmt.Printf( "byteArr[%d]とbyteArr[%d]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹å·®: %d 惐悤惈 \n " , i, i+ 1 , diff) } // int32é…åˆ—ć®å “åˆ int32Arr := [ 5 ] int32 { 10 , 20 , 30 , 40 , 50 } fmt.Println( "int32é…åˆ—ć§ć®éš£ęŽ„č¦ē“ ć®ć‚¢ćƒ‰ćƒ¬ć‚¹å·®:" ) for i := 0 ; i < len (int32Arr)- 1 ; i++ { addr1 := uintptr ( unsafe.Pointer (&int32Arr[i])) addr2 := uintptr ( unsafe.Pointer (&int32Arr[i+ 1 ])) diff := addr2 - addr1 fmt.Printf( "int32Arr[%d]とint32Arr[%d]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹å·®: %d 惐悤惈 \n " , i, i+ 1 , diff) } } å®Ÿč”Œēµęžœ === 64ćƒ“ćƒƒćƒˆć‚·ć‚¹ćƒ†ćƒ ć§ć®intć®ć‚µć‚¤ć‚ŗćØćƒ”ćƒ¢ćƒŖćƒ¬ć‚¤ć‚¢ć‚¦ćƒˆ === 1. ćƒ‡ćƒ¼ć‚æåž‹ć®ć‚µć‚¤ć‚ŗ int のサイズ: 8 惐悤惈 int32 のサイズ: 4 惐悤惈 int64 のサイズ: 8 惐悤惈 byte のサイズ: 1 惐悤惈 2. é…åˆ—å†…ć§ć®č¦ē“ ć®é…ē½® å„č¦ē“ ć®ćƒ”ćƒ¢ćƒŖć‚¢ćƒ‰ćƒ¬ć‚¹: arr[0] = 10, ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x4000074cf8 arr[1] = 20, ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x4000074d00 arr[2] = 30, ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x4000074d08 arr[3] = 40, ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x4000074d10 arr[4] = 50, ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x4000074d18 3. éš£ęŽ„ć™ć‚‹č¦ē“ é–“ć®ć‚¢ćƒ‰ćƒ¬ć‚¹å·®: arr[0]とarr[1]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹å·®: 8 惐悤惈 arr[1]とarr[2]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹å·®: 8 惐悤惈 arr[2]とarr[3]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹å·®: 8 惐悤惈 arr[3]とarr[4]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹å·®: 8 惐悤惈 4. ć‚¹ćƒ©ć‚¤ć‚¹ć§ć®å®Ÿéš›ć®ę¤œčØ¼ s1ć®ćƒć‚¤ćƒ³ć‚æ: 0x4000074d30 (要瓠[0]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹) s1[1]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x4000074d38 s3ć®ćƒć‚¤ćƒ³ć‚æ: 0x4000074d38 (要瓠[0]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹) s1[0]とs1[1]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹å·®: 8 惐悤惈 s1[1]とs3[0]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹: åŒć˜ć‹? true 5. ē•°ćŖć‚‹ćƒ‡ćƒ¼ć‚æåž‹ć§ć®ęÆ”č¼ƒ byteé…åˆ—ć§ć®éš£ęŽ„č¦ē“ ć®ć‚¢ćƒ‰ćƒ¬ć‚¹å·®: byteArr[0]とbyteArr[1]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹å·®: 1 惐悤惈 byteArr[1]とbyteArr[2]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹å·®: 1 惐悤惈 byteArr[2]とbyteArr[3]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹å·®: 1 惐悤惈 byteArr[3]とbyteArr[4]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹å·®: 1 惐悤惈 int32é…åˆ—ć§ć®éš£ęŽ„č¦ē“ ć®ć‚¢ćƒ‰ćƒ¬ć‚¹å·®: int32Arr[0]とint32Arr[1]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹å·®: 4 惐悤惈 int32Arr[1]とint32Arr[2]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹å·®: 4 惐悤惈 int32Arr[2]とint32Arr[3]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹å·®: 4 惐悤惈 int32Arr[3]とint32Arr[4]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹å·®: 4 惐悤惈 ć‚¹ćƒ©ć‚¤ć‚¹ć®å®¹é‡ę‹”å¼µć‚¢ćƒ«ć‚“ćƒŖć‚ŗćƒ ć«ć¤ć„ć¦ ć‚µćƒ³ćƒ—ćƒ«3ļ¼ˆå®Ÿč£…ļ¼‹å®Ÿč”Œēµęžœļ¼‰ å®Ÿč£… package main import ( "fmt" ) func main() { fmt.Println( "=== Goć‚¹ćƒ©ć‚¤ć‚¹ć®å®¹é‡ę‹”å¼µć‚¢ćƒ«ć‚“ćƒŖć‚ŗćƒ ę¤œčØ¼ === \n " ) // 1. s4ć®ć‚±ćƒ¼ć‚¹ć‚’å†ē¾ćƒ»åˆ†ęž fmt.Println( "1. s4ć®ć‚±ćƒ¼ć‚¹åˆ†ęž: append(s4, 4, 5, 6, 7)" ) s4 := [] int { 1 , 2 , 3 } // len=3, cap=3 fmt.Printf( "s4 åˆęœŸēŠ¶ę…‹: len=%d, cap=%d \n " , len (s4), cap (s4)) fmt.Println( "- ē¾åœØć®č¦ē“ ę•°: 3" ) fmt.Println( "- čæ½åŠ ć™ć‚‹č¦ē“ ę•°: 4個" ) fmt.Println( "- åæ…č¦ćŖåˆčØˆč¦ē“ ę•°: 3 + 4 = 7" ) fmt.Println( "- å®¹é‡ć‚’2å€ć«ć™ć‚‹ćØ: 3 Ɨ 2 = 6" ) fmt.Println( "- 必要な要瓠数(7) > 2å€ć®å®¹é‡(6) ćŖć®ć§ć€å®¹é‡ćÆ7ä»„äøŠćŒåæ…č¦" ) s4 = append (s4, 4 , 5 , 6 , 7 ) fmt.Printf( "s4 ę‹”å¼µå¾Œ: len=%d, cap=%d \n " , len (s4), cap (s4)) fmt.Println( "→ ēµęžœ: 7ćŒåæ…č¦ć ćŒć€å®Ÿéš›ćÆ8ć«ćŖć£ć¦ć„ć‚‹ļ¼ˆćƒ”ćƒ¢ćƒŖć‚¢ćƒ©ć‚¤ćƒ”ćƒ³ćƒˆļ¼‰" ) // 2. s5ć®ć‚±ćƒ¼ć‚¹ć‚’å†ē¾ćƒ»åˆ†ęž fmt.Println( " \n 2. s5ć®ć‚±ćƒ¼ć‚¹åˆ†ęž: append(s5, 40)" ) s5 := make ([] int , 3 , 3 ) // len=3, cap=3 s5[ 0 ], s5[ 1 ], s5[ 2 ] = 10 , 20 , 30 fmt.Printf( "s5 åˆęœŸēŠ¶ę…‹: len=%d, cap=%d \n " , len (s5), cap (s5)) fmt.Println( "- ē¾åœØć®č¦ē“ ę•°: 3" ) fmt.Println( "- čæ½åŠ ć™ć‚‹č¦ē“ ę•°: 1個" ) fmt.Println( "- åæ…č¦ćŖåˆčØˆč¦ē“ ę•°: 3 + 1 = 4" ) fmt.Println( "- å®¹é‡ć‚’2å€ć«ć™ć‚‹ćØ: 3 Ɨ 2 = 6" ) fmt.Println( "- 必要な要瓠数(4) < 2å€ć®å®¹é‡(6) ćŖć®ć§ć€å®¹é‡ćÆ6になる" ) s5 = append (s5, 40 ) fmt.Printf( "s5 ę‹”å¼µå¾Œ: len=%d, cap=%d \n " , len (s5), cap (s5)) fmt.Println( "→ ēµęžœ: äŗˆęƒ³é€šć‚Š6になっている" ) // 3. Goć®å®¹é‡ę‹”å¼µćƒ«ćƒ¼ćƒ«ć®ę¤œčØ¼ fmt.Println( " \n 3. Goć®å®¹é‡ę‹”å¼µćƒ«ćƒ¼ćƒ«:" ) fmt.Println( "ć‚¹ćƒ†ćƒƒćƒ—1: ę–°ć—ć„å®¹é‡ć‚’ę±ŗå®š" ) fmt.Println( " - ē¾åœØć®å®¹é‡ć‚’2å€ć™ć‚‹" ) fmt.Println( " - ć‚‚ć—åæ…č¦ćŖč¦ē“ ę•°ćŒ2å€å®¹é‡ć‚ˆć‚Šå¤§ćć‘ć‚Œć°ć€åæ…č¦ćŖč¦ē“ ę•°ć‚’ęŽ”ē”Ø" ) fmt.Println( "ć‚¹ćƒ†ćƒƒćƒ—2: ćƒ”ćƒ¢ćƒŖć‚¢ćƒ©ć‚¤ćƒ”ćƒ³ćƒˆčŖæę•“" ) fmt.Println( " - CPUćØćƒ”ćƒ¢ćƒŖć®åŠ¹ēŽ‡åŒ–ć®ćŸć‚ć€ē‰¹å®šć®ć‚µć‚¤ć‚ŗć«čŖæę•“ć•ć‚Œć‚‹" ) // 4. ć•ć¾ć–ć¾ćŖćƒ‘ć‚æćƒ¼ćƒ³ć§ć®ę¤œčØ¼ fmt.Println( " \n 4. ć•ć¾ć–ć¾ćŖćƒ‘ć‚æćƒ¼ćƒ³ć§ć®ę¤œčØ¼" ) testCases := [] struct { name string initialCap int appendCount int }{ { "1つ追加" , 3 , 1 }, { "2つ追加" , 3 , 2 }, { "3つ追加" , 3 , 3 }, { "4つ追加" , 3 , 4 }, { "5つ追加" , 3 , 5 }, { "10つ追加" , 3 , 10 }, } for _, tc := range testCases { s := make ([] int , tc.initialCap, tc.initialCap) fmt.Printf( " \n %s (åˆęœŸå®¹é‡:%d): \n " , tc.name, tc.initialCap) // čæ½åŠ ć™ć‚‹č¦ē“ ć‚’ęŗ–å‚™ toAppend := make ([] int , tc.appendCount) for i := range toAppend { toAppend[i] = 100 + i } fmt.Printf( " čæ½åŠ å‰: len=%d, cap=%d \n " , len (s), cap (s)) fmt.Printf( " čæ½åŠ č¦ē“ ę•°: %d \n " , tc.appendCount) fmt.Printf( " åæ…č¦åˆčØˆč¦ē“ ę•°: %d \n " , tc.initialCap+tc.appendCount) fmt.Printf( " 2倍容量: %d \n " , tc.initialCap* 2 ) s = append (s, toAppend...) fmt.Printf( " ēµęžœ: len=%d, cap=%d \n " , len (s), cap (s)) // äŗˆęƒ³ćØå®Ÿéš›ć®ęÆ”č¼ƒ expected := tc.initialCap * 2 needed := tc.initialCap + tc.appendCount if needed > expected { fmt.Printf( " → 必要数(%d) > 2倍容量(%d) なので %dä»„äøŠćŒåæ…č¦" , needed, expected, needed) } else { fmt.Printf( " → 必要数(%d) ≤ 2倍容量(%d) なので %d前後" , needed, expected, expected) } fmt.Printf( " → å®Ÿéš›ćÆ%d \n " , cap (s)) } } å®Ÿč”Œēµęžœ === Goć‚¹ćƒ©ć‚¤ć‚¹ć®å®¹é‡ę‹”å¼µć‚¢ćƒ«ć‚“ćƒŖć‚ŗćƒ ę¤œčØ¼ === 1. s4ć®ć‚±ćƒ¼ć‚¹åˆ†ęž: append(s4, 4, 5, 6, 7) s4 åˆęœŸēŠ¶ę…‹: len=3, cap=3 - ē¾åœØć®č¦ē“ ę•°: 3 - čæ½åŠ ć™ć‚‹č¦ē“ ę•°: 4個 - åæ…č¦ćŖåˆčØˆč¦ē“ ę•°: 3 + 4 = 7 - å®¹é‡ć‚’2å€ć«ć™ć‚‹ćØ: 3 Ɨ 2 = 6 - 必要な要瓠数(7) > 2å€ć®å®¹é‡(6) ćŖć®ć§ć€å®¹é‡ćÆ7ä»„äøŠćŒåæ…č¦ s4 ę‹”å¼µå¾Œ: len=7, cap=8 → ēµęžœ: 7ćŒåæ…č¦ć ćŒć€å®Ÿéš›ćÆ8ć«ćŖć£ć¦ć„ć‚‹ļ¼ˆćƒ”ćƒ¢ćƒŖć‚¢ćƒ©ć‚¤ćƒ”ćƒ³ćƒˆļ¼‰ 2. s5ć®ć‚±ćƒ¼ć‚¹åˆ†ęž: append(s5, 40) s5 åˆęœŸēŠ¶ę…‹: len=3, cap=3 - ē¾åœØć®č¦ē“ ę•°: 3 - čæ½åŠ ć™ć‚‹č¦ē“ ę•°: 1個 - åæ…č¦ćŖåˆčØˆč¦ē“ ę•°: 3 + 1 = 4 - å®¹é‡ć‚’2å€ć«ć™ć‚‹ćØ: 3 Ɨ 2 = 6 - 必要な要瓠数(4) < 2å€ć®å®¹é‡(6) ćŖć®ć§ć€å®¹é‡ćÆ6になる s5 ę‹”å¼µå¾Œ: len=4, cap=6 → ēµęžœ: äŗˆęƒ³é€šć‚Š6になっている 3. Goć®å®¹é‡ę‹”å¼µćƒ«ćƒ¼ćƒ«: ć‚¹ćƒ†ćƒƒćƒ—1: ę–°ć—ć„å®¹é‡ć‚’ę±ŗå®š - ē¾åœØć®å®¹é‡ć‚’2å€ć™ć‚‹ - ć‚‚ć—åæ…č¦ćŖč¦ē“ ę•°ćŒ2å€å®¹é‡ć‚ˆć‚Šå¤§ćć‘ć‚Œć°ć€åæ…č¦ćŖč¦ē“ ę•°ć‚’ęŽ”ē”Ø ć‚¹ćƒ†ćƒƒćƒ—2: ćƒ”ćƒ¢ćƒŖć‚¢ćƒ©ć‚¤ćƒ”ćƒ³ćƒˆčŖæę•“ - CPUćØćƒ”ćƒ¢ćƒŖć®åŠ¹ēŽ‡åŒ–ć®ćŸć‚ć€ē‰¹å®šć®ć‚µć‚¤ć‚ŗć«čŖæę•“ć•ć‚Œć‚‹ 4. ć•ć¾ć–ć¾ćŖćƒ‘ć‚æćƒ¼ćƒ³ć§ć®ę¤œčØ¼ 1つ追加 (åˆęœŸå®¹é‡:3): čæ½åŠ å‰: len=3, cap=3 čæ½åŠ č¦ē“ ę•°: 1 åæ…č¦åˆčØˆč¦ē“ ę•°: 4 2倍容量: 6 ēµęžœ: len=4, cap=6 → 必要数(4) ≤ 2倍容量(6) なので 6前後 → å®Ÿéš›ćÆ6 2つ追加 (åˆęœŸå®¹é‡:3): čæ½åŠ å‰: len=3, cap=3 čæ½åŠ č¦ē“ ę•°: 2 åæ…č¦åˆčØˆč¦ē“ ę•°: 5 2倍容量: 6 ēµęžœ: len=5, cap=6 → 必要数(5) ≤ 2倍容量(6) なので 6前後 → å®Ÿéš›ćÆ6 3つ追加 (åˆęœŸå®¹é‡:3): čæ½åŠ å‰: len=3, cap=3 čæ½åŠ č¦ē“ ę•°: 3 åæ…č¦åˆčØˆč¦ē“ ę•°: 6 2倍容量: 6 ēµęžœ: len=6, cap=6 → 必要数(6) ≤ 2倍容量(6) なので 6前後 → å®Ÿéš›ćÆ6 4つ追加 (åˆęœŸå®¹é‡:3): čæ½åŠ å‰: len=3, cap=3 čæ½åŠ č¦ē“ ę•°: 4 åæ…č¦åˆčØˆč¦ē“ ę•°: 7 2倍容量: 6 ēµęžœ: len=7, cap=8 → 必要数(7) > 2倍容量(6) なので 7ä»„äøŠćŒåæ…č¦ → å®Ÿéš›ćÆ8 5つ追加 (åˆęœŸå®¹é‡:3): čæ½åŠ å‰: len=3, cap=3 čæ½åŠ č¦ē“ ę•°: 5 åæ…č¦åˆčØˆč¦ē“ ę•°: 8 2倍容量: 6 ēµęžœ: len=8, cap=8 → 必要数(8) > 2倍容量(6) なので 8ä»„äøŠćŒåæ…č¦ → å®Ÿéš›ćÆ8 10つ追加 (åˆęœŸå®¹é‡:3): čæ½åŠ å‰: len=3, cap=3 čæ½åŠ č¦ē“ ę•°: 10 åæ…č¦åˆčØˆč¦ē“ ę•°: 13 2倍容量: 6 ēµęžœ: len=13, cap=14 → 必要数(13) > 2倍容量(6) なので 13ä»„äøŠćŒåæ…č¦ → å®Ÿéš›ćÆ14 クイズ2 ## **šŸŽÆ クイズ第2問** ę¬”ć®å­¦ēæ’ć‚¹ćƒ†ćƒƒćƒ—ćØć—ć¦ć€**ć‚¹ćƒ©ć‚¤ć‚¹ć®å®¹é‡ę‹”å¼µć‚¢ćƒ«ć‚“ćƒŖć‚ŗćƒ **ć«ć¤ć„ć¦ę·±ęŽ˜ć‚Šć—ć¾ć—ć‚‡ć†ć€‚ **問锌**: ć‚µćƒ³ćƒ—ćƒ«1ć®å®Ÿč”Œć§ć€ ` s4 ` ć®å®¹é‡ćŒ ` 3→8 ` 态 ` s5 ` ć®å®¹é‡ćŒ ` 3→6 ` ćØē•°ćŖć‚‹ęˆé•·ćƒ‘ć‚æćƒ¼ćƒ³ć‚’ē¤ŗć—ć¾ć—ćŸć€‚ **ćŖćœåŒć˜åˆęœŸå®¹é‡3ćŖć®ć«ć€ę‹”å¼µå¾Œć®å®¹é‡ćŒē•°ćŖć‚‹ć®ć§ć—ć‚‡ć†ć‹ļ¼Ÿ** ćƒ’ćƒ³ćƒˆļ¼š - ` s4 ` は ` append(s4, 4, 5, 6, 7) ` (4ć¤ć®č¦ē“ ć‚’čæ½åŠ ļ¼‰ - ` s5 ` は ` append(s5, 40) ` (1ć¤ć®č¦ē“ ć‚’čæ½åŠ ļ¼‰ - Goć®å®¹é‡ę‹”å¼µć‚¢ćƒ«ć‚“ćƒŖć‚ŗćƒ ć‚’č€ƒćˆć¦ćæć¦ćć ć•ć„ ć‚ćŖćŸć®ęŽØē†ć‚’ćŠčžć‹ć›ćć ć•ć„ļ¼ ę¬”ć«ć€ć‚¹ćƒ©ć‚¤ć‚¹ć®å®¹é‡ę‹”å¼µć‚¢ćƒ«ć‚“ćƒŖć‚ŗćƒ ć«ć¤ć„ć¦ę·±ęŽ˜ć‚Šć—ć¾ć—ćŸć€‚ ć‚¹ćƒ©ć‚¤ć‚¹ć®ęŒć¤å®¹é‡ć‚’č¶…ćˆć¦č¦ē“ ć‚’čæ½åŠ ć—ć‚ˆć†ćØć™ć‚‹å “åˆć€å†…éƒØēš„ć«å®¹é‡ćŒę‹”å¼µć•ć‚Œć¦ć„ćć¾ć™ć€‚ ć“ć®ę™‚ć€ę‹”å¼µēŽ‡ćŒ1.25怜2å€ēØ‹åŗ¦ć«åŽć¾ć‚‹ć‚ˆć†ć«å®¹é‡ć‚’ę‹”å¼µć—ć¾ć™ 3 怂 ć¾ćŸć€č©³ē“°ćÆå‰²ę„›ć—ć¾ć™ćŒć€ęœ€ēµ‚ēš„ćŖå®¹é‡ćÆć‚µć‚¤ć‚ŗć‚Æćƒ©ć‚¹ 4 ćØć„ć†ć‚‚ć®ć«åŸŗć„ć„ć¦ę±ŗå®šć•ć‚Œć¾ć™ć€‚ // nextslicecap computes the next appropriate slice length. func nextslicecap(newLen, oldCap int ) int { newcap := oldCap doublecap := newcap + newcap if newLen > doublecap { return newLen } const threshold = 256 if oldCap < threshold { return doublecap } for { // Transition from growing 2x for small slices // to growing 1.25x for large slices. This formula // gives a smooth-ish transition between the two. newcap += (newcap + 3 *threshold) >> 2 // We need to check `newcap >= newLen` and whether `newcap` overflowed. // newLen is guaranteed to be larger than zero, hence // when newcap overflows then `uint(newcap) > uint(newLen)`. // This allows to check for both with the same comparison. if uint (newcap) >= uint (newLen) { break } } // Set newcap to the requested cap when // the newcap calculation overflowed. if newcap <= 0 { return newLen } return newcap } čØˆē®—é‡ćØćƒ‘ćƒ•ć‚©ćƒ¼ćƒžćƒ³ć‚¹ć®č¦ē‚¹ ć‚µćƒ³ćƒ—ćƒ«4ļ¼ˆå®Ÿč£…ļ¼‹å®Ÿč”Œēµęžœļ¼‰ å®Ÿč£… package main import ( "fmt" "time" ) func main() { fmt.Println( "=== ć‚¹ćƒ©ć‚¤ć‚¹ę“ä½œć®ę™‚é–“ćƒ»ē©ŗé–“čØˆē®—é‡åˆ†ęž === \n " ) // 1. appendę“ä½œć®å„Ÿå“čØˆē®—é‡ fmt.Println( "1. appendę“ä½œć®å„Ÿå“čØˆē®—é‡åˆ†ęž" ) measureAppendPerformance() // 2. äŗ‹å‰å®¹é‡ęŒ‡å®šć®åŠ¹ęžœ fmt.Println( " \n 2. äŗ‹å‰å®¹é‡ęŒ‡å®šć®åŠ¹ęžœ" ) measurePreallocationEffect() // 3. ć‚¹ćƒ©ć‚¤ć‚·ćƒ³ć‚°ę“ä½œć®čØˆē®—é‡ fmt.Println( " \n 3. ć‚¹ćƒ©ć‚¤ć‚·ćƒ³ć‚°ę“ä½œć®čØˆē®—é‡" ) measureSlicingPerformance() // 4. copyę“ä½œć®čØˆē®—é‡ fmt.Println( " \n 4. copyę“ä½œć®čØˆē®—é‡" ) measureCopyPerformance() } // appendę“ä½œć®å„Ÿå“čØˆē®—é‡ć‚’ęø¬å®š func measureAppendPerformance() { sizes := [] int { 1000 , 10000 , 100000 , 1000000 } fmt.Println( "å‹•ēš„appendļ¼ˆå®¹é‡ęœŖęŒ‡å®šļ¼‰:" ) for _, size := range sizes { start := time.Now() var s [] int for i := 0 ; i < size; i++ { s = append (s, i) } duration := time.Since(start) fmt.Printf( " 要瓠数 %7d: %v (1č¦ē“ ć‚ćŸć‚Š: %v) \n " , size, duration, duration/time.Duration(size)) } fmt.Println( " \n ē†č«–ēš„åˆ†ęž:" ) fmt.Println( " - 各appendę“ä½œ: O(1) å„Ÿå“ę™‚é–“" ) fmt.Println( " - nå›žć®append: O(n) åˆčØˆę™‚é–“" ) fmt.Println( " - å®¹é‡ę‹”å¼µę™‚ć®ć‚³ćƒ”ćƒ¼: O(k) (kćÆē¾åœØć®č¦ē“ ę•°)" ) fmt.Println( " - 拔張頻度: log(n) å›žļ¼ˆå®¹é‡ćŒå€ć€…ć§å¢—åŠ ļ¼‰" ) fmt.Println( " - åˆčØˆć‚³ćƒ”ćƒ¼ę™‚é–“: O(n) (å¹¾ä½•ē“šę•°ć®å’Œ)" ) } // äŗ‹å‰å®¹é‡ęŒ‡å®šć®åŠ¹ęžœć‚’ęø¬å®š func measurePreallocationEffect() { size := 1000000 // å®¹é‡ęœŖęŒ‡å®š start := time.Now() var s1 [] int for i := 0 ; i < size; i++ { s1 = append (s1, i) } duration1 := time.Since(start) // å®¹é‡äŗ‹å‰ęŒ‡å®š start = time.Now() s2 := make ([] int , 0 , size) for i := 0 ; i < size; i++ { s2 = append (s2, i) } duration2 := time.Since(start) // ć‚¤ćƒ³ćƒ‡ćƒƒć‚Æć‚¹ē›“ęŽ„ä»£å…„ start = time.Now() s3 := make ([] int , size) for i := 0 ; i < size; i++ { s3[i] = i } duration3 := time.Since(start) fmt.Printf( "要瓠数 %d ć§ć®ęÆ”č¼ƒ: \n " , size) fmt.Printf( " å‹•ēš„append: %v \n " , duration1) fmt.Printf( " äŗ‹å‰å®¹é‡ē¢ŗäæ: %v (%.1fx速恄) \n " , duration2, float64 (duration1)/ float64 (duration2)) fmt.Printf( " ē›“ęŽ„ä»£å…„: %v (%.1fx速恄) \n " , duration3, float64 (duration1)/ float64 (duration3)) fmt.Println( " \n ē†č«–ēš„åˆ†ęž:" ) fmt.Println( " - å‹•ēš„append: O(n) + å®¹é‡ę‹”å¼µć‚³ć‚¹ćƒˆ" ) fmt.Println( " - äŗ‹å‰å®¹é‡ē¢ŗäæ: O(n) (å®¹é‡ę‹”å¼µćŖć—)" ) fmt.Println( " - ē›“ęŽ„ä»£å…„: O(n) (appendé–¢ę•°ć‚³ćƒ¼ćƒ«ć‚Ŗćƒ¼ćƒćƒ¼ćƒ˜ćƒƒćƒ‰ćŖć—)" ) } // ć‚¹ćƒ©ć‚¤ć‚·ćƒ³ć‚°ę“ä½œć®čØˆē®—é‡ć‚’ęø¬å®š func measureSlicingPerformance() { sizes := [] int { 1000 , 10000 , 100000 , 1000000 } fmt.Println( "ć‚¹ćƒ©ć‚¤ć‚·ćƒ³ć‚°ę“ä½œ:" ) for _, size := range sizes { // å¤§ććŖć‚¹ćƒ©ć‚¤ć‚¹ć‚’ä½œęˆ s := make ([] int , size) for i := range s { s[i] = i } start := time.Now() // ć‚¹ćƒ©ć‚¤ć‚·ćƒ³ć‚°ę“ä½œļ¼ˆå¤šę•°å›žå®Ÿč”Œļ¼‰ iterations := 100000 for i := 0 ; i < iterations; i++ { _ = s[ 100 : size- 100 ] // ć‚¹ćƒ©ć‚¤ć‚·ćƒ³ć‚° } duration := time.Since(start) fmt.Printf( " å…ƒć‚µć‚¤ć‚ŗ %7d: %v (1ę“ä½œć‚ćŸć‚Š: %v) \n " , size, duration, duration/time.Duration(iterations)) } fmt.Println( " \n ē†č«–ēš„åˆ†ęž:" ) fmt.Println( " - ć‚¹ćƒ©ć‚¤ć‚·ćƒ³ć‚°ę“ä½œ: O(1)" ) fmt.Println( " - ꖰ恗恄sliceę§‹é€ ä½“ć‚’ä½œęˆć™ć‚‹ć ć‘" ) fmt.Println( " - å…ƒć®é…åˆ—ć‚’ć‚³ćƒ”ćƒ¼ć—ćŖć„" ) fmt.Println( " - ćƒć‚¤ćƒ³ć‚æć€é•·ć•ć€å®¹é‡ć®čØˆē®—ć®ćæ" ) } // copyę“ä½œć®čØˆē®—é‡ć‚’ęø¬å®š func measureCopyPerformance() { sizes := [] int { 1000 , 10000 , 100000 , 1000000 } fmt.Println( "copyę“ä½œ:" ) for _, size := range sizes { src := make ([] int , size) dst := make ([] int , size) for i := range src { src[i] = i } start := time.Now() copy (dst, src) duration := time.Since(start) fmt.Printf( " 要瓠数 %7d: %v (1č¦ē“ ć‚ćŸć‚Š: %v) \n " , size, duration, duration/time.Duration(size)) } fmt.Println( " \n ē†č«–ēš„åˆ†ęž:" ) fmt.Println( " - copyę“ä½œ: O(n) (nćÆć‚³ćƒ”ćƒ¼ć™ć‚‹č¦ē“ ę•°)" ) fmt.Println( " - ćƒ”ćƒ¢ćƒŖé–“ć®ē·šå½¢ć‚³ćƒ”ćƒ¼" ) fmt.Println( " - ćƒćƒ¼ćƒ‰ć‚¦ć‚§ć‚¢ęœ€é©åŒ–ļ¼ˆmemmoveļ¼‰ć«ć‚ˆć‚Šé«˜é€Ÿ" ) } å®Ÿč”Œēµęžœ === ć‚¹ćƒ©ć‚¤ć‚¹ę“ä½œć®ę™‚é–“ćƒ»ē©ŗé–“čØˆē®—é‡åˆ†ęž === 1. appendę“ä½œć®å„Ÿå“čØˆē®—é‡åˆ†ęž å‹•ēš„appendļ¼ˆå®¹é‡ęœŖęŒ‡å®šļ¼‰: 要瓠数 1000: 52.625µs (1č¦ē“ ć‚ćŸć‚Š: 52ns) 要瓠数 10000: 470.917µs (1č¦ē“ ć‚ćŸć‚Š: 47ns) 要瓠数 100000: 4.316875ms (1č¦ē“ ć‚ćŸć‚Š: 43ns) 要瓠数 1000000: 66.438667ms (1č¦ē“ ć‚ćŸć‚Š: 66ns) ē†č«–ēš„åˆ†ęž: - 各appendę“ä½œ: O(1) å„Ÿå“ę™‚é–“ - nå›žć®append: O(n) åˆčØˆę™‚é–“ - å®¹é‡ę‹”å¼µę™‚ć®ć‚³ćƒ”ćƒ¼: O(k) (kćÆē¾åœØć®č¦ē“ ę•°) - 拔張頻度: log(n) å›žļ¼ˆå®¹é‡ćŒå€ć€…ć§å¢—åŠ ļ¼‰ - åˆčØˆć‚³ćƒ”ćƒ¼ę™‚é–“: O(n) (å¹¾ä½•ē“šę•°ć®å’Œ) 2. äŗ‹å‰å®¹é‡ęŒ‡å®šć®åŠ¹ęžœ 要瓠数 1000000 ć§ć®ęÆ”č¼ƒ: å‹•ēš„append: 46.498333ms äŗ‹å‰å®¹é‡ē¢ŗäæ: 105.046542ms (0.4x速恄) ē›“ęŽ„ä»£å…„: 2.286375ms (20.3x速恄) ē†č«–ēš„åˆ†ęž: - å‹•ēš„append: O(n) + å®¹é‡ę‹”å¼µć‚³ć‚¹ćƒˆ - äŗ‹å‰å®¹é‡ē¢ŗäæ: O(n) (å®¹é‡ę‹”å¼µćŖć—) - ē›“ęŽ„ä»£å…„: O(n) (appendé–¢ę•°ć‚³ćƒ¼ćƒ«ć‚Ŗćƒ¼ćƒćƒ¼ćƒ˜ćƒƒćƒ‰ćŖć—) 3. ć‚¹ćƒ©ć‚¤ć‚·ćƒ³ć‚°ę“ä½œć®čØˆē®—é‡ ć‚¹ćƒ©ć‚¤ć‚·ćƒ³ć‚°ę“ä½œ: å…ƒć‚µć‚¤ć‚ŗ 1000: 96.375µs (1ę“ä½œć‚ćŸć‚Š: 0s) å…ƒć‚µć‚¤ć‚ŗ 10000: 82.917µs (1ę“ä½œć‚ćŸć‚Š: 0s) å…ƒć‚µć‚¤ć‚ŗ 100000: 123.167µs (1ę“ä½œć‚ćŸć‚Š: 1ns) å…ƒć‚µć‚¤ć‚ŗ 1000000: 179.917µs (1ę“ä½œć‚ćŸć‚Š: 1ns) ē†č«–ēš„åˆ†ęž: - ć‚¹ćƒ©ć‚¤ć‚·ćƒ³ć‚°ę“ä½œ: O(1) - ꖰ恗恄sliceę§‹é€ ä½“ć‚’ä½œęˆć™ć‚‹ć ć‘ - å…ƒć®é…åˆ—ć‚’ć‚³ćƒ”ćƒ¼ć—ćŖć„ - ćƒć‚¤ćƒ³ć‚æć€é•·ć•ć€å®¹é‡ć®čØˆē®—ć®ćæ 4. copyę“ä½œć®čØˆē®—é‡ copyę“ä½œ: 要瓠数 1000: 708ns (1č¦ē“ ć‚ćŸć‚Š: 0s) 要瓠数 10000: 14.791µs (1č¦ē“ ć‚ćŸć‚Š: 1ns) 要瓠数 100000: 220.584µs (1č¦ē“ ć‚ćŸć‚Š: 2ns) 要瓠数 1000000: 1.174583ms (1č¦ē“ ć‚ćŸć‚Š: 1ns) ē†č«–ēš„åˆ†ęž: - copyę“ä½œ: O(n) (nćÆć‚³ćƒ”ćƒ¼ć™ć‚‹č¦ē“ ę•°) - ćƒ”ćƒ¢ćƒŖé–“ć®ē·šå½¢ć‚³ćƒ”ćƒ¼ - ćƒćƒ¼ćƒ‰ć‚¦ć‚§ć‚¢ęœ€é©åŒ–ļ¼ˆmemmoveļ¼‰ć«ć‚ˆć‚Šé«˜é€Ÿ ē¶šć‘ć¦ć€ć‚¹ćƒ©ć‚¤ć‚¹ć®ä½œęˆę–¹ę³•åˆ„ć«å‡¦ē†ę™‚é–“ć‚’čØˆęø¬ć—ćƒ‘ćƒ•ć‚©ćƒ¼ćƒžćƒ³ć‚¹ęÆ”č¼ƒć‚’å®Ÿę–½ć—ć¾ć—ćŸć€‚ ć‚µćƒ³ćƒ—ćƒ«4ć‚’å®Ÿč”Œć™ć‚‹ć“ćØć§ć€ć‚¹ćƒ©ć‚¤ć‚¹ć®ä½œęˆę–¹ę³•ć«ć‚ˆć£ć¦ä»„äø‹ć®ć‚ˆć†ćŖćƒ‘ćƒ•ć‚©ćƒ¼ćƒžćƒ³ć‚¹ē‰¹ę€§ćŒć‚ć‚‹ćØč€ƒćˆć‚‰ć‚Œć‚‹ć“ćØćŒē¢ŗčŖć§ćć¾ć—ćŸć€‚ åˆęœŸåŒ–ę™‚å®¹é‡ęŒ‡å®šćŖć—ļ¼†forćƒ«ćƒ¼ćƒ—ć§append: O(n) + append ć‚³ćƒ¼ćƒ«ć‚Ŗćƒ¼ćƒćƒ¼ćƒ˜ćƒƒćƒ‰ + å®¹é‡ę‹”å¼µ åˆęœŸåŒ–ę™‚å®¹é‡ęŒ‡å®šć‚ć‚Šļ¼†forćƒ«ćƒ¼ćƒ—ć§append: O(n) + append ć‚³ćƒ¼ćƒ«ć‚Ŗćƒ¼ćƒćƒ¼ćƒ˜ćƒƒćƒ‰ åˆęœŸåŒ–ę™‚å®¹é‡ęŒ‡å®šć‚ć‚Šļ¼†forćƒ«ćƒ¼ćƒ—ć§ć‚¤ćƒ³ćƒ‡ćƒƒć‚Æć‚¹ęŒ‡å®šä»£å…„: O(n) ć‚¹ćƒ©ć‚¤ć‚·ćƒ³ć‚° ( s2 := s1[start:end] ): O(1) ćƒć‚¤ćƒ³ć‚æć€é•·ć•ć€å®¹é‡ć‚’čØˆē®—ć—ć¦ę–°ć—ć„ slice ć‚’ä½œęˆć™ć‚‹ć ć‘ copy : O(n) ( n ćÆć‚³ćƒ”ćƒ¼ć™ć‚‹č¦ē“ ę•°ļ¼‰ memmove ć«ć‚ˆć‚‹ęœ€é©åŒ–ć§é«˜é€Ÿ ćƒ”ćƒ¢ćƒŖé ˜åŸŸć«ä¾å­˜ļ¼ˆå°ć•ć„ć‚¹ćƒ©ć‚¤ć‚¹ćÆé€Ÿćć€å¤§ćć„ć‚¹ćƒ©ć‚¤ć‚¹ćÆé…ććŖć‚‹å‚¾å‘ļ¼‰ ć‚¹ćƒ©ć‚¤ć‚¹ćŒčµ·ć“ć™ćƒ”ćƒ¢ćƒŖćƒŖćƒ¼ć‚Æ ć‚µćƒ³ćƒ—ćƒ«5ļ¼ˆå®Ÿč£…ļ¼‹å®Ÿč”Œēµęžœļ¼‰ å®Ÿč£… package main import ( "fmt" "runtime" "time" "unsafe" ) // ć‚°ćƒ­ćƒ¼ćƒćƒ«å¤‰ę•°ć§äæęŒć™ć‚‹ć“ćØć§ć€ć‚¬ćƒ™ćƒ¼ć‚øć‚³ćƒ¬ć‚Æć‚·ćƒ§ćƒ³ć‚’é˜²ć var globalSlices [][] int // å•é”Œć®ć‚ć‚‹é–¢ę•°ļ¼šå¤§ććŖé…åˆ—ć®ć†ć”äø€éƒØć ć‘ć‚’čæ”ć™ func createLeakySlice(size int ) [] int { fmt.Printf( " %dč¦ē“ ć®é…åˆ—ć‚’ä½œęˆäø­... \n " , size) large := make ([] int , size) for i := range large { large[i] = i } // ęœ€åˆć®10č¦ē“ ć ć‘ć‚’čæ”ć™ļ¼ˆćƒ”ćƒ¢ćƒŖćƒŖćƒ¼ć‚Æļ¼ļ¼‰ small := large[: 10 ] fmt.Printf( " čæ”ć™ć‚¹ćƒ©ć‚¤ć‚¹: len=%d, cap=%d \n " , len (small), cap (small)) return small } // ę”¹å–„ć•ć‚ŒćŸé–¢ę•°ļ¼šåæ…č¦ćŖéƒØåˆ†ć ć‘ć‚’ć‚³ćƒ”ćƒ¼ć—ć¦čæ”ć™ func createFixedSlice(size int ) [] int { fmt.Printf( " %dč¦ē“ ć®é…åˆ—ć‚’ä½œęˆäø­... \n " , size) large := make ([] int , size) for i := range large { large[i] = i } // åæ…č¦ćŖéƒØåˆ†ć ć‘ć‚’ć‚³ćƒ”ćƒ¼ small := make ([] int , 10 ) copy (small, large[: 10 ]) fmt.Printf( " čæ”ć™ć‚¹ćƒ©ć‚¤ć‚¹: len=%d, cap=%d \n " , len (small), cap (small)) return small // 関数終了時に large ćÆč§£ę”¾ć•ć‚Œć‚‹ } func printMemoryStats(label string ) { runtime.GC() // å¼·åˆ¶ēš„ć«ć‚¬ćƒ™ćƒ¼ć‚øć‚³ćƒ¬ć‚Æć‚·ćƒ§ćƒ³ć‚’å®Ÿč”Œ runtime.GC() // 2å›žå®Ÿč”Œć—ć¦ć‚ˆć‚Šē¢ŗå®Ÿć« time.Sleep( 100 * time.Millisecond) // GCć®å®Œäŗ†ć‚’å¾…ć¤ var m runtime.MemStats runtime.ReadMemStats(&m) fmt.Printf( "%s: \n " , label) fmt.Printf( " Alloc: %.2f MB (ē¾åœØä½æē”Øäø­ć®ćƒ”ćƒ¢ćƒŖ) \n " , float64 (m.Alloc)/ 1024 / 1024 ) fmt.Printf( " TotalAlloc: %.2f MB (ē“ÆčØˆå‰²ć‚Šå½“ć¦ćƒ”ćƒ¢ćƒŖ) \n " , float64 (m.TotalAlloc)/ 1024 / 1024 ) fmt.Printf( " Sys: %.2f MB (ć‚·ć‚¹ćƒ†ćƒ ć‹ć‚‰å–å¾—ć—ćŸćƒ”ćƒ¢ćƒŖ) \n " , float64 (m.Sys)/ 1024 / 1024 ) fmt.Printf( " NumGC: %d (ć‚¬ćƒ™ćƒ¼ć‚øć‚³ćƒ¬ć‚Æć‚·ćƒ§ćƒ³å›žę•°) \n " , m.NumGC) fmt.Println() } func main() { fmt.Println( "=== ćƒ”ćƒ¢ćƒŖćƒŖćƒ¼ć‚Æå®ŸčØ¼å®ŸéØ“ === \n " ) printMemoryStats( "åˆęœŸēŠ¶ę…‹" ) // å®ŸéØ“1: ćƒ”ćƒ¢ćƒŖćƒŖćƒ¼ć‚Æć‚ć‚Šļ¼ˆå¤§ććŖć‚µć‚¤ć‚ŗć§ē¢ŗå®Ÿć«ē¢ŗčŖļ¼‰ fmt.Println( "=== å®ŸéØ“1: ćƒ”ćƒ¢ćƒŖćƒŖćƒ¼ć‚Æć‚ć‚Šļ¼ˆå•é”Œć®ć‚ć‚‹ćƒ‘ć‚æćƒ¼ćƒ³ļ¼‰ ===" ) // č¤‡ę•°ć®å¤§ććŖć‚¹ćƒ©ć‚¤ć‚¹ć‚’ä½œęˆć—ć¦äæęŒ sizes := [] int { 1000000 , 2000000 , 3000000 } // 1M, 2M, 3M要瓠 for i, size := range sizes { fmt.Printf( "ć‚¹ćƒ†ćƒƒćƒ— %d: %dč¦ē“ ć®é…åˆ—ć‹ć‚‰10č¦ē“ ć‚¹ćƒ©ć‚¤ć‚¹ć‚’ä½œęˆ \n " , i+ 1 , size) leakySlice := createLeakySlice(size) // ć‚°ćƒ­ćƒ¼ćƒćƒ«å¤‰ę•°ć«äæå­˜ć—ć¦ć‚¬ćƒ™ćƒ¼ć‚øć‚³ćƒ¬ć‚Æć‚·ćƒ§ćƒ³ć‚’é˜²ć globalSlices = append (globalSlices, leakySlice) printMemoryStats(fmt.Sprintf( "ć‚¹ćƒ†ćƒƒćƒ— %d 後" , i+ 1 )) // åŒć˜é…åˆ—ć‚’å‚ē…§ć—ć¦ć„ć‚‹ć“ćØć‚’ē¢ŗčŖ fmt.Printf( " å‚ē…§å…ˆē¢ŗčŖ: 容量=%d (å…ƒć®é…åˆ—ć‚µć‚¤ć‚ŗćØåŒć˜=%t) \n " , cap (leakySlice), cap (leakySlice) == size) fmt.Println( " → å¤§ććŖé…åˆ—å…Øä½“ćŒćƒ”ćƒ¢ćƒŖć«ę®‹ć£ć¦ć„ć‚‹ļ¼ \n " ) } fmt.Printf( "å•é”Œćƒ‘ć‚æćƒ¼ćƒ³åˆčØˆ: %då€‹ć®ć‚¹ćƒ©ć‚¤ć‚¹ć‚’äæęŒ \n " , len (globalSlices)) fmt.Printf( "å®Ÿéš›ć«ä½æē”Øć—ć¦ć„ć‚‹č¦ē“ ę•°: %d \n " , len (globalSlices)* 10 ) totalCapacity := 0 for _, s := range globalSlices { totalCapacity += cap (s) } fmt.Printf( "å®Ÿéš›ć«ē¢ŗäæć•ć‚Œć¦ć„ć‚‹å®¹é‡: %d (%.0få€ć®ē„”é§„) \n " , totalCapacity, float64 (totalCapacity)/ float64 ( len (globalSlices)* 10 )) printMemoryStats( "å•é”Œćƒ‘ć‚æćƒ¼ćƒ³å®Œäŗ†å¾Œ" ) // ć‚°ćƒ­ćƒ¼ćƒćƒ«å¤‰ę•°ć‚’ć‚ÆćƒŖć‚¢ globalSlices = nil printMemoryStats( "å•é”Œćƒ‘ć‚æćƒ¼ćƒ³ć‚ÆćƒŖć‚¢å¾Œ" ) // å®ŸéØ“2: ćƒ”ćƒ¢ćƒŖćƒŖćƒ¼ć‚ÆćŖć— fmt.Println( "=== å®ŸéØ“2: ćƒ”ćƒ¢ćƒŖćƒŖćƒ¼ć‚ÆćŖć—ļ¼ˆę”¹å–„ć•ć‚ŒćŸćƒ‘ć‚æćƒ¼ćƒ³ļ¼‰ ===" ) var fixedSlices [][] int for i, size := range sizes { fmt.Printf( "ć‚¹ćƒ†ćƒƒćƒ— %d: %dč¦ē“ ć®é…åˆ—ć‹ć‚‰10č¦ē“ ć‚¹ćƒ©ć‚¤ć‚¹ć‚’ä½œęˆļ¼ˆć‚³ćƒ”ćƒ¼ē‰ˆļ¼‰ \n " , i+ 1 , size) fixedSlice := createFixedSlice(size) fixedSlices = append (fixedSlices, fixedSlice) printMemoryStats(fmt.Sprintf( "ę”¹å–„ć‚¹ćƒ†ćƒƒćƒ— %d 後" , i+ 1 )) fmt.Printf( " å‚ē…§å…ˆē¢ŗčŖ: 容量=%d (ęœ€å°é™=%t) \n " , cap (fixedSlice), cap (fixedSlice) == 10 ) fmt.Println( " → åæ…č¦ćŖåˆ†ć ć‘ćƒ”ćƒ¢ćƒŖć‚’ä½æē”Øļ¼ \n " ) } printMemoryStats( "ę”¹å–„ćƒ‘ć‚æćƒ¼ćƒ³å®Œäŗ†å¾Œ" ) // å®ŸéØ“3: å®Ÿéš›ć®ćƒ”ćƒ¢ćƒŖć‚¢ćƒ‰ćƒ¬ć‚¹ć®ęÆ”č¼ƒ fmt.Println( "=== å®ŸéØ“3: ćƒ”ćƒ¢ćƒŖć‚¢ćƒ‰ćƒ¬ć‚¹ęÆ”č¼ƒå®ŸéØ“ ===" ) demonstrateAddressComparison() } func demonstrateAddressComparison() { // å¤§ććŖé…åˆ—ć‚’ä½œęˆ bigArray := make ([] int , 1000000 ) for i := range bigArray { bigArray[i] = i } fmt.Printf( "å…ƒć®å¤§ććŖé…åˆ—ć®ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x%x \n " , uintptr ( unsafe.Pointer (&bigArray[ 0 ]))) // å•é”Œć®ć‚ć‚‹ć‚¹ćƒ©ć‚¤ć‚·ćƒ³ć‚° problematicSlice := bigArray[: 5 ] fmt.Printf( "å•é”Œć‚¹ćƒ©ć‚¤ć‚¹ć®ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x%x (同じ=%t) \n " , uintptr ( unsafe.Pointer (&problematicSlice[ 0 ])), unsafe.Pointer (&bigArray[ 0 ]) == unsafe.Pointer (&problematicSlice[ 0 ])) // ę”¹å–„ć•ć‚ŒćŸć‚³ćƒ”ćƒ¼ fixedSlice := make ([] int , 5 ) copy (fixedSlice, bigArray[: 5 ]) fmt.Printf( "ę”¹å–„ć‚¹ćƒ©ć‚¤ć‚¹ć®ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x%x (異なる=%t) \n " , uintptr ( unsafe.Pointer (&fixedSlice[ 0 ])), unsafe.Pointer (&bigArray[ 0 ]) != unsafe.Pointer (&fixedSlice[ 0 ])) fmt.Println( " \n ćƒ”ćƒ¢ćƒŖå‚ē…§ć®é•ć„:" ) fmt.Printf( "å•é”Œć‚¹ćƒ©ć‚¤ć‚¹: len=%d, cap=%d (å¤§ććŖé…åˆ—ć‚’å‚ē…§) \n " , len (problematicSlice), cap (problematicSlice)) fmt.Printf( "ę”¹å–„ć‚¹ćƒ©ć‚¤ć‚¹: len=%d, cap=%d (ē‹¬ē«‹ć—ćŸå°ć•ćŖé…åˆ—) \n " , len (fixedSlice), cap (fixedSlice)) // bigArray悒nil恫恗恦悂态problematicSlicećŒå‚ē…§ć‚’äæęŒ fmt.Println( " \n å…ƒć®é…åˆ—ć‚’nilć«ć—ćŸå “åˆ:" ) bigArray = nil runtime.GC() fmt.Printf( "problematicSlice[0] = %d (ć¾ć ć‚¢ć‚Æć‚»ć‚¹åÆčƒ½) \n " , problematicSlice[ 0 ]) fmt.Printf( "å®¹é‡ćÆä¾ē„¶ćØć—ć¦: %d \n " , cap (problematicSlice)) fmt.Println( "→ å¤§ććŖé…åˆ—ćÆč§£ę”¾ć•ć‚Œć¦ć„ćŖć„ļ¼" ) } å®Ÿč”Œēµęžœ === ćƒ”ćƒ¢ćƒŖćƒŖćƒ¼ć‚Æå®ŸčØ¼å®ŸéØ“ === åˆęœŸēŠ¶ę…‹: Alloc: 0.11 MB (ē¾åœØä½æē”Øäø­ć®ćƒ”ćƒ¢ćƒŖ) TotalAlloc: 0.12 MB (ē“ÆčØˆå‰²ć‚Šå½“ć¦ćƒ”ćƒ¢ćƒŖ) Sys: 6.52 MB (ć‚·ć‚¹ćƒ†ćƒ ć‹ć‚‰å–å¾—ć—ćŸćƒ”ćƒ¢ćƒŖ) NumGC: 2 (ć‚¬ćƒ™ćƒ¼ć‚øć‚³ćƒ¬ć‚Æć‚·ćƒ§ćƒ³å›žę•°) === å®ŸéØ“1: ćƒ”ćƒ¢ćƒŖćƒŖćƒ¼ć‚Æć‚ć‚Šļ¼ˆå•é”Œć®ć‚ć‚‹ćƒ‘ć‚æćƒ¼ćƒ³ļ¼‰ === ć‚¹ćƒ†ćƒƒćƒ— 1: 1000000č¦ē“ ć®é…åˆ—ć‹ć‚‰10č¦ē“ ć‚¹ćƒ©ć‚¤ć‚¹ć‚’ä½œęˆ 1000000č¦ē“ ć®é…åˆ—ć‚’ä½œęˆäø­... čæ”ć™ć‚¹ćƒ©ć‚¤ć‚¹: len=10, cap=1000000 ć‚¹ćƒ†ćƒƒćƒ— 1 後: Alloc: 7.75 MB (ē¾åœØä½æē”Øäø­ć®ćƒ”ćƒ¢ćƒŖ) TotalAlloc: 7.75 MB (ē“ÆčØˆå‰²ć‚Šå½“ć¦ćƒ”ćƒ¢ćƒŖ) Sys: 14.52 MB (ć‚·ć‚¹ćƒ†ćƒ ć‹ć‚‰å–å¾—ć—ćŸćƒ”ćƒ¢ćƒŖ) NumGC: 5 (ć‚¬ćƒ™ćƒ¼ć‚øć‚³ćƒ¬ć‚Æć‚·ćƒ§ćƒ³å›žę•°) å‚ē…§å…ˆē¢ŗčŖ: 容量=1000000 (å…ƒć®é…åˆ—ć‚µć‚¤ć‚ŗćØåŒć˜=true) → å¤§ććŖé…åˆ—å…Øä½“ćŒćƒ”ćƒ¢ćƒŖć«ę®‹ć£ć¦ć„ć‚‹ļ¼ ć‚¹ćƒ†ćƒƒćƒ— 2: 2000000č¦ē“ ć®é…åˆ—ć‹ć‚‰10č¦ē“ ć‚¹ćƒ©ć‚¤ć‚¹ć‚’ä½œęˆ 2000000č¦ē“ ć®é…åˆ—ć‚’ä½œęˆäø­... čæ”ć™ć‚¹ćƒ©ć‚¤ć‚¹: len=10, cap=2000000 ć‚¹ćƒ†ćƒƒćƒ— 2 後: Alloc: 23.02 MB (ē¾åœØä½æē”Øäø­ć®ćƒ”ćƒ¢ćƒŖ) TotalAlloc: 23.02 MB (ē“ÆčØˆå‰²ć‚Šå½“ć¦ćƒ”ćƒ¢ćƒŖ) Sys: 30.52 MB (ć‚·ć‚¹ćƒ†ćƒ ć‹ć‚‰å–å¾—ć—ćŸćƒ”ćƒ¢ćƒŖ) NumGC: 8 (ć‚¬ćƒ™ćƒ¼ć‚øć‚³ćƒ¬ć‚Æć‚·ćƒ§ćƒ³å›žę•°) å‚ē…§å…ˆē¢ŗčŖ: 容量=2000000 (å…ƒć®é…åˆ—ć‚µć‚¤ć‚ŗćØåŒć˜=true) → å¤§ććŖé…åˆ—å…Øä½“ćŒćƒ”ćƒ¢ćƒŖć«ę®‹ć£ć¦ć„ć‚‹ļ¼ ć‚¹ćƒ†ćƒƒćƒ— 3: 3000000č¦ē“ ć®é…åˆ—ć‹ć‚‰10č¦ē“ ć‚¹ćƒ©ć‚¤ć‚¹ć‚’ä½œęˆ 3000000č¦ē“ ć®é…åˆ—ć‚’ä½œęˆäø­... čæ”ć™ć‚¹ćƒ©ć‚¤ć‚¹: len=10, cap=3000000 ć‚¹ćƒ†ćƒƒćƒ— 3 後: Alloc: 45.91 MB (ē¾åœØä½æē”Øäø­ć®ćƒ”ćƒ¢ćƒŖ) TotalAlloc: 45.92 MB (ē“ÆčØˆå‰²ć‚Šå½“ć¦ćƒ”ćƒ¢ćƒŖ) Sys: 54.52 MB (ć‚·ć‚¹ćƒ†ćƒ ć‹ć‚‰å–å¾—ć—ćŸćƒ”ćƒ¢ćƒŖ) NumGC: 11 (ć‚¬ćƒ™ćƒ¼ć‚øć‚³ćƒ¬ć‚Æć‚·ćƒ§ćƒ³å›žę•°) å‚ē…§å…ˆē¢ŗčŖ: 容量=3000000 (å…ƒć®é…åˆ—ć‚µć‚¤ć‚ŗćØåŒć˜=true) → å¤§ććŖé…åˆ—å…Øä½“ćŒćƒ”ćƒ¢ćƒŖć«ę®‹ć£ć¦ć„ć‚‹ļ¼ å•é”Œćƒ‘ć‚æćƒ¼ćƒ³åˆčØˆ: 3å€‹ć®ć‚¹ćƒ©ć‚¤ć‚¹ć‚’äæęŒ å®Ÿéš›ć«ä½æē”Øć—ć¦ć„ć‚‹č¦ē“ ę•°: 30 å®Ÿéš›ć«ē¢ŗäæć•ć‚Œć¦ć„ć‚‹å®¹é‡: 6000000 (200000å€ć®ē„”é§„) å•é”Œćƒ‘ć‚æćƒ¼ćƒ³å®Œäŗ†å¾Œ: Alloc: 45.91 MB (ē¾åœØä½æē”Øäø­ć®ćƒ”ćƒ¢ćƒŖ) TotalAlloc: 45.92 MB (ē“ÆčØˆå‰²ć‚Šå½“ć¦ćƒ”ćƒ¢ćƒŖ) Sys: 54.52 MB (ć‚·ć‚¹ćƒ†ćƒ ć‹ć‚‰å–å¾—ć—ćŸćƒ”ćƒ¢ćƒŖ) NumGC: 13 (ć‚¬ćƒ™ćƒ¼ć‚øć‚³ćƒ¬ć‚Æć‚·ćƒ§ćƒ³å›žę•°) å•é”Œćƒ‘ć‚æćƒ¼ćƒ³ć‚ÆćƒŖć‚¢å¾Œ: Alloc: 0.12 MB (ē¾åœØä½æē”Øäø­ć®ćƒ”ćƒ¢ćƒŖ) TotalAlloc: 45.92 MB (ē“ÆčØˆå‰²ć‚Šå½“ć¦ćƒ”ćƒ¢ćƒŖ) Sys: 54.52 MB (ć‚·ć‚¹ćƒ†ćƒ ć‹ć‚‰å–å¾—ć—ćŸćƒ”ćƒ¢ćƒŖ) NumGC: 15 (ć‚¬ćƒ™ćƒ¼ć‚øć‚³ćƒ¬ć‚Æć‚·ćƒ§ćƒ³å›žę•°) === å®ŸéØ“2: ćƒ”ćƒ¢ćƒŖćƒŖćƒ¼ć‚ÆćŖć—ļ¼ˆę”¹å–„ć•ć‚ŒćŸćƒ‘ć‚æćƒ¼ćƒ³ļ¼‰ === ć‚¹ćƒ†ćƒƒćƒ— 1: 1000000č¦ē“ ć®é…åˆ—ć‹ć‚‰10č¦ē“ ć‚¹ćƒ©ć‚¤ć‚¹ć‚’ä½œęˆļ¼ˆć‚³ćƒ”ćƒ¼ē‰ˆļ¼‰ 1000000č¦ē“ ć®é…åˆ—ć‚’ä½œęˆäø­... čæ”ć™ć‚¹ćƒ©ć‚¤ć‚¹: len=10, cap=10 ę”¹å–„ć‚¹ćƒ†ćƒƒćƒ— 1 後: Alloc: 0.12 MB (ē¾åœØä½æē”Øäø­ć®ćƒ”ćƒ¢ćƒŖ) TotalAlloc: 53.56 MB (ē“ÆčØˆå‰²ć‚Šå½“ć¦ćƒ”ćƒ¢ćƒŖ) Sys: 54.77 MB (ć‚·ć‚¹ćƒ†ćƒ ć‹ć‚‰å–å¾—ć—ćŸćƒ”ćƒ¢ćƒŖ) NumGC: 18 (ć‚¬ćƒ™ćƒ¼ć‚øć‚³ćƒ¬ć‚Æć‚·ćƒ§ćƒ³å›žę•°) å‚ē…§å…ˆē¢ŗčŖ: 容量=10 (ęœ€å°é™=true) → åæ…č¦ćŖåˆ†ć ć‘ćƒ”ćƒ¢ćƒŖć‚’ä½æē”Øļ¼ ć‚¹ćƒ†ćƒƒćƒ— 2: 2000000č¦ē“ ć®é…åˆ—ć‹ć‚‰10č¦ē“ ć‚¹ćƒ©ć‚¤ć‚¹ć‚’ä½œęˆļ¼ˆć‚³ćƒ”ćƒ¼ē‰ˆļ¼‰ 2000000č¦ē“ ć®é…åˆ—ć‚’ä½œęˆäø­... čæ”ć™ć‚¹ćƒ©ć‚¤ć‚¹: len=10, cap=10 ę”¹å–„ć‚¹ćƒ†ćƒƒćƒ— 2 後: Alloc: 0.12 MB (ē¾åœØä½æē”Øäø­ć®ćƒ”ćƒ¢ćƒŖ) TotalAlloc: 68.83 MB (ē“ÆčØˆå‰²ć‚Šå½“ć¦ćƒ”ćƒ¢ćƒŖ) Sys: 54.77 MB (ć‚·ć‚¹ćƒ†ćƒ ć‹ć‚‰å–å¾—ć—ćŸćƒ”ćƒ¢ćƒŖ) NumGC: 21 (ć‚¬ćƒ™ćƒ¼ć‚øć‚³ćƒ¬ć‚Æć‚·ćƒ§ćƒ³å›žę•°) å‚ē…§å…ˆē¢ŗčŖ: 容量=10 (ęœ€å°é™=true) → åæ…č¦ćŖåˆ†ć ć‘ćƒ”ćƒ¢ćƒŖć‚’ä½æē”Øļ¼ ć‚¹ćƒ†ćƒƒćƒ— 3: 3000000č¦ē“ ć®é…åˆ—ć‹ć‚‰10č¦ē“ ć‚¹ćƒ©ć‚¤ć‚¹ć‚’ä½œęˆļ¼ˆć‚³ćƒ”ćƒ¼ē‰ˆļ¼‰ 3000000č¦ē“ ć®é…åˆ—ć‚’ä½œęˆäø­... čæ”ć™ć‚¹ćƒ©ć‚¤ć‚¹: len=10, cap=10 ę”¹å–„ć‚¹ćƒ†ćƒƒćƒ— 3 後: Alloc: 0.12 MB (ē¾åœØä½æē”Øäø­ć®ćƒ”ćƒ¢ćƒŖ) TotalAlloc: 91.72 MB (ē“ÆčØˆå‰²ć‚Šå½“ć¦ćƒ”ćƒ¢ćƒŖ) Sys: 54.77 MB (ć‚·ć‚¹ćƒ†ćƒ ć‹ć‚‰å–å¾—ć—ćŸćƒ”ćƒ¢ćƒŖ) NumGC: 24 (ć‚¬ćƒ™ćƒ¼ć‚øć‚³ćƒ¬ć‚Æć‚·ćƒ§ćƒ³å›žę•°) å‚ē…§å…ˆē¢ŗčŖ: 容量=10 (ęœ€å°é™=true) → åæ…č¦ćŖåˆ†ć ć‘ćƒ”ćƒ¢ćƒŖć‚’ä½æē”Øļ¼ ę”¹å–„ćƒ‘ć‚æćƒ¼ćƒ³å®Œäŗ†å¾Œ: Alloc: 0.12 MB (ē¾åœØä½æē”Øäø­ć®ćƒ”ćƒ¢ćƒŖ) TotalAlloc: 91.72 MB (ē“ÆčØˆå‰²ć‚Šå½“ć¦ćƒ”ćƒ¢ćƒŖ) Sys: 54.77 MB (ć‚·ć‚¹ćƒ†ćƒ ć‹ć‚‰å–å¾—ć—ćŸćƒ”ćƒ¢ćƒŖ) NumGC: 26 (ć‚¬ćƒ™ćƒ¼ć‚øć‚³ćƒ¬ć‚Æć‚·ćƒ§ćƒ³å›žę•°) === å®ŸéØ“3: ćƒ”ćƒ¢ćƒŖć‚¢ćƒ‰ćƒ¬ć‚¹ęÆ”č¼ƒå®ŸéØ“ === å…ƒć®å¤§ććŖé…åˆ—ć®ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x400018c000 å•é”Œć‚¹ćƒ©ć‚¤ć‚¹ć®ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x400018c000 (同じ=true) ę”¹å–„ć‚¹ćƒ©ć‚¤ć‚¹ć®ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x40000d7c08 (異なる=true) ćƒ”ćƒ¢ćƒŖå‚ē…§ć®é•ć„: å•é”Œć‚¹ćƒ©ć‚¤ć‚¹: len=5, cap=1000000 (å¤§ććŖé…åˆ—ć‚’å‚ē…§) ę”¹å–„ć‚¹ćƒ©ć‚¤ć‚¹: len=5, cap=5 (ē‹¬ē«‹ć—ćŸå°ć•ćŖé…åˆ—) å…ƒć®é…åˆ—ć‚’nilć«ć—ćŸå “åˆ: problematicSlice[0] = 0 (ć¾ć ć‚¢ć‚Æć‚»ć‚¹åÆčƒ½) å®¹é‡ćÆä¾ē„¶ćØć—ć¦: 1000000 → å¤§ććŖé…åˆ—ćÆč§£ę”¾ć•ć‚Œć¦ć„ćŖć„ļ¼ クイズ3 ## **šŸŽÆ クイズ第3問** ä»Šåŗ¦ćÆć€ć‚¹ćƒ©ć‚¤ć‚¹ć®**ćƒ”ćƒ¢ćƒŖćƒŖćƒ¼ć‚ÆćØå…±ęœ‰é…åˆ—ć®å•é”Œ**について学習しましょう。 ć‚µćƒ³ćƒ—ćƒ«5ć®å®Ÿč”ŒēµęžœćØå•é”Œē‚¹ć‚’äŗˆęƒ³ć—ć¦ćć ć•ć„ļ¼š **č³Ŗå•ļ¼š** 1. ` small ` ć®é•·ć•ćØå®¹é‡ćÆć„ćć¤ć«ćŖć‚‹ć§ć—ć‚‡ć†ć‹ļ¼Ÿ 2. 100äø‡č¦ē“ ć®å¤§ććŖé…åˆ—ćÆć‚¬ćƒ™ćƒ¼ć‚øć‚³ćƒ¬ć‚Æć‚·ćƒ§ćƒ³ć•ć‚Œć‚‹ć§ć—ć‚‡ć†ć‹ļ¼Ÿ 3. ć‚‚ć—ć‚¬ćƒ™ćƒ¼ć‚øć‚³ćƒ¬ć‚Æć‚·ćƒ§ćƒ³ć•ć‚ŒćŖć„å “åˆć€ćŖćœć§ć—ć‚‡ć†ć‹ļ¼Ÿ ć“ć‚ŒćÆå®Ÿéš›ć®é–‹ē™ŗć§ć‚ˆćé­é‡ć™ć‚‹**ćƒ”ćƒ¢ćƒŖćƒŖćƒ¼ć‚Æ**ć®åŽŸå› ćØćŖć‚‹é‡č¦ćŖå•é”Œć§ć™ć€‚ć‚ćŖćŸć®äŗˆęƒ³ć‚’ćŠčžć‹ć›ćć ć•ć„ļ¼ ć“ć“ć§ćÆć€ć‚¹ćƒ©ć‚¤ć‚¹čµ·å› ć®ćƒ”ćƒ¢ćƒŖćƒŖćƒ¼ć‚Æć«ć¤ć„ć¦ę¤œčØ¼ć—ć¾ć—ćŸć€‚ ļ¼ˆē¢ŗäæćƒ”ćƒ¢ćƒŖé ˜åŸŸć®č§£ę”¾åæ˜ć‚Œć§ćÆćŖć slice ć®å‚ē…§ć«ć‚ˆć‚‹ę®‹å­˜ćŖć®ć§ć€ åŽ³åÆ†ć«ćÆćƒ”ćƒ¢ćƒŖćƒŖćƒ¼ć‚ÆćØćÆčØ€ć‚ćŖć„ć‹ć‚‚ć—ć‚Œć¾ć›ć‚“ćŒć€ć“ć“ć§ćÆćƒ”ćƒ¢ćƒŖćƒŖćƒ¼ć‚ÆćØć„ć†ę–‡č„ˆć§č©±ć‚’é€²ć‚ć¦ć„ćć¾ć™ć€‚ļ¼‰ ć“ć®ć‚¹ćƒ†ćƒƒćƒ—ć®ę¤œčØ¼ć‹ć‚‰ć€ä»„äø‹ć®ć‚ˆć†ćŖč€ƒåÆŸć‚’å¾—ć‚‹ć“ćØćŒć§ćć¾ć—ćŸć€‚ ć‚¹ćƒ©ć‚¤ć‚¹ćÆé…åˆ—ćøć®å‚ē…§ć§ć‚ć‚Šć€éƒØåˆ†ć‚¹ćƒ©ć‚¤ć‚¹ćŒē”Ÿå­˜ć—ć¦ć„ć‚‹é™ć‚Šć€å…ƒé…åˆ—ćÆ GC ć§č§£ę”¾ć•ć‚ŒćŖć„ć€‚ ć‚¹ćƒ©ć‚¤ć‚¹ćŒå°ć•ćć¦ć‚‚å¤§ćć„é…åˆ—ć‚’å‚ē…§ć—ć¦ć„ć‚‹å “åˆćÆć€é…åˆ—ćŒē¢ŗäæć—ć¦ć„ć‚‹å¤§ććŖćƒ”ćƒ¢ćƒŖé ˜åŸŸćÆč§£ę”¾ć•ć‚ŒćŖć„ć€‚ å‚ē…§ćŒę®‹ć‚‰ćŖć‘ć‚Œć°č‰Æć„ć®ć§ć€ copy é–¢ę•°ćŖć©ć‚’åˆ©ē”Øć™ć‚‹ć“ćØć§å›žéæåÆčƒ½ ę–‡å­—åˆ—ć«ćŠć‘ć‚‹ćƒ”ćƒ¢ćƒŖćƒŖćƒ¼ć‚Æ ć‚µćƒ³ćƒ—ćƒ«6ļ¼ˆå®Ÿč£…ļ¼‹å®Ÿč”Œēµęžœļ¼‰ å®Ÿč£… package main import ( "bufio" "fmt" "os" "runtime" "strings" "sync" "time" "unsafe" ) // å¤§ććŖćƒ†ć‚¹ćƒˆćƒ•ć‚”ć‚¤ćƒ«ć‚’ä½œęˆļ¼ˆē“„50MB) func createLargeTestLogFile() { file, err := os.Create( "large_test.log" ) if err != nil { fmt.Printf( "ćƒ•ć‚”ć‚¤ćƒ«ä½œęˆć‚Øćƒ©ćƒ¼: %v \n " , err) return } defer file.Close() // ē“„50MBć®ćƒ­ć‚°ćƒ•ć‚”ć‚¤ćƒ«ć‚’ē”Ÿęˆ for i := 0 ; i < 500000 ; i++ { if i% 10000 == 0 { file.WriteString(fmt.Sprintf( "ERROR: Critical error occurred at line %d - %s \n " , i, strings.Repeat( "important data " , 10 ))) } else { file.WriteString(fmt.Sprintf( "INFO: Normal operation at line %d - %s \n " , i, strings.Repeat( "lorem ipsum dolor sit amet consectetur adipiscing elit " , 5 ))) } } fmt.Printf( "å¤§ććŖćƒ†ć‚¹ćƒˆćƒ•ć‚”ć‚¤ćƒ« (large_test.log) ć‚’ä½œęˆć—ć¾ć—ćŸ \n " ) } // å•é”Œć®ć‚ć‚‹å…ƒć®ć‚³ćƒ¼ćƒ‰ļ¼ˆć‚ˆć‚Šę˜Žē¢ŗć«å•é”Œć‚’ē¤ŗć™ćƒćƒ¼ć‚øćƒ§ćƒ³ļ¼‰ func processLogFileProblematic(filename string ) [] string { content, err := os.ReadFile(filename) if err != nil { return nil } // é‡č¦: ę–‡å­—åˆ—å¤‰ę›ć§å…ƒć®ćƒć‚¤ćƒˆé…åˆ—ćØćÆåˆ„ć«ę–‡å­—åˆ—ē”Øćƒ”ćƒ¢ćƒŖć‚’ē¢ŗäæ contentStr := string (content) lines := strings.Split(contentStr, " \n " ) var errorLines [] string for _, line := range lines { if strings.Contains(line, "ERROR" ) { // ć“ć“ćŒå•é”Œ: line は contentStr ćøć®å‚ē…§ errorLines = append (errorLines, line) } } // content と contentStr ćÆć‚ÆćƒŖć‚¢ć—ć¦ć‚‚ć€ // errorLines ć®å„č¦ē“ ćŒ contentStr ćøć®å‚ē…§ć‚’äæęŒ return errorLines } // 改善策1: ć‚¹ćƒˆćƒŖćƒ¼ćƒŸćƒ³ć‚°å‡¦ē† func processLogFileStreaming(filename string ) [] string { file, err := os.Open(filename) if err != nil { return nil } defer file.Close() var errorLines [] string scanner := bufio.NewScanner(file) for scanner.Scan() { line := scanner.Text() if strings.Contains(line, "ERROR" ) { // strings.Clone ć§å…ƒćƒ•ć‚”ć‚¤ćƒ«ćøć®å‚ē…§ć‚’åˆ‡ę–­ errorLines = append (errorLines, strings.Clone(line)) } } return errorLines } // 改善策4-1: ćƒ•ć‚”ć‚¤ćƒ«åˆ†å‰²å‡¦ē† func processLogFileChunked(filename string , chunkSize int64 ) [] string { file, err := os.Open(filename) if err != nil { return nil } defer file.Close() var errorLines [] string // ćƒ•ć‚”ć‚¤ćƒ«ć‚’ęŒ‡å®šć‚µć‚¤ć‚ŗć®ćƒćƒ£ćƒ³ć‚Æć«åˆ†å‰²ć—ć¦å‡¦ē† buffer := make ([] byte , chunkSize) var remainder [] byte for { n, err := file.Read(buffer) if n == 0 { break } // å‰å›žć®ä½™ć‚ŠćØä»Šå›žčŖ­ć‚“ć ćƒ‡ćƒ¼ć‚æć‚’ēµåˆ chunk := append (remainder, buffer[:n]...) // ęœ€å¾Œć®äøå®Œå…ØćŖč”Œć‚’ę¬”å›žć«ęŒć”č¶Šć— lastNewline := strings.LastIndex( string (chunk), " \n " ) if lastNewline == - 1 { remainder = chunk continue } processChunk := chunk[:lastNewline] remainder = chunk[lastNewline+ 1 :] // ćƒćƒ£ćƒ³ć‚Æå†…ć§ć‚Øćƒ©ćƒ¼č”Œć‚’ę¤œē“¢ lines := strings.Split( string (processChunk), " \n " ) for _, line := range lines { if strings.Contains(line, "ERROR" ) { errorLines = append (errorLines, strings.Clone(line)) } } if err != nil { break } } // ęœ€å¾Œć®ä½™ć‚Šć‚‚å‡¦ē† if len (remainder) > 0 && strings.Contains( string (remainder), "ERROR" ) { errorLines = append (errorLines, strings.Clone( string (remainder))) } return errorLines } // 改善策4-2: äø¦åˆ—å‡¦ē† func processLogFileParallel(filename string ) [] string { content, err := os.ReadFile(filename) if err != nil { return nil } lines := strings.Split( string (content), " \n " ) // ćƒÆćƒ¼ć‚«ćƒ¼ę•° numWorkers := runtime.NumCPU() chunkSize := len (lines) / numWorkers var wg sync.WaitGroup var mu sync.Mutex var errorLines [] string for i := 0 ; i < numWorkers; i++ { wg.Add( 1 ) go func (start, end int ) { defer wg.Done() var localErrors [] string for j := start; j < end && j < len (lines); j++ { if strings.Contains(lines[j], "ERROR" ) { localErrors = append (localErrors, strings.Clone(lines[j])) } } mu.Lock() errorLines = append (errorLines, localErrors...) mu.Unlock() }(i*chunkSize, (i+ 1 )*chunkSize) } wg.Wait() return errorLines } // 改善策4-3: ć‚¤ćƒ³ćƒ‡ćƒƒć‚Æć‚¹åˆ©ē”Ø type LogIndex struct { ErrorLineNumbers [] int filename string } func buildLogIndex(filename string ) *LogIndex { file, err := os.Open(filename) if err != nil { return nil } defer file.Close() var errorLines [] int scanner := bufio.NewScanner(file) lineNumber := 0 for scanner.Scan() { if strings.Contains(scanner.Text(), "ERROR" ) { errorLines = append (errorLines, lineNumber) } lineNumber++ } return &LogIndex{ ErrorLineNumbers: errorLines, filename: filename, } } func (idx *LogIndex) GetErrorLines() [] string { file, err := os.Open(idx.filename) if err != nil { return nil } defer file.Close() var errorLines [] string scanner := bufio.NewScanner(file) lineNumber := 0 errorIndex := 0 for scanner.Scan() && errorIndex < len (idx.ErrorLineNumbers) { if lineNumber == idx.ErrorLineNumbers[errorIndex] { errorLines = append (errorLines, strings.Clone(scanner.Text())) errorIndex++ } lineNumber++ } return errorLines } func printMemoryStats(label string ) { runtime.GC() runtime.GC() time.Sleep( 100 * time.Millisecond) var m runtime.MemStats runtime.ReadMemStats(&m) fmt.Printf( "%s: %.2f MB \n " , label, float64 (m.Alloc)/ 1024 / 1024 ) } func demonstrateMemoryLeak() { fmt.Println( "=== å®Ÿéš›ć®ćƒ”ćƒ¢ćƒŖćƒŖćƒ¼ć‚Æå®ŸčØ¼ļ¼ˆå¤§ććŖćƒ•ć‚”ć‚¤ćƒ«ä½æē”Øļ¼‰ ===" ) printMemoryStats( "åˆęœŸēŠ¶ę…‹" ) // å•é”Œć®ć‚ć‚‹ćƒćƒ¼ć‚øćƒ§ćƒ³ć§ćƒ”ćƒ¢ćƒŖćƒŖćƒ¼ć‚Æć‚’å®ŸčØ¼ fmt.Println( "å•é”Œć®ć‚ć‚‹ćƒćƒ¼ć‚øćƒ§ćƒ³å®Ÿč”Œ..." ) // ćƒ”ćƒ¢ćƒŖćƒŖćƒ¼ć‚Æć‚’ē¶­ęŒć™ć‚‹ćŸć‚ć«ēµęžœć‚’äæęŒ var leakyResults [] string leakyResults = processLogFileProblematic( "large_test.log" ) fmt.Printf( "ć‚Øćƒ©ćƒ¼č”Œę•°: %d \n " , len (leakyResults)) printMemoryStats( "å•é”Œē‰ˆå®Ÿč”Œå¾Œļ¼ˆēµęžœäæęŒäø­ļ¼‰" ) // ēµęžœć®ę–‡å­—åˆ—ćŒå…ƒćƒ•ć‚”ć‚¤ćƒ«ćøć®å‚ē…§ć‚’äæęŒć—ć¦ć„ć‚‹ć“ćØć‚’čØ¼ę˜Ž fmt.Printf( "ęœ€åˆć®ć‚Øćƒ©ćƒ¼č”Œć®ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x%x \n " , (*(* unsafe.Pointer )( unsafe.Pointer (&leakyResults[ 0 ])))) // leakyResultsć‚’ć‚ÆćƒŖć‚¢ć™ć‚‹ć¾ć§ćƒ”ćƒ¢ćƒŖćÆč§£ę”¾ć•ć‚ŒćŖć„ fmt.Println( "ēµęžœć‚’ć‚ÆćƒŖć‚¢ć—ć¾ć™..." ) leakyResults = nil printMemoryStats( "å•é”Œē‰ˆēµęžœć‚ÆćƒŖć‚¢å¾Œ" ) // ę”¹å–„ē‰ˆć®å®Ÿč”Œ fmt.Println( "ę”¹å–„ē‰ˆļ¼ˆć‚¹ćƒˆćƒŖćƒ¼ćƒŸćƒ³ć‚°ļ¼‰å®Ÿč”Œ..." ) streamingResults := processLogFileStreaming( "large_test.log" ) fmt.Printf( "ć‚Øćƒ©ćƒ¼č”Œę•°: %d \n " , len (streamingResults)) printMemoryStats( "ę”¹å–„ē‰ˆå®Ÿč”Œå¾Œ" ) fmt.Printf( "ę”¹å–„ē‰ˆć‚Øćƒ©ćƒ¼č”Œć®ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x%x \n " , (*(* unsafe.Pointer )( unsafe.Pointer (&streamingResults[ 0 ])))) fmt.Println( "→ ē•°ćŖć‚‹ć‚¢ćƒ‰ćƒ¬ć‚¹ = ē‹¬ē«‹ć—ćŸę–‡å­—åˆ—" ) } func compareAllApproaches() { fmt.Println( " \n === å…Øę”¹å–„ē­–ć®ćƒ‘ćƒ•ć‚©ćƒ¼ćƒžćƒ³ć‚¹ęÆ”č¼ƒ ===" ) approaches := [] struct { name string fn func ( string ) [] string }{ { "å•é”Œē‰ˆ" , processLogFileProblematic}, { "ć‚¹ćƒˆćƒŖćƒ¼ćƒŸćƒ³ć‚°ē‰ˆ" , processLogFileStreaming}, { "ćƒćƒ£ćƒ³ć‚Æåˆ†å‰²ē‰ˆ" , func (f string ) [] string { return processLogFileChunked(f, 1024 * 1024 ) }}, // 1MB chunks { "äø¦åˆ—å‡¦ē†ē‰ˆ" , processLogFileParallel}, } for _, approach := range approaches { start := time.Now() result := approach.fn( "large_test.log" ) duration := time.Since(start) fmt.Printf( "%-12s: %v (%d蔌) \n " , approach.name, duration, len (result)) } // ć‚¤ćƒ³ćƒ‡ćƒƒć‚Æć‚¹åˆ©ē”Øć®ćƒ†ć‚¹ćƒˆ fmt.Println( " \n ć‚¤ćƒ³ćƒ‡ćƒƒć‚Æć‚¹åˆ©ē”Øē‰ˆ:" ) start := time.Now() index := buildLogIndex( "large_test.log" ) indexBuildTime := time.Since(start) start = time.Now() indexResults := index.GetErrorLines() queryTime := time.Since(start) fmt.Printf( " ć‚¤ćƒ³ćƒ‡ćƒƒć‚Æć‚¹ę§‹ēÆ‰: %v \n " , indexBuildTime) fmt.Printf( " ć‚Æć‚ØćƒŖå®Ÿč”Œ: %v (%d蔌) \n " , queryTime, len (indexResults)) fmt.Printf( " åˆčØˆę™‚é–“: %v \n " , indexBuildTime+queryTime) } func main() { fmt.Println( "=== ę”¹å–„ē‰ˆåˆ†ęžćØćƒ”ćƒ¢ćƒŖćƒŖćƒ¼ć‚Æå®ŸčØ¼ === \n " ) // å¤§ććŖćƒ†ć‚¹ćƒˆćƒ•ć‚”ć‚¤ćƒ«ć‚’ä½œęˆ createLargeTestLogFile() // å®Ÿéš›ć®ćƒ”ćƒ¢ćƒŖćƒŖćƒ¼ć‚Æć‚’å®ŸčØ¼ demonstrateMemoryLeak() // å…Øę”¹å–„ē­–ć®ęÆ”č¼ƒ compareAllApproaches() // 改善策の詳瓰解説 explainAdvancedStrategies() } func explainAdvancedStrategies() { fmt.Println( " \n === 改善策4の詳瓰解説 ===" ) fmt.Println( "1. ćƒ•ć‚”ć‚¤ćƒ«åˆ†å‰²å‡¦ē†:" ) fmt.Println( " āœ… ćƒ•ć‚”ć‚¤ćƒ«ć‚’å°ć•ćŖćƒćƒ£ćƒ³ć‚Æļ¼ˆä¾‹ļ¼š1MBļ¼‰ć«åˆ†å‰²" ) fmt.Println( " āœ… ćƒ”ćƒ¢ćƒŖä½æē”Øé‡ć‚’äø€å®šć«äæęŒ" ) fmt.Println( " āœ… å·Øå¤§ćƒ•ć‚”ć‚¤ćƒ«ć§ć‚‚å‡¦ē†åÆčƒ½" ) fmt.Println( " āš ļø č”Œć®å¢ƒē•Œć‚’é©åˆ‡ć«å‡¦ē†ć™ć‚‹åæ…č¦" ) fmt.Println( " \n 2. äø¦åˆ—å‡¦ē†:" ) fmt.Println( " āœ… CPUć‚³ć‚¢ę•°ć«åæœć˜ć¦ćƒÆćƒ¼ć‚«ćƒ¼ć‚’čµ·å‹•" ) fmt.Println( " āœ… ćƒ•ć‚”ć‚¤ćƒ«ć‚’åˆ†å‰²ć—ć¦äø¦åˆ—ę¤œē“¢" ) fmt.Println( " āœ… ćƒžćƒ«ćƒć‚³ć‚¢CPUć§é«˜é€ŸåŒ–" ) fmt.Println( " āš ļø ćƒ”ćƒ¢ćƒŖä½æē”Øé‡ćÆå¢—åŠ " ) fmt.Println( " \n 3. ć‚¤ćƒ³ćƒ‡ćƒƒć‚Æć‚¹åˆ©ē”Ø:" ) fmt.Println( " āœ… äŗ‹å‰ć«ć‚Øćƒ©ćƒ¼č”Œć®ä½ē½®ć‚’ć‚¤ćƒ³ćƒ‡ćƒƒć‚Æć‚¹åŒ–" ) fmt.Println( " āœ… åŒć˜ćƒ•ć‚”ć‚¤ćƒ«ć‚’ä½•åŗ¦ć‚‚ę¤œē“¢ć™ć‚‹å “åˆć«åŠ¹ēŽ‡ēš„" ) fmt.Println( " āœ… éƒØåˆ†ēš„ćŖēµęžœå–å¾—ćŒé«˜é€Ÿ" ) fmt.Println( " āš ļø ć‚¤ćƒ³ćƒ‡ćƒƒć‚Æć‚¹ę§‹ēÆ‰ę™‚é–“ćŒåæ…č¦" ) fmt.Println( " \n 4. ćć®ä»–ć®é«˜åŗ¦ćŖę‰‹ę³•:" ) fmt.Println( " - Memory-mapped files (mmap)" ) fmt.Println( " - åœ§ēø®ćƒ•ć‚”ć‚¤ćƒ«ć®ē›“ęŽ„å‡¦ē†" ) fmt.Println( " - ćƒ‡ćƒ¼ć‚æćƒ™ćƒ¼ć‚¹ćøć®äŗ‹å‰ć‚¤ćƒ³ćƒćƒ¼ćƒˆ" ) fmt.Println( " - ę­£č¦č”Øē¾ć®ęœ€é©åŒ–" ) } å®Ÿč”Œēµęžœ === ę”¹å–„ē‰ˆåˆ†ęžćØćƒ”ćƒ¢ćƒŖćƒŖćƒ¼ć‚Æå®ŸčØ¼ === å¤§ććŖćƒ†ć‚¹ćƒˆćƒ•ć‚”ć‚¤ćƒ« (large _ test.log) ć‚’ä½œęˆć—ć¾ć—ćŸ === å®Ÿéš›ć®ćƒ”ćƒ¢ćƒŖćƒŖćƒ¼ć‚Æå®ŸčØ¼ļ¼ˆå¤§ććŖćƒ•ć‚”ć‚¤ćƒ«ä½æē”Øļ¼‰ === åˆęœŸēŠ¶ę…‹: 0.12 MB å•é”Œć®ć‚ć‚‹ćƒćƒ¼ć‚øćƒ§ćƒ³å®Ÿč”Œ... ć‚Øćƒ©ćƒ¼č”Œę•°: 50 å•é”Œē‰ˆå®Ÿč”Œå¾Œļ¼ˆēµęžœäæęŒäø­ļ¼‰: 150.69 MB ęœ€åˆć®ć‚Øćƒ©ćƒ¼č”Œć®ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x4009c12000 ēµęžœć‚’ć‚ÆćƒŖć‚¢ć—ć¾ć™... å•é”Œē‰ˆēµęžœć‚ÆćƒŖć‚¢å¾Œ: 0.12 MB ę”¹å–„ē‰ˆļ¼ˆć‚¹ćƒˆćƒŖćƒ¼ćƒŸćƒ³ć‚°ļ¼‰å®Ÿč”Œ... ć‚Øćƒ©ćƒ¼č”Œę•°: 50 ę”¹å–„ē‰ˆå®Ÿč”Œå¾Œ: 0.13 MB ę”¹å–„ē‰ˆć‚Øćƒ©ćƒ¼č”Œć®ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x400009e0d0 → ē•°ćŖć‚‹ć‚¢ćƒ‰ćƒ¬ć‚¹ = ē‹¬ē«‹ć—ćŸę–‡å­—åˆ— === å…Øę”¹å–„ē­–ć®ćƒ‘ćƒ•ć‚©ćƒ¼ćƒžćƒ³ć‚¹ęÆ”č¼ƒ === å•é”Œē‰ˆ : 325.162209ms (50蔌) ć‚¹ćƒˆćƒŖćƒ¼ćƒŸćƒ³ć‚°ē‰ˆ : 94.33525ms (50蔌) ćƒćƒ£ćƒ³ć‚Æåˆ†å‰²ē‰ˆ : 253.834459ms (50蔌) äø¦åˆ—å‡¦ē†ē‰ˆ : 852.810793ms (50蔌) ć‚¤ćƒ³ćƒ‡ćƒƒć‚Æć‚¹åˆ©ē”Øē‰ˆ: ć‚¤ćƒ³ćƒ‡ćƒƒć‚Æć‚¹ę§‹ēÆ‰: 1.729318959s ć‚Æć‚ØćƒŖå®Ÿč”Œ: 107.052958ms (50蔌) åˆčØˆę™‚é–“: 1.836371917s === 改善策4の詳瓰解説 === 1. ćƒ•ć‚”ć‚¤ćƒ«åˆ†å‰²å‡¦ē†: āœ… ćƒ•ć‚”ć‚¤ćƒ«ć‚’å°ć•ćŖćƒćƒ£ćƒ³ć‚Æļ¼ˆä¾‹ļ¼š1MBļ¼‰ć«åˆ†å‰² āœ… ćƒ”ćƒ¢ćƒŖä½æē”Øé‡ć‚’äø€å®šć«äæęŒ āœ… å·Øå¤§ćƒ•ć‚”ć‚¤ćƒ«ć§ć‚‚å‡¦ē†åÆčƒ½ āš ļø č”Œć®å¢ƒē•Œć‚’é©åˆ‡ć«å‡¦ē†ć™ć‚‹åæ…č¦ 2. äø¦åˆ—å‡¦ē†: āœ… CPUć‚³ć‚¢ę•°ć«åæœć˜ć¦ćƒÆćƒ¼ć‚«ćƒ¼ć‚’čµ·å‹• āœ… ćƒ•ć‚”ć‚¤ćƒ«ć‚’åˆ†å‰²ć—ć¦äø¦åˆ—ę¤œē“¢ āœ… ćƒžćƒ«ćƒć‚³ć‚¢CPUć§é«˜é€ŸåŒ– āš ļø ćƒ”ćƒ¢ćƒŖä½æē”Øé‡ćÆå¢—åŠ  3. ć‚¤ćƒ³ćƒ‡ćƒƒć‚Æć‚¹åˆ©ē”Ø: āœ… äŗ‹å‰ć«ć‚Øćƒ©ćƒ¼č”Œć®ä½ē½®ć‚’ć‚¤ćƒ³ćƒ‡ćƒƒć‚Æć‚¹åŒ– āœ… åŒć˜ćƒ•ć‚”ć‚¤ćƒ«ć‚’ä½•åŗ¦ć‚‚ę¤œē“¢ć™ć‚‹å “åˆć«åŠ¹ēŽ‡ēš„ āœ… éƒØåˆ†ēš„ćŖēµęžœå–å¾—ćŒé«˜é€Ÿ āš ļø ć‚¤ćƒ³ćƒ‡ćƒƒć‚Æć‚¹ę§‹ēÆ‰ę™‚é–“ćŒåæ…č¦ 4. ćć®ä»–ć®é«˜åŗ¦ćŖę‰‹ę³•: - Memory-mapped files (mmap) - åœ§ēø®ćƒ•ć‚”ć‚¤ćƒ«ć®ē›“ęŽ„å‡¦ē† - ćƒ‡ćƒ¼ć‚æćƒ™ćƒ¼ć‚¹ćøć®äŗ‹å‰ć‚¤ćƒ³ćƒćƒ¼ćƒˆ - ę­£č¦č”Øē¾ć®ęœ€é©åŒ– クイズ4 ## **šŸŽÆ ęœ€ēµ‚å­¦ēæ’ē¢ŗčŖć‚Æć‚¤ć‚ŗ** ē“ ę™“ć‚‰ć—ć„ē†č§£åŠ›ć‚’ē¤ŗć—ć¦ć„ćŸć ć„ćŸć®ć§ć€ęœ€ēµ‚å•é”Œć«é€²ćæć¾ć—ć‚‡ć†ļ¼ **問锌**: å•é”Œć®ć‚ć‚‹ć‚³ćƒ¼ćƒ‰ä¾‹ć®å•é”Œē‚¹ćØę”¹å–„ē­–ć‚’čŖ¬ę˜Žć—ć¦ćć ć•ć„ļ¼š **åˆ†ęžé …ē›®:** 1. **ćƒ”ćƒ¢ćƒŖåŠ¹ēŽ‡ę€§**ć®å•é”Œē‚¹ 2. **ę™‚é–“čØˆē®—é‡**ć®å•é”Œē‚¹ 3. **å…·ä½“ēš„ćŖę”¹å–„ē­–**悒2つ仄上 ć‚ćŖćŸć®ć‚¹ćƒ©ć‚¤ć‚¹å­¦ēæ’ć®é›†å¤§ęˆćØć—ć¦ć€åˆ†ęžć‚’ćŠčžć‹ć›ćć ć•ć„ļ¼ ć‚¹ćƒ©ć‚¤ć‚¹ć®ćƒ”ćƒ¢ćƒŖćƒŖćƒ¼ć‚Æć®č©±ć‹ć‚‰ē™ŗå±•ć—ć¦ć€ę–‡å­—åˆ—ć®ćƒ”ćƒ¢ćƒŖćƒŖćƒ¼ć‚Æć«ć¤ć„ć¦ć®ę¤œčØ¼ć‚‚å®Ÿę–½ć—ć¾ć—ćŸć€‚ ć“ć“ć§ćÆć€å•é”Œć®ć‚ć‚‹ć‚³ćƒ¼ćƒ‰ļ¼ˆä»„äø‹å‚ē…§ļ¼‰ćŒęē¤ŗć•ć‚Œć€ćć®å•é”Œē‚¹ćØę”¹å–„ē­–ć‚’č€ƒćˆć•ć›ć‚‰ć‚Œć¾ć—ćŸć€‚ func processLogFile(filename string ) [] string { // 10MB ć®ćƒ­ć‚°ćƒ•ć‚”ć‚¤ćƒ«ć‚’čŖ­ćæč¾¼ćæ content, _ := os.ReadFile(filename) // 10MB lines := strings.Split( string (content), " \n " ) // 100äø‡č”Œ // ć‚Øćƒ©ćƒ¼č”Œć ć‘ć‚’ęŠ½å‡ŗļ¼ˆé€šåøøćÆę•°č”Œļ¼‰ var errorLines [] string for _, line := range lines { if strings.Contains(line, "ERROR" ) { errorLines = append (errorLines, line) } } return errorLines // é€šåøøćÆ2-3蔌 } Go 恮 string åž‹ćÆć€å†…éƒØēš„ć«ćÆć€Œå®Ÿćƒ‡ćƒ¼ć‚æćƒć‚¤ćƒˆåˆ—ćøć®ćƒć‚¤ćƒ³ć‚æć€ćØć€Œé•·ć•ć€ć‚’ęŒć¤ę§‹é€ ä½“ć§ć™ 5 怂 type stringStruct struct { str unsafe.Pointer len int } å®Ÿéš›ć«ć€ä»„äø‹ć®ć‚ˆć†ćŖć‚³ćƒ¼ćƒ‰ć§ string åž‹ć®å¤‰ę•°ć‚’ for ę–‡ć§ćƒ«ćƒ¼ćƒ—ć™ć‚‹ć“ćØć‚‚åÆčƒ½ć§ć™ć€‚ func forString() { a := "hello, world" for i, s := range a { fmt.Println(i, s) } } äøŠčØ˜ć‚’čøć¾ćˆć‚‹ćØć€ć“ć®ć‚³ćƒ¼ćƒ‰ć«ć¤ć„ć¦ä»„äø‹ć®ć‚ˆć†ćŖå•é”Œē‚¹ćØę”¹å–„ē­–ćŒč€ƒćˆć‚‰ć‚Œć¾ć™ć€‚ å•é”Œē‚¹: os.ReadFile ć§å¤§ććŖćƒ•ć‚”ć‚¤ćƒ«ć®å…Øä½“ć‚’äø€åŗ¦ć«čŖ­ćæč¾¼ć‚“ć§ć„ć‚‹ strings.Split ć®å„č”ŒćŒå…ƒć®ę–‡å­—åˆ—ć‚’å‚ē…§ć™ć‚‹ć®ć§ć€é–¢ę•°ēµ‚äŗ†å¾Œć‚‚ćƒ”ćƒ¢ćƒŖć«ę®‹å­˜ć—ć€GCć§č§£ę”¾ć•ć‚ŒćŖć„ ćƒ«ćƒ¼ćƒ—ć§å¤§ććŖć‚¹ćƒ©ć‚¤ć‚¹ć®č¦ē“ å…Øć¦ć‚’čµ°ęŸ»ć—ć¦ć„ć‚‹ 改善策: é•·ć•ćŒę—¢ēŸ„ć®å “åˆćÆäŗ‹å‰ć«å®¹é‡ć‚’ē¢ŗäæć™ć‚‹ć“ćØć§å†å‰²ć‚Šå½“ć¦ć‚’å‰Šęø› äø¦åˆ—å‡¦ē† ę§‹é€ ä½“ć‚’å°Žå…„ć—ć¦åÆ¾č±”ćØćŖć‚‹ć‚¤ćƒ³ćƒ‡ćƒƒć‚Æć‚¹ć‚’äæęŒć™ć‚‹ć“ćØć§ć‚¤ćƒ³ćƒ‡ćƒƒć‚Æć‚¹å‚ē…§ć™ć‚‹ éƒØåˆ†ēš„ćŖēµęžœå–å¾—ć‚„ē¹°ć‚Ščæ”ć—ę¤œē“¢ć«ęœ‰åŠ¹ ćƒćƒ£ćƒ³ć‚Æå‡¦ē† ć‚¹ćƒˆćƒŖćƒ¼ćƒŸćƒ³ć‚°å‡¦ē† å¤šę¬”å…ƒć‚¹ćƒ©ć‚¤ć‚¹ć®ćƒ«ćƒ¼ćƒ—å‡¦ē† ć‚µćƒ³ćƒ—ćƒ«7ļ¼ˆå®Ÿč£…ļ¼‹å®Ÿč”Œēµęžœļ¼‰ å®Ÿč£… package main import ( "fmt" "math/rand" "runtime" "time" "unsafe" ) // čØˆē®—é‡ć®åˆ†ęžćØćƒ‘ćƒ•ć‚©ćƒ¼ćƒžćƒ³ć‚¹ęø¬å®šć‚’č”Œć†é–¢ę•° func measurePerformance(name string , fn func ()) time.Duration { runtime.GC() // ęø¬å®šå‰ć«GCć‚’å®Ÿč”Œ start := time.Now() fn() duration := time.Since(start) fmt.Printf( "%-25s: %v \n " , name, duration) return duration } // 1. O(n^2) - åŸŗęœ¬ēš„ćŖ2é‡ćƒ«ćƒ¼ćƒ— func basicNestedLoop(size int ) { data := make ([][] int , size) for i := range data { data[i] = make ([] int , size) } // éžåŠ¹ēŽ‡ćŖć‚¢ć‚Æć‚»ć‚¹ćƒ‘ć‚æćƒ¼ćƒ³ļ¼ˆåˆ—å„Ŗå…ˆļ¼‰ for j := 0 ; j < size; j++ { for i := 0 ; i < size; i++ { data[i][j] = i + j } } } // 2. ę”¹å–„ē‰ˆ - ć‚­ćƒ£ćƒƒć‚·ćƒ„åŠ¹ēŽ‡ć®č‰Æć„ć‚¢ć‚Æć‚»ć‚¹ćƒ‘ć‚æćƒ¼ćƒ³ func optimizedNestedLoop(size int ) { data := make ([][] int , size) for i := range data { data[i] = make ([] int , size) } // åŠ¹ēŽ‡ēš„ćŖć‚¢ć‚Æć‚»ć‚¹ćƒ‘ć‚æćƒ¼ćƒ³ļ¼ˆč”Œå„Ŗå…ˆļ¼‰ for i := 0 ; i < size; i++ { for j := 0 ; j < size; j++ { data[i][j] = i + j } } } // 3. 1ę¬”å…ƒć‚¹ćƒ©ć‚¤ć‚¹ć‚’ä½æē”Øć—ćŸęœ€é©åŒ– func flattenedArrayOptimization(size int ) { // 2ę¬”å…ƒé…åˆ—ć‚’1ę¬”å…ƒé…åˆ—ćØć—ć¦č”Øē¾ data := make ([] int , size*size) for i := 0 ; i < size; i++ { for j := 0 ; j < size; j++ { // 2ę¬”å…ƒåŗ§ęØ™ć‚’1ę¬”å…ƒć‚¤ćƒ³ćƒ‡ćƒƒć‚Æć‚¹ć«å¤‰ę› data[i*size+j] = i + j } } } // 4. O(n^3) - č”Œåˆ—ä¹—ē®—ć®ä¾‹ func matrixMultiplication(a, b [][] int , size int ) [][] int { result := make ([][] int , size) for i := range result { result[i] = make ([] int , size) } // ęØ™ęŗ–ēš„ćŖč”Œåˆ—ä¹—ē®— O(n^3) for i := 0 ; i < size; i++ { for j := 0 ; j < size; j++ { for k := 0 ; k < size; k++ { result[i][j] += a[i][k] * b[k][j] } } } return result } // 5. ęœ€é©åŒ–ć•ć‚ŒćŸč”Œåˆ—ä¹—ē®—ļ¼ˆćƒ«ćƒ¼ćƒ—é †åŗć®ę”¹å–„ļ¼‰ func optimizedMatrixMultiplication(a, b [][] int , size int ) [][] int { result := make ([][] int , size) for i := range result { result[i] = make ([] int , size) } // ćƒ«ćƒ¼ćƒ—é †åŗć‚’å¤‰ę›“ć—ć¦ć‚­ćƒ£ćƒƒć‚·ćƒ„åŠ¹ēŽ‡ć‚’ę”¹å–„ for i := 0 ; i < size; i++ { for k := 0 ; k < size; k++ { for j := 0 ; j < size; j++ { result[i][j] += a[i][k] * b[k][j] } } } return result } // 6. ćƒ–ćƒ­ćƒƒć‚ÆåŒ–ć«ć‚ˆć‚‹ęœ€é©åŒ– func blockedMatrixMultiplication(a, b [][] int , size, blockSize int ) [][] int { result := make ([][] int , size) for i := range result { result[i] = make ([] int , size) } // ćƒ–ćƒ­ćƒƒć‚Æå˜ä½ć§å‡¦ē†ć—ć¦ć‚­ćƒ£ćƒƒć‚·ćƒ„åŠ¹ēŽ‡ć‚’ęœ€å¤§åŒ– for ii := 0 ; ii < size; ii += blockSize { for jj := 0 ; jj < size; jj += blockSize { for kk := 0 ; kk < size; kk += blockSize { // ćƒ–ćƒ­ćƒƒć‚Æå†…ć®å‡¦ē† maxI := min(ii+blockSize, size) maxJ := min(jj+blockSize, size) maxK := min(kk+blockSize, size) for i := ii; i < maxI; i++ { for j := jj; j < maxJ; j++ { for k := kk; k < maxK; k++ { result[i][j] += a[i][k] * b[k][j] } } } } } } return result } // 7. äø¦åˆ—åŒ–ć«ć‚ˆć‚‹ęœ€é©åŒ– func parallelMatrixMultiplication(a, b [][] int , size int ) [][] int { result := make ([][] int , size) for i := range result { result[i] = make ([] int , size) } numWorkers := runtime.NumCPU() rowsPerWorker := size / numWorkers done := make ( chan bool , numWorkers) for worker := 0 ; worker < numWorkers; worker++ { startRow := worker * rowsPerWorker endRow := startRow + rowsPerWorker if worker == numWorkers- 1 { endRow = size // ęœ€å¾Œć®ćƒÆćƒ¼ć‚«ćƒ¼ćÆę®‹ć‚Šć‚’å…Øć¦å‡¦ē† } go func (start, end int ) { for i := start; i < end; i++ { for j := 0 ; j < size; j++ { for k := 0 ; k < size; k++ { result[i][j] += a[i][k] * b[k][j] } } } done <- true }(startRow, endRow) } // å…ØćƒÆćƒ¼ć‚«ćƒ¼ć®å®Œäŗ†ć‚’å¾…ę©Ÿ for i := 0 ; i < numWorkers; i++ { <-done } return result } // 8. ćƒ”ćƒ¢ćƒŖć‚¢ć‚Æć‚»ć‚¹ćƒ‘ć‚æćƒ¼ćƒ³ć®åˆ†ęž func analyzeMemoryAccessPatterns() { fmt.Println( "=== ćƒ”ćƒ¢ćƒŖć‚¢ć‚Æć‚»ć‚¹ćƒ‘ć‚æćƒ¼ćƒ³ć®åˆ†ęž ===" ) size := 1000 // č”Œå„Ŗå…ˆć‚¢ć‚Æć‚»ć‚¹ data := make ([][] int , size) for i := range data { data[i] = make ([] int , size) } fmt.Printf( "2ę¬”å…ƒć‚¹ćƒ©ć‚¤ć‚¹ć®ćƒ”ćƒ¢ćƒŖćƒ¬ć‚¤ć‚¢ć‚¦ćƒˆåˆ†ęž: \n " ) fmt.Printf( "dataé…åˆ—ć®ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x%x \n " , uintptr ( unsafe.Pointer (&data[ 0 ]))) for i := 0 ; i < 3 ; i++ { fmt.Printf( "data[%d]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x%x \n " , i, uintptr ( unsafe.Pointer (&data[i][ 0 ]))) if i > 0 { prevAddr := uintptr ( unsafe.Pointer (&data[i- 1 ][ 0 ])) currAddr := uintptr ( unsafe.Pointer (&data[i][ 0 ])) fmt.Printf( " å‰ć®č”ŒćØć®å·®: %d 惐悤惈 \n " , currAddr-prevAddr) } } // 1ę¬”å…ƒé…åˆ—ć§ć®é€£ē¶šć‚¢ć‚Æć‚»ć‚¹ flatData := make ([] int , size*size) fmt.Printf( " \n 1ę¬”å…ƒć‚¹ćƒ©ć‚¤ć‚¹ć®ćƒ”ćƒ¢ćƒŖćƒ¬ć‚¤ć‚¢ć‚¦ćƒˆ: \n " ) fmt.Printf( "flatData[0]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x%x \n " , uintptr ( unsafe.Pointer (&flatData[ 0 ]))) fmt.Printf( "flatData[1000]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x%x \n " , uintptr ( unsafe.Pointer (&flatData[ 1000 ]))) fmt.Printf( "é€£ē¶šć™ć‚‹č¦ē“ ć®å·®: %d 惐悤惈 \n " , uintptr ( unsafe.Pointer (&flatData[ 1 ]))- uintptr ( unsafe.Pointer (&flatData[ 0 ]))) } // čØˆē®—é‡ć®å®ŸčØ¼å®ŸéØ“ func demonstrateComplexityGrowth() { fmt.Println( " \n === čØˆē®—é‡ć®å®ŸčØ¼å®ŸéØ“ ===" ) sizes := [] int { 100 , 200 , 400 , 800 } fmt.Println( "åŸŗęœ¬ēš„ćŖ2é‡ćƒ«ćƒ¼ćƒ— O(n^2):" ) var prevDuration time.Duration for i, size := range sizes { duration := measurePerformance(fmt.Sprintf( "サイズ %d" , size), func () { basicNestedLoop(size) }) if i > 0 { ratio := float64 (duration) / float64 (prevDuration) theoretical := float64 (size*size) / float64 (sizes[i- 1 ]*sizes[i- 1 ]) fmt.Printf( " å®Ÿęø¬ęÆ”ēŽ‡: %.2fx, 理論値(n^2): %.2fx \n " , ratio, theoretical) } prevDuration = duration } fmt.Println( " \n č”Œåˆ—ä¹—ē®— O(n^3):" ) prevDuration = 0 for i, size := range sizes[: 3 ] { // ć‚µć‚¤ć‚ŗć‚’åˆ¶é™ļ¼ˆę™‚é–“ćŒć‹ć‹ć‚‹ćŸć‚ļ¼‰ a := createRandomMatrix(size) b := createRandomMatrix(size) duration := measurePerformance(fmt.Sprintf( "サイズ %d" , size), func () { matrixMultiplication(a, b, size) }) if i > 0 { ratio := float64 (duration) / float64 (prevDuration) theoretical := float64 (size*size*size) / float64 (sizes[i- 1 ]*sizes[i- 1 ]*sizes[i- 1 ]) fmt.Printf( " å®Ÿęø¬ęÆ”ēŽ‡: %.2fx, 理論値(n^3): %.2fx \n " , ratio, theoretical) } prevDuration = duration } } // ć‚­ćƒ£ćƒƒć‚·ćƒ„åŠ¹ēŽ‡ć®ęÆ”č¼ƒå®ŸéØ“ func compareCacheEfficiency() { fmt.Println( " \n === ć‚­ćƒ£ćƒƒć‚·ćƒ„åŠ¹ēŽ‡ć®ęÆ”č¼ƒ ===" ) size := 1000 fmt.Println( "2ę¬”å…ƒé…åˆ—ć‚¢ć‚Æć‚»ć‚¹ćƒ‘ć‚æćƒ¼ćƒ³ć®ęÆ”č¼ƒ:" ) // éžåŠ¹ēŽ‡ćŖćƒ‘ć‚æćƒ¼ćƒ³ļ¼ˆåˆ—å„Ŗå…ˆļ¼‰ measurePerformance( "åˆ—å„Ŗå…ˆć‚¢ć‚Æć‚»ć‚¹ļ¼ˆéžåŠ¹ēŽ‡ļ¼‰" , func () { basicNestedLoop(size) }) // åŠ¹ēŽ‡ēš„ćŖćƒ‘ć‚æćƒ¼ćƒ³ļ¼ˆč”Œå„Ŗå…ˆļ¼‰ measurePerformance( "č”Œå„Ŗå…ˆć‚¢ć‚Æć‚»ć‚¹ļ¼ˆåŠ¹ēŽ‡ēš„ļ¼‰" , func () { optimizedNestedLoop(size) }) // 1ę¬”å…ƒé…åˆ—ćƒ‘ć‚æćƒ¼ćƒ³ measurePerformance( "1ę¬”å…ƒé…åˆ—ļ¼ˆęœ€é©ļ¼‰" , func () { flattenedArrayOptimization(size) }) } // č”Œåˆ—ä¹—ē®—ć®ęœ€é©åŒ–ę‰‹ę³•ęÆ”č¼ƒ func compareMatrixOptimizations() { fmt.Println( " \n === č”Œåˆ—ä¹—ē®—ć®ęœ€é©åŒ–ę‰‹ę³•ęÆ”č¼ƒ ===" ) size := 256 blockSize := 32 a := createRandomMatrix(size) b := createRandomMatrix(size) fmt.Printf( "č”Œåˆ—ć‚µć‚¤ć‚ŗ: %dx%d \n " , size, size) // ęØ™ęŗ–ēš„ćŖå®Ÿč£… measurePerformance( "ęØ™ęŗ–å®Ÿč£… (i-j-k)" , func () { matrixMultiplication(a, b, size) }) // ćƒ«ćƒ¼ćƒ—é †åŗęœ€é©åŒ– measurePerformance( "ćƒ«ćƒ¼ćƒ—é †åŗęœ€é©åŒ– (i-k-j)" , func () { optimizedMatrixMultiplication(a, b, size) }) // ćƒ–ćƒ­ćƒƒć‚ÆåŒ– measurePerformance( "ćƒ–ćƒ­ćƒƒć‚ÆåŒ–" , func () { blockedMatrixMultiplication(a, b, size, blockSize) }) // äø¦åˆ—åŒ– measurePerformance( "äø¦åˆ—åŒ–" , func () { parallelMatrixMultiplication(a, b, size) }) } // ćƒ©ćƒ³ćƒ€ćƒ ćŖč”Œåˆ—ć‚’ē”Ÿęˆ func createRandomMatrix(size int ) [][] int { matrix := make ([][] int , size) for i := range matrix { matrix[i] = make ([] int , size) for j := range matrix[i] { matrix[i][j] = rand.Intn( 100 ) } } return matrix } // miné–¢ę•°ļ¼ˆGo 1.21ęœŖęŗ€ć§ć®äŗ’ę›ę€§ļ¼‰ func min(a, b int ) int { if a < b { return a } return b } // ęœ€é©åŒ–ć®ćƒ™ć‚¹ćƒˆćƒ—ćƒ©ć‚Æćƒ†ć‚£ć‚¹č§£čŖ¬ func explainOptimizationPrinciples() { fmt.Println( " \n === ćƒć‚¹ćƒˆćƒ«ćƒ¼ćƒ—ęœ€é©åŒ–ć®åŽŸå‰‡ ===" ) fmt.Println( "1. ę™‚é–“čØˆē®—é‡ć®ē†č§£:" ) fmt.Println( " - O(n): ē·šå½¢ę™‚é–“" ) fmt.Println( " - O(n^2): 2é‡ćƒ«ćƒ¼ćƒ— - ćƒ‡ćƒ¼ć‚æć‚µć‚¤ć‚ŗ2å€ć§å‡¦ē†ę™‚é–“4倍" ) fmt.Println( " - O(n^3): 3é‡ćƒ«ćƒ¼ćƒ— - ćƒ‡ćƒ¼ć‚æć‚µć‚¤ć‚ŗ2å€ć§å‡¦ē†ę™‚é–“8倍" ) fmt.Println( " \n 2. ć‚­ćƒ£ćƒƒć‚·ćƒ„åŠ¹ēŽ‡ć®ęœ€é©åŒ–:" ) fmt.Println( " - CPUć‚­ćƒ£ćƒƒć‚·ćƒ„ćƒ©ć‚¤ćƒ³ļ¼ˆé€šåøø64ćƒć‚¤ćƒˆļ¼‰ć‚’ę„č­˜" ) fmt.Println( " - é€£ē¶šć—ćŸćƒ”ćƒ¢ćƒŖć‚¢ć‚Æć‚»ć‚¹ć‚’å„Ŗå…ˆ" ) fmt.Println( " - č”Œå„Ŗå…ˆć‚¢ć‚Æć‚»ć‚¹ļ¼ˆrow-majorļ¼‰ć®ęŽ”ē”Ø" ) fmt.Println( " \n 3. ćƒ«ćƒ¼ćƒ—é †åŗć®ęœ€é©åŒ–:" ) fmt.Println( " - å†…å“ćƒ«ćƒ¼ćƒ—ć§é€£ē¶šćƒ”ćƒ¢ćƒŖć‚¢ć‚Æć‚»ć‚¹" ) fmt.Println( " - 一時変数の擻用" ) fmt.Println( " - äøč¦ćŖčØˆē®—ć®å¤–å“ćøć®ē§»å‹•" ) fmt.Println( " \n 4. ćƒ‡ćƒ¼ć‚æę§‹é€ ć®ęœ€é©åŒ–:" ) fmt.Println( " - 2ę¬”å…ƒé…åˆ— → 1ę¬”å…ƒé…åˆ—ļ¼ˆćƒ•ćƒ©ćƒƒćƒˆåŒ–ļ¼‰" ) fmt.Println( " - ę§‹é€ ä½“é…åˆ— → é…åˆ—ć®ę§‹é€ ä½“ļ¼ˆAoS → SoA)" ) fmt.Println( " - ćƒ”ćƒ¢ćƒŖå±€ę‰€ę€§ć®å‘äøŠ" ) fmt.Println( " \n 5. ć‚¢ćƒ«ć‚“ćƒŖć‚ŗćƒ ćƒ¬ćƒ™ćƒ«ć®ę”¹å–„:" ) fmt.Println( " - ćƒ–ćƒ­ćƒƒć‚ÆåŒ–ļ¼ˆć‚æć‚¤ćƒŖćƒ³ć‚°ļ¼‰" ) fmt.Println( " - äø¦åˆ—åŒ–" ) fmt.Println( " - čØˆē®—é‡ć®å‰Šęø›ļ¼ˆO(n^3) → O(n^2.8)など)" ) } func main() { fmt.Println( "=== ę·±ć„ćƒć‚¹ćƒˆćƒ«ćƒ¼ćƒ—å‡¦ē†ć®č² č·åˆ†ęžćØęœ€é©åŒ– === \n " ) // ćƒ©ćƒ³ćƒ€ćƒ ć‚·ćƒ¼ćƒ‰ć‚’čØ­å®š rand.Seed(time.Now().UnixNano()) // 1. ćƒ”ćƒ¢ćƒŖć‚¢ć‚Æć‚»ć‚¹ćƒ‘ć‚æćƒ¼ćƒ³ć®åˆ†ęž analyzeMemoryAccessPatterns() // 2. čØˆē®—é‡ć®å®ŸčØ¼å®ŸéØ“ demonstrateComplexityGrowth() // 3. ć‚­ćƒ£ćƒƒć‚·ćƒ„åŠ¹ēŽ‡ć®ęÆ”č¼ƒ compareCacheEfficiency() // 4. č”Œåˆ—ä¹—ē®—ć®ęœ€é©åŒ–ęÆ”č¼ƒ compareMatrixOptimizations() // 5. ęœ€é©åŒ–åŽŸå‰‡ć®č§£čŖ¬ explainOptimizationPrinciples() } å®Ÿč”Œēµęžœ === ę·±ć„ćƒć‚¹ćƒˆćƒ«ćƒ¼ćƒ—å‡¦ē†ć®č² č·åˆ†ęžćØęœ€é©åŒ– === === ćƒ”ćƒ¢ćƒŖć‚¢ć‚Æć‚»ć‚¹ćƒ‘ć‚æćƒ¼ćƒ³ć®åˆ†ęž === 2ę¬”å…ƒć‚¹ćƒ©ć‚¤ć‚¹ć®ćƒ”ćƒ¢ćƒŖćƒ¬ć‚¤ć‚¢ć‚¦ćƒˆåˆ†ęž: dataé…åˆ—ć®ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x40000b8008 data[0]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x40000be000 data[1]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x40000c0000 å‰ć®č”ŒćØć®å·®: 8192 惐悤惈 data[2]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x40000c2000 å‰ć®č”ŒćØć®å·®: 8192 惐悤惈 1ę¬”å…ƒć‚¹ćƒ©ć‚¤ć‚¹ć®ćƒ”ćƒ¢ćƒŖćƒ¬ć‚¤ć‚¢ć‚¦ćƒˆ: flatData[0]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x4000900000 flatData[1000]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x4000901f40 é€£ē¶šć™ć‚‹č¦ē“ ć®å·®: 8 惐悤惈 === čØˆē®—é‡ć®å®ŸčØ¼å®ŸéØ“ === åŸŗęœ¬ēš„ćŖ2é‡ćƒ«ćƒ¼ćƒ— O(n^2): サイズ 100 : 19.416µs サイズ 200 : 125.584µs å®Ÿęø¬ęÆ”ēŽ‡: 6.47x, 理論値(n^2): 4.00x サイズ 400 : 1.697583ms å®Ÿęø¬ęÆ”ēŽ‡: 13.52x, 理論値(n^2): 4.00x サイズ 800 : 6.607292ms å®Ÿęø¬ęÆ”ēŽ‡: 3.89x, 理論値(n^2): 4.00x č”Œåˆ—ä¹—ē®— O(n^3): サイズ 100 : 2.359708ms サイズ 200 : 20.511333ms å®Ÿęø¬ęÆ”ēŽ‡: 8.69x, 理論値(n^3): 8.00x サイズ 400 : 198.047333ms å®Ÿęø¬ęÆ”ēŽ‡: 9.66x, 理論値(n^3): 8.00x === ć‚­ćƒ£ćƒƒć‚·ćƒ„åŠ¹ēŽ‡ć®ęÆ”č¼ƒ === 2ę¬”å…ƒé…åˆ—ć‚¢ć‚Æć‚»ć‚¹ćƒ‘ć‚æćƒ¼ćƒ³ć®ęÆ”č¼ƒ: åˆ—å„Ŗå…ˆć‚¢ć‚Æć‚»ć‚¹ļ¼ˆéžåŠ¹ēŽ‡ļ¼‰ : 12.861208ms č”Œå„Ŗå…ˆć‚¢ć‚Æć‚»ć‚¹ļ¼ˆåŠ¹ēŽ‡ēš„ļ¼‰ : 1.88625ms 1ę¬”å…ƒé…åˆ—ļ¼ˆęœ€é©ļ¼‰ : 3.14025ms === č”Œåˆ—ä¹—ē®—ć®ęœ€é©åŒ–ę‰‹ę³•ęÆ”č¼ƒ === č”Œåˆ—ć‚µć‚¤ć‚ŗ: 256x256 ęØ™ęŗ–å®Ÿč£… (i-j-k) : 44.545958ms ćƒ«ćƒ¼ćƒ—é †åŗęœ€é©åŒ– (i-k-j) : 23.862583ms ćƒ–ćƒ­ćƒƒć‚ÆåŒ– : 31.418ms äø¦åˆ—åŒ– : 7.782542ms === ćƒć‚¹ćƒˆćƒ«ćƒ¼ćƒ—ęœ€é©åŒ–ć®åŽŸå‰‡ === 1. ę™‚é–“čØˆē®—é‡ć®ē†č§£: - O(n): ē·šå½¢ę™‚é–“ - O(n^2): 2é‡ćƒ«ćƒ¼ćƒ— - ćƒ‡ćƒ¼ć‚æć‚µć‚¤ć‚ŗ2å€ć§å‡¦ē†ę™‚é–“4倍 - O(n^3): 3é‡ćƒ«ćƒ¼ćƒ— - ćƒ‡ćƒ¼ć‚æć‚µć‚¤ć‚ŗ2å€ć§å‡¦ē†ę™‚é–“8倍 2. ć‚­ćƒ£ćƒƒć‚·ćƒ„åŠ¹ēŽ‡ć®ęœ€é©åŒ–: - CPUć‚­ćƒ£ćƒƒć‚·ćƒ„ćƒ©ć‚¤ćƒ³ļ¼ˆé€šåøø64ćƒć‚¤ćƒˆļ¼‰ć‚’ę„č­˜ - é€£ē¶šć—ćŸćƒ”ćƒ¢ćƒŖć‚¢ć‚Æć‚»ć‚¹ć‚’å„Ŗå…ˆ - č”Œå„Ŗå…ˆć‚¢ć‚Æć‚»ć‚¹ļ¼ˆrow-majorļ¼‰ć®ęŽ”ē”Ø 3. ćƒ«ćƒ¼ćƒ—é †åŗć®ęœ€é©åŒ–: - å†…å“ćƒ«ćƒ¼ćƒ—ć§é€£ē¶šćƒ”ćƒ¢ćƒŖć‚¢ć‚Æć‚»ć‚¹ - 一時変数の擻用 - äøč¦ćŖčØˆē®—ć®å¤–å“ćøć®ē§»å‹• 4. ćƒ‡ćƒ¼ć‚æę§‹é€ ć®ęœ€é©åŒ–: - 2ę¬”å…ƒé…åˆ— → 1ę¬”å…ƒé…åˆ—ļ¼ˆćƒ•ćƒ©ćƒƒćƒˆåŒ–ļ¼‰ - ę§‹é€ ä½“é…åˆ— → é…åˆ—ć®ę§‹é€ ä½“ļ¼ˆAoS → SoA) - ćƒ”ćƒ¢ćƒŖå±€ę‰€ę€§ć®å‘äøŠ 5. ć‚¢ćƒ«ć‚“ćƒŖć‚ŗćƒ ćƒ¬ćƒ™ćƒ«ć®ę”¹å–„: - ćƒ–ćƒ­ćƒƒć‚ÆåŒ–ļ¼ˆć‚æć‚¤ćƒŖćƒ³ć‚°ļ¼‰ - äø¦åˆ—åŒ– - čØˆē®—é‡ć®å‰Šęø›ļ¼ˆO(n^3) → O(n^2.8)など) å¤šę¬”å…ƒć‚¹ćƒ©ć‚¤ć‚¹ć®ćƒ«ćƒ¼ćƒ—å‡¦ē†ćÆč² č·ćŒé«˜ćć€ćƒ‘ćƒ•ć‚©ćƒ¼ćƒžćƒ³ć‚¹ćŒę‚ŖåŒ–ć—ćŒć”ć§ć™ć€‚ ę„šē›“ć«ćƒ«ćƒ¼ćƒ—å‡¦ē†ć™ć‚‹å “åˆć€ę·±ć• d に対して O(n^d) ć®čØˆē®—é‡ćØćŖć£ć¦ć—ć¾ć„ć¾ć™ć€‚ ćć“ć§ć€ä»„äø‹ć®ć‚ˆć†ćŖåÆ¾ē­–ć‚’č€ƒćˆć‚‹ć“ćØćŒć§ćć¾ć™ć€‚ ć‚­ćƒ£ćƒƒć‚·ćƒ„åŠ¹ēŽ‡ć®ę”¹å–„ č”Œåˆ—ę¼”ē®—ć®ć‚ˆć†ćŖå‡¦ē†ć®å “åˆćÆäø¦åˆ—å‡¦ē†ćŒęœ‰åŠ¹ ćƒć‚¹ćƒˆć—ćŸę§‹é€ ä½“ć®ćƒ«ćƒ¼ćƒ—å‡¦ē† ć‚µćƒ³ćƒ—ćƒ«8ļ¼ˆå®Ÿč£…ļ¼‹å®Ÿč”Œēµęžœļ¼‰ å®Ÿč£… package main import ( "fmt" "runtime" "strings" "time" "unsafe" ) // === å•é”Œć®ć‚ć‚‹ę·±ć„ćƒć‚¹ćƒˆę§‹é€  === // Level3: ęœ€äø‹å±¤ - ę–‡å­—åˆ—ć®ć‚¹ćƒ©ć‚¤ć‚¹ type NestedLevel3 struct { ID int Messages [] string Metadata map [ string ] string } // Level2: 中間層 - Level3ć®ć‚¹ćƒ©ć‚¤ć‚¹ type NestedLevel2 struct { GroupID string Items []NestedLevel3 Timestamp time.Time IsActive bool } // Level1: ęœ€äøŠå±¤ - Level2ć®ć‚¹ćƒ©ć‚¤ć‚¹ type NestedLevel1 struct { UserID string Groups []NestedLevel2 Settings map [ string ] interface {} } // === ęœ€é©åŒ–ć•ć‚ŒćŸå¹³å¦ćŖę§‹é€  === // ćƒ•ćƒ©ćƒƒćƒˆåŒ–ć•ć‚ŒćŸćƒ‡ćƒ¼ć‚æę§‹é€  type FlatData struct { UserIDs [] string GroupIDs [] string MessageIDs [] int Messages [] string // ć‚¤ćƒ³ćƒ‡ćƒƒć‚Æć‚¹ćƒ†ćƒ¼ćƒ–ćƒ« UserGroupStart [] int // å„ćƒ¦ćƒ¼ć‚¶ćƒ¼ć®ć‚°ćƒ«ćƒ¼ćƒ—é–‹å§‹ä½ē½® UserGroupEnd [] int // å„ćƒ¦ćƒ¼ć‚¶ćƒ¼ć®ć‚°ćƒ«ćƒ¼ćƒ—ēµ‚äŗ†ä½ē½® GroupMsgStart [] int // å„ć‚°ćƒ«ćƒ¼ćƒ—ć®ćƒ”ćƒƒć‚»ćƒ¼ć‚øé–‹å§‹ä½ē½® GroupMsgEnd [] int // å„ć‚°ćƒ«ćƒ¼ćƒ—ć®ćƒ”ćƒƒć‚»ćƒ¼ć‚øēµ‚äŗ†ä½ē½® } // === äø­é–“ēš„ćŖę§‹é€ ļ¼ˆę§‹é€ ä½“é…åˆ—ļ¼‰ === type SemiFlat struct { UserID string GroupID string MessageID int Message string } // ćƒ†ć‚¹ćƒˆćƒ‡ćƒ¼ć‚æē”Ÿęˆ func createNestedTestData(numUsers, numGroups, numMessages int ) []NestedLevel1 { data := make ([]NestedLevel1, numUsers) for i := 0 ; i < numUsers; i++ { user := NestedLevel1{ UserID: fmt.Sprintf( "user_%d" , i), Groups: make ([]NestedLevel2, numGroups), Settings: make ( map [ string ] interface {}), } for j := 0 ; j < numGroups; j++ { group := NestedLevel2{ GroupID: fmt.Sprintf( "group_%d_%d" , i, j), Items: make ([]NestedLevel3, numMessages), Timestamp: time.Now(), IsActive: true , } for k := 0 ; k < numMessages; k++ { item := NestedLevel3{ ID: k, Messages: [] string { fmt.Sprintf( "Message %d from user %d group %d" , k, i, j), fmt.Sprintf( "Additional info %d" , k), }, Metadata: map [ string ] string { "type" : "info" , "source" : fmt.Sprintf( "user_%d" , i), }, } group.Items[k] = item } user.Groups[j] = group } data[i] = user } return data } // å¹³å¦åŒ–ć•ć‚ŒćŸćƒ‡ćƒ¼ć‚æē”Ÿęˆ func createFlatTestData(numUsers, numGroups, numMessages int ) *FlatData { totalMessages := numUsers * numGroups * numMessages * 2 // 各item恫2ć¤ć®ćƒ”ćƒƒć‚»ćƒ¼ć‚ø flat := &FlatData{ UserIDs: make ([] string , 0 , numUsers), GroupIDs: make ([] string , 0 , numUsers*numGroups), MessageIDs: make ([] int , 0 , totalMessages), Messages: make ([] string , 0 , totalMessages), UserGroupStart: make ([] int , numUsers), UserGroupEnd: make ([] int , numUsers), GroupMsgStart: make ([] int , numUsers*numGroups), GroupMsgEnd: make ([] int , numUsers*numGroups), } userIdx := 0 groupIdx := 0 msgIdx := 0 for i := 0 ; i < numUsers; i++ { flat.UserIDs = append (flat.UserIDs, fmt.Sprintf( "user_%d" , i)) flat.UserGroupStart[userIdx] = groupIdx for j := 0 ; j < numGroups; j++ { flat.GroupIDs = append (flat.GroupIDs, fmt.Sprintf( "group_%d_%d" , i, j)) flat.GroupMsgStart[groupIdx] = msgIdx for k := 0 ; k < numMessages; k++ { // 2ć¤ć®ćƒ”ćƒƒć‚»ćƒ¼ć‚øć‚’čæ½åŠ  flat.MessageIDs = append (flat.MessageIDs, k, k) flat.Messages = append (flat.Messages, fmt.Sprintf( "Message %d from user %d group %d" , k, i, j), fmt.Sprintf( "Additional info %d" , k)) msgIdx += 2 } flat.GroupMsgEnd[groupIdx] = msgIdx groupIdx++ } flat.UserGroupEnd[userIdx] = groupIdx userIdx++ } return flat } // === ćƒ‘ćƒ•ć‚©ćƒ¼ćƒžćƒ³ć‚¹ęø¬å®š === // 1. ćƒć‚¹ćƒˆę§‹é€ ć§ć®ę¤œē“¢å‡¦ē† func searchInNestedStructure(data []NestedLevel1, searchTerm string ) [] string { var results [] string for _, user := range data { for _, group := range user.Groups { for _, item := range group.Items { for _, message := range item.Messages { if strings.Contains(message, searchTerm) { results = append (results, fmt.Sprintf( "User: %s, Group: %s, Message: %s" , user.UserID, group.GroupID, message)) } } } } } return results } // 2. å¹³å¦åŒ–ć•ć‚ŒćŸę§‹é€ ć§ć®ę¤œē“¢å‡¦ē† func searchInFlatStructure(data *FlatData, searchTerm string ) [] string { var results [] string for i, message := range data.Messages { if strings.Contains(message, searchTerm) { // åÆ¾åæœć™ć‚‹ćƒ¦ćƒ¼ć‚¶ćƒ¼ćØć‚°ćƒ«ćƒ¼ćƒ—ć‚’é€†å¼•ć userIdx := findUserForMessage(data, i) groupIdx := findGroupForMessage(data, i) results = append (results, fmt.Sprintf( "User: %s, Group: %s, Message: %s" , data.UserIDs[userIdx], data.GroupIDs[groupIdx], message)) } } return results } // ćƒ”ćƒƒć‚»ćƒ¼ć‚øć‚¤ćƒ³ćƒ‡ćƒƒć‚Æć‚¹ć‹ć‚‰ćƒ¦ćƒ¼ć‚¶ćƒ¼ć‚’ē‰¹å®š func findUserForMessage(data *FlatData, msgIdx int ) int { for i := 0 ; i < len (data.UserGroupStart); i++ { start := data.UserGroupStart[i] end := data.UserGroupEnd[i] for j := start; j < end; j++ { if msgIdx >= data.GroupMsgStart[j] && msgIdx < data.GroupMsgEnd[j] { return i } } } return - 1 } // ćƒ”ćƒƒć‚»ćƒ¼ć‚øć‚¤ćƒ³ćƒ‡ćƒƒć‚Æć‚¹ć‹ć‚‰ć‚°ćƒ«ćƒ¼ćƒ—ć‚’ē‰¹å®š func findGroupForMessage(data *FlatData, msgIdx int ) int { for i := 0 ; i < len (data.GroupMsgStart); i++ { if msgIdx >= data.GroupMsgStart[i] && msgIdx < data.GroupMsgEnd[i] { return i } } return - 1 } // 3. ćƒ”ćƒ¢ćƒŖć‚¢ć‚Æć‚»ć‚¹ćƒ‘ć‚æćƒ¼ćƒ³ć®åˆ†ęž func analyzeMemoryLayout() { fmt.Println( "=== ćƒ”ćƒ¢ćƒŖćƒ¬ć‚¤ć‚¢ć‚¦ćƒˆåˆ†ęž ===" ) // å°ć•ćŖć‚µćƒ³ćƒ—ćƒ«ć§ćƒ”ćƒ¢ćƒŖé…ē½®ć‚’ē¢ŗčŖ nested := createNestedTestData( 2 , 2 , 2 ) fmt.Println( "ćƒć‚¹ćƒˆę§‹é€ ć®ćƒ”ćƒ¢ćƒŖé…ē½®:" ) fmt.Printf( "nested[0]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x%x \n " , uintptr ( unsafe.Pointer (&nested[ 0 ]))) fmt.Printf( "nested[0].Groups[0]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x%x \n " , uintptr ( unsafe.Pointer (&nested[ 0 ].Groups[ 0 ]))) fmt.Printf( "nested[0].Groups[0].Items[0]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x%x \n " , uintptr ( unsafe.Pointer (&nested[ 0 ].Groups[ 0 ].Items[ 0 ]))) // ćƒć‚¤ćƒ³ć‚æć®ę·±ć•ć‚’ē¢ŗčŖ level1Addr := uintptr ( unsafe.Pointer (&nested[ 0 ])) groupsAddr := uintptr ( unsafe.Pointer (&nested[ 0 ].Groups[ 0 ])) itemsAddr := uintptr ( unsafe.Pointer (&nested[ 0 ].Groups[ 0 ].Items[ 0 ])) fmt.Printf( "Level1 → Groups: %d ćƒć‚¤ćƒˆå·® \n " , groupsAddr-level1Addr) fmt.Printf( "Groups → Items: %d ćƒć‚¤ćƒˆå·® \n " , itemsAddr-groupsAddr) // ć‚¹ćƒ©ć‚¤ć‚¹ć®é…åˆ—é ˜åŸŸć®ć‚¢ćƒ‰ćƒ¬ć‚¹ if len (nested[ 0 ].Groups) > 0 && len (nested[ 0 ].Groups[ 0 ].Items) > 0 { fmt.Printf( "Groupsé…åˆ—ć®å®Ÿćƒ‡ćƒ¼ć‚æ: 0x%x \n " , uintptr ( unsafe.Pointer (&nested[ 0 ].Groups[ 0 ]))) fmt.Printf( "Itemsé…åˆ—ć®å®Ÿćƒ‡ćƒ¼ć‚æ: 0x%x \n " , uintptr ( unsafe.Pointer (&nested[ 0 ].Groups[ 0 ].Items[ 0 ]))) } // ćƒ•ćƒ©ćƒƒćƒˆę§‹é€ ć®ē¢ŗčŖ flat := createFlatTestData( 2 , 2 , 2 ) fmt.Println( " \n ćƒ•ćƒ©ćƒƒćƒˆę§‹é€ ć®ćƒ”ćƒ¢ćƒŖé…ē½®:" ) fmt.Printf( "Messagesé…åˆ—ć®é–‹å§‹: 0x%x \n " , uintptr ( unsafe.Pointer (&flat.Messages[ 0 ]))) if len (flat.Messages) > 1 { fmt.Printf( "Messages[1]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x%x \n " , uintptr ( unsafe.Pointer (&flat.Messages[ 1 ]))) fmt.Printf( "é€£ē¶šč¦ē“ ć®å·®: %d 惐悤惈 \n " , uintptr ( unsafe.Pointer (&flat.Messages[ 1 ]))- uintptr ( unsafe.Pointer (&flat.Messages[ 0 ]))) } } // 4. ć‚¬ćƒ™ćƒ¼ć‚øć‚³ćƒ¬ć‚Æć‚·ćƒ§ćƒ³ćøć®å½±éŸæęø¬å®š func measureGCImpact() { fmt.Println( " \n === ć‚¬ćƒ™ćƒ¼ć‚øć‚³ćƒ¬ć‚Æć‚·ćƒ§ćƒ³å½±éŸæęø¬å®š ===" ) runtime.GC() var m1 runtime.MemStats runtime.ReadMemStats(&m1) // ćƒć‚¹ćƒˆę§‹é€ ć®ćƒ‡ćƒ¼ć‚æä½œęˆ fmt.Println( "ćƒć‚¹ćƒˆę§‹é€ ćƒ‡ćƒ¼ć‚æä½œęˆ..." ) nested := createNestedTestData( 100 , 10 , 10 ) runtime.GC() var m2 runtime.MemStats runtime.ReadMemStats(&m2) fmt.Printf( "ćƒć‚¹ćƒˆę§‹é€ ćƒ”ćƒ¢ćƒŖä½æē”Øé‡: %.2f MB \n " , float64 (m2.Alloc-m1.Alloc)/ 1024 / 1024 ) fmt.Printf( "ćƒć‚¹ćƒˆę§‹é€ ć‚Ŗćƒ–ć‚øć‚§ć‚Æćƒˆę•°å¢—åŠ : %d \n " , m2.HeapObjects-m1.HeapObjects) // ćƒ•ćƒ©ćƒƒćƒˆę§‹é€ ć®ćƒ‡ćƒ¼ć‚æä½œęˆ fmt.Println( "ćƒ•ćƒ©ćƒƒćƒˆę§‹é€ ćƒ‡ćƒ¼ć‚æä½œęˆ..." ) flat := createFlatTestData( 100 , 10 , 10 ) runtime.GC() var m3 runtime.MemStats runtime.ReadMemStats(&m3) fmt.Printf( "ćƒ•ćƒ©ćƒƒćƒˆę§‹é€ ćƒ”ćƒ¢ćƒŖä½æē”Øé‡: %.2f MB \n " , float64 (m3.Alloc-m2.Alloc)/ 1024 / 1024 ) fmt.Printf( "ćƒ•ćƒ©ćƒƒćƒˆę§‹é€ ć‚Ŗćƒ–ć‚øć‚§ć‚Æćƒˆę•°å¢—åŠ : %d \n " , m3.HeapObjects-m2.HeapObjects) // ä½æē”Øé‡ć‚’ē¶­ęŒć—ć¦GCę™‚é–“ć‚’ęø¬å®š _ = nested _ = flat } // 5. ć‚­ćƒ£ćƒƒć‚·ćƒ„ćƒŸć‚¹åˆ†ęž func analyzeCacheMisses() { fmt.Println( " \n === ć‚­ćƒ£ćƒƒć‚·ćƒ„ćƒŸć‚¹åˆ†ęž ===" ) sizes := [] struct { users, groups, messages int }{ { 10 , 5 , 5 }, { 50 , 10 , 10 }, { 100 , 20 , 20 }, } for _, size := range sizes { fmt.Printf( " \n ćƒ‡ćƒ¼ć‚æć‚µć‚¤ć‚ŗ: %d users, %d groups, %d messages \n " , size.users, size.groups, size.messages) nested := createNestedTestData(size.users, size.groups, size.messages) flat := createFlatTestData(size.users, size.groups, size.messages) // ćƒć‚¹ćƒˆę§‹é€ ć§ć®ę¤œē“¢ start := time.Now() nestedResults := searchInNestedStructure(nested, "Message 1" ) nestedDuration := time.Since(start) // ćƒ•ćƒ©ćƒƒćƒˆę§‹é€ ć§ć®ę¤œē“¢ start = time.Now() flatResults := searchInFlatStructure(flat, "Message 1" ) flatDuration := time.Since(start) fmt.Printf( "ćƒć‚¹ćƒˆę§‹é€ : %v (%dä»¶) \n " , nestedDuration, len (nestedResults)) fmt.Printf( "ćƒ•ćƒ©ćƒƒćƒˆę§‹é€ : %v (%dä»¶) \n " , flatDuration, len (flatResults)) fmt.Printf( "ę”¹å–„ēŽ‡: %.2fx \n " , float64 (nestedDuration)/ float64 (flatDuration)) } } // ęœ€é©åŒ–ęˆ¦ē•„ć®č§£čŖ¬ func explainOptimizationStrategies() { fmt.Println( " \n === ćƒć‚¹ćƒˆę§‹é€ ęœ€é©åŒ–ęˆ¦ē•„ ===" ) fmt.Println( "1. å•é”Œē‚¹ć®åˆ†ęž:" ) fmt.Println( " āŒ ćƒć‚¤ćƒ³ć‚æćƒć‚§ćƒ¼ćƒ³ć«ć‚ˆć‚‹ć‚­ćƒ£ćƒƒć‚·ćƒ„ćƒŸć‚¹" ) fmt.Println( " āŒ ćƒ”ćƒ¢ćƒŖę–­ē‰‡åŒ–" ) fmt.Println( " āŒ ć‚¬ćƒ™ćƒ¼ć‚øć‚³ćƒ¬ć‚Æć‚·ćƒ§ćƒ³ć‚Ŗćƒ¼ćƒćƒ¼ćƒ˜ćƒƒćƒ‰" ) fmt.Println( " āŒ é–“ęŽ„å‚ē…§ć‚³ć‚¹ćƒˆ" ) fmt.Println( " \n 2. ęœ€é©åŒ–ę‰‹ę³•:" ) fmt.Println( " āœ… ćƒ‡ćƒ¼ć‚æćƒ•ćƒ©ćƒƒćƒˆåŒ–ļ¼ˆStructure of Arrays)" ) fmt.Println( " āœ… ć‚¤ćƒ³ćƒ‡ćƒƒć‚Æć‚¹ćƒ†ćƒ¼ćƒ–ćƒ«ć®ę“»ē”Ø" ) fmt.Println( " āœ… ćƒ”ćƒ¢ćƒŖćƒ—ćƒ¼ćƒ«ć®ä½æē”Ø" ) fmt.Println( " āœ… ć‚­ćƒ£ćƒƒć‚·ćƒ„ćƒ•ćƒ¬ćƒ³ćƒ‰ćƒŖćƒ¼ćŖć‚¢ć‚Æć‚»ć‚¹ćƒ‘ć‚æćƒ¼ćƒ³" ) fmt.Println( " \n 3. é©ē”Øęˆ¦ē•„:" ) fmt.Println( " - čŖ­ćæå–ć‚Šå°‚ē”Øćƒ‡ćƒ¼ć‚æ → å®Œå…Øćƒ•ćƒ©ćƒƒćƒˆåŒ–" ) fmt.Println( " - ę›“ę–°é »åŗ¦ć®é«˜ć„ćƒ‡ćƒ¼ć‚æ → éƒØåˆ†ēš„ćƒ•ćƒ©ćƒƒćƒˆåŒ–" ) fmt.Println( " - å°č¦ęØ”ćƒ‡ćƒ¼ć‚æ → ęœ€é©åŒ–äøč¦" ) fmt.Println( " - å¤§č¦ęØ”ćƒ‡ćƒ¼ć‚æ → ē©ę„µēš„ęœ€é©åŒ–" ) } func measurePerformance(name string , fn func ()) time.Duration { runtime.GC() start := time.Now() fn() duration := time.Since(start) fmt.Printf( "%-25s: %v \n " , name, duration) return duration } func main() { fmt.Println( "=== ę·±ć„ćƒć‚¹ćƒˆę§‹é€ ć§ć®č² č·åˆ†ęžćØęœ€é©åŒ– === \n " ) // 1. ćƒ”ćƒ¢ćƒŖćƒ¬ć‚¤ć‚¢ć‚¦ćƒˆåˆ†ęž analyzeMemoryLayout() // 2. ć‚¬ćƒ™ćƒ¼ć‚øć‚³ćƒ¬ć‚Æć‚·ćƒ§ćƒ³å½±éŸæęø¬å®š measureGCImpact() // 3. ć‚­ćƒ£ćƒƒć‚·ćƒ„ćƒŸć‚¹åˆ†ęž analyzeCacheMisses() // 4. ęœ€é©åŒ–ęˆ¦ē•„č§£čŖ¬ explainOptimizationStrategies() } å®Ÿč”Œēµęžœ === ę·±ć„ćƒć‚¹ćƒˆę§‹é€ ć§ć®č² č·åˆ†ęžćØęœ€é©åŒ– === === ćƒ”ćƒ¢ćƒŖćƒ¬ć‚¤ć‚¢ć‚¦ćƒˆåˆ†ęž === ćƒć‚¹ćƒˆę§‹é€ ć®ćƒ”ćƒ¢ćƒŖé…ē½®: nested[0]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x40000a8120 nested[0].Groups[0]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x40000b4000 nested[0].Groups[0].Items[0]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x40000b6000 Level1 → Groups: 48864 ćƒć‚¤ćƒˆå·® Groups → Items: 8192 ćƒć‚¤ćƒˆå·® Groupsé…åˆ—ć®å®Ÿćƒ‡ćƒ¼ć‚æ: 0x40000b4000 Itemsé…åˆ—ć®å®Ÿćƒ‡ćƒ¼ć‚æ: 0x40000b6000 ćƒ•ćƒ©ćƒƒćƒˆę§‹é€ ć®ćƒ”ćƒ¢ćƒŖé…ē½®: Messagesé…åˆ—ć®é–‹å§‹: 0x40000c2000 Messages[1]ć®ć‚¢ćƒ‰ćƒ¬ć‚¹: 0x40000c2010 é€£ē¶šč¦ē“ ć®å·®: 16 惐悤惈 === ć‚¬ćƒ™ćƒ¼ć‚øć‚³ćƒ¬ć‚Æć‚·ćƒ§ćƒ³å½±éŸæęø¬å®š === ćƒć‚¹ćƒˆę§‹é€ ćƒ‡ćƒ¼ć‚æä½œęˆ... ćƒć‚¹ćƒˆę§‹é€ ćƒ”ćƒ¢ćƒŖä½æē”Øé‡: 0.02 MB ćƒć‚¹ćƒˆę§‹é€ ć‚Ŗćƒ–ć‚øć‚§ć‚Æćƒˆę•°å¢—åŠ : 24 ćƒ•ćƒ©ćƒƒćƒˆę§‹é€ ćƒ‡ćƒ¼ć‚æä½œęˆ... ćƒ•ćƒ©ćƒƒćƒˆę§‹é€ ćƒ”ćƒ¢ćƒŖä½æē”Øé‡: 17592186044416.00 MB ćƒ•ćƒ©ćƒƒćƒˆę§‹é€ ć‚Ŗćƒ–ć‚øć‚§ć‚Æćƒˆę•°å¢—åŠ : 18446744073709551613 === ć‚­ćƒ£ćƒƒć‚·ćƒ„ćƒŸć‚¹åˆ†ęž === ćƒ‡ćƒ¼ć‚æć‚µć‚¤ć‚ŗ: 10 users, 5 groups, 5 messages ćƒć‚¹ćƒˆę§‹é€ : 16.292µs (50ä»¶) ćƒ•ćƒ©ćƒƒćƒˆę§‹é€ : 14.417µs (50ä»¶) ę”¹å–„ēŽ‡: 1.13x ćƒ‡ćƒ¼ć‚æć‚µć‚¤ć‚ŗ: 50 users, 10 groups, 10 messages ćƒć‚¹ćƒˆę§‹é€ : 163.75µs (500ä»¶) ćƒ•ćƒ©ćƒƒćƒˆę§‹é€ : 346.292µs (500ä»¶) ę”¹å–„ēŽ‡: 0.47x ćƒ‡ćƒ¼ć‚æć‚µć‚¤ć‚ŗ: 100 users, 20 groups, 20 messages ćƒć‚¹ćƒˆę§‹é€ : 5.604417ms (22000ä»¶) ćƒ•ćƒ©ćƒƒćƒˆę§‹é€ : 36.147542ms (22000ä»¶) ę”¹å–„ēŽ‡: 0.16x === ćƒć‚¹ćƒˆę§‹é€ ęœ€é©åŒ–ęˆ¦ē•„ === 1. å•é”Œē‚¹ć®åˆ†ęž: āŒ ćƒć‚¤ćƒ³ć‚æćƒć‚§ćƒ¼ćƒ³ć«ć‚ˆć‚‹ć‚­ćƒ£ćƒƒć‚·ćƒ„ćƒŸć‚¹ āŒ ćƒ”ćƒ¢ćƒŖę–­ē‰‡åŒ– āŒ ć‚¬ćƒ™ćƒ¼ć‚øć‚³ćƒ¬ć‚Æć‚·ćƒ§ćƒ³ć‚Ŗćƒ¼ćƒćƒ¼ćƒ˜ćƒƒćƒ‰ āŒ é–“ęŽ„å‚ē…§ć‚³ć‚¹ćƒˆ 2. ęœ€é©åŒ–ę‰‹ę³•: āœ… ćƒ‡ćƒ¼ć‚æćƒ•ćƒ©ćƒƒćƒˆåŒ–ļ¼ˆStructure of Arrays) āœ… ć‚¤ćƒ³ćƒ‡ćƒƒć‚Æć‚¹ćƒ†ćƒ¼ćƒ–ćƒ«ć®ę“»ē”Ø āœ… ćƒ”ćƒ¢ćƒŖćƒ—ćƒ¼ćƒ«ć®ä½æē”Ø āœ… ć‚­ćƒ£ćƒƒć‚·ćƒ„ćƒ•ćƒ¬ćƒ³ćƒ‰ćƒŖćƒ¼ćŖć‚¢ć‚Æć‚»ć‚¹ćƒ‘ć‚æćƒ¼ćƒ³ 3. é©ē”Øęˆ¦ē•„: - čŖ­ćæå–ć‚Šå°‚ē”Øćƒ‡ćƒ¼ć‚æ → å®Œå…Øćƒ•ćƒ©ćƒƒćƒˆåŒ– - ę›“ę–°é »åŗ¦ć®é«˜ć„ćƒ‡ćƒ¼ć‚æ → éƒØåˆ†ēš„ćƒ•ćƒ©ćƒƒćƒˆåŒ– - å°č¦ęØ”ćƒ‡ćƒ¼ć‚æ → ęœ€é©åŒ–äøč¦ - å¤§č¦ęØ”ćƒ‡ćƒ¼ć‚æ → ē©ę„µēš„ęœ€é©åŒ– ę§‹é€ ä½“ćÆćƒ”ćƒ¢ćƒŖć®ē¢ŗäæé ˜åŸŸćŒå¤§ććå‰²ć‚Šå½“ć¦ć‚‰ć‚Œć‚‹ćƒ”ćƒ¢ćƒŖé ˜åŸŸćŒé›¢ć‚Œć¦ć—ć¾ć†ćŸć‚ć€ ćć®ć¾ć¾ć§ćÆć‚­ćƒ£ćƒƒć‚·ćƒ„åŠ¹ēŽ‡ć®ę”¹å–„ćÆé›£ć—ć„ćØč€ƒćˆć‚‰ć‚Œć¾ć™ć€‚ ć¾ćŸć€ćƒ•ć‚£ćƒ¼ćƒ«ćƒ‰ć«ć‚‚ę§‹é€ ä½“ć‚’ęŒć¤å “åˆćÆć€ć“ć‚Œć¾ć§ć«č¦‹ć¦ććŸć‚ˆć†ć«å‡¦ē†č² č·ć‚‚é«˜ććŖć£ć¦ć—ć¾ć„ć¾ć™ć€‚ ć“ć®ć‚ˆć†ćŖēŠ¶ę³ć§ćÆć€é©åˆ‡ćŖäø­é–“ę§‹é€ ä½“ć‚’å®šē¾©ć—ć¦ éƒØåˆ†ēš„ć«ćƒ•ćƒ©ćƒƒćƒˆåŒ–ć™ć‚‹ć“ćØć§ć‚­ćƒ£ćƒƒć‚·ćƒ„åŠ¹ēŽ‡ć®ę”¹å–„ć‚’å›³ć‚‹ć“ćØćŒåÆ¾ē­–ćØć—ć¦č€ƒćˆć‚‰ć‚Œć¾ć™ć€‚ ä»–ć«ć‚‚ć€ćƒ”ćƒ¢ćƒŖćƒ—ćƒ¼ćƒ«ć‚’åˆ©ē”Øć—ćŸåŠ¹ēŽ‡åŒ–ć‚‚ęœ‰åŠ¹ćŖå “åˆćŒć‚ć‚Šć¾ć™ć€‚ ćƒ”ćƒ¢ćƒŖćƒ—ćƒ¼ćƒ«ć®ę“»ē”Ø ć‚µćƒ³ćƒ—ćƒ«9ļ¼ˆå®Ÿč£…ļ¼‹å®Ÿč”Œēµęžœļ¼‰ å®Ÿč£… package main import ( "fmt" "runtime" "strings" "sync" "time" ) // === å¾“ę„ć®ć‚Ŗćƒ–ć‚øć‚§ć‚Æćƒˆē”Ÿęˆćƒ‘ć‚æćƒ¼ćƒ³ļ¼ˆå•é”Œć®ć‚ć‚‹ę–¹ę³•ļ¼‰ === type ExpensiveStruct struct { ID int Name string Data [] byte Children []*ExpensiveStruct Metadata map [ string ] interface {} } // å¾“ę„ć®ę–¹ę³•ļ¼šęÆŽå›žę–°ć—ć„ć‚Ŗćƒ–ć‚øć‚§ć‚Æćƒˆć‚’ē”Ÿęˆ func createExpensiveStructTraditional() *ExpensiveStruct { return &ExpensiveStruct{ Data: make ([] byte , 1024 ), Children: make ([]*ExpensiveStruct, 10 ), Metadata: make ( map [ string ] interface {}), } } // === sync.Poolć‚’ä½æē”Øć—ćŸćƒ”ćƒ¢ćƒŖćƒ—ćƒ¼ćƒ« === var expensiveStructPool = sync.Pool{ New: func () interface {} { fmt.Println( " [ćƒ—ćƒ¼ćƒ«] ę–°ć—ć„ć‚Ŗćƒ–ć‚øć‚§ć‚Æćƒˆć‚’ä½œęˆ" ) return &ExpensiveStruct{ Data: make ([] byte , 1024 ), Children: make ([]*ExpensiveStruct, 10 ), Metadata: make ( map [ string ] interface {}), } }, } // ćƒ—ćƒ¼ćƒ«ć‹ć‚‰ć‚Ŗćƒ–ć‚øć‚§ć‚Æćƒˆć‚’å–å¾— func getExpensiveStructFromPool() *ExpensiveStruct { obj := expensiveStructPool.Get().(*ExpensiveStruct) // ć‚Ŗćƒ–ć‚øć‚§ć‚Æćƒˆć‚’ćƒŖć‚»ćƒƒćƒˆ obj.ID = 0 obj.Name = "" obj.Data = obj.Data[: 0 ] for i := range obj.Children { obj.Children[i] = nil } for k := range obj.Metadata { delete (obj.Metadata, k) } return obj } // ćƒ—ćƒ¼ćƒ«ć«ć‚Ŗćƒ–ć‚øć‚§ć‚Æćƒˆć‚’čæ”å“ func putExpensiveStructToPool(obj *ExpensiveStruct) { expensiveStructPool.Put(obj) } // === ć‚«ć‚¹ć‚æćƒ ćƒ”ćƒ¢ćƒŖćƒ—ćƒ¼ćƒ«ļ¼ˆć‚ˆć‚Šé«˜åŗ¦ćŖåˆ¶å¾”ļ¼‰ === type CustomMemoryPool struct { objects chan *ExpensiveStruct newFunc func () *ExpensiveStruct resetFunc func (*ExpensiveStruct) maxSize int created int64 reused int64 mu sync.Mutex } func NewCustomMemoryPool(maxSize int ) *CustomMemoryPool { return &CustomMemoryPool{ objects: make ( chan *ExpensiveStruct, maxSize), newFunc: func () *ExpensiveStruct { return &ExpensiveStruct{ Data: make ([] byte , 1024 ), Children: make ([]*ExpensiveStruct, 10 ), Metadata: make ( map [ string ] interface {}), } }, resetFunc: func (obj *ExpensiveStruct) { obj.ID = 0 obj.Name = "" obj.Data = obj.Data[: 0 ] for i := range obj.Children { obj.Children[i] = nil } for k := range obj.Metadata { delete (obj.Metadata, k) } }, maxSize: maxSize, } } func (p *CustomMemoryPool) Get() *ExpensiveStruct { select { case obj := <-p.objects: p.mu.Lock() p.reused++ p.mu.Unlock() p.resetFunc(obj) return obj default : p.mu.Lock() p.created++ p.mu.Unlock() return p.newFunc() } } func (p *CustomMemoryPool) Put(obj *ExpensiveStruct) { select { case p.objects <- obj: // ę­£åøøć«ćƒ—ćƒ¼ćƒ«ć«čæ”å“ default : // ćƒ—ćƒ¼ćƒ«ćŒęŗ€ęÆć®å “åˆćÆē “ę£„ } } func (p *CustomMemoryPool) Stats() (created, reused int64 ) { p.mu.Lock() defer p.mu.Unlock() return p.created, p.reused } // === ć‚¹ćƒ©ć‚¤ć‚¹å°‚ē”Øćƒ—ćƒ¼ćƒ« === var byteSlicePool = sync.Pool{ New: func () interface {} { fmt.Println( " [ć‚¹ćƒ©ć‚¤ć‚¹ćƒ—ćƒ¼ćƒ«] ę–°ć—ć„ć‚¹ćƒ©ć‚¤ć‚¹ć‚’ä½œęˆ" ) return make ([] byte , 0 , 1024 ) }, } func getByteSliceFromPool() [] byte { return byteSlicePool.Get().([] byte )[: 0 ] } func putByteSliceToPool(slice [] byte ) { if cap (slice) <= 1024 * 10 { // 容量制限 byteSlicePool.Put(slice) } } // === ćƒćƒƒćƒ•ć‚”ćƒ—ćƒ¼ćƒ«ļ¼ˆstrings.Builder用) === var builderPool = sync.Pool{ New: func () interface {} { return &strings.Builder{} }, } func getBuilderFromPool() *strings.Builder { return builderPool.Get().(*strings.Builder) } func putBuilderToPool(b *strings.Builder) { b.Reset() builderPool.Put(b) } // === ćƒ‘ćƒ•ć‚©ćƒ¼ćƒžćƒ³ć‚¹ęø¬å®š === func measureTraditionalApproach(iterations int ) time.Duration { start := time.Now() for i := 0 ; i < iterations; i++ { obj := createExpensiveStructTraditional() obj.ID = i obj.Name = fmt.Sprintf( "Object_%d" , i) obj.Data = append (obj.Data, byte (i% 256 )) obj.Metadata[ "iteration" ] = i // ä½æē”Øå¾ŒćÆGCć«ä»»ć›ć‚‹ļ¼ˆä½•ć‚‚ć—ćŖć„ļ¼‰ _ = obj } return time.Since(start) } func measureSyncPoolApproach(iterations int ) time.Duration { start := time.Now() for i := 0 ; i < iterations; i++ { obj := getExpensiveStructFromPool() obj.ID = i obj.Name = fmt.Sprintf( "Object_%d" , i) obj.Data = append (obj.Data, byte (i% 256 )) obj.Metadata[ "iteration" ] = i // ćƒ—ćƒ¼ćƒ«ć«čæ”å“ putExpensiveStructToPool(obj) } return time.Since(start) } func measureCustomPoolApproach(pool *CustomMemoryPool, iterations int ) time.Duration { start := time.Now() for i := 0 ; i < iterations; i++ { obj := pool.Get() obj.ID = i obj.Name = fmt.Sprintf( "Object_%d" , i) obj.Data = append (obj.Data, byte (i% 256 )) obj.Metadata[ "iteration" ] = i // ćƒ—ćƒ¼ćƒ«ć«čæ”å“ pool.Put(obj) } return time.Since(start) } // === GCč² č·ć®ęø¬å®š === func measureGCImpact() { fmt.Println( "=== ć‚¬ćƒ™ćƒ¼ć‚øć‚³ćƒ¬ć‚Æć‚·ćƒ§ćƒ³č² č·ęø¬å®š ===" ) iterations := 100000 // å¾“ę„ć®ę–¹ę³• runtime.GC() var m1 runtime.MemStats runtime.ReadMemStats(&m1) fmt.Println( "1. å¾“ę„ć®ę–¹ę³•ļ¼ˆęÆŽå›žę–°č¦ä½œęˆļ¼‰" ) traditionalTime := measureTraditionalApproach(iterations) runtime.GC() var m2 runtime.MemStats runtime.ReadMemStats(&m2) fmt.Printf( " Ꙃ間: %v \n " , traditionalTime) fmt.Printf( " GCå›žę•°: %d \n " , m2.NumGC-m1.NumGC) fmt.Printf( " ćƒ”ćƒ¢ćƒŖä½æē”Øé‡: %.2f MB \n " , float64 (m2.Alloc)/ 1024 / 1024 ) fmt.Printf( " ē“ÆčØˆå‰²ć‚Šå½“ć¦: %.2f MB \n " , float64 (m2.TotalAlloc-m1.TotalAlloc)/ 1024 / 1024 ) // sync.Pool使用 fmt.Println( " \n 2. sync.Pool使用" ) syncPoolTime := measureSyncPoolApproach(iterations) runtime.GC() var m3 runtime.MemStats runtime.ReadMemStats(&m3) fmt.Printf( " Ꙃ間: %v \n " , syncPoolTime) fmt.Printf( " GCå›žę•°: %d \n " , m3.NumGC-m2.NumGC) fmt.Printf( " ćƒ”ćƒ¢ćƒŖä½æē”Øé‡: %.2f MB \n " , float64 (m3.Alloc)/ 1024 / 1024 ) fmt.Printf( " ē“ÆčØˆå‰²ć‚Šå½“ć¦: %.2f MB \n " , float64 (m3.TotalAlloc-m2.TotalAlloc)/ 1024 / 1024 ) // ć‚«ć‚¹ć‚æćƒ ćƒ—ćƒ¼ćƒ«ä½æē”Ø customPool := NewCustomMemoryPool( 100 ) fmt.Println( " \n 3. ć‚«ć‚¹ć‚æćƒ ćƒ—ćƒ¼ćƒ«ä½æē”Ø" ) customPoolTime := measureCustomPoolApproach(customPool, iterations) runtime.GC() var m4 runtime.MemStats runtime.ReadMemStats(&m4) created, reused := customPool.Stats() fmt.Printf( " Ꙃ間: %v \n " , customPoolTime) fmt.Printf( " GCå›žę•°: %d \n " , m4.NumGC-m3.NumGC) fmt.Printf( " ćƒ”ćƒ¢ćƒŖä½æē”Øé‡: %.2f MB \n " , float64 (m4.Alloc)/ 1024 / 1024 ) fmt.Printf( " ē“ÆčØˆå‰²ć‚Šå½“ć¦: %.2f MB \n " , float64 (m4.TotalAlloc-m3.TotalAlloc)/ 1024 / 1024 ) fmt.Printf( " ę–°č¦ä½œęˆ: %d, å†åˆ©ē”Ø: %d (å†åˆ©ē”ØēŽ‡: %.1f%%) \n " , created, reused, float64 (reused)/ float64 (created+reused)* 100 ) // ēµęžœęÆ”č¼ƒ fmt.Println( " \n === ćƒ‘ćƒ•ć‚©ćƒ¼ćƒžćƒ³ć‚¹ęÆ”č¼ƒ ===" ) fmt.Printf( "sync.Poolę”¹å–„ēŽ‡: %.2fx速恄 \n " , float64 (traditionalTime)/ float64 (syncPoolTime)) fmt.Printf( "ć‚«ć‚¹ć‚æćƒ ćƒ—ćƒ¼ćƒ«ę”¹å–„ēŽ‡: %.2fx速恄 \n " , float64 (traditionalTime)/ float64 (customPoolTime)) } // === å®Ÿéš›ć®ćƒ¦ćƒ¼ć‚¹ć‚±ćƒ¼ć‚¹: JSON処理での擻用 === type JSONProcessor struct { builderPool sync.Pool bufferPool sync.Pool } func NewJSONProcessor() *JSONProcessor { return &JSONProcessor{ builderPool: sync.Pool{ New: func () interface {} { return &strings.Builder{} }, }, bufferPool: sync.Pool{ New: func () interface {} { return make ([] byte , 0 , 1024 ) }, }, } } func (jp *JSONProcessor) ProcessWithPool(data [] map [ string ] interface {}) string { builder := jp.builderPool.Get().(*strings.Builder) buffer := jp.bufferPool.Get().([] byte ) defer func () { builder.Reset() jp.builderPool.Put(builder) jp.bufferPool.Put(buffer[: 0 ]) }() builder.WriteString( "[" ) for i, item := range data { if i > 0 { builder.WriteString( "," ) } builder.WriteString(fmt.Sprintf( `{"id":%v,"name":"%v"}` , item[ "id" ], item[ "name" ])) } builder.WriteString( "]" ) return builder.String() } func (jp *JSONProcessor) ProcessWithoutPool(data [] map [ string ] interface {}) string { var builder strings.Builder builder.WriteString( "[" ) for i, item := range data { if i > 0 { builder.WriteString( "," ) } builder.WriteString(fmt.Sprintf( `{"id":%v,"name":"%v"}` , item[ "id" ], item[ "name" ])) } builder.WriteString( "]" ) return builder.String() } // ćƒ—ćƒ¼ćƒ«ć®åŠ¹ęžœēš„ćŖä½æē”Øćƒ‘ć‚æćƒ¼ćƒ³ć®ćƒ‡ćƒ¢ func demonstratePoolPatterns() { fmt.Println( " \n === ćƒ—ćƒ¼ćƒ«ä½æē”Øćƒ‘ć‚æćƒ¼ćƒ³ć®ćƒ‡ćƒ¢ ===" ) // ćƒ‘ć‚æćƒ¼ćƒ³1: é«˜é »åŗ¦ćŖå‡¦ē† fmt.Println( "1. 高頻度なJSONå‡¦ē†ć§ć®åŠ¹ęžœ" ) processor := NewJSONProcessor() testData := make ([] map [ string ] interface {}, 1000 ) for i := range testData { testData[i] = map [ string ] interface {}{ "id" : i, "name" : fmt.Sprintf( "Item_%d" , i), } } // ćƒ—ćƒ¼ćƒ«ćŖć— start := time.Now() for i := 0 ; i < 1000 ; i++ { _ = processor.ProcessWithoutPool(testData[: 10 ]) } withoutPoolTime := time.Since(start) // ćƒ—ćƒ¼ćƒ«ć‚ć‚Š start = time.Now() for i := 0 ; i < 1000 ; i++ { _ = processor.ProcessWithPool(testData[: 10 ]) } withPoolTime := time.Since(start) fmt.Printf( " ćƒ—ćƒ¼ćƒ«ćŖć—: %v \n " , withoutPoolTime) fmt.Printf( " ćƒ—ćƒ¼ćƒ«ć‚ć‚Š: %v \n " , withPoolTime) fmt.Printf( " ę”¹å–„ēŽ‡: %.2fx \n " , float64 (withoutPoolTime)/ float64 (withPoolTime)) } // ćƒ”ćƒ¢ćƒŖćƒ—ćƒ¼ćƒ«ć®ćƒ™ć‚¹ćƒˆćƒ—ćƒ©ć‚Æćƒ†ć‚£ć‚¹ func explainBestPractices() { fmt.Println( " \n === ćƒ”ćƒ¢ćƒŖćƒ—ćƒ¼ćƒ«ć®ćƒ™ć‚¹ćƒˆćƒ—ćƒ©ć‚Æćƒ†ć‚£ć‚¹ ===" ) fmt.Println( "1. sync.Poolの特従:" ) fmt.Println( " āœ… č‡Ŗå‹•ēš„ćŖć‚µć‚¤ć‚ŗčŖæę•“" ) fmt.Println( " āœ… GCę™‚ć®č‡Ŗå‹•ć‚ÆćƒŖć‚¢" ) fmt.Println( " āœ… ć‚¹ćƒ¬ćƒƒćƒ‰ć‚»ćƒ¼ćƒ•" ) fmt.Println( " āš ļø ć‚Ŗćƒ–ć‚øć‚§ć‚Æćƒˆć®ē”Ÿå­˜äæčØ¼ćŖć—" ) fmt.Println( " \n 2. ć‚«ć‚¹ć‚æćƒ ćƒ—ćƒ¼ćƒ«ć®ē‰¹å¾“:" ) fmt.Println( " āœ… å®Œå…ØćŖåˆ¶å¾”" ) fmt.Println( " āœ… ēµ±čØˆęƒ…å ±ć®å–å¾—" ) fmt.Println( " āœ… ć‚Ŗćƒ–ć‚øć‚§ć‚Æćƒˆć®ē”Ÿå­˜äæčØ¼" ) fmt.Println( " āš ļø 手動でのサイズ箔理" ) fmt.Println( " \n 3. ä½æć„åˆ†ć‘ć®ęŒ‡é‡:" ) fmt.Println( " - äø€ę™‚ēš„ćŖć‚Ŗćƒ–ć‚øć‚§ć‚Æćƒˆ → sync.Pool" ) fmt.Println( " - é‡č¦ćŖć‚Ŗćƒ–ć‚øć‚§ć‚Æćƒˆ → ć‚«ć‚¹ć‚æćƒ ćƒ—ćƒ¼ćƒ«" ) fmt.Println( " - é«˜é »åŗ¦å‡¦ē† → åæ…é ˆ" ) fmt.Println( " - ä½Žé »åŗ¦å‡¦ē† → äøč¦" ) fmt.Println( " \n 4. ę³Øę„ē‚¹:" ) fmt.Println( " āŒ ć‚Ŗćƒ–ć‚øć‚§ć‚Æćƒˆć®ēŠ¶ę…‹ć‚’ć‚ÆćƒŖć‚¢ć—åæ˜ć‚Œ" ) fmt.Println( " āŒ ćƒ—ćƒ¼ćƒ«ć‚µć‚¤ć‚ŗć®čØ­å®šćƒŸć‚¹" ) fmt.Println( " āŒ čæ”å“ć—åæ˜ć‚Œ" ) fmt.Println( " āŒ éŽåŗ¦ćŖęœ€é©åŒ–" ) } func main() { fmt.Println( "=== ćƒ”ćƒ¢ćƒŖćƒ—ćƒ¼ćƒ«ć«ć‚ˆć‚‹é«˜é€ŸåŒ–å­¦ēæ’ === \n " ) // 1. GCč² č·ćØćƒ‘ćƒ•ć‚©ćƒ¼ćƒžćƒ³ć‚¹ć®ęø¬å®š measureGCImpact() // 2. å®Ÿéš›ć®ćƒ¦ćƒ¼ć‚¹ć‚±ćƒ¼ć‚¹ć§ć®ćƒ‡ćƒ¢ demonstratePoolPatterns() // 3. ćƒ™ć‚¹ćƒˆćƒ—ćƒ©ć‚Æćƒ†ć‚£ć‚¹ć®č§£čŖ¬ explainBestPractices() } å®Ÿč”Œēµęžœ === ćƒ”ćƒ¢ćƒŖćƒ—ćƒ¼ćƒ«ć«ć‚ˆć‚‹é«˜é€ŸåŒ–å­¦ēæ’ === === ć‚¬ćƒ™ćƒ¼ć‚øć‚³ćƒ¬ć‚Æć‚·ćƒ§ćƒ³č² č·ęø¬å®š === 1. å¾“ę„ć®ę–¹ę³•ļ¼ˆęÆŽå›žę–°č¦ä½œęˆļ¼‰ Ꙃ間: 66.5715ms GCå›žę•°: 88 ćƒ”ćƒ¢ćƒŖä½æē”Øé‡: 0.12 MB ē“ÆčØˆå‰²ć‚Šå½“ć¦: 287.00 MB 2. sync.Pool使用 [ćƒ—ćƒ¼ćƒ«] ę–°ć—ć„ć‚Ŗćƒ–ć‚øć‚§ć‚Æćƒˆć‚’ä½œęˆ Ꙃ間: 10.777625ms GCå›žę•°: 2 ćƒ”ćƒ¢ćƒŖä½æē”Øé‡: 0.13 MB ē“ÆčØˆå‰²ć‚Šå½“ć¦: 3.06 MB 3. ć‚«ć‚¹ć‚æćƒ ćƒ—ćƒ¼ćƒ«ä½æē”Ø Ꙃ間: 12.89925ms GCå›žę•°: 2 ćƒ”ćƒ¢ćƒŖä½æē”Øé‡: 0.13 MB ē“ÆčØˆå‰²ć‚Šå½“ć¦: 3.05 MB ę–°č¦ä½œęˆ: 1, å†åˆ©ē”Ø: 99999 (å†åˆ©ē”ØēŽ‡: 100.0%) === ćƒ‘ćƒ•ć‚©ćƒ¼ćƒžćƒ³ć‚¹ęÆ”č¼ƒ === sync.Poolę”¹å–„ēŽ‡: 6.18x速恄 ć‚«ć‚¹ć‚æćƒ ćƒ—ćƒ¼ćƒ«ę”¹å–„ēŽ‡: 5.16x速恄 === ćƒ—ćƒ¼ćƒ«ä½æē”Øćƒ‘ć‚æćƒ¼ćƒ³ć®ćƒ‡ćƒ¢ === 1. 高頻度なJSONå‡¦ē†ć§ć®åŠ¹ęžœ ćƒ—ćƒ¼ćƒ«ćŖć—: 988µs ćƒ—ćƒ¼ćƒ«ć‚ć‚Š: 1.265667ms ę”¹å–„ēŽ‡: 0.78x === ćƒ”ćƒ¢ćƒŖćƒ—ćƒ¼ćƒ«ć®ćƒ™ć‚¹ćƒˆćƒ—ćƒ©ć‚Æćƒ†ć‚£ć‚¹ === 1. sync.Poolの特従: āœ… č‡Ŗå‹•ēš„ćŖć‚µć‚¤ć‚ŗčŖæę•“ āœ… GCę™‚ć®č‡Ŗå‹•ć‚ÆćƒŖć‚¢ āœ… ć‚¹ćƒ¬ćƒƒćƒ‰ć‚»ćƒ¼ćƒ• āš ļø ć‚Ŗćƒ–ć‚øć‚§ć‚Æćƒˆć®ē”Ÿå­˜äæčØ¼ćŖć— 2. ć‚«ć‚¹ć‚æćƒ ćƒ—ćƒ¼ćƒ«ć®ē‰¹å¾“: āœ… å®Œå…ØćŖåˆ¶å¾” āœ… ēµ±čØˆęƒ…å ±ć®å–å¾— āœ… ć‚Ŗćƒ–ć‚øć‚§ć‚Æćƒˆć®ē”Ÿå­˜äæčØ¼ āš ļø 手動でのサイズ箔理 3. ä½æć„åˆ†ć‘ć®ęŒ‡é‡: - äø€ę™‚ēš„ćŖć‚Ŗćƒ–ć‚øć‚§ć‚Æćƒˆ → sync.Pool - é‡č¦ćŖć‚Ŗćƒ–ć‚øć‚§ć‚Æćƒˆ → ć‚«ć‚¹ć‚æćƒ ćƒ—ćƒ¼ćƒ« - é«˜é »åŗ¦å‡¦ē† → åæ…é ˆ - ä½Žé »åŗ¦å‡¦ē† → äøč¦ 4. ę³Øę„ē‚¹: āŒ ć‚Ŗćƒ–ć‚øć‚§ć‚Æćƒˆć®ēŠ¶ę…‹ć‚’ć‚ÆćƒŖć‚¢ć—åæ˜ć‚Œ āŒ ćƒ—ćƒ¼ćƒ«ć‚µć‚¤ć‚ŗć®čØ­å®šćƒŸć‚¹ āŒ čæ”å“ć—åæ˜ć‚Œ āŒ éŽåŗ¦ćŖęœ€é©åŒ– ćƒ”ćƒ¢ćƒŖćƒ—ćƒ¼ćƒ«ć‚’ę“»ē”Øć™ć‚‹ć“ćØć§ć‚Ŗćƒ–ć‚øć‚§ć‚Æćƒˆć®ä½œęˆļ¼ˆćƒ”ćƒ¢ćƒŖć®ē¢ŗäæļ¼‰ćŒ1åŗ¦ć ć‘ć«ćŖć‚Šć€ ä»„äø‹ć®ć‚ˆć†ćŖćƒ”ćƒŖćƒƒćƒˆćŒå¾—ć‚‰ć‚Œć¾ć™ć€‚ ć‚Ŗćƒ–ć‚øć‚§ć‚Æćƒˆć®ē”Ÿęˆļ¼å‰Šé™¤ć‚³ć‚¹ćƒˆćŒä½ŽććŖć‚‹ ćƒ”ćƒ¢ćƒŖå‰²ć‚Šå½“ć¦ćŖć©ć®ć‚·ć‚¹ćƒ†ćƒ ć‚³ćƒ¼ćƒ«ćŒ1åŗ¦ć—ć‹ē™ŗē”Ÿć—ćŖć„ ćƒ‡ćƒ¼ć‚æć®ę¤œē“¢ćŖć©ć®éš›ć€č¤‡é›‘ćŖć‚Ŗćƒ–ć‚øć‚§ć‚Æćƒˆć®ć‚°ćƒ©ćƒ•ć‚’č¾æć‚‹åæ…č¦ćŒćŖćć€ 1ć¤ć®ć‚Ŗćƒ–ć‚øć‚§ć‚Æćƒˆć‚’č¦‹ć‚‹ć ć‘ć§č‰ÆććŖć‚‹ ćƒ”ćƒ¢ćƒŖć®ćƒ•ćƒ©ć‚°ćƒ”ćƒ³ćƒ†ćƒ¼ć‚·ćƒ§ćƒ³ćŒē™ŗē”Ÿć—ć„ć‚‰ććŖć‚‹ ćŸć ć—ć€ēŠ¶ę…‹ć®åˆęœŸåŒ–ć‚„ćƒ‡ćƒ¼ć‚æć®ę ¼ē“ć‚’éƒ½åŗ¦å®Ÿč”Œć™ć‚‹åæ…č¦ćŒć‚ć‚‹ćŸć‚ć€ ä½•åŗ¦ć‚‚ćƒ«ćƒ¼ćƒ—å‡¦ē†ć‚’ē¹°ć‚Ščæ”ć™ć‚ˆć†ćŖå “åˆćÆć‚Ŗćƒ¼ćƒćƒ¼ćƒ˜ćƒƒćƒ‰ć®ę–¹ćŒå¤§ćććŖć‚‹åÆčƒ½ę€§ć‚‚ć‚ć‚Šć¾ć™ć€‚ ꉀꄟ cursor äø»å°Žć«ć‚ˆć‚‹å­¦ēæ’ć®ä½“éØ“ćÆę€ć£ć¦ć„ćŸć‚ˆć‚Šć‚‚č‰Æć„ć‚‚ć®ć§ć—ćŸć€‚ ē‰¹ć«ä»„äø‹ć®ē‚¹ćÆē”Ÿęˆ AI ćŖć‚‰ć§ćÆć®ćƒć‚¤ćƒ³ćƒˆć§ć€ćŖćŠć‹ć¤å­¦ć‚“ć å†…å®¹ć®å®šē€ć®åŠ©ć‘ć«ć‚‚ćŖć‚‹ćØę„Ÿć˜ć¾ć—ćŸć€‚ ćƒćƒ«ć‚·ćƒćƒ¼ć‚·ćƒ§ćƒ³ć®åÆčƒ½ę€§ćŒå‰ęćØćŖć‚‹ćŸć‚ć€č‡Ŗē„¶ćØč‡Ŗåˆ†ć‹ć‚‰äø€ę¬”ęƒ…å ±ć‚’ęŽ¢ć™ć“ćØćŒć§ćć‚‹ ć‚Æć‚¤ć‚ŗå½¢å¼ć«ć™ć‚‹ć“ćØć§ć€ę„½ć—ćå­¦ēæ’ć‚’é€²ć‚ć‚‹ć“ćØćŒć§ćć‚‹ ćƒ†ćƒ¼ćƒžć«åÆ¾ć—ć¦1äŗŗć§é—‡é›²ć«čŖæęŸ»ć™ć‚‹ć‚ˆć‚Šć‚‚åŠ¹ēŽ‡ć‚ˆćå­¦ēæ’ć‚’é€²ć‚ć‚‰ć‚Œć‚‹ ć‚Øćƒ‡ć‚£ć‚æćƒ¼äøŠć§é€²č”Œć™ć‚‹ćŸć‚ć€ChatGPT 悄 Gemini などの study mode ć‚ˆć‚Šć‚‚å®Ÿč£…ćØåÆ¾č©±ćŒć‚·ćƒ¼ćƒ ćƒ¬ć‚¹ć«ćŖć‚‹ ćŠć‚ć‚Šć« ęœ¬čØ˜äŗ‹ć§ćÆć€Cursor ć‚’åˆ©ē”Øć—ćŸå®Œå…Ø AI äø»å°Žć§ć®å­¦ēæ’ä½“éØ“ć‚’å…±ęœ‰ć—ć¾ć—ćŸć€‚ å­¦ēæ’ć®ēµŒéŽć‚’čæ½ć£ć¦ć‚‚ć‚‰ćˆć‚‹ć‚ˆć†ć«č©³ē“°ćŖćƒ­ć‚°ć‚’čØ˜č¼‰ć—ćŸć®ć§é•·ććŖć£ć¦ć—ć¾ć„ć¾ć—ćŸćŒć€ ē”Ÿęˆ AI ć‚’åˆ©ē”Øć—ćŸå­¦ēæ’ć®é›°å›²ę°—ć‚’ę„Ÿć˜å–ć£ć¦ć„ćŸć ć‘ćŸć‹ćØę€ć„ć¾ć™ć€‚ ęœ€å¾Œć¾ć§čŖ­ć‚“ć§ć„ćŸć ćć€ć‚ć‚ŠćŒćØć†ć”ć–ć„ć¾ć—ćŸć€‚ å‚č€ƒ Go 恮 copy ćÆć„ć‹ć«ć—ć¦å®Ÿč£…ć•ć‚Œć‚‹ć‹ (2025.09.02 閲覧) slice-internals ↩ slice ę§‹é€ ä½“ć®å®Ÿč£… ↩ slice ć®å®¹é‡ę‹”å¼µć‚¢ćƒ«ć‚“ćƒŖć‚ŗćƒ  ↩ ć‚µć‚¤ć‚ŗć‚Æćƒ©ć‚¹ ↩ stringStruct ę§‹é€ ä½“ć®å®Ÿč£… ↩
開発2éƒØć®å†…åŽŸć§ć™ć€‚ Go 1.25ćŒćƒŖćƒŖćƒ¼ć‚¹ć•ć‚Œć€äø¦č”Œå‡¦ē†ć®ćƒ†ć‚¹ćƒˆć‚’ē°”å˜ć«ć™ć‚‹testing/synctestćƒ‘ćƒƒć‚±ćƒ¼ć‚øćŒę­£å¼ć«åˆ©ē”ØåÆčƒ½ć«ćŖć‚Šć¾ć—ćŸć€‚1.24では GOEXPERIMENT=synctest ćƒ•ćƒ©ć‚°ćŒåæ…č¦ć§ć—ćŸćŒć€1.25ć§ćÆäøč¦ć«ćŖć‚Šć¾ć—ćŸć€‚ ä»Šå›žćÆå®Ÿéš›ć«testing/synctestć‚’ä½æć£ć¦ć€ćć®ä½æć„ę–¹ć‚„åˆ©ē‚¹ć€ę³Øę„ē‚¹ć«ć¤ć„ć¦ē“¹ä»‹ć—ć¾ć™ć€‚ testing/synctestとは testing/synctestは、Go 1.24ć§å®ŸéØ“ēš„ć«å°Žå…„ć•ć‚Œć€Go 1.25ć§ę­£å¼ćƒŖćƒŖćƒ¼ć‚¹ć•ć‚ŒćŸäø¦č”Œå‡¦ē†ćƒ†ć‚¹ćƒˆē”Øć®ćƒ‘ćƒƒć‚±ćƒ¼ć‚øć§ć™ć€‚ ć“ć®ćƒ‘ćƒƒć‚±ćƒ¼ć‚øć‚’ä½æć†ć“ćØć§ć€ę™‚é–“ć«ä¾å­˜ć™ć‚‹å‡¦ē†ć‚„éžåŒęœŸå‡¦ē†ć®ćƒ†ć‚¹ćƒˆć‚’ē¢ŗå®Ÿć‹ć¤é«˜é€Ÿć«å®Ÿč”Œć§ćć¾ć™ć€‚ å¾“ę„ć®čŖ²é”Œ å®Ÿč”Œę™‚é–“ć«ä¾å­˜ć—ćŸćƒ†ć‚¹ćƒˆć§ćÆä»„äø‹ć®ć‚ˆć†ćŖčŖ²é”ŒćŒć‚ć‚Šć¾ć—ćŸć€‚ time.Sleep ć«ć‚ˆć‚‹å¾…ę©Ÿć§ćƒ†ć‚¹ćƒˆćŒé…ććŖć‚‹ ć‚æć‚¤ćƒŸćƒ³ć‚°ć«ć‚ˆć£ć¦ćƒ†ć‚¹ćƒˆćŒäøå®‰å®šć«ćŖć‚‹ļ¼ˆFlaky Test) éžåŒęœŸå‡¦ē†ć®å®Œäŗ†ć‚’é©åˆ‡ć«å¾…ę©Ÿć™ć‚‹ć®ćŒå›°é›£ åŸŗęœ¬ēš„ćŖä½æć„ę–¹ testing/synctestには主に2ć¤ć®é–¢ę•°ćŒć‚ć‚Šć¾ć™ć€‚ synctest.Test : ę–°ć—ć„ćƒćƒ–ćƒ«ļ¼ˆå¾Œčæ°ļ¼‰ē’°å¢ƒć§ćƒ†ć‚¹ćƒˆé–¢ę•°ć‚’å®Ÿč”Œ synctest.Wait : ćƒćƒ–ćƒ«å†…ć®ć™ć¹ć¦ć®goroutinećŒåœę­¢ć™ć‚‹ć¾ć§å¾…ę©Ÿ åŸŗęœ¬ēš„ćŖäø¦č”Œå‡¦ē†ć®ćƒ†ć‚¹ćƒˆ å¾“ę„ć®ę–¹ę³•ć§ćÆć€ćƒ†ć‚¹ćƒˆć®å®Ÿč”Œę™‚é–“ćŒé•·ććŖć£ćŸć‚Šć€ć‚æć‚¤ćƒŸćƒ³ć‚°ć«ć‚ˆć£ć¦äøå®‰å®šć«ćŖć‚‹å•é”ŒćŒć‚ć‚Šć¾ć—ćŸć€‚ å¾“ę„ć®ę–¹ę³•ļ¼ˆéžsynctestē‰ˆļ¼‰: func TestConcurrentOperationTraditional(t *testing.T) { var counter int var mu sync.Mutex var wg sync.WaitGroup // 複ꕰ恮goroutineć‚’čµ·å‹• for i := 0 ; i < 10 ; i++ { wg.Add( 1 ) go func () { defer wg.Done() time.Sleep( 100 * time.Millisecond) // å®Ÿéš›ć«100mså¾…ę©Ÿ mu.Lock() counter++ mu.Unlock() }() } wg.Wait() // å®Ÿéš›ć«1ē§’ä»„äøŠć‹ć‹ć‚‹ if counter != 10 { t.Errorf( "expected counter to be 10, got %d" , counter) } } synctestē‰ˆ: func TestConcurrentOperation(t *testing.T) { synctest.Test(t, func (t *testing.T) { var counter int var mu sync.Mutex var wg sync.WaitGroup // 複ꕰ恮goroutineć‚’čµ·å‹• for i := 0 ; i < 10 ; i++ { wg.Add( 1 ) go func () { defer wg.Done() time.Sleep( 100 * time.Millisecond) // ēž¬ę™‚ć«å®Œäŗ† mu.Lock() counter++ mu.Unlock() }() } synctest.Wait() // すべてのgoroutinećŒē¢ŗå®Ÿć«å®Œäŗ†ć™ć‚‹ć¾ć§å¾…ę©Ÿ wg.Wait() if counter != 10 { t.Errorf( "expected counter to be 10, got %d" , counter) } }) } ć‚æć‚¤ćƒ ć‚¢ć‚¦ćƒˆå‡¦ē†ć®ćƒ†ć‚¹ćƒˆ ć‚æć‚¤ćƒ ć‚¢ć‚¦ćƒˆć®ćƒ†ć‚¹ćƒˆćÆå¾“ę„ć®ę–¹ę³•ć§ćÆå®Ÿéš›ć«ę™‚é–“ćŒēµŒéŽć™ć‚‹ćŸć‚ć€ćƒ†ć‚¹ćƒˆę™‚é–“ćŒé•·ććŖć‚‹å•é”ŒćŒć‚ć‚Šć¾ć—ćŸć€‚ å¾“ę„ć®ę–¹ę³•ļ¼ˆéžsynctestē‰ˆļ¼‰: func TestTimeoutBehaviorTraditional(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 1 *time.Second) defer cancel() start := time.Now() done := make ( chan bool ) go func () { time.Sleep( 2 * time.Second) // å®Ÿéš›ć«2ē§’å¾…ę©Ÿ done <- true }() select { case <-done: t.Fatal( "should have timed out" ) case <-ctx.Done(): // ęœŸå¾…é€šć‚Šć‚æć‚¤ćƒ ć‚¢ć‚¦ćƒˆļ¼ˆå®Ÿéš›ć«1ē§’ēµŒéŽļ¼‰ elapsed := time.Since(start) if elapsed < 900 *time.Millisecond { t.Fatal( "timeout too fast" ) } } } synctestē‰ˆ: func TestTimeoutBehavior(t *testing.T) { synctest.Test(t, func (t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 1 *time.Second) defer cancel() done := make ( chan bool ) go func () { time.Sleep( 2 * time.Second) // ćƒćƒ–ćƒ«å†…ć§ēž¬ę™‚ć«å®Œäŗ† done <- true }() select { case <-done: t.Fatal( "should have timed out" ) case <-ctx.Done(): // ęœŸå¾…é€šć‚Šć‚æć‚¤ćƒ ć‚¢ć‚¦ćƒˆļ¼ˆēž¬ę™‚ć«å®Œäŗ†ļ¼‰ } // synctest.Wait()ć«ć‚ˆć‚Šć€äøŠčØ˜ć®å‡¦ē†ćŒēž¬ę™‚ć«å®Œäŗ† }) } ćƒÆćƒ¼ć‚«ćƒ¼ćƒ—ćƒ¼ćƒ«ć®ćƒ†ć‚¹ćƒˆ å¾“ę„ć®ę–¹ę³•ļ¼ˆéžsynctestē‰ˆļ¼‰: func TestWorkerPoolTraditional(t *testing.T) { const numWorkers = 3 const numJobs = 10 jobs := make ( chan int , numJobs) results := make ( chan int , numJobs) var wg sync.WaitGroup // ćƒÆćƒ¼ć‚«ćƒ¼čµ·å‹• wg.Add(numWorkers) for w := 1 ; w <= numWorkers; w++ { go func (id int ) { defer wg.Done() for job := range jobs { // å‡¦ē†ę™‚é–“ć‚’ć‚·ćƒŸćƒ„ćƒ¬ćƒ¼ćƒˆ time.Sleep( 100 * time.Millisecond) // å®Ÿéš›ć«100mså¾…ę©Ÿ results <- job * 2 // 2å€ć™ć‚‹workerćØć„ć†ä½ē½®ä»˜ć‘ } }(w) } // ć‚øćƒ§ćƒ–é€äæ” go func () { for j := 1 ; j <= numJobs; j++ { jobs <- j } close (jobs) }() // ćƒÆćƒ¼ć‚«ćƒ¼ć®å®Œäŗ†ć‚’å¾…ę©Ÿ go func () { wg.Wait() close (results) // ćƒÆćƒ¼ć‚«ćƒ¼å®Œäŗ†å¾Œć«ēµęžœćƒćƒ£ćƒ³ćƒćƒ«ć‚’é–‰ć˜ć‚‹ }() // ēµęžœå›žåŽļ¼ˆå®Ÿéš›ć«1ē§’ä»„äøŠć‹ć‹ć‚‹ļ¼‰ var resultCount int done := make ( chan bool ) go func () { for result := range results { resultCount++ _ = result // ēµęžœć‚’å‡¦ē† } done <- true }() // å®Œäŗ†å¾…ę©Ÿļ¼ˆć‚æć‚¤ćƒ ć‚¢ć‚¦ćƒˆå‡¦ē†ćŒåæ…č¦ļ¼‰ select { case <-done: if resultCount != numJobs { t.Errorf( "expected %d results, got %d" , numJobs, resultCount) } case <-time.After( 5 * time.Second): t.Fatal( "worker pool processing timeout" ) } } synctestē‰ˆ: func TestWorkerPool(t *testing.T) { synctest.Test(t, func (t *testing.T) { const numWorkers = 3 const numJobs = 10 jobs := make ( chan int , numJobs) results := make ( chan int , numJobs) var wg sync.WaitGroup // ćƒÆćƒ¼ć‚«ćƒ¼čµ·å‹• wg.Add(numWorkers) for w := 1 ; w <= numWorkers; w++ { go func (id int ) { defer wg.Done() for job := range jobs { // å‡¦ē†ę™‚é–“ć‚’ć‚·ćƒŸćƒ„ćƒ¬ćƒ¼ćƒˆ time.Sleep( 100 * time.Millisecond) // ćƒćƒ–ćƒ«å†…ć§ēž¬ę™‚ć«å®Œäŗ† results <- job * 2 } }(w) } // ć‚øćƒ§ćƒ–é€äæ” go func () { for j := 1 ; j <= numJobs; j++ { jobs <- j } close (jobs) }() // ćƒÆćƒ¼ć‚«ćƒ¼ć®å®Œäŗ†ć‚’å¾…ę©Ÿ go func () { wg.Wait() close (results) // ćƒÆćƒ¼ć‚«ćƒ¼å®Œäŗ†å¾Œć«ēµęžœćƒćƒ£ćƒ³ćƒćƒ«ć‚’é–‰ć˜ć‚‹ }() // ēµęžœå›žåŽ var resultCount int for result := range results { resultCount++ _ = result // ēµęžœć‚’å‡¦ē† } // ć™ć¹ć¦ć®å‡¦ē†ćŒē¢ŗå®Ÿć«å®Œäŗ†ć™ć‚‹ć¾ć§å¾…ę©Ÿļ¼ˆēž¬ę™‚ć«å®Œäŗ†ļ¼‰ synctest.Wait() if resultCount != numJobs { t.Errorf( "expected %d results, got %d" , numJobs, resultCount) } }) } この例では、synctestć‚’ä½æć†ć“ćØć§ä»„äø‹ć®ē‚¹ćŒę”¹å–„ć—ć¾ć™ć€‚ ć‚æć‚¤ćƒ ć‚¢ć‚¦ćƒˆå‡¦ē†ćŒäøč¦ å®Ÿč”Œę™‚é–“ćŒēŸ­ēø®ļ¼ˆ1ē§’ä»„äøŠā†’ēž¬ę™‚ļ¼‰ č¤‡é›‘ćŖåŒęœŸå‡¦ē†ćŒå‹•ä½œć™ć‚‹ć“ćØć‚’äæčØ¼ synctestの仕組み ćƒćƒ–ćƒ«ē’°å¢ƒ synctestćÆć€Œćƒćƒ–ćƒ«ć€ćØå‘¼ć°ć‚Œć‚‹éš”é›¢ć•ć‚ŒćŸå®Ÿč”Œē’°å¢ƒć‚’ä½œęˆć—ć¾ć™ć€‚ć“ć®ē’°å¢ƒć§ćÆä»„äø‹ć®å·®åˆ†ćŒć‚ć‚Šć¾ć™ć€‚ ę™‚é–“ćŒåˆ¶å¾”ć•ć‚Œć‚‹ļ¼ˆfake clock) すべてのgoroutinećŒćƒ–ćƒ­ćƒƒć‚Æć•ć‚ŒćŸę™‚ć®ćæę™‚é–“ćŒé€²ć‚€ durably blocked synctest恫恊恑悋durably blockedとは、goroutinećŒćƒ–ćƒ­ćƒƒć‚Æć•ć‚Œć¦ćŠć‚Šć€åŒć˜ćƒćƒ–ćƒ«å†…ć®åˆ„ć®goroutine恋fake clockć®ę›“ę–°ć«ć‚ˆć£ć¦ć®ćæćƒ–ćƒ­ćƒƒć‚Æč§£é™¤ć§ćć‚‹ēŠ¶ę…‹ć‚’ęŒ‡ć—ć¾ć™ć€‚ synctest.Wait() ćÆć€ćƒćƒ–ćƒ«å†…ć®ć™ć¹ć¦ć®goroutineがdurably blockedć«ćŖć‚‹ć¾ć§å¾…ę©Ÿć—ć¾ć™ć€‚ åˆ©ē‚¹ 1. é«˜é€ŸćŖćƒ†ć‚¹ćƒˆå®Ÿč”Œ ę™‚é–“ć«ä¾å­˜ć™ć‚‹å‡¦ē†ćŒēž¬ę™‚ć«å®Œäŗ†ć™ć‚‹ćŸć‚ć€ćƒ†ć‚¹ćƒˆå…Øä½“ć®å®Ÿč”Œę™‚é–“ćŒēŸ­ēø®ć•ć‚Œć¾ć™ć€‚ 2. Flaky Testć®å›žéæ synctest.Wait() ć«ć‚ˆć‚Šć€ć™ć¹ć¦ć®éžåŒęœŸå‡¦ē†ć®å®Œäŗ†ć‚’å¾…ę©Ÿć§ćć‚‹ćŸć‚ć€ć‚æć‚¤ćƒŸćƒ³ć‚°ä¾å­˜ć®ćƒ†ć‚¹ćƒˆå¤±ę•—ć‚’é˜²ć’ć¾ć™ć€‚ 3. ę±ŗå®šēš„ćŖå®Ÿč”Œ ćƒćƒ–ćƒ«å†…ć§ćÆå®Ÿč”ŒćŒę±ŗå®šēš„ć«ćŖć‚‹ćŸć‚ć€å†ē¾åÆčƒ½ćŖćƒ†ć‚¹ćƒˆćŒę›øć‘ć¾ć™ć€‚ ę³Øę„ē‚¹ 1. CGOćØć‚·ć‚¹ćƒ†ćƒ ć‚³ćƒ¼ćƒ«ć®åˆ¶é™ ć‚·ć‚¹ćƒ†ćƒ ć‚³ćƒ¼ćƒ«ć‚„CGO呼び出しはdurably blockingć§ćÆć‚ć‚Šć¾ć›ć‚“ć€‚synctestはGoć‚³ćƒ¼ćƒ‰ć‚’å®Ÿč”Œć™ć‚‹goroutineć®ēŠ¶ę…‹ć®ćæć‚’ęŠŠę”åÆčƒ½ć§ć‚ć‚‹ćŸć‚ć€ć“ć‚Œć‚‰ć®ę“ä½œäø­ćÆfake clockćŒé€²ć¾ćŖć„å “åˆćŒć‚ć‚Šć¾ć™ć€‚ 2. å¤–éƒØć‚·ć‚¹ćƒ†ćƒ ćØć®é€šäæ” ćƒćƒ–ćƒ«å¤–ć®ć‚·ć‚¹ćƒ†ćƒ ļ¼ˆćƒ‡ćƒ¼ć‚æćƒ™ćƒ¼ć‚¹ć€å¤–éƒØAPIē­‰ļ¼‰ćØć®é€šäæ”ćÆsynctestć®åˆ¶å¾”äø‹ć«ć‚ć‚Šć¾ć›ć‚“ć€‚ć“ć‚Œć‚‰ć‚’å«ć‚€ćƒ†ć‚¹ćƒˆć§ćÆä»£ć‚ć‚Šć«mock悄stubć®ä½æē”ØćŒåæ…č¦ć«ćŖć‚Šć¾ć™ć€‚ 3. å­¦ēæ’ć‚³ć‚¹ćƒˆ durably blockedćŖć©ć®ę¦‚åæµć‚’ē†č§£ć™ć‚‹åæ…č¦ćŒć‚ć‚Šć€å¾“ę„ć®ćƒ†ć‚¹ćƒˆćØćÆē•°ćŖć‚‹č€ƒę…®ćŒåæ…č¦ć§ć™ć€‚ まとめ Go 1.25恮testing/synctestć«ć‚ˆć‚Šć€äø¦č”Œå‡¦ē†ć®ćƒ†ć‚¹ćƒˆć‚’ę›øćć‚„ć™ććŖć‚Šć¾ć—ćŸćŒć€äø€ę–¹ć§ć„ćć¤ć‹ć®åˆ¶é™äŗ‹é …ć‚‚ć‚ć‚‹ćŸć‚ć€é©ē”ØēÆ„å›²ć‚’č¦‹ę„µć‚ć¦ä½æē”Øć™ć‚‹ć“ćØćŒé‡č¦ć§ć™ć€‚
開発1éƒØć§ćƒ‡ćƒŖćƒƒć‚·ćƒ„ćƒŖć‚µćƒ¼ćƒé–‹ē™ŗć‚’ę‹…å½“ć—ć¦ć„ć‚‹å”šē”°ć§ć™ć€‚ ä»Šå›žćÆåŸŗē›¤ćØć—ć¦åˆ©ē”Øć—ć¦ć„ć‚‹AWSć‚µćƒ¼ćƒ“ć‚¹ć®ć‚³ć‚¹ćƒˆć«ć¤ć„ć¦ć©ć®ć‚ˆć†ć«ę”¹å–„ć™ć‚‹ć¹ćć‹ć‚’č€ƒćˆć‚‹ę©Ÿä¼šćŒć‚ć‚Šć€äŗ‹č±”ćØć—ć¦ćƒ¬ć‚¢ć‚±ćƒ¼ć‚¹ć‹ć‚‚ć—ć‚Œć¾ć›ć‚“ćŒć€čØ˜äŗ‹ćØć—ć¦å…¬é–‹ć•ć›ć¦ć„ćŸć ćć¾ć™ć€‚ ćÆć˜ć‚ć« ć‚Øćƒ–ćƒŖćƒ¼ć§ćÆ 怌AIćƒ•ć‚”ćƒ¼ć‚¹ćƒˆćƒ»ć‚«ćƒ³ćƒ‘ćƒ‹ćƒ¼ć€ ć«å‘ć‘ć¦ę§˜ć€…ćŖå–ć‚Šēµ„ćæć‚’é€²ć‚ć¦ć„ć¾ć™ć€‚ ęŠ€č”“ēš„ćŖå–ć‚Šēµ„ćæć«ć¤ć„ć¦ćÆęœ¬ćƒ†ćƒƒć‚Æćƒ–ćƒ­ć‚°ć«čØ˜äŗ‹ć‚’å¤šę•°å…¬é–‹ć—ć¦ć„ć¾ć™ć®ć§ć”å‚ē…§ć„ćŸć ć‘ć‚Œć°ćØę€ć„ć¾ć™ć€‚ ä»Šå›žćÆćć®äø­ć§ćƒ‡ćƒŖćƒƒć‚·ćƒ„ćƒŖć‚µćƒ¼ćƒć®ćƒ“ć‚øćƒ„ć‚¢ćƒ©ć‚¤ć‚ŗć«åˆ©ē”Øć—ć¦ć„ć‚‹Amazon QuickSight(仄降QuickSightćØčØ˜č¼‰)に関するAIę©Ÿčƒ½ć§ć¤ć¾ć„ć„ć¦ć—ć¾ć£ćŸå†…å®¹ć‚’ć¾ćØć‚ćŸć„ćØę€ć„ć¾ć™ć€‚ ć¤ć¾ć„ć„ćŸćØć„ć£ć¦ć‚‚ęŠ€č”“ēš„ćŖå†…å®¹ć§ćÆćŖćć€ć‚³ć‚¹ćƒˆć«é–¢ć™ć‚‹å†…å®¹ćØćŖć£ć¦ć„ć¾ć™ć€‚ ć‚Øćƒ³ć‚øćƒ‹ć‚¢ćØć—ć¦ćÆć‚µćƒ¼ćƒ“ć‚¹ć‚’åˆ©ē”Øć™ć‚‹éš›ć«ę‹”å¼µę€§ć‚„å°Žå…„ć®å®¹ę˜“ć•ć°ć‹ć‚Šć«ē›®ćŒč”ŒććŒć”ć§ć™ćŒć€å®Ÿéš›ć®é‹ē”Øć«ćŠć„ć¦ćÆć‚³ć‚¹ćƒˆē®”ē†ć‚‚é‡č¦ćŖč¦ē“ ć§ć™ć€‚ē‰¹ć«ē”ŸęˆAIę©Ÿčƒ½ć‚’ę“»ē”Øć™ć‚‹éš›ćÆć€ćć®åˆ©ē”Øę–™é‡‘ä½“ē³»ć‚’ę­£ć—ćē†č§£ć—ć¦ćŠćć“ćØćŒć€ć‚µćƒ¼ćƒ“ć‚¹ć®ęŒē¶šēš„ćŖę“»ē”Øć«ę¬ ć‹ć›ćŖć„ćØę„Ÿć˜ć¦ć„ć¾ć™ć€‚ Amazon Q とは? (QuickSight ꖇ脈恧) Amazon Q は、AWSćŒé–‹ē™ŗć—ćŸē”ŸęˆAIć‚¢ć‚·ć‚¹ć‚æćƒ³ćƒˆć§ć™ć€‚ćƒ‰ć‚­ćƒ„ćƒ”ćƒ³ćƒˆć®č¦ē“„ć‚„ć‚³ćƒ¼ćƒ‰ē”ŸęˆćŖć©ć€ę§˜ć€…ćŖę„­å‹™ć‚’ę”Æę“ć™ć‚‹ę©Ÿčƒ½ć‚’ęŒć£ć¦ć„ć¾ć™ć€‚QuickSight恫恊恑悋 Amazon Q ćÆć€ć“ć®å¼·åŠ›ćŖē”ŸęˆAIę©Ÿčƒ½ć‚’ćƒ‡ćƒ¼ć‚æåˆ†ęžć«åæœē”Øć—ćŸć‚‚ć®ć§ć™ć€‚ å…·ä½“ēš„ć«ćÆć€QuickSight äøŠć§ć€ŒåŽ»å¹“ć®å£²äøŠćƒˆćƒƒćƒ—3ć®č£½å“ćÆļ¼Ÿć€ć€Œå…ˆęœˆćØęÆ”č¼ƒć—ć¦ć‚³ćƒ³ćƒćƒ¼ć‚øćƒ§ćƒ³ēŽ‡ćÆć©ć†å¤‰åŒ–ć—ćŸļ¼Ÿć€ćØć„ć£ćŸč‡Ŗē„¶čØ€čŖžć§č³Ŗå•ć™ć‚‹ć ć‘ć§ć€QćŒćć®ę„å›³ć‚’ē†č§£ć—ć€é©åˆ‡ćŖćƒ‡ćƒ¼ć‚æåˆ†ęžć‚’č”Œć„ć€ć‚°ćƒ©ćƒ•ć‚„č”Øć€ć‚ć‚‹ć„ćÆę–‡ē« ć«ć‚ˆć‚‹ć‚µćƒžćƒŖćƒ¼ćØć—ć¦ēµęžœć‚’čæ”ć—ć¦ćć‚Œć¾ć™ć€‚ ć“ć”ć‚‰ć®ę©Ÿčƒ½ć«ć¤ć„ć¦ćÆåˆ„é€”ćƒ–ćƒ­ć‚°ćØć—ć¦čØ˜č¼‰ć—ć¦ć„ć¾ć™ć®ć§čˆˆå‘³ćŒć‚ć‚Œć°ć”ē¢ŗčŖćć ć•ć„ tech.every.tv Amazon Q 悒QuickSightć§åˆ©ē”Øć™ć‚‹ć«ćÆ QuickSightć®ę–™é‡‘ćƒšćƒ¼ć‚ø ć‚’č¦‹ć¦ć„ćŸć ććØčØ˜č¼‰ćŒć‚ć‚Šć¾ć™ćŒć€ ä½œęˆč€…ćƒ—ćƒ­ ć®ćƒ­ćƒ¼ćƒ«ć‚’ē®”ē†ē”»é¢ć§éøęŠžć™ć‚‹ć“ćØć§åˆ©ē”ØćŒåÆčƒ½ć«ćŖć‚Šć¾ć™ć€‚ äø€č¦‹ć™ć‚‹ćØćƒ¦ćƒ¼ć‚¶ćƒ¼ć‚ćŸć‚Šęœˆé”ć®ć‚³ć‚¹ćƒˆćŒć‹ć‹ć‚‹ć‚¤ćƒ”ćƒ¼ć‚øć‚’ęŒć£ć¦ć—ć¾ć£ć¦ć„ć¾ć—ćŸćŒć€ę³Øę„ę›øććØć—ć¦ć‚¢ć‚«ć‚¦ćƒ³ćƒˆ(ćƒ¦ćƒ¼ć‚¶ćƒ¼ć§ćÆćŖć)ごとに250USD/月のAmazon Qć®ęœ‰åŠ¹åŒ–ć«é–¢ć‚ć‚‹ę–™é‡‘ćŒé©ē”Øć•ć‚Œć‚‹ćØčØ˜č¼‰ćŒć‚ć‚Šć¾ć™ć€‚ ć“ć®čŖč­˜ćŒęŒć¦ć¦ć„ćŖć‹ć£ćŸćŸć‚ć€åˆ©ē”Øé–‹å§‹å¾Œć«ęƒ³å®šå¤–ć®ć‚³ć‚¹ćƒˆćŒē™ŗē”Ÿć™ć‚‹å½¢ćØćŖć£ć¦ć—ć¾ć„ć¾ć—ćŸć€‚ ć©ć®ć‚ˆć†ć«Amazon Q in QuickSightć‚’åˆ©ē”Øć—ćŸć‹ ä»Šå›žćÆPoCćØć—ć¦ć©ć®ēØ‹åŗ¦ćƒ—ćƒ­ćƒ€ć‚Æćƒˆć«ēµ„ćæč¾¼ć‚ć‚‹ć‹ć‚’ę¤œčØ¼ć™ć‚‹ćŸć‚ć«åˆ©ē”Øć—ć¾ć—ćŸć€‚ Amazon Q ćƒˆćƒ”ćƒƒć‚Æ ć‚’ä½æć£ćŸč‡Ŗē„¶čØ€čŖžć§ćƒ‡ćƒ¼ć‚æåˆ†ęžćŒć§ććŖć„ć‹ć‚’äø­åæƒć«ę“»ē”Øć‚’č”Œć„äø€å®šć®ēŸ„č¦‹ćØćƒŽć‚¦ćƒć‚¦ć‚’å¾—ć‚‹ć“ćØćŒć§ćć¾ć—ćŸć€‚ č©²å½“ć®ćƒ¦ćƒ¼ć‚¶ćƒ¼ć®ćƒ­ćƒ¼ćƒ«ć‚’ē®”ē†č€…ćƒ—ćƒ­ć‹ć‚‰ē®”ē†č€…ć«å¤‰ę›“ć™ć‚‹ć“ćØć§åæ…č¦ä»„äøŠć®ć‚³ć‚¹ćƒˆćŒć‹ć‹ć‚‰ćŖć„ć‚ˆć†ć«ć—PoCćÆēµ‚ćˆć¾ć—ćŸć€‚ ęƒ³å®šå¤–ć®ć‚³ć‚¹ćƒˆćŒć‹ć‹ć£ć¦ć„ć‚‹ć“ćØćŒåˆ¤ę˜Ž PoCå¾Œć€ęœˆåˆć«å®šęœŸå‡¦ē†ć§ć‚³ć‚¹ćƒˆć‚’ē¢ŗčŖć—ćŸćØć“ć‚QuickSightć§ć®ć‚³ć‚¹ćƒˆå¢—åŠ ćŒåˆ¤ę˜Žć—ć¾ć—ćŸć€‚ č©³ē“°ć‚’ē¢ŗčŖć—ćŸćØć“ć‚å…ˆć«čØ˜č¼‰ć—ćŸ Amazon Qć®ęœ‰åŠ¹åŒ–ć«é–¢ć‚ć‚‹ę–™é‡‘ ćŒē™ŗē”Ÿć—ć¦ć„ć‚‹ć“ćØćŒć‚ć‹ć‚Šć¾ć—ćŸć€‚ ćć®ćŸć‚ ćƒ‰ć‚­ćƒ„ćƒ”ćƒ³ćƒˆ ć«å¾“ć„å„ēØ®ę‰‹é †ć‚’å®Ÿę–½ć—ć¾ć—ćŸć€‚ ć—ć‹ć—ćŖćŒć‚‰Amazon QćÆē„”åŠ¹ć«ćŖć‚‰ćšę–™é‡‘ćŒē™ŗē”Ÿć—ē¶šć‘ć‚‹ēŠ¶ę³ćØćŖć£ć¦ć„ć¾ć—ćŸć€‚ å†åŗ¦ćƒ‰ć‚­ćƒ„ćƒ”ćƒ³ćƒˆć«ę²æć£ć¦å‡¦ē†ć‚’č”ŒćŖć£ćŸć‚Šč©¦č”ŒéŒÆčŖ¤ć‚’č”Œć„ć¾ć—ćŸćŒę”¹å–„ć›ćšć€ęœ€ēµ‚ēš„ć«ćÆåÆ¾åæœć—ćŸęœˆć®ēæŒęœˆć®ęœˆåˆć«ē¢ŗčŖć—ćŸéš›ć«Amazon QćŒē„”åŠ¹åŒ–ć•ć‚Œć¦ć„ć‚‹ć“ćØćŒć‚ć‹ć‚Šć¾ć—ćŸć€‚ć©ć†ć‚„ć‚‰čØ­å®šå¤‰ę›“ćŒå³ę™‚åę˜ ć•ć‚Œć‚‹ć‚ć‘ć§ćÆćŖćć€ęœˆå˜ä½ć§å‡¦ē†ć•ć‚Œć‚‹ä»•ę§˜ć®ć‚ˆć†ć§ć—ćŸć€‚ ēµ‚ć‚ć‚Šć« ä»Šå›žćÆć€Amazon Q in QuickSightć®åˆ©ē”Øć§ē™ŗē”Ÿć—ćŸć‚³ć‚¹ćƒˆć®å•é”Œć«ć¤ć„ć¦ć€åŽŸå› ćØåÆ¾åæœē­–ć‚’å…±ęœ‰ć—ć¾ć—ćŸć€‚ PoC自体はAmazon Qć®ęœ‰ē”Øę€§ć‚’ē¢ŗčŖć§ćć‚‹ęœ‰ę„ē¾©ćŖć‚‚ć®ć§ć—ćŸć€‚ć“ć®ēµŒéØ“ć‚’ē„”é§„ć«ć›ćšć€ä»Šå¾ŒćÆć‚³ć‚¹ćƒˆē®”ē†ć®ćƒ—ćƒ­ć‚»ć‚¹ć‚’å¼·åŒ–ć—ć€ć‚ˆć‚ŠčØˆē”»ēš„ćŖęŠ€č”“ę¤œčØ¼ć‚’é€²ć‚ć¦ć„ć‘ć‚Œć°ćØč€ƒćˆć¦ć„ć¾ć™ć€‚ęœ¬čØ˜äŗ‹ćŒć€åŒę§˜ć®čŖ²é”Œć«ē›“é¢ć™ć‚‹åÆčƒ½ę€§ć®ć‚ć‚‹ę–¹ć€…ć®å‚č€ƒć«ćŖć‚Œć°å¹øć„ć§ć™ć€‚
ćÆć˜ć‚ć« ć‚Øćƒ–ćƒŖćƒ¼ć§ ćƒ˜ćƒ«ć‚·ć‚« ć®ć‚µćƒ¼ćƒćƒ¼ć‚µć‚¤ćƒ‰ć®é–‹ē™ŗć‚’ć—ć¦ć„ć‚‹ čµ¤å· 恧恙怂 ćƒ˜ćƒ«ć‚·ć‚«ć§ćÆć€APIć‚µćƒ¼ćƒćƒ¼ć®ē›£č¦–ć‚’Sentryć‚’ē”Øć„ć¦č”Œć£ć¦ćŠć‚Šć€é–‹ē™ŗäø­ć®ćƒ—ćƒƒć‚·ćƒ„é€šēŸ„åŸŗē›¤ć«ć‚‚Sentryć‚’å°Žå…„ć™ć‚‹ć“ćØć«ćŖć‚Šć¾ć—ćŸć€‚é€šēŸ„åŸŗē›¤ćŒäø€åŗ¦ć«å‡¦ē†ć™ć‚‹ćƒ‡ćƒ¼ć‚æé‡ćÆć€ćƒ¦ćƒ¼ć‚¶ćƒ¼ć®ę•°ć«åÆ¾ć—ć¦ē·šå½¢ć«å¤šććŖć£ć¦ć„ćć¾ć™ć€‚ćć®ćŸć‚ć€ć©ć®ć‚ˆć†ć«ē›£č¦–ć‚’č”Œć†ć‹ćÆé‡č¦ćŖę¤œčØŽäŗ‹é …ćØćŖć‚Šć¾ć™ć€‚ ęœ¬čØ˜äŗ‹ć§ćÆć€ä»Šå›žå®Ÿč£…ć—ćŸćƒ—ćƒƒć‚·ćƒ„é€šēŸ„åŸŗē›¤ć®ć‚¢ćƒ©ćƒ¼ćƒˆčØ­čØˆć«ćŠć‘ć‚‹č‡Ŗåˆ†ć®č€ƒćˆć«ć¤ć„ć¦čæ°ć¹ć‚ˆć†ćØę€ć„ć¾ć™ć€‚ čƒŒę™Æ ćƒ˜ćƒ«ć‚·ć‚«ć®ćƒ—ćƒƒć‚·ćƒ„é€šēŸ„åŸŗē›¤ćÆć€AWS LambdaがFirebase Cloud Messagingć«ćƒ”ćƒƒć‚»ćƒ¼ć‚øć‚’é€ć‚‹ę§‹ęˆć«ćŖć£ć¦ćŠć‚Šć€ć¾ćØć¾ć£ćŸćƒ”ćƒƒć‚»ćƒ¼ć‚øć®é›†åˆć‚’forę–‡ć§å‡¦ē†ć—ć¦ć„ć¾ć™ć€‚é€šåøøć®å®Ÿč£…ć§ćÆć€ć‚Øćƒ©ćƒ¼ćŒē™ŗē”Ÿć—ćŸę™‚ē‚¹ć§å³åŗ§ć«ćƒ«ćƒ¼ćƒ—ć‚’åœę­¢ć™ć‚‹ć“ćØć‚‚ć—ć°ć—ć°ć§ć™ćŒć€ćƒ—ćƒƒć‚·ćƒ„é€šēŸ„ć§ćÆ ć§ćć‚‹ć ć‘å¤šćć®ćƒ¦ćƒ¼ć‚¶ćƒ¼ć«é€šēŸ„ć‚’å±Šć‘ć‚‹ ć“ćØćŒé‡č¦ć§ć™ć€‚ćć®ćŸć‚ć€äø€éƒØć®ćƒ”ćƒƒć‚»ćƒ¼ć‚øć§ć‚Øćƒ©ćƒ¼ćŒē™ŗē”Ÿć—ć¦ć‚‚ć€ć™ć¹ć¦ć®ćƒ”ćƒƒć‚»ćƒ¼ć‚øć«åÆ¾ć—ć¦å‡¦ē†ć‚’ē¶™ē¶šć™ć‚‹čØ­čØˆć‚’ęŽ”ē”Øć—ć¦ć„ć¾ć™ć€‚ć—ć‹ć—ć“ć®čØ­čØˆć§å˜ē“”ć«ć‚Øćƒ©ćƒ¼ć”ćØć«ć‚¢ćƒ©ćƒ¼ćƒˆć‚’å‡ŗć—ć¦ć—ć¾ć†ćØć€äø€åŗ¦ć«å‡ŗć‚‹ć‚¢ćƒ©ćƒ¼ćƒˆć®ę•°ćŒč†Øå¤§ć«ćŖć‚‹å±é™ŗćŒć‚ć‚Šć¾ć™ć€‚ ć‚¢ćƒ©ćƒ¼ćƒˆčØ­čØˆć®ć‚¢ćƒ—ćƒ­ćƒ¼ćƒ ć“ć‚Œć‚‰ć®čŖ²é”Œć‚’č§£ę±ŗć™ć‚‹ćŸć‚ć€ä»„äø‹ć®2ć¤ć®ę–¹é‡ć§ć‚¢ćƒ©ćƒ¼ćƒˆčØ­čØˆć‚’č”Œć„ć¾ć—ćŸć€‚ć“ć‚Œć‚‰ćÆć©ć”ć‚‰ć‹1ć¤ćØć„ć†ć‚‚ć®ć§ćÆćŖćć€ē›øäŗ’ć«é–¢é€£ć—åˆć†ć‚‚ć®ć ćØč€ƒćˆć¦ć„ć¾ć™ć€‚ é©åˆ‡ćŖć‚Øćƒ©ćƒ¼ć®ć‚°ćƒ«ćƒ¼ćƒ”ćƒ³ć‚° : ć‚Øćƒ©ćƒ¼ć®ēØ®é”žć”ćØć«é©åˆ‡ć«åˆ†é”žć—ć€åŒć˜ēØ®é”žć®ć‚Øćƒ©ćƒ¼ć‚’äø€ć¤ć®Issueć«ć¾ćØć‚ć‚‹ć“ćØć§ć€å•é”Œć®å…Øä½“åƒć‚’ęŠŠę”ć—ć‚„ć™ćć™ć‚‹ ć‚¢ćƒ©ćƒ¼ćƒˆę•°ć®ęœ€å°åŒ– : ć‚Øćƒ©ćƒ¼ćŒå¤§é‡ć«ē™ŗē”Ÿć—ć¦ć‚‚ć€ć‚¢ćƒ©ćƒ¼ćƒˆć®ę•°ćÆęœ€å°é™ć«ęŠ‘ćˆć‚‹ é©åˆ‡ćŖć‚Øćƒ©ćƒ¼ć®ć‚°ćƒ«ćƒ¼ćƒ”ćƒ³ć‚° Sentry恮Issueć‚°ćƒ«ćƒ¼ćƒ”ćƒ³ć‚°ć®ä»•ēµ„ćæ Sentryは Issue ćØć„ć†å˜ä½ć§ć‚Øćƒ©ćƒ¼ć‚’ć‚°ćƒ«ćƒ¼ćƒ”ćƒ³ć‚°ć—ć¾ć™ć€‚Issueć‚’č­˜åˆ„ć™ć‚‹ć®ćŒ fingerprint ćØć„ć†ć‚­ćƒ¼ć§ć€é€šåøøćÆć‚Øćƒ©ćƒ¼ćƒ”ćƒƒć‚»ćƒ¼ć‚øć‚„ć‚¹ć‚æćƒƒć‚Æćƒˆćƒ¬ćƒ¼ć‚¹ć‹ć‚‰č‡Ŗå‹•ē”Ÿęˆć•ć‚Œć¾ć™ć€‚ å¤§č¦ęØ”ćŖćƒ—ćƒ­ć‚øć‚§ć‚Æćƒˆć§ćÆć€ć‚Øćƒ©ćƒ¼ć®ēØ®é”žćŒå¤šę§˜ć§fingerprintć‚’č‡Ŗå‰ć§ē®”ē†ć™ć‚‹ć“ćØćÆē¾å®Ÿēš„ć§ćÆć‚ć‚Šć¾ć›ć‚“ć€‚åÆ¾ć—ć¦ä»Šå›žć®Lambdać®å®Ÿč£…ćÆå°č¦ęØ”ć§ć€ć‚Øćƒ©ćƒ¼ć®ēØ®é”žć‚‚é™å®šēš„ć§ć™ć€‚ćć®ćŸć‚ć€fingerprintć‚’ę˜Žē¤ŗēš„ć«ē®”ē†ć™ć‚‹ć“ćØć§ć€å®‰å®šć—ćŸć‚°ćƒ«ćƒ¼ćƒ”ćƒ³ć‚°ć‚’å®Ÿē¾ć—ć¾ć—ćŸć€‚ // fingerprintの設定 type ErrorKey string // 例 const ( ErrorKeyFCMSend ErrorKey = "fcm_send_failed" ErrorKeyDDBSaveRequest ErrorKey = "ddb_save_request_failed" ) sentry-go ćƒ‘ćƒƒć‚±ćƒ¼ć‚øć§ćÆć€ sentry.WithScope 内で SetFingerprint を使うことでfingerprintć‚’čØ­å®šć§ćć¾ć™ć€‚ import "github.com/getsentry/sentry-go" sentry.WithScope( func (scope *sentry.Scope) { // fingerprintの設定 // fingerprintć®åž‹ćÆ`[]string`ć§ć€č¤‡ę•°čØ­å®šć‚‚åÆčƒ½ scope.SetFingerprint(fingerprint) // Sentryć«ć‚Øćƒ©ćƒ¼ć‚’é€äæ” sentry.CaptureException(err) }) ć‚Øćƒ©ćƒ¼ćƒ”ćƒƒć‚»ćƒ¼ć‚øć®ę”¹å–„ ćć®ä»–ć€å¤–éƒØSDKć‚’å¤šē”Øć—ć¦ć„ć‚‹ćŸć‚ć€ć‚Øćƒ©ćƒ¼ćƒ”ćƒƒć‚»ćƒ¼ć‚øć ć‘ć§ćÆä½•ćŒčµ·ććŸć®ć‹åˆ†ć‹ć‚‰ćŖć„å “åˆćŒć‚ć‚Šć¾ć™ć€‚ćć®ćŸć‚ć€ć‚Øćƒ©ćƒ¼ćƒ”ćƒƒć‚»ćƒ¼ć‚øć« ErrorKey ć‚’ćƒ—ćƒ¬ćƒ•ć‚£ćƒƒć‚Æć‚¹ćØć—ć¦ä»˜äøŽć—ć€ć‚Øćƒ©ćƒ¼ć®ēØ®é”žć‚’ę˜Žē¢ŗć«ć—ć¾ć—ćŸć€‚ ć‚¢ćƒ©ćƒ¼ćƒˆę•°ć®ęœ€å°åŒ– å‰čæ°ć®ćØćŠć‚Šć€ć‚Øćƒ©ćƒ¼ć®ćŸć³ć«ć‚¢ćƒ©ćƒ¼ćƒˆć‚’å‡ŗć—ć¦ć„ć¦ćÆć‚­ćƒŖćŒć‚ć‚Šć¾ć›ć‚“ć€‚ćć“ć§ć€forćƒ«ćƒ¼ćƒ—å†…ć§ćÆć‚Øćƒ©ćƒ¼ć‚’čØ˜éŒ²ć™ć‚‹ć ć‘ć§ć‚¢ćƒ©ćƒ¼ćƒˆćÆé€äæ”ć›ćšć€å‡¦ē†å®Œäŗ†å¾Œć«1å›žć ć‘é›†ē“„ć—ćŸć‚Øćƒ©ćƒ¼ć‚’Sentryć«é€äæ”ć™ć‚‹ę–¹å¼ć‚’ęŽ”ē”Øć—ć¾ć—ćŸć€‚ åˆęœŸę”ˆ: errors.Join ęœ€åˆć«č€ƒćˆćŸć®ćÆ errors.Join ć‚’ä½æē”Øć™ć‚‹ę–¹ę³•ć§ć—ćŸć€‚ var aggErr error = nil for _, message := range messages { err := message.Send() aggErr = errors.Join(aggErr, err) } return aggErr ć—ć‹ć—ć€ć“ć®ę–¹ę³•ć«ćÆå•é”ŒćŒć‚ć‚Šć¾ć™ć€‚ä¾‹ćˆć°ć€10ä»¶ć®ćƒ”ćƒƒć‚»ćƒ¼ć‚øćŒ2ć¤ć®ćƒćƒƒćƒć§å‡¦ē†ć•ć‚Œć€ćć‚Œćžć‚Œ3件と2ä»¶ćŒå¤±ę•—ć—ćŸå “åˆćÆä»„äø‹ć®ć‚ˆć†ćŖć‚Øćƒ©ćƒ¼ć«ćŖć‚Šć¾ć™ć€‚ // 惐惃惁1(3件失敗) failed to send message failed to send message failed to send message // 惐惃惁2(2件失敗) failed to send message failed to send message fingerprintć§ć‚°ćƒ«ćƒ¼ćƒ”ćƒ³ć‚°ćÆć§ćć¾ć™ćŒć€ć‚Øćƒ©ćƒ¼ćƒ”ćƒƒć‚»ćƒ¼ć‚øćŒå†—é•·ć§ć€å½±éŸæåŗ¦ć®ęŠŠę”ćŒå›°é›£ć§ć™ć€‚ ę”¹å–„ę”ˆ: ć‚Øćƒ©ćƒ¼é›†ē“„ę§‹é€ ä½“ ćć“ć§ä»Šå›žćÆć€ć‚Øćƒ©ćƒ¼é›†ē“„å°‚ē”Øć®ę§‹é€ ä½“ć‚’čØ­čØˆć—ć¾ć—ćŸć€‚ type ErrorSample struct { RequestID string NotificationType string Error error } type ErrorAggregate struct { Count int Sample ErrorSample } type ErrorAggregates map [ErrorKey]*ErrorAggregate ć“ć®ę§‹é€ ä½“ć«ć‚ˆć‚Šć€ć‚Øćƒ©ćƒ¼ć®ē™ŗē”Ÿå›žę•°ć‚„č©³ē“°ć‚’ć‚’åŠ¹ēŽ‡ēš„ć«ē®”ē†ć§ćć¾ć™ć€‚ę—¢ć«ć‚°ćƒ«ćƒ¼ćƒ”ćƒ³ć‚°ćÆå®Ÿē¾ć§ćć¦ć„ć‚‹ćŸć‚ć€ć‚µćƒ³ćƒ—ćƒ«ćÆć²ćØć¾ćš1ä»¶ć§ååˆ†ć ćØć„ć†č€ƒćˆć®ć‚‚ćØę§‹é€ ä½“ć‚’å®šē¾©ć—ć¦ć„ć¾ć™ć€‚ ćƒ”ć‚½ćƒƒćƒ‰ć®å®Ÿč£… 構造体には仄下の3ć¤ć®ćƒ”ć‚½ćƒƒćƒ‰ć‚’å®Ÿč£…ć—ć¾ć—ćŸć€‚ AddSample : ć‚µćƒ³ćƒ—ćƒ«ćŒęœŖčØ­å®šć®å “åˆć®ćæć€ć‚Øćƒ©ćƒ¼ć‚µćƒ³ćƒ—ćƒ«ć‚’äæå­˜ Increment : ć‚Øćƒ©ćƒ¼ć‚«ć‚¦ćƒ³ćƒˆć‚’1増加 Emit : é›†ē“„ēµęžœć‚’ć‚Øćƒ©ćƒ¼ēØ®åˆ„ć”ćØć«1ć‚¤ćƒ™ćƒ³ćƒˆć ć‘Sentryに送俔 Emit ćÆå¤§ä½“ä»„äø‹ć®ć‚ˆć†ć«å®Ÿč£…ć—ć¦ć„ć¾ć™ć€‚Sentry恮Tagć‚’ę“»ē”Øć™ć‚‹ć“ćØć§ć€å½±éŸæåŗ¦ć‚„é€šēŸ„ēØ®åˆ„ćŖć©ć‚’čæ½ćˆć‚‹ć‚ˆć†ć«ć—ć¾ć—ćŸć€‚ func (m ErrorAggregates) Emit() { for errKey, agg := range m { tags := map [ string ] string { "error_key" : string (errKey), "count" : strconv.Itoa(agg.Count), "sample_request_id" : agg.Sample.RequestID, "sample_notification_type" : agg.Sample.NotificationType, } CaptureErrorWithContext( agg.Sample.Error, tags, [] string { "push_to_fcm" , string (errKey)}, ) } } まとめ ä»Šå›žć®ć‚¢ćƒ©ćƒ¼ćƒˆčØ­čØˆć«ć‚ˆć‚Šć€ä»„äø‹ć‚’å®Ÿē¾ć—ć¾ć—ćŸć€‚ ć‚¢ćƒ©ćƒ¼ćƒˆę•°ć®å¤§å¹…å‰Šęø› : ć‚Øćƒ©ćƒ¼ćŒå¤§é‡ē™ŗē”Ÿć—ć¦ć‚‚ć€ć‚¢ćƒ©ćƒ¼ćƒˆę•°ćÆęœ€å°é™ć«ęŠ‘åˆ¶ å•é”Œć®čæ…é€ŸćŖē‰¹å®š : ć‚Øćƒ©ćƒ¼ć®ēØ®é”žćØå½±éŸæåŗ¦ć‚’äø€ē›®ć§ęŠŠę”åÆčƒ½ ć‚·ć‚¹ćƒ†ćƒ ē›£č¦–ćÆåŗƒå¤§ćŖåˆ†é‡Žć§ć‚ć‚Šć€č‡Ŗåˆ†ć‚‚ć¾ć ć¾ć ēŸ„ć‚‰ćŖć„ć“ćØćŒå¤šćć‚ć‚Šć¾ć™ć€‚ä»Šå¾Œć‚‚ć‚ˆć‚Šč‰Æć„ć‚¢ćƒ©ćƒ¼ćƒˆčØ­čØˆć‚’čæ½ę±‚ć—ć¦ć„ć‘ćŸć‚‰ćØę€ć„ć¾ć™ć€‚ ęœ€å¾Œć¾ć§ćŠčŖ­ćæć„ćŸć ćć‚ć‚ŠćŒćØć†ć”ć–ć„ć¾ć—ćŸć€‚ å‚č€ƒę–‡ēŒ® getsentry/sentry-go: The official Go SDK for Sentry (sentry.io) Getting Started With Sentry Sentry恮Issue Groupingの仕組みについて解説 #sentry - Qiita
ćÆć˜ć‚ć« こんにごは! 2025å¹“ć®å¤ć€ę Ŗå¼ä¼šē¤¾ć‚Øćƒ–ćƒŖćƒ¼ć§1ćƒ¶ęœˆé–“ć‚¤ćƒ³ć‚æćƒ¼ćƒ³ć‚·ćƒƒćƒ—ć«å‚åŠ ć—ć¦ć„ć‚‹åøøęœØę³°ęˆć§ć™ć€‚é…å±žć•ć‚ŒćŸć®ćÆć€å›½å†…ęœ€å¤§ē“šć®ćƒ¬ć‚·ćƒ”å‹•ē”»ćƒ”ćƒ‡ć‚£ć‚¢ć€Œćƒ‡ćƒŖćƒƒć‚·ćƒ„ć‚­ćƒƒćƒćƒ³ć€ć®ćƒ‡ćƒ¼ć‚æć‚’ę“»ē”Øć—ć¦é£Ÿć®ćƒˆćƒ¬ćƒ³ćƒ‰ć‚’åˆ†ęžć—ć€ćƒ”ćƒ¼ć‚«ćƒ¼ćƒ»å°å£²ć®ę„ę€ę±ŗå®šć‚’ć‚µćƒćƒ¼ćƒˆć™ć‚‹ć€Œćƒ‡ćƒŖćƒƒć‚·ćƒ„ćƒŖć‚µćƒ¼ćƒć€éƒØé–€ć§ć™ć€‚ 恓恮1ćƒ¶ęœˆé–“ć§ē§ćŒå–ć‚Šēµ„ć‚“ć ć®ćÆć€ å‹•ē”»č¦–č“ćƒ‡ćƒ¼ć‚æć‚’ē”Øć„ćŸę–°ćŸćŖćƒˆćƒ¬ćƒ³ćƒ‰åˆ†ęžę©Ÿčƒ½ć®é–‹ē™ŗ ć§ć™ć€‚ć“ć®čØ˜äŗ‹ć§ćÆć€ćƒ¦ćƒ¼ć‚¶ćƒ¼ć®éš ć‚ŒćŸćƒ‹ćƒ¼ć‚ŗć‹ć‚‰ę©Ÿčƒ½é–‹ē™ŗć®čƒŒę™Æć€ćć—ć¦äŗŗē”Ÿåˆć®SQLćØę ¼é—˜ć—ćŖćŒć‚‰ćƒ‡ćƒ¼ć‚æć‚’åÆč¦–åŒ–ć™ć‚‹ć¾ć§ć®ęŠ€č”“ēš„ćŖč£å“ć‚’ä¼ćˆć¦ć„ć‘ć‚Œć°ćØę€ć„ć¾ć™ć€‚ čƒŒę™Æć€€ćŖćœć€Œå‹•ē”»č¦–č“ćƒ‡ćƒ¼ć‚æć€ć®åˆ†ęžćŒåæ…č¦ć ć£ćŸć®ć‹ļ¼Ÿ ćƒ‡ćƒŖćƒƒć‚·ćƒ„ć‚­ćƒƒćƒćƒ³ć‚¢ćƒ—ćƒŖć®ę¤œē“¢ćƒ‡ćƒ¼ć‚æćƒ»ćƒ¬ć‚·ćƒ”é–²č¦§ćƒ‡ćƒ¼ć‚æć‚’ć‚‚ćØć«ć€ę¶ˆč²»č€…ć®é£Ÿćƒˆćƒ¬ćƒ³ćƒ‰ć‚„čˆˆå‘³é–¢åæƒć‚’åˆ†ęžć§ćć‚‹ćƒ„ćƒ¼ćƒ«ć§ć™ć€‚ę¶ˆč²»č€…åˆ†ęžć‚„čŖæęŸ»ćŖć©ć®å·„ę•°å‰Šęø›ć‚„ć€ćŠå®¢ę§˜ć®ćƒ‹ćƒ¼ć‚ŗć‚’čøć¾ćˆćŸå•†å“é–‹ē™ŗļ¼č²©äæƒć‚’å®Ÿē¾ć§ćć€ę–½ē­–ć®ē¢ŗåŗ¦ć‚‚é«˜ć‚ć‚‹ć“ćØćŒåÆčƒ½ć§ć™ć€‚ ć™ć§ć«ę¤œē“¢é »åŗ¦ćƒ©ćƒ³ć‚­ćƒ³ć‚°ćŖć©ć®ę©Ÿčƒ½ćÆå­˜åœØć—ć¦ć„ć¾ć—ćŸćŒć€ę—¢å­˜ćƒ¦ćƒ¼ć‚¶ćƒ¼ćøć®ćƒ’ć‚¢ćƒŖćƒ³ć‚°ć‹ć‚‰ć€ć•ć‚‰ć«ę·±ć„ć‚¤ćƒ³ć‚µć‚¤ćƒˆć‚’ę±‚ć‚ć‚‹å£°ćŒę˜Žć‚‰ć‹ć«ćŖć£ć¦ćć¾ć—ćŸć€‚ ćƒ¦ćƒ¼ć‚¶ćƒ¼ćŒęœ¬å½“ć«ēŸ„ć‚ŠćŸć‹ć£ćŸć“ćØ: ć€Œä»Šé€±ć€ć€Žé¶č‚‰ć€ć®ę¤œē“¢ćŒå¢—ćˆćŸć€ćØć„ć†äŗ‹å®Ÿļ¼ˆ 点 ļ¼‰ć ć‘ć§ćŖćć€ć€ŒćŖćœå¢—ćˆćŸć®ć‹ļ¼Ÿå…·ä½“ēš„ć«ć©ć‚“ćŖ é¶č‚‰ę–™ē† ļ¼ˆć‚øćƒ£ćƒ³ćƒ«ļ¼‰ćŒäŗŗę°—ćŖć®ć‹ļ¼Ÿć€ļ¼ˆ 線 ļ¼‰ć‚’ēŸ„ć‚ŠćŸć„ć€‚ ē‰¹å®šć®ćƒ¬ć‚·ćƒ”ćŒäŗŗę°—ćŖē†ē”±ć€ä¾‹ćˆć°ć€Œć“ć®ćƒćƒ³ćƒćƒ¼ć‚°ćŒäŗŗę°—ćŖć®ćÆć€ "ę™‚ēŸ­" 悄 "節瓄" ćØć„ć£ćŸć‚æć‚°ćŒéŸæć„ć¦ć„ć‚‹ć‹ć‚‰ć§ćÆćŖć„ć‹ļ¼Ÿć€ćØć„ć†čƒŒę™Æļ¼ˆ 面 ļ¼‰ć‚’ę‰ćˆćŸć„ć€‚ ę¶ˆč²»č€…ćŒć€Œä»Šć€ęœ¬å½“ć«ä½œć‚ŠćŸć„ę–™ē†ć€ć®ćƒˆćƒ¬ćƒ³ćƒ‰ć‚’ć€ć‚ˆć‚Šč§£åƒåŗ¦é«˜ćęŠŠę”ć—ćŸć„ć€‚ ć“ć‚Œć‚‰ć®ćƒ‹ćƒ¼ć‚ŗćÆć€å˜äø€ć®ćƒ¬ć‚·ćƒ”äŗŗę°—ć‚’čæ½ć†ć ć‘ć§ćÆęŗ€ćŸć›ć¾ć›ć‚“ć€‚ćć“ć§åæ…č¦ć«ćŖć£ć¦ćć‚‹ć®ćŒ å‹•ē”»č¦–č“ćƒ‡ćƒ¼ć‚æ ćØćƒ¬ć‚·ćƒ”ć«ä»˜äøŽć•ć‚ŒćŸ タグ 恧恙怂 ćƒ¦ćƒ¼ć‚¶ćƒ¼ćŒć©ć®ćƒ¬ć‚·ćƒ”å‹•ē”»ć‚’ć„ć¤č¦–č“ć—ćŸć‹ć€ćć—ć¦ćć®ćƒ¬ć‚·ćƒ”ć«ć©ć‚“ćŖć‚æć‚°ļ¼ˆä¾‹ļ¼šć€Œ#ē°”å˜ć€ć€Œ#é¶ć‚€ć­č‚‰ć€ć€Œ#ćŠå¼å½“ć€ļ¼‰ćŒä»˜ć„ć¦ć„ć‚‹ć‹ć‚’ęŽ›ć‘åˆć‚ć›ć‚‹ć“ćØć§ć€ ē‰¹å®šć®ć‚æć‚°ć‚’ęŒć¤ćƒ¬ć‚·ćƒ”ē¾¤ćŒć€ć„ć¤ć€ć©ć‚Œćć‚‰ć„č¦‹ć‚‰ć‚Œć¦ć„ć‚‹ć‹ ć‚’ę™‚ē³»åˆ—ć§åÆč¦–åŒ–ć§ćć¾ć™ć€‚ć“ć‚Œć«ć‚ˆć‚Šć€ć€Œē‚¹ć€ć®åˆ†ęžć‹ć‚‰č„±å“ć—ć€ćƒˆćƒ¬ćƒ³ćƒ‰ć®ć€Œē·šć€ćØć€Œé¢ć€ć‚’ę‰ćˆć‚‹ć‚¤ćƒ³ć‚µć‚¤ćƒˆć‚’ęä¾›ć§ćć‚‹ćØč€ƒćˆć¾ć—ćŸć€‚ ć“ć®ä»®čŖ¬ć«åŸŗć„ćć€ ęŒ‡å®šć—ćŸć‚æć‚°ć«åˆč‡“ć™ć‚‹ćƒ¬ć‚·ćƒ”ć®č¦–č“ćƒˆćƒ¬ćƒ³ćƒ‰ć‚’ę™‚ē³»åˆ—ć‚°ćƒ©ćƒ•ć§åÆč¦–åŒ–ć—ć€ęœŸé–“å†…ć§ē‰¹ć«äŗŗę°—ć ć£ćŸćƒ¬ć‚·ćƒ”ć‚’ćƒ©ćƒ³ć‚­ćƒ³ć‚°å½¢å¼ć§ęē¤ŗć™ć‚‹ ć€ćØć„ć†ę–°ę©Ÿčƒ½ć®é–‹ē™ŗćƒ—ćƒ­ć‚øć‚§ć‚ÆćƒˆćŒć‚¹ć‚æćƒ¼ćƒˆć—ć¾ć—ćŸć€‚ ćƒ‡ćƒ¼ć‚æåŠ å·„ć€€åˆć‚ć¦ć®SQL ä»Šå›žć®é–‹ē™ŗć§ćÆć€ćƒ‡ćƒ¼ć‚æåŸŗē›¤ćØć—ć¦ Databricks ć‚’åˆ©ē”Øć—ć¾ć—ćŸć€‚ćƒ‡ćƒŖćƒƒć‚·ćƒ„ć‚­ćƒƒćƒćƒ³ć‹ć‚‰é€ć‚‰ć‚Œć¦ćć‚‹č†Øå¤§ćŖč¦–č“ćƒ­ć‚°ćƒ‡ćƒ¼ć‚æć‚’ć€SQLć‚’ä½æć£ć¦ęŠ½å‡ŗćƒ»åŠ å·„ć—ć€BIćƒ„ćƒ¼ćƒ«ć§ć‚ć‚‹Amazon QuickSightć§ćƒ“ć‚øćƒ„ć‚¢ćƒ«ć‚’ä½œęˆć™ć‚‹ć®ćŒäø»ćŖęµć‚Œć§ć™ć€‚ ć“ć“ć§ē§ć®å‰ć«ē«‹ć”ćÆć ć‹ć£ćŸć®ćŒć€ äŗŗē”Ÿåˆć®SQL ć§ć—ćŸć€‚ęœ€åˆćÆSELECT * FROM ...ć™ć‚‰ćŠć¼ć¤ć‹ćŖć„ēŠ¶ę…‹ć§ć—ćŸćŒć€ć‚Øćƒ–ćƒŖćƒ¼ć«ä»£ć€…å—ć‘ē¶™ćŒć‚Œć‚‹ć€ŒSQLć‚‚ćć‚‚ćč³‡ę–™ć€ćØć€ćƒ”ćƒ³ć‚æćƒ¼ć®ē†±åæƒćŖć”ęŒ‡å°Žć®ćŠć‹ć’ć§ć€ęœ€ēµ‚ēš„ć«ćÆęœ€ä½Žé™ć®ćƒŖćƒ•ć‚”ćƒ¬ćƒ³ć‚¹č¾¼ćæć§č¤‡é›‘ćŖć‚Æć‚ØćƒŖć‚’čŖ­ćæę›øćć§ćć‚‹ćƒ¬ćƒ™ćƒ«ć«ęˆé•·ć§ćć¾ć—ćŸļ¼ SQL恋态Python(Pandas)ć‹ļ¼Ÿ ćƒ‡ćƒ¼ć‚æåŠ å·„ćØčžć„ć¦ć€ęœ€åˆćÆć€ŒSQLć‚’ä½æē”Øć—ćŖćć¦ć‚‚ć€Python恮Pandasćƒ©ć‚¤ćƒ–ćƒ©ćƒŖć§ć‚‚åŒć˜ć“ćØćŒć§ćć‚‹ć®ć§ćÆļ¼Ÿć€ćØč€ƒćˆć¦ć„ć¾ć—ćŸć€‚ć—ć‹ć—ć€ć‚¤ćƒ³ć‚æćƒ¼ćƒ³ć‚’é€šć˜ć¦ć€å¤§č¦ęØ”ćƒ‡ćƒ¼ć‚æć‚’ę‰±ć†äøŠć§ć®SQLć®å„Ŗä½ę€§ć‚’ę„Ÿć˜ć‚‹ć“ćØćŒä½•åŗ¦ć‚‚ć‚ć‚Šć¾ć—ćŸć€‚ ćƒ‡ćƒ¼ć‚æé›†čØˆćØć‚Æć‚ØćƒŖć®ęœ€é©åŒ– SQLćÆć€ćƒ‡ćƒ¼ć‚æćƒ™ćƒ¼ć‚¹ć‹ć‚‰åæ…č¦ćŖćƒ‡ćƒ¼ć‚æć‚’åŠ¹ēŽ‡ēš„ć«ęŠ½å‡ŗćƒ»é›†čØˆć™ć‚‹ćŸć‚ć«ęœ€é©åŒ–ć•ć‚ŒćŸčØ€čŖžć§ć™ć€‚GROUP BY, JOIN, WHEREćØć„ć£ćŸå‘½ä»¤ćÆć€ćƒ‡ćƒ¼ć‚æćƒ™ćƒ¼ć‚¹ć‚Øćƒ³ć‚øćƒ³ćŒęœ€ć‚‚åŠ¹ēŽ‡ēš„ćŖå®Ÿč”ŒčØˆē”»ć‚’ē«‹ć¦ć¦å‡¦ē†ć—ć¦ćć‚Œć‚‹ćŸć‚ć€ćƒ†ćƒ©ćƒć‚¤ćƒˆē“šć®ćƒ‡ćƒ¼ć‚æć«åÆ¾ć—ć¦ć‚‚éžåøøć«é«˜é€Ÿć§ć™ć€‚äø€ę–¹ć€PandasćÆćƒ‡ćƒ¼ć‚æć‚’äø€åŗ¦ćƒ”ćƒ¢ćƒŖć«å…Øć¦čŖ­ćæč¾¼ć‚“ć§ć‹ć‚‰å‡¦ē†ć™ć‚‹ćŸć‚ć€ćƒ‡ćƒ¼ć‚æć‚µć‚¤ć‚ŗćŒå¤§ćććŖć‚‹ćØćƒ”ćƒ¢ćƒŖäøč¶³ć«é™„ć£ćŸć‚Šć€ćƒ‘ćƒ•ć‚©ćƒ¼ćƒžćƒ³ć‚¹ćŒč‘—ć—ćä½Žäø‹ć—ćŸć‚Šć™ć‚‹åÆčƒ½ę€§ćŒć‚ć‚Šć¾ć™ć€‚ ćƒ‡ćƒ¼ć‚æć®ę•“åˆę€§ ćƒ‡ćƒ¼ć‚æćƒ™ćƒ¼ć‚¹ć§ćÆć€ć‚¹ć‚­ćƒ¼ćƒžå®šē¾©ļ¼ˆPRIMARY KEY, FOREIGN KEYćŖć©ļ¼‰ć«ć‚ˆć£ć¦ć€ćƒ‡ćƒ¼ć‚æć®é‡č¤‡ć‚„äøę­£ćŖå€¤ć‚’ć‚·ć‚¹ćƒ†ćƒ ćƒ¬ćƒ™ćƒ«ć§é˜²ć„ć§ćć‚Œć¾ć™ć€‚ć“ć‚Œć«ć‚ˆć‚Šć€ćƒ‡ćƒ¼ć‚æć®å“č³ŖćŒę‹…äæć•ć‚Œć€ć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³å“ćÆå®‰åæƒć—ć¦ćƒ‡ćƒ¼ć‚æć‚’ę‰±ćˆć¾ć™ć€‚ć‚‚ć—ć“ć‚Œć‚’Pythonć®ć‚³ćƒ¼ćƒ‰ć ć‘ć§å®Ÿē¾ć—ć‚ˆć†ćØć™ć‚‹ćØć€ć‚ć‚‰ć‚†ć‚‹å‡¦ē†ć«ę•“åˆę€§ćƒć‚§ćƒƒć‚Æć®ćƒ­ć‚øćƒƒć‚Æć‚’č‡Ŗå‰ć§å®Ÿč£…ć™ć‚‹åæ…č¦ćŒć‚ć‚Šć€éžåøøć«ē…©é›‘ć«ćŖć‚Šć¾ć™ć€‚ åŠ¹ēŽ‡ēš„ćŖå·®åˆ†ę›“ę–°ćØćƒˆćƒ©ćƒ³ć‚¶ć‚Æć‚·ćƒ§ćƒ³ ä»Šå›žć®ćƒ—ćƒ­ć‚øć‚§ć‚Æćƒˆć§ćÆć€ęÆŽę—„å¢—ćˆē¶šć‘ć‚‹č¦–č“ćƒ­ć‚°ćƒ‡ćƒ¼ć‚æć‚’åŠ¹ēŽ‡ēš„ć«é›†čØˆćƒ†ćƒ¼ćƒ–ćƒ«ćøåę˜ ć•ć›ć‚‹åæ…č¦ćŒć‚ć‚Šć¾ć—ćŸć€‚ć‚‚ć—Pandasć§å®Ÿč£…ć™ć‚‹ćŖć‚‰ć€ć€ŒéŽåŽ»ć®å…Øćƒ‡ćƒ¼ć‚æć‚’čŖ­ćæč¾¼ćæć€ä»Šę—„ć®ćƒ‡ćƒ¼ć‚æć‚’čæ½åŠ ć—ć€å…Øćƒ‡ćƒ¼ć‚æć‚’ę›øćå‡ŗć™ć€ćØć„ć†éžåŠ¹ēŽ‡ćŖå‡¦ē†ć«ćŖć‚ŠćŒć”ć§ć™ć€‚ć—ć‹ć—ć€DatabricksćŒęŽ”ē”Øć—ć¦ć„ć‚‹ Delta Lake ćØć„ć†ęŠ€č”“ćØć€ćć‚Œć‚’ę“ä½œć™ć‚‹PySparkć‚³ćƒ¼ćƒ‰ć‚’ä½æćˆć°ć€ć“ć®čŖ²é”Œć‚’č§£ę±ŗć§ćć¾ć™ć€‚ å®Ÿéš›ć«ę›øć„ćŸć‚³ćƒ¼ćƒ‰ćŒć“ć”ć‚‰ć§ć™ć€‚ # ęÆŽę—„å¢—åˆ†ć®ę›“ę–°ć‚’ć™ć‚‹ column_selected_filtered_data \ .write \ .format( "delta" ) \ .mode( "overwrite" ) \ .partitionBy( "event_date" ) \ .option( "replaceWhere" , f "event_date >= '{event_date}'" ) \ .save( "delta_table_path/sample_analytics_table" ) 注: å®Ÿéš›ć®é–‹ē™ŗć§ćÆć€å¼Šē¤¾å†…ć®ćƒ‡ćƒ¼ć‚æåŸŗē›¤ć«åˆć‚ć›ć¦ćƒ†ćƒ¼ćƒ–ćƒ«åć‚„ć‚¹ć‚­ćƒ¼ćƒžę§‹é€ ć‚’čŖæę•“ć—ć¦ć„ć¾ć™ć€‚ ć“ć®ć‚³ćƒ¼ćƒ‰ćÆć€å˜ć«ćƒ‡ćƒ¼ć‚æć‚’äøŠę›øćć—ć¦ć„ć‚‹ć®ć§ćÆć‚ć‚Šć¾ć›ć‚“ć€‚ partitionBy("event_date") : ć¾ćšć€ćƒ‡ćƒ¼ć‚æćŒ event_date ļ¼ˆę—„ä»˜ļ¼‰ć”ćØć«ē‰©ē†ēš„ć«åˆ†å‰²ć—ć¦äæå­˜ć•ć‚Œć¦ć„ć¾ć™ć€‚ option("replaceWhere", ...) : ćć—ć¦ć€ć“ć®ć‚Ŗćƒ—ć‚·ćƒ§ćƒ³ć§ć€Œ event_date ćŒęŒ‡å®šć—ćŸę—„ä»˜ä»„é™ć®ćƒ‡ćƒ¼ć‚æć®ćæć‚’ē½®ćę›ćˆć‚‹ć€ćØć„ć†ęŒ‡ē¤ŗć‚’å‡ŗć—ć¦ć„ć¾ć™ć€‚ ć“ć‚Œć«ć‚ˆć‚Šć€éŽåŽ»ć®č†Øå¤§ćŖćƒ‡ćƒ¼ć‚æć‚’äø€åˆ‡č§¦ć‚‹ć“ćØćŖćć€ē›“čæ‘ć®ćƒ‡ćƒ¼ć‚æć ć‘ć‚’ę›“ę–°ć§ćć¾ć™ć€‚ćƒ•ć‚”ć‚¤ćƒ«ćƒ™ćƒ¼ć‚¹ć§å‡¦ē†ć‚’č”Œć†Pandaså˜ä½“ć§ćÆć€åŒę§˜ć®åŠ¹ēŽ‡ę€§ćØå®‰å…Øę€§ć‚’ę‹…äæć™ć‚‹ć®ćÆéžåøøć«å›°é›£ć§ć™ć€‚ SQLć‚³ćƒ¼ćƒ‰č§£čŖ¬ ć‚¤ćƒ³ć‚æćƒ¼ćƒ³äø­ć«ę›øć„ćŸć€ē‰¹ć«å°č±”ēš„ć ć£ćŸć‚Æć‚ØćƒŖć®äø€éƒØć‚’ć”ē“¹ä»‹ć—ć¾ć™ć€‚ SELECT l.event_date, r.recipe_id, CONCAT(r.lead, r.title) AS title, r.image, r.tags, r.ingredients, r.category, r.taste FROM distinct_event_dates AS l CROSS JOIN recipe_master AS r WHERE r.advertiser_id IS NULL ć“ć®ć‚Æć‚ØćƒŖć®äø»å½¹ćÆ CROSS JOIN 恧恙怂SQLåˆå­¦č€…ć ć£ćŸē§ćÆęœ€åˆć“ć®ä»•ēµ„ćæć®ē†č§£ć«č‹¦ć—ćæć¾ć—ćŸć€‚ CROSS JOIN は、2ć¤ć®ćƒ†ćƒ¼ćƒ–ćƒ«ć®å…Øćƒ¬ć‚³ćƒ¼ćƒ‰ć‚’ē·å½“ćŸć‚Šć§ēµ„ćæåˆć‚ć›ć‚‹ēµåˆę–¹ę³•ć§ć™ć€‚ć“ć®ć‚Æć‚ØćƒŖć§ćÆć€ distinct_event_dates ļ¼ˆåˆ†ęžåÆ¾č±”ęœŸé–“ć®ę—„ä»˜ćƒŖć‚¹ćƒˆć‚’ęŒć¤ćƒ†ćƒ¼ćƒ–ćƒ«ļ¼‰ćØ recipe_master ļ¼ˆå…Øć¦ć®ćƒ¬ć‚·ćƒ”ęƒ…å ±ć‚’ęŒć¤ćƒžć‚¹ć‚æćƒ†ćƒ¼ćƒ–ćƒ«ļ¼‰ć‚’ęŽ›ć‘åˆć‚ć›ć¦ć„ć¾ć™ć€‚ ćŖćœć“ć‚“ćŖć“ćØć‚’ć™ć‚‹ć®ć‹ļ¼Ÿ ć“ć‚ŒćÆć€ć€Œåˆ†ęžęœŸé–“äø­ć®ć™ć¹ć¦ć®ę—„ä»˜ Ɨ ć™ć¹ć¦ć®ćƒ¬ć‚·ćƒ”ć€ć®ēµ„ćæåˆć‚ć›ć‚’ęŒć¤ć€å·Øå¤§ćŖ"čØ­čØˆå›³"ć®ć‚ˆć†ćŖćƒ™ćƒ¼ć‚¹ćƒ†ćƒ¼ćƒ–ćƒ«ć‚’ä½œęˆć™ć‚‹ćŸć‚ć§ć™ć€‚ ć“ć®å¾Œć€å®Ÿéš›ć®å‹•ē”»č¦–č“ćƒ­ć‚°ć‚’é›†čØˆć—ćŸå‹•ē”»č¦–č“å›žę•°ć®ćƒ†ćƒ¼ćƒ–ćƒ«ćØć“ć®ćƒ™ćƒ¼ć‚¹ćƒ†ćƒ¼ćƒ–ćƒ«ć‚’ LEFT JOIN ć—ć¾ć™ć€‚ć™ć‚‹ćØć€ćŸćØćˆē‰¹å®šć®ę—„ć«ć‚ć‚‹ćƒ¬ć‚·ćƒ”ćŒ1å›žć‚‚č¦–č“ć•ć‚ŒćŖć‹ć£ćŸćØć—ć¦ć‚‚ć€č¦–č“ę•°ć€Œ0ć€ć®ćƒ‡ćƒ¼ć‚æćØć—ć¦ćć”ć‚“ćØę®‹ć™ć“ćØćŒć§ćć‚‹ć®ć§ć™ć€‚ć“ć‚Œć«ć‚ˆć‚Šć€ę™‚ē³»åˆ—ć‚°ćƒ©ćƒ•ćŒé€”åˆ‡ć‚Œć‚‹ć“ćØćŖćć€ę­£ē¢ŗćŖćƒˆćƒ¬ćƒ³ćƒ‰åˆ†ęžćŒåÆčƒ½ć«ćŖć‚Šć¾ć™ć€‚ CROSS JOIN ćÆć€ę™‚ē³»åˆ—åˆ†ęžć«ćŠć‘ć‚‹ćƒ‡ćƒ¼ć‚æę¬ ęć‚’é˜²ććŸć‚ć®ć€éžåøøć«é‡č¦ćŖćƒ†ć‚Æćƒ‹ćƒƒć‚Æć§ć—ćŸć€‚ ćƒ‡ćƒ¼ć‚æć‚’"č¦‹ćˆć‚‹åŒ–"ć™ć‚‹ļ¼šAmazon QuickSightの擻用 ćƒ‡ćƒ¼ć‚æćƒ‘ć‚¤ćƒ—ćƒ©ć‚¤ćƒ³ć®ęœ€ēµ‚å·„ēØ‹ćÆć€åŠ å·„ć—ćŸćƒ‡ćƒ¼ć‚æć‚’ćƒ“ć‚øćƒć‚¹ć®ę„ę€ę±ŗå®šć«ē¹‹ć’ć‚‹ćŸć‚ć®ć€ŒåÆč¦–åŒ–ć€ć§ć™ć€‚ć“ć“ć§ę“»čŗć—ćŸć®ćŒć€AWSćŒęä¾›ć™ć‚‹BIć‚µćƒ¼ćƒ“ć‚¹ć€ Amazon QuickSight 恧恙怂 QuickSightは、Amazon Athena悄DatabricksćØć„ć£ćŸćƒ‡ćƒ¼ć‚æć‚½ćƒ¼ć‚¹ć«ē›“ęŽ„ęŽ„ē¶šć—ć€ćƒ‰ćƒ©ćƒƒć‚°ļ¼†ćƒ‰ćƒ­ćƒƒćƒ—ć®ē›“ę„Ÿēš„ćŖę“ä½œć§ćƒ‡ćƒ¼ć‚æć‚’ć‚°ćƒ©ćƒ•ć‚„č”Øć«å¤‰ę›ć—ć€ć‚¤ćƒ³ć‚æćƒ©ć‚Æćƒ†ć‚£ćƒ–ćŖćƒ€ćƒƒć‚·ćƒ„ćƒœćƒ¼ćƒ‰ć‚’é«˜é€Ÿć«ä½œęˆć§ćć‚‹ć‚Æćƒ©ć‚¦ćƒ‰ć‚µćƒ¼ćƒ“ć‚¹ć§ć™ć€‚ ä»Šå›žćÆQuickSightを使って、 ć‚æć‚°ļ¼ˆć‚øćƒ£ćƒ³ćƒ«ļ¼‰ć”ćØć®č¦–č“ę•°ęŽØē§»ć‚’ē¤ŗć™ęŠ˜ć‚Œē·šć‚°ćƒ©ćƒ• ęœŸé–“å†…ć§äŗŗę°—ć®é«˜ć‹ć£ćŸćƒ¬ć‚·ćƒ”ć®ćƒ©ćƒ³ć‚­ćƒ³ć‚°č”Ø ćŖć©ć‚’ē››ć‚Šč¾¼ć‚“ć ćƒ€ćƒƒć‚·ćƒ„ćƒœćƒ¼ćƒ‰ć‚’ä½œęˆć—ć¾ć—ćŸć€‚(äø‹ć®å†™ēœŸ)ć“ć‚Œć«ć‚ˆć‚Šć€ćƒ¦ćƒ¼ć‚¶ćƒ¼ćŒćƒœć‚æćƒ³äø€ć¤ć§ęœ€ę–°ć®ćƒˆćƒ¬ćƒ³ćƒ‰ć‚’ęŠŠę”ć—ć€ćƒ”ćƒ¼ć‚«ćƒ¼ćƒ»å°å£²ćøć®ęę”ˆć«ę“»ć‹ć›ć‚‹ć‚ˆć†ć«ćŖć‚Šć¾ć—ćŸć€‚ ć‚¤ćƒ³ć‚æćƒ¼ćƒ³ć‚·ćƒƒćƒ—ć‚’ēµ‚ćˆć¦ļ¼šå­¦ć³ćØä»Šå¾Œć®å±•ęœ› 恓恮1ćƒ¶ęœˆć§å¾—ćŸå¤§ććŖå­¦ć³ ć“ć®ć‚¤ćƒ³ć‚æćƒ¼ćƒ³ć‚·ćƒƒćƒ—ćÆć€ē§ć«ćØć£ć¦åˆć‚ć¦å°½ćć—ć®1ćƒ¶ęœˆć§ć—ćŸć€‚ē‰¹ć«ć€ęŠ€č”“é¢ćØćƒžć‚¤ćƒ³ćƒ‰é¢ć§å¤§ććŖęˆé•·ćŒć‚ć£ćŸćØę„Ÿć˜ć¦ć„ć¾ć™ć€‚ ćƒ“ćƒƒć‚°ćƒ‡ćƒ¼ć‚æćØåøøć«éš£ć‚Šåˆć‚ć›ć®ęœ€é©åŒ–ę€č€ƒ : å€‹äŗŗé–‹ē™ŗć§ćÆę„č­˜ć™ć‚‹ć“ćØć®ćŖć‹ć£ćŸć€Œćƒ‡ćƒ¼ć‚æć®å·Øå¤§ć•ć€ć«ć€č‰Æć„ę„å‘³ć§ä½•åŗ¦ć‚‚ę®“ć‚‰ć‚Œć¾ć—ćŸć€‚1ć¤ć®ć‚Æć‚ØćƒŖć§ć‚‚ć‹ćŖć‚Šć®ę™‚é–“ćŒć‹ć‹ć‚‹ć“ćØć‚‚ć‚ć‚Šć€é«˜é€Ÿć«ēµęžœć‚’å‡ŗć™ćŸć‚ć®ć‚Æć‚ØćƒŖćƒ‘ćƒ•ć‚©ćƒ¼ćƒžćƒ³ć‚¹ć®ęœ€é©åŒ–ć®č¦–ē‚¹ćŒåøøć«ę±‚ć‚ć‚‰ć‚Œć¾ć—ćŸć€‚ć“ć‚ŒćÆć€ćƒ‡ćƒ¼ć‚æć‚Øćƒ³ć‚øćƒ‹ć‚¢ćƒŖćƒ³ć‚°ć®ę ¹å¹¹ć«č§¦ć‚Œć‚‹č²“é‡ćŖēµŒéØ“ć§ć—ćŸć€‚ ć€Œč‡Ŗåˆ†ć®ćŸć‚ć€ć‹ć‚‰ć€Œćƒćƒ¼ćƒ ć®ćŸć‚ć€ć®é–‹ē™ŗćø : git push ć™ć‚‹ęŒ‡ćŒéœ‡ćˆćŸć®ć‚‚č‰Æć„ę€ć„å‡ŗć§ć™ć€‚ćƒ–ćƒ©ćƒ³ćƒęˆ¦ē•„ć€ćƒ—ćƒ«ćƒŖć‚Æć‚Øć‚¹ćƒˆć§ć®äøåÆ§ćŖčŖ¬ę˜Žć€ćƒ¬ćƒ“ćƒ„ćƒ¼ć§ć®ēš„ē¢ŗćŖęŒ‡ę‘˜ć€‚ćć®å…Øć¦ćŒć€ć€Œć‚³ćƒ¼ćƒ‰ćÆę›øć„ćŸäŗŗć ć‘ć®ć‚‚ć®ć§ćÆćŖćć€ćƒćƒ¼ćƒ ć®č³‡ē”£ć§ć‚ć‚‹ć€ćØć„ć†ć“ćØć‚’ę•™ćˆć¦ćć‚Œć¾ć—ćŸć€‚ä»–äŗŗćŒčŖ­ć‚€ć“ćØć‚’å‰ęćØć—ćŸć€åÆčŖ­ę€§ć®é«˜ć„ć‚³ćƒ¼ćƒ‰ć‚’ę›øćę„č­˜ćŒå©ćč¾¼ć¾ć‚Œć¾ć—ćŸć€‚ ćƒ¦ćƒ¼ć‚¶ćƒ¼ć®ć‚¤ćƒ³ć‚µć‚¤ćƒˆć‚’ć€Œå…ˆå›žć‚Šć€ć™ć‚‹ćƒ—ćƒ­ćƒ€ć‚Æćƒˆé–‹ē™ŗ : ę©Ÿčƒ½é–‹ē™ŗć®čƒŒę™Æć«ć‚ć£ćŸć‚ˆć†ć«ć€ćƒ—ćƒ­ćƒ€ć‚ÆćƒˆćÆåøøć«ćƒ¦ćƒ¼ć‚¶ćƒ¼ć®čŖ²é”Œč§£ę±ŗć®ćŸć‚ć«ć‚ć‚Šć¾ć™ć€‚ć€Œć“ć‚“ćŖę©Ÿčƒ½ćŒć‚ć£ćŸć‚‰ä¾æåˆ©ć ć‚ć†ć€ćØć„ć†ä»®čŖ¬ć‚’ē«‹ć¦ć€ćƒ’ć‚¢ćƒŖćƒ³ć‚°ć‚’é€šć—ć¦ę¤œčØ¼ć—ć€ćƒ¦ćƒ¼ć‚¶ćƒ¼ć®ę€č€ƒć‚’å…ˆå›žć‚Šć—ć¦ä¾”å€¤ć‚’ęä¾›ć™ć‚‹ć€‚ć“ć®äø€é€£ć®ćƒ—ćƒ­ć‚»ć‚¹ć‚’é–“čæ‘ć§ä½“éØ“ć§ććŸć“ćØćÆć€ä»Šå¾Œć®ć‚­ćƒ£ćƒŖć‚¢ć®ē¤Žć«ćŖć‚‹ćÆćšć§ć™ć€‚ ć“ć‚Œć‹ć‚‰ęŒ‘ęˆ¦ć—ćŸć„ć“ćØļ¼šć€ŒćŖćœć€ć‚’č§£ćę˜Žć‹ć™ćƒˆćƒ¬ćƒ³ćƒ‰åˆ†ęžćø ä»Šå›žé–‹ē™ŗć—ćŸę©Ÿčƒ½ćÆć€é£Ÿć®ćƒˆćƒ¬ćƒ³ćƒ‰ć‚’åÆč¦–åŒ–ć™ć‚‹å¤§ććŖäø€ę­©ćØćŖć‚Šć¾ć—ćŸć€‚ć—ć‹ć—ć€åŒę™‚ć«ę–°ćŸćŖčŖ²é”ŒćØć€ć‚ˆć‚Šå¤§ććŖåÆčƒ½ę€§ć‚‚č¦‹ćˆć¦ćć¾ć—ćŸć€‚ å‹•ē”»č¦–č“ćƒ‡ćƒ¼ć‚æćÆć€ćƒ†ćƒ¬ćƒ“ē•Ŗēµ„ć§ć®ē“¹ä»‹ć‚„SNSć§ć®ę‹”ę•£ć«ć‚ˆć£ć¦ć€ć”ćēŸ­ę™‚é–“ć§ēˆ†ē™ŗēš„ć«å†ē”Ÿę•°ćŒä¼øć³ć‚‹ļ¼ˆćƒć‚ŗć‚‹ļ¼‰ćØć„ć†ē‰¹å¾“ćŒć‚ć‚Šć¾ć™ć€‚ć¾ćŸć€å­£ēÆ€ę€§ć®å¤‰å‹•ć‚‚å¤§ććć€å®ŸćÆå¤šćć®č¦ē“ ćŒēµ”ćæåˆć£ć¦č¤‡é›‘ć«ćŖć£ć¦ć„ć¾ć™ć€‚ē¾ēŠ¶ć§ćÆć€ćƒ‡ćƒ¼ć‚æć®č†Øå¤§ć•ć‹ć‚‰ćƒ€ćƒƒć‚·ćƒ„ćƒœćƒ¼ćƒ‰ć®č”Øē¤ŗć«ę™‚é–“ćŒć‹ć‹ć£ć¦ć—ć¾ć†ćØć„ć†ćƒ‘ćƒ•ć‚©ćƒ¼ćƒžćƒ³ć‚¹é¢ć®čŖ²é”Œć‚‚ę®‹ć£ć¦ć„ć¾ć™ć€‚ ćć—ć¦ä½•ć‚ˆć‚Šć€ä»Šå¾ŒęŒ‘ęˆ¦ć—ćŸć„ć®ćÆć€ć€Œä½•ćŒęµč”Œć£ć¦ć„ć‚‹ć‹ć€ć‚’ęē¤ŗć™ć‚‹ć ć‘ć§ćŖćć€ć€ŒćŖćœćć‚ŒćŒęµč”Œć£ć¦ć„ć‚‹ć®ć‹ć€ćØć„ć†ć‚¤ćƒ³ć‚µć‚¤ćƒˆć¾ć§ęä¾›ć™ć‚‹ć“ćØć§ć™ć€‚ ä¾‹ćˆć°ć€ć€Œ"é¶ć‚€ć­č‚‰"ć®č¦–č“ę•°ćŒę€„å¢—ć—ćŸć€ćØć„ć†äŗ‹å®Ÿć ć‘ć§ćŖćć€ć€Œćć‚ŒćÆć€äŗŗę°—ćƒ†ćƒ¬ćƒ“ē•Ŗēµ„ć§"節瓄"ć‚’ćƒ†ćƒ¼ćƒžć«ć—ćŸē‰¹é›†ćŒēµ„ć¾ć‚Œć€SNS恧"ē°”å˜ć§ćŠć„ć—ć„é¶ć‚€ć­č‚‰ćƒ¬ć‚·ćƒ”"ćØć„ć†ęŠ•ēØæćŒę‹”ę•£ć•ć‚ŒćŸć‹ć‚‰ć ć€ćØć„ć†čƒŒę™Æć¾ć§ć‚’č§£ćę˜Žć‹ć—ćŸć„ć€‚ć„ć‚ć°ć€ ćƒć‚ŗć®å› ę•°åˆ†č§£ 恧恙怂 ć“ć®ć€ŒćŖćœć€ć‚’č§£ćę˜Žć‹ć™ć“ćØćŒć§ćć‚Œć°ć€ćƒ‡ćƒŖćƒƒć‚·ćƒ„ćƒŖć‚µćƒ¼ćƒćÆćƒ”ćƒ¼ć‚«ćƒ¼ćƒ»å°å£²ć«åÆ¾ć—ć¦ć€ć‚ˆć‚Šē²¾åŗ¦ć®é«˜ć„éœ€č¦äŗˆęø¬ć‚„ć€åŠ¹ęžœēš„ćŖč²©å£²ęˆ¦ē•„ć®ē«‹ę”ˆć‚’ć‚µćƒćƒ¼ćƒˆć§ćć‚‹ćÆćšć§ć™ć€‚ ęœ€å¾Œć«ćŖć‚Šć¾ć™ćŒć€1ćƒ¶ęœˆé–“ęø©ć‹ćć€ćć—ć¦ē†±åæƒć«ć”ęŒ‡å°Žćć ć•ć£ćŸćƒ”ćƒ³ć‚æćƒ¼ć®ēš†ę§˜ć€ćƒ‡ćƒŖćƒƒć‚·ćƒ„ćƒŖć‚µćƒ¼ćƒéƒØé–€ć®ēš†ę§˜ć€ęœ¬å½“ć«ć‚ć‚ŠćŒćØć†ć”ć–ć„ć¾ć—ćŸļ¼
ćÆć˜ć‚ć« こんにごは! 2025å¹“ć®å¤ć€ę Ŗå¼ä¼šē¤¾ć‚Øćƒ–ćƒŖćƒ¼ć§1ćƒ¶ęœˆé–“ć‚¤ćƒ³ć‚æćƒ¼ćƒ³ć‚·ćƒƒćƒ—ć«å‚åŠ ć—ć¦ć„ć‚‹åøøęœØę³°ęˆć§ć™ć€‚é…å±žć•ć‚ŒćŸć®ćÆć€å›½å†…ęœ€å¤§ē“šć®ćƒ¬ć‚·ćƒ”å‹•ē”»ćƒ”ćƒ‡ć‚£ć‚¢ć€Œćƒ‡ćƒŖćƒƒć‚·ćƒ„ć‚­ćƒƒćƒćƒ³ć€ć®ćƒ‡ćƒ¼ć‚æć‚’ę“»ē”Øć—ć¦é£Ÿć®ćƒˆćƒ¬ćƒ³ćƒ‰ć‚’åˆ†ęžć—ć€ćƒ”ćƒ¼ć‚«ćƒ¼ćƒ»å°å£²ć®ę„ę€ę±ŗå®šć‚’ć‚µćƒćƒ¼ćƒˆć™ć‚‹ć€Œćƒ‡ćƒŖćƒƒć‚·ćƒ„ćƒŖć‚µćƒ¼ćƒć€éƒØé–€ć§ć™ć€‚ 恓恮1ćƒ¶ęœˆé–“ć§ē§ćŒå–ć‚Šēµ„ć‚“ć ć®ćÆć€ å‹•ē”»č¦–č“ćƒ‡ćƒ¼ć‚æć‚’ē”Øć„ćŸę–°ćŸćŖćƒˆćƒ¬ćƒ³ćƒ‰åˆ†ęžę©Ÿčƒ½ć®é–‹ē™ŗ ć§ć™ć€‚ć“ć®čØ˜äŗ‹ć§ćÆć€ćƒ¦ćƒ¼ć‚¶ćƒ¼ć®éš ć‚ŒćŸćƒ‹ćƒ¼ć‚ŗć‹ć‚‰ę©Ÿčƒ½é–‹ē™ŗć®čƒŒę™Æć€ćć—ć¦äŗŗē”Ÿåˆć®SQLćØę ¼é—˜ć—ćŖćŒć‚‰ćƒ‡ćƒ¼ć‚æć‚’åÆč¦–åŒ–ć™ć‚‹ć¾ć§ć®ęŠ€č”“ēš„ćŖč£å“ć‚’ä¼ćˆć¦ć„ć‘ć‚Œć°ćØę€ć„ć¾ć™ć€‚ čƒŒę™Æć€€ćŖćœć€Œå‹•ē”»č¦–č“ćƒ‡ćƒ¼ć‚æć€ć®åˆ†ęžćŒåæ…č¦ć ć£ćŸć®ć‹ļ¼Ÿ ćƒ‡ćƒŖćƒƒć‚·ćƒ„ć‚­ćƒƒćƒćƒ³ć‚¢ćƒ—ćƒŖć®ę¤œē“¢ćƒ‡ćƒ¼ć‚æćƒ»ćƒ¬ć‚·ćƒ”é–²č¦§ćƒ‡ćƒ¼ć‚æć‚’ć‚‚ćØć«ć€ę¶ˆč²»č€…ć®é£Ÿćƒˆćƒ¬ćƒ³ćƒ‰ć‚„čˆˆå‘³é–¢åæƒć‚’åˆ†ęžć§ćć‚‹ćƒ„ćƒ¼ćƒ«ć§ć™ć€‚ę¶ˆč²»č€…åˆ†ęžć‚„čŖæęŸ»ćŖć©ć®å·„ę•°å‰Šęø›ć‚„ć€ćŠå®¢ę§˜ć®ćƒ‹ćƒ¼ć‚ŗć‚’čøć¾ćˆćŸå•†å“é–‹ē™ŗļ¼č²©äæƒć‚’å®Ÿē¾ć§ćć€ę–½ē­–ć®ē¢ŗåŗ¦ć‚‚é«˜ć‚ć‚‹ć“ćØćŒåÆčƒ½ć§ć™ć€‚ ć™ć§ć«ę¤œē“¢é »åŗ¦ćƒ©ćƒ³ć‚­ćƒ³ć‚°ćŖć©ć®ę©Ÿčƒ½ćÆå­˜åœØć—ć¦ć„ć¾ć—ćŸćŒć€ę—¢å­˜ćƒ¦ćƒ¼ć‚¶ćƒ¼ćøć®ćƒ’ć‚¢ćƒŖćƒ³ć‚°ć‹ć‚‰ć€ć•ć‚‰ć«ę·±ć„ć‚¤ćƒ³ć‚µć‚¤ćƒˆć‚’ę±‚ć‚ć‚‹å£°ćŒę˜Žć‚‰ć‹ć«ćŖć£ć¦ćć¾ć—ćŸć€‚ ćƒ¦ćƒ¼ć‚¶ćƒ¼ćŒęœ¬å½“ć«ēŸ„ć‚ŠćŸć‹ć£ćŸć“ćØ: ć€Œä»Šé€±ć€ć€Žé¶č‚‰ć€ć®ę¤œē“¢ćŒå¢—ćˆćŸć€ćØć„ć†äŗ‹å®Ÿļ¼ˆ 点 ļ¼‰ć ć‘ć§ćŖćć€ć€ŒćŖćœå¢—ćˆćŸć®ć‹ļ¼Ÿå…·ä½“ēš„ć«ć©ć‚“ćŖ é¶č‚‰ę–™ē† ļ¼ˆć‚øćƒ£ćƒ³ćƒ«ļ¼‰ćŒäŗŗę°—ćŖć®ć‹ļ¼Ÿć€ļ¼ˆ 線 ļ¼‰ć‚’ēŸ„ć‚ŠćŸć„ć€‚ ē‰¹å®šć®ćƒ¬ć‚·ćƒ”ćŒäŗŗę°—ćŖē†ē”±ć€ä¾‹ćˆć°ć€Œć“ć®ćƒćƒ³ćƒćƒ¼ć‚°ćŒäŗŗę°—ćŖć®ćÆć€ "ę™‚ēŸ­" 悄 "節瓄" ćØć„ć£ćŸć‚æć‚°ćŒéŸæć„ć¦ć„ć‚‹ć‹ć‚‰ć§ćÆćŖć„ć‹ļ¼Ÿć€ćØć„ć†čƒŒę™Æļ¼ˆ 面 ļ¼‰ć‚’ę‰ćˆćŸć„ć€‚ ę¶ˆč²»č€…ćŒć€Œä»Šć€ęœ¬å½“ć«ä½œć‚ŠćŸć„ę–™ē†ć€ć®ćƒˆćƒ¬ćƒ³ćƒ‰ć‚’ć€ć‚ˆć‚Šč§£åƒåŗ¦é«˜ćęŠŠę”ć—ćŸć„ć€‚ ć“ć‚Œć‚‰ć®ćƒ‹ćƒ¼ć‚ŗćÆć€å˜äø€ć®ćƒ¬ć‚·ćƒ”äŗŗę°—ć‚’čæ½ć†ć ć‘ć§ćÆęŗ€ćŸć›ć¾ć›ć‚“ć€‚ćć“ć§åæ…č¦ć«ćŖć£ć¦ćć‚‹ć®ćŒ å‹•ē”»č¦–č“ćƒ‡ćƒ¼ć‚æ ćØćƒ¬ć‚·ćƒ”ć«ä»˜äøŽć•ć‚ŒćŸ タグ 恧恙怂 ćƒ¦ćƒ¼ć‚¶ćƒ¼ćŒć©ć®ćƒ¬ć‚·ćƒ”å‹•ē”»ć‚’ć„ć¤č¦–č“ć—ćŸć‹ć€ćć—ć¦ćć®ćƒ¬ć‚·ćƒ”ć«ć©ć‚“ćŖć‚æć‚°ļ¼ˆä¾‹ļ¼šć€Œ#ē°”å˜ć€ć€Œ#é¶ć‚€ć­č‚‰ć€ć€Œ#ćŠå¼å½“ć€ļ¼‰ćŒä»˜ć„ć¦ć„ć‚‹ć‹ć‚’ęŽ›ć‘åˆć‚ć›ć‚‹ć“ćØć§ć€ ē‰¹å®šć®ć‚æć‚°ć‚’ęŒć¤ćƒ¬ć‚·ćƒ”ē¾¤ćŒć€ć„ć¤ć€ć©ć‚Œćć‚‰ć„č¦‹ć‚‰ć‚Œć¦ć„ć‚‹ć‹ ć‚’ę™‚ē³»åˆ—ć§åÆč¦–åŒ–ć§ćć¾ć™ć€‚ć“ć‚Œć«ć‚ˆć‚Šć€ć€Œē‚¹ć€ć®åˆ†ęžć‹ć‚‰č„±å“ć—ć€ćƒˆćƒ¬ćƒ³ćƒ‰ć®ć€Œē·šć€ćØć€Œé¢ć€ć‚’ę‰ćˆć‚‹ć‚¤ćƒ³ć‚µć‚¤ćƒˆć‚’ęä¾›ć§ćć‚‹ćØč€ƒćˆć¾ć—ćŸć€‚ ć“ć®ä»®čŖ¬ć«åŸŗć„ćć€ ęŒ‡å®šć—ćŸć‚æć‚°ć«åˆč‡“ć™ć‚‹ćƒ¬ć‚·ćƒ”ć®č¦–č“ćƒˆćƒ¬ćƒ³ćƒ‰ć‚’ę™‚ē³»åˆ—ć‚°ćƒ©ćƒ•ć§åÆč¦–åŒ–ć—ć€ęœŸé–“å†…ć§ē‰¹ć«äŗŗę°—ć ć£ćŸćƒ¬ć‚·ćƒ”ć‚’ćƒ©ćƒ³ć‚­ćƒ³ć‚°å½¢å¼ć§ęē¤ŗć™ć‚‹ ć€ćØć„ć†ę–°ę©Ÿčƒ½ć®é–‹ē™ŗćƒ—ćƒ­ć‚øć‚§ć‚ÆćƒˆćŒć‚¹ć‚æćƒ¼ćƒˆć—ć¾ć—ćŸć€‚ ćƒ‡ćƒ¼ć‚æåŠ å·„ć€€åˆć‚ć¦ć®SQL ä»Šå›žć®é–‹ē™ŗć§ćÆć€ćƒ‡ćƒ¼ć‚æåŸŗē›¤ćØć—ć¦ Databricks ć‚’åˆ©ē”Øć—ć¾ć—ćŸć€‚ćƒ‡ćƒŖćƒƒć‚·ćƒ„ć‚­ćƒƒćƒćƒ³ć‹ć‚‰é€ć‚‰ć‚Œć¦ćć‚‹č†Øå¤§ćŖč¦–č“ćƒ­ć‚°ćƒ‡ćƒ¼ć‚æć‚’ć€SQLć‚’ä½æć£ć¦ęŠ½å‡ŗćƒ»åŠ å·„ć—ć€BIćƒ„ćƒ¼ćƒ«ć§ć‚ć‚‹Amazon QuickSightć§ćƒ“ć‚øćƒ„ć‚¢ćƒ«ć‚’ä½œęˆć™ć‚‹ć®ćŒäø»ćŖęµć‚Œć§ć™ć€‚ ć“ć“ć§ē§ć®å‰ć«ē«‹ć”ćÆć ć‹ć£ćŸć®ćŒć€ äŗŗē”Ÿåˆć®SQL ć§ć—ćŸć€‚ęœ€åˆćÆSELECT * FROM ...ć™ć‚‰ćŠć¼ć¤ć‹ćŖć„ēŠ¶ę…‹ć§ć—ćŸćŒć€ć‚Øćƒ–ćƒŖćƒ¼ć«ä»£ć€…å—ć‘ē¶™ćŒć‚Œć‚‹ć€ŒSQLć‚‚ćć‚‚ćč³‡ę–™ć€ćØć€ćƒ”ćƒ³ć‚æćƒ¼ć®ē†±åæƒćŖć”ęŒ‡å°Žć®ćŠć‹ć’ć§ć€ęœ€ēµ‚ēš„ć«ćÆęœ€ä½Žé™ć®ćƒŖćƒ•ć‚”ćƒ¬ćƒ³ć‚¹č¾¼ćæć§č¤‡é›‘ćŖć‚Æć‚ØćƒŖć‚’čŖ­ćæę›øćć§ćć‚‹ćƒ¬ćƒ™ćƒ«ć«ęˆé•·ć§ćć¾ć—ćŸļ¼ SQL恋态Python(Pandas)ć‹ļ¼Ÿ ćƒ‡ćƒ¼ć‚æåŠ å·„ćØčžć„ć¦ć€ęœ€åˆćÆć€ŒSQLć‚’ä½æē”Øć—ćŖćć¦ć‚‚ć€Python恮Pandasćƒ©ć‚¤ćƒ–ćƒ©ćƒŖć§ć‚‚åŒć˜ć“ćØćŒć§ćć‚‹ć®ć§ćÆļ¼Ÿć€ćØč€ƒćˆć¦ć„ć¾ć—ćŸć€‚ć—ć‹ć—ć€ć‚¤ćƒ³ć‚æćƒ¼ćƒ³ć‚’é€šć˜ć¦ć€å¤§č¦ęØ”ćƒ‡ćƒ¼ć‚æć‚’ę‰±ć†äøŠć§ć®SQLć®å„Ŗä½ę€§ć‚’ę„Ÿć˜ć‚‹ć“ćØćŒä½•åŗ¦ć‚‚ć‚ć‚Šć¾ć—ćŸć€‚ ćƒ‡ćƒ¼ć‚æé›†čØˆćØć‚Æć‚ØćƒŖć®ęœ€é©åŒ– SQLćÆć€ćƒ‡ćƒ¼ć‚æćƒ™ćƒ¼ć‚¹ć‹ć‚‰åæ…č¦ćŖćƒ‡ćƒ¼ć‚æć‚’åŠ¹ēŽ‡ēš„ć«ęŠ½å‡ŗćƒ»é›†čØˆć™ć‚‹ćŸć‚ć«ęœ€é©åŒ–ć•ć‚ŒćŸčØ€čŖžć§ć™ć€‚GROUP BY, JOIN, WHEREćØć„ć£ćŸå‘½ä»¤ćÆć€ćƒ‡ćƒ¼ć‚æćƒ™ćƒ¼ć‚¹ć‚Øćƒ³ć‚øćƒ³ćŒęœ€ć‚‚åŠ¹ēŽ‡ēš„ćŖå®Ÿč”ŒčØˆē”»ć‚’ē«‹ć¦ć¦å‡¦ē†ć—ć¦ćć‚Œć‚‹ćŸć‚ć€ćƒ†ćƒ©ćƒć‚¤ćƒˆē“šć®ćƒ‡ćƒ¼ć‚æć«åÆ¾ć—ć¦ć‚‚éžåøøć«é«˜é€Ÿć§ć™ć€‚äø€ę–¹ć€PandasćÆćƒ‡ćƒ¼ć‚æć‚’äø€åŗ¦ćƒ”ćƒ¢ćƒŖć«å…Øć¦čŖ­ćæč¾¼ć‚“ć§ć‹ć‚‰å‡¦ē†ć™ć‚‹ćŸć‚ć€ćƒ‡ćƒ¼ć‚æć‚µć‚¤ć‚ŗćŒå¤§ćććŖć‚‹ćØćƒ”ćƒ¢ćƒŖäøč¶³ć«é™„ć£ćŸć‚Šć€ćƒ‘ćƒ•ć‚©ćƒ¼ćƒžćƒ³ć‚¹ćŒč‘—ć—ćä½Žäø‹ć—ćŸć‚Šć™ć‚‹åÆčƒ½ę€§ćŒć‚ć‚Šć¾ć™ć€‚ ćƒ‡ćƒ¼ć‚æć®ę•“åˆę€§ ćƒ‡ćƒ¼ć‚æćƒ™ćƒ¼ć‚¹ć§ćÆć€ć‚¹ć‚­ćƒ¼ćƒžå®šē¾©ļ¼ˆPRIMARY KEY, FOREIGN KEYćŖć©ļ¼‰ć«ć‚ˆć£ć¦ć€ćƒ‡ćƒ¼ć‚æć®é‡č¤‡ć‚„äøę­£ćŖå€¤ć‚’ć‚·ć‚¹ćƒ†ćƒ ćƒ¬ćƒ™ćƒ«ć§é˜²ć„ć§ćć‚Œć¾ć™ć€‚ć“ć‚Œć«ć‚ˆć‚Šć€ćƒ‡ćƒ¼ć‚æć®å“č³ŖćŒę‹…äæć•ć‚Œć€ć‚¢ćƒ—ćƒŖć‚±ćƒ¼ć‚·ćƒ§ćƒ³å“ćÆå®‰åæƒć—ć¦ćƒ‡ćƒ¼ć‚æć‚’ę‰±ćˆć¾ć™ć€‚ć‚‚ć—ć“ć‚Œć‚’Pythonć®ć‚³ćƒ¼ćƒ‰ć ć‘ć§å®Ÿē¾ć—ć‚ˆć†ćØć™ć‚‹ćØć€ć‚ć‚‰ć‚†ć‚‹å‡¦ē†ć«ę•“åˆę€§ćƒć‚§ćƒƒć‚Æć®ćƒ­ć‚øćƒƒć‚Æć‚’č‡Ŗå‰ć§å®Ÿč£…ć™ć‚‹åæ…č¦ćŒć‚ć‚Šć€éžåøøć«ē…©é›‘ć«ćŖć‚Šć¾ć™ć€‚ åŠ¹ēŽ‡ēš„ćŖå·®åˆ†ę›“ę–°ćØćƒˆćƒ©ćƒ³ć‚¶ć‚Æć‚·ćƒ§ćƒ³ ä»Šå›žć®ćƒ—ćƒ­ć‚øć‚§ć‚Æćƒˆć§ćÆć€ęÆŽę—„å¢—ćˆē¶šć‘ć‚‹č¦–č“ćƒ­ć‚°ćƒ‡ćƒ¼ć‚æć‚’åŠ¹ēŽ‡ēš„ć«é›†čØˆćƒ†ćƒ¼ćƒ–ćƒ«ćøåę˜ ć•ć›ć‚‹åæ…č¦ćŒć‚ć‚Šć¾ć—ćŸć€‚ć‚‚ć—Pandasć§å®Ÿč£…ć™ć‚‹ćŖć‚‰ć€ć€ŒéŽåŽ»ć®å…Øćƒ‡ćƒ¼ć‚æć‚’čŖ­ćæč¾¼ćæć€ä»Šę—„ć®ćƒ‡ćƒ¼ć‚æć‚’čæ½åŠ ć—ć€å…Øćƒ‡ćƒ¼ć‚æć‚’ę›øćå‡ŗć™ć€ćØć„ć†éžåŠ¹ēŽ‡ćŖå‡¦ē†ć«ćŖć‚ŠćŒć”ć§ć™ć€‚ć—ć‹ć—ć€DatabricksćŒęŽ”ē”Øć—ć¦ć„ć‚‹ Delta Lake ćØć„ć†ęŠ€č”“ćØć€ćć‚Œć‚’ę“ä½œć™ć‚‹PySparkć‚³ćƒ¼ćƒ‰ć‚’ä½æćˆć°ć€ć“ć®čŖ²é”Œć‚’č§£ę±ŗć§ćć¾ć™ć€‚ å®Ÿéš›ć«ę›øć„ćŸć‚³ćƒ¼ćƒ‰ćŒć“ć”ć‚‰ć§ć™ć€‚ # ęÆŽę—„å¢—åˆ†ć®ę›“ę–°ć‚’ć™ć‚‹ column_selected_filtered_data \ .write \ .format( "delta" ) \ .mode( "overwrite" ) \ .partitionBy( "event_date" ) \ .option( "replaceWhere" , f "event_date >= '{event_date}'" ) \ .save( "delta_table_path/sample_analytics_table" ) 注: å®Ÿéš›ć®é–‹ē™ŗć§ćÆć€å¼Šē¤¾å†…ć®ćƒ‡ćƒ¼ć‚æåŸŗē›¤ć«åˆć‚ć›ć¦ćƒ†ćƒ¼ćƒ–ćƒ«åć‚„ć‚¹ć‚­ćƒ¼ćƒžę§‹é€ ć‚’čŖæę•“ć—ć¦ć„ć¾ć™ć€‚ ć“ć®ć‚³ćƒ¼ćƒ‰ćÆć€å˜ć«ćƒ‡ćƒ¼ć‚æć‚’äøŠę›øćć—ć¦ć„ć‚‹ć®ć§ćÆć‚ć‚Šć¾ć›ć‚“ć€‚ partitionBy("event_date") : ć¾ćšć€ćƒ‡ćƒ¼ć‚æćŒ event_date ļ¼ˆę—„ä»˜ļ¼‰ć”ćØć«ē‰©ē†ēš„ć«åˆ†å‰²ć—ć¦äæå­˜ć•ć‚Œć¦ć„ć¾ć™ć€‚ option("replaceWhere", ...) : ćć—ć¦ć€ć“ć®ć‚Ŗćƒ—ć‚·ćƒ§ćƒ³ć§ć€Œ event_date ćŒęŒ‡å®šć—ćŸę—„ä»˜ä»„é™ć®ćƒ‡ćƒ¼ć‚æć®ćæć‚’ē½®ćę›ćˆć‚‹ć€ćØć„ć†ęŒ‡ē¤ŗć‚’å‡ŗć—ć¦ć„ć¾ć™ć€‚ ć“ć‚Œć«ć‚ˆć‚Šć€éŽåŽ»ć®č†Øå¤§ćŖćƒ‡ćƒ¼ć‚æć‚’äø€åˆ‡č§¦ć‚‹ć“ćØćŖćć€ē›“čæ‘ć®ćƒ‡ćƒ¼ć‚æć ć‘ć‚’ę›“ę–°ć§ćć¾ć™ć€‚ćƒ•ć‚”ć‚¤ćƒ«ćƒ™ćƒ¼ć‚¹ć§å‡¦ē†ć‚’č”Œć†Pandaså˜ä½“ć§ćÆć€åŒę§˜ć®åŠ¹ēŽ‡ę€§ćØå®‰å…Øę€§ć‚’ę‹…äæć™ć‚‹ć®ćÆéžåøøć«å›°é›£ć§ć™ć€‚ SQLć‚³ćƒ¼ćƒ‰č§£čŖ¬ ć‚¤ćƒ³ć‚æćƒ¼ćƒ³äø­ć«ę›øć„ćŸć€ē‰¹ć«å°č±”ēš„ć ć£ćŸć‚Æć‚ØćƒŖć®äø€éƒØć‚’ć”ē“¹ä»‹ć—ć¾ć™ć€‚ SELECT l.event_date, r.recipe_id, CONCAT(r.lead, r.title) AS title, r.image, r.tags, r.ingredients, r.category, r.taste FROM distinct_event_dates AS l CROSS JOIN recipe_master AS r WHERE r.advertiser_id IS NULL ć“ć®ć‚Æć‚ØćƒŖć®äø»å½¹ćÆ CROSS JOIN 恧恙怂SQLåˆå­¦č€…ć ć£ćŸē§ćÆęœ€åˆć“ć®ä»•ēµ„ćæć®ē†č§£ć«č‹¦ć—ćæć¾ć—ćŸć€‚ CROSS JOIN は、2ć¤ć®ćƒ†ćƒ¼ćƒ–ćƒ«ć®å…Øćƒ¬ć‚³ćƒ¼ćƒ‰ć‚’ē·å½“ćŸć‚Šć§ēµ„ćæåˆć‚ć›ć‚‹ēµåˆę–¹ę³•ć§ć™ć€‚ć“ć®ć‚Æć‚ØćƒŖć§ćÆć€ distinct_event_dates ļ¼ˆåˆ†ęžåÆ¾č±”ęœŸé–“ć®ę—„ä»˜ćƒŖć‚¹ćƒˆć‚’ęŒć¤ćƒ†ćƒ¼ćƒ–ćƒ«ļ¼‰ćØ recipe_master ļ¼ˆå…Øć¦ć®ćƒ¬ć‚·ćƒ”ęƒ…å ±ć‚’ęŒć¤ćƒžć‚¹ć‚æćƒ†ćƒ¼ćƒ–ćƒ«ļ¼‰ć‚’ęŽ›ć‘åˆć‚ć›ć¦ć„ć¾ć™ć€‚ ćŖćœć“ć‚“ćŖć“ćØć‚’ć™ć‚‹ć®ć‹ļ¼Ÿ ć“ć‚ŒćÆć€ć€Œåˆ†ęžęœŸé–“äø­ć®ć™ć¹ć¦ć®ę—„ä»˜ Ɨ ć™ć¹ć¦ć®ćƒ¬ć‚·ćƒ”ć€ć®ēµ„ćæåˆć‚ć›ć‚’ęŒć¤ć€å·Øå¤§ćŖ"čØ­čØˆå›³"ć®ć‚ˆć†ćŖćƒ™ćƒ¼ć‚¹ćƒ†ćƒ¼ćƒ–ćƒ«ć‚’ä½œęˆć™ć‚‹ćŸć‚ć§ć™ć€‚ ć“ć®å¾Œć€å®Ÿéš›ć®å‹•ē”»č¦–č“ćƒ­ć‚°ć‚’é›†čØˆć—ćŸå‹•ē”»č¦–č“å›žę•°ć®ćƒ†ćƒ¼ćƒ–ćƒ«ćØć“ć®ćƒ™ćƒ¼ć‚¹ćƒ†ćƒ¼ćƒ–ćƒ«ć‚’ LEFT JOIN ć—ć¾ć™ć€‚ć™ć‚‹ćØć€ćŸćØćˆē‰¹å®šć®ę—„ć«ć‚ć‚‹ćƒ¬ć‚·ćƒ”ćŒ1å›žć‚‚č¦–č“ć•ć‚ŒćŖć‹ć£ćŸćØć—ć¦ć‚‚ć€č¦–č“ę•°ć€Œ0ć€ć®ćƒ‡ćƒ¼ć‚æćØć—ć¦ćć”ć‚“ćØę®‹ć™ć“ćØćŒć§ćć‚‹ć®ć§ć™ć€‚ć“ć‚Œć«ć‚ˆć‚Šć€ę™‚ē³»åˆ—ć‚°ćƒ©ćƒ•ćŒé€”åˆ‡ć‚Œć‚‹ć“ćØćŖćć€ę­£ē¢ŗćŖćƒˆćƒ¬ćƒ³ćƒ‰åˆ†ęžćŒåÆčƒ½ć«ćŖć‚Šć¾ć™ć€‚ CROSS JOIN ćÆć€ę™‚ē³»åˆ—åˆ†ęžć«ćŠć‘ć‚‹ćƒ‡ćƒ¼ć‚æę¬ ęć‚’é˜²ććŸć‚ć®ć€éžåøøć«é‡č¦ćŖćƒ†ć‚Æćƒ‹ćƒƒć‚Æć§ć—ćŸć€‚ ćƒ‡ćƒ¼ć‚æć‚’"č¦‹ćˆć‚‹åŒ–"ć™ć‚‹ļ¼šAmazon QuickSightの擻用 ćƒ‡ćƒ¼ć‚æćƒ‘ć‚¤ćƒ—ćƒ©ć‚¤ćƒ³ć®ęœ€ēµ‚å·„ēØ‹ćÆć€åŠ å·„ć—ćŸćƒ‡ćƒ¼ć‚æć‚’ćƒ“ć‚øćƒć‚¹ć®ę„ę€ę±ŗå®šć«ē¹‹ć’ć‚‹ćŸć‚ć®ć€ŒåÆč¦–åŒ–ć€ć§ć™ć€‚ć“ć“ć§ę“»čŗć—ćŸć®ćŒć€AWSćŒęä¾›ć™ć‚‹BIć‚µćƒ¼ćƒ“ć‚¹ć€ Amazon QuickSight 恧恙怂 QuickSightは、Amazon Athena悄DatabricksćØć„ć£ćŸćƒ‡ćƒ¼ć‚æć‚½ćƒ¼ć‚¹ć«ē›“ęŽ„ęŽ„ē¶šć—ć€ćƒ‰ćƒ©ćƒƒć‚°ļ¼†ćƒ‰ćƒ­ćƒƒćƒ—ć®ē›“ę„Ÿēš„ćŖę“ä½œć§ćƒ‡ćƒ¼ć‚æć‚’ć‚°ćƒ©ćƒ•ć‚„č”Øć«å¤‰ę›ć—ć€ć‚¤ćƒ³ć‚æćƒ©ć‚Æćƒ†ć‚£ćƒ–ćŖćƒ€ćƒƒć‚·ćƒ„ćƒœćƒ¼ćƒ‰ć‚’é«˜é€Ÿć«ä½œęˆć§ćć‚‹ć‚Æćƒ©ć‚¦ćƒ‰ć‚µćƒ¼ćƒ“ć‚¹ć§ć™ć€‚ ä»Šå›žćÆQuickSightを使って、 ć‚æć‚°ļ¼ˆć‚øćƒ£ćƒ³ćƒ«ļ¼‰ć”ćØć®č¦–č“ę•°ęŽØē§»ć‚’ē¤ŗć™ęŠ˜ć‚Œē·šć‚°ćƒ©ćƒ• ęœŸé–“å†…ć§äŗŗę°—ć®é«˜ć‹ć£ćŸćƒ¬ć‚·ćƒ”ć®ćƒ©ćƒ³ć‚­ćƒ³ć‚°č”Ø ćŖć©ć‚’ē››ć‚Šč¾¼ć‚“ć ćƒ€ćƒƒć‚·ćƒ„ćƒœćƒ¼ćƒ‰ć‚’ä½œęˆć—ć¾ć—ćŸć€‚(äø‹ć®å†™ēœŸ)ć“ć‚Œć«ć‚ˆć‚Šć€ćƒ¦ćƒ¼ć‚¶ćƒ¼ćŒćƒœć‚æćƒ³äø€ć¤ć§ęœ€ę–°ć®ćƒˆćƒ¬ćƒ³ćƒ‰ć‚’ęŠŠę”ć—ć€ćƒ”ćƒ¼ć‚«ćƒ¼ćƒ»å°å£²ćøć®ęę”ˆć«ę“»ć‹ć›ć‚‹ć‚ˆć†ć«ćŖć‚Šć¾ć—ćŸć€‚ ć‚¤ćƒ³ć‚æćƒ¼ćƒ³ć‚·ćƒƒćƒ—ć‚’ēµ‚ćˆć¦ļ¼šå­¦ć³ćØä»Šå¾Œć®å±•ęœ› 恓恮1ćƒ¶ęœˆć§å¾—ćŸå¤§ććŖå­¦ć³ ć“ć®ć‚¤ćƒ³ć‚æćƒ¼ćƒ³ć‚·ćƒƒćƒ—ćÆć€ē§ć«ćØć£ć¦åˆć‚ć¦å°½ćć—ć®1ćƒ¶ęœˆć§ć—ćŸć€‚ē‰¹ć«ć€ęŠ€č”“é¢ćØćƒžć‚¤ćƒ³ćƒ‰é¢ć§å¤§ććŖęˆé•·ćŒć‚ć£ćŸćØę„Ÿć˜ć¦ć„ć¾ć™ć€‚ ćƒ“ćƒƒć‚°ćƒ‡ćƒ¼ć‚æćØåøøć«éš£ć‚Šåˆć‚ć›ć®ęœ€é©åŒ–ę€č€ƒ : å€‹äŗŗé–‹ē™ŗć§ćÆę„č­˜ć™ć‚‹ć“ćØć®ćŖć‹ć£ćŸć€Œćƒ‡ćƒ¼ć‚æć®å·Øå¤§ć•ć€ć«ć€č‰Æć„ę„å‘³ć§ä½•åŗ¦ć‚‚ę®“ć‚‰ć‚Œć¾ć—ćŸć€‚1ć¤ć®ć‚Æć‚ØćƒŖć§ć‚‚ć‹ćŖć‚Šć®ę™‚é–“ćŒć‹ć‹ć‚‹ć“ćØć‚‚ć‚ć‚Šć€é«˜é€Ÿć«ēµęžœć‚’å‡ŗć™ćŸć‚ć®ć‚Æć‚ØćƒŖćƒ‘ćƒ•ć‚©ćƒ¼ćƒžćƒ³ć‚¹ć®ęœ€é©åŒ–ć®č¦–ē‚¹ćŒåøøć«ę±‚ć‚ć‚‰ć‚Œć¾ć—ćŸć€‚ć“ć‚ŒćÆć€ćƒ‡ćƒ¼ć‚æć‚Øćƒ³ć‚øćƒ‹ć‚¢ćƒŖćƒ³ć‚°ć®ę ¹å¹¹ć«č§¦ć‚Œć‚‹č²“é‡ćŖēµŒéØ“ć§ć—ćŸć€‚ ć€Œč‡Ŗåˆ†ć®ćŸć‚ć€ć‹ć‚‰ć€Œćƒćƒ¼ćƒ ć®ćŸć‚ć€ć®é–‹ē™ŗćø : git push ć™ć‚‹ęŒ‡ćŒéœ‡ćˆćŸć®ć‚‚č‰Æć„ę€ć„å‡ŗć§ć™ć€‚ćƒ–ćƒ©ćƒ³ćƒęˆ¦ē•„ć€ćƒ—ćƒ«ćƒŖć‚Æć‚Øć‚¹ćƒˆć§ć®äøåÆ§ćŖčŖ¬ę˜Žć€ćƒ¬ćƒ“ćƒ„ćƒ¼ć§ć®ēš„ē¢ŗćŖęŒ‡ę‘˜ć€‚ćć®å…Øć¦ćŒć€ć€Œć‚³ćƒ¼ćƒ‰ćÆę›øć„ćŸäŗŗć ć‘ć®ć‚‚ć®ć§ćÆćŖćć€ćƒćƒ¼ćƒ ć®č³‡ē”£ć§ć‚ć‚‹ć€ćØć„ć†ć“ćØć‚’ę•™ćˆć¦ćć‚Œć¾ć—ćŸć€‚ä»–äŗŗćŒčŖ­ć‚€ć“ćØć‚’å‰ęćØć—ćŸć€åÆčŖ­ę€§ć®é«˜ć„ć‚³ćƒ¼ćƒ‰ć‚’ę›øćę„č­˜ćŒå©ćč¾¼ć¾ć‚Œć¾ć—ćŸć€‚ ćƒ¦ćƒ¼ć‚¶ćƒ¼ć®ć‚¤ćƒ³ć‚µć‚¤ćƒˆć‚’ć€Œå…ˆå›žć‚Šć€ć™ć‚‹ćƒ—ćƒ­ćƒ€ć‚Æćƒˆé–‹ē™ŗ : ę©Ÿčƒ½é–‹ē™ŗć®čƒŒę™Æć«ć‚ć£ćŸć‚ˆć†ć«ć€ćƒ—ćƒ­ćƒ€ć‚ÆćƒˆćÆåøøć«ćƒ¦ćƒ¼ć‚¶ćƒ¼ć®čŖ²é”Œč§£ę±ŗć®ćŸć‚ć«ć‚ć‚Šć¾ć™ć€‚ć€Œć“ć‚“ćŖę©Ÿčƒ½ćŒć‚ć£ćŸć‚‰ä¾æåˆ©ć ć‚ć†ć€ćØć„ć†ä»®čŖ¬ć‚’ē«‹ć¦ć€ćƒ’ć‚¢ćƒŖćƒ³ć‚°ć‚’é€šć—ć¦ę¤œčØ¼ć—ć€ćƒ¦ćƒ¼ć‚¶ćƒ¼ć®ę€č€ƒć‚’å…ˆå›žć‚Šć—ć¦ä¾”å€¤ć‚’ęä¾›ć™ć‚‹ć€‚ć“ć®äø€é€£ć®ćƒ—ćƒ­ć‚»ć‚¹ć‚’é–“čæ‘ć§ä½“éØ“ć§ććŸć“ćØćÆć€ä»Šå¾Œć®ć‚­ćƒ£ćƒŖć‚¢ć®ē¤Žć«ćŖć‚‹ćÆćšć§ć™ć€‚ ć“ć‚Œć‹ć‚‰ęŒ‘ęˆ¦ć—ćŸć„ć“ćØļ¼šć€ŒćŖćœć€ć‚’č§£ćę˜Žć‹ć™ćƒˆćƒ¬ćƒ³ćƒ‰åˆ†ęžćø ä»Šå›žé–‹ē™ŗć—ćŸę©Ÿčƒ½ćÆć€é£Ÿć®ćƒˆćƒ¬ćƒ³ćƒ‰ć‚’åÆč¦–åŒ–ć™ć‚‹å¤§ććŖäø€ę­©ćØćŖć‚Šć¾ć—ćŸć€‚ć—ć‹ć—ć€åŒę™‚ć«ę–°ćŸćŖčŖ²é”ŒćØć€ć‚ˆć‚Šå¤§ććŖåÆčƒ½ę€§ć‚‚č¦‹ćˆć¦ćć¾ć—ćŸć€‚ å‹•ē”»č¦–č“ćƒ‡ćƒ¼ć‚æćÆć€ćƒ†ćƒ¬ćƒ“ē•Ŗēµ„ć§ć®ē“¹ä»‹ć‚„SNSć§ć®ę‹”ę•£ć«ć‚ˆć£ć¦ć€ć”ćēŸ­ę™‚é–“ć§ēˆ†ē™ŗēš„ć«å†ē”Ÿę•°ćŒä¼øć³ć‚‹ļ¼ˆćƒć‚ŗć‚‹ļ¼‰ćØć„ć†ē‰¹å¾“ćŒć‚ć‚Šć¾ć™ć€‚ć¾ćŸć€å­£ēÆ€ę€§ć®å¤‰å‹•ć‚‚å¤§ććć€å®ŸćÆå¤šćć®č¦ē“ ćŒēµ”ćæåˆć£ć¦č¤‡é›‘ć«ćŖć£ć¦ć„ć¾ć™ć€‚ē¾ēŠ¶ć§ćÆć€ćƒ‡ćƒ¼ć‚æć®č†Øå¤§ć•ć‹ć‚‰ćƒ€ćƒƒć‚·ćƒ„ćƒœćƒ¼ćƒ‰ć®č”Øē¤ŗć«ę™‚é–“ćŒć‹ć‹ć£ć¦ć—ć¾ć†ćØć„ć†ćƒ‘ćƒ•ć‚©ćƒ¼ćƒžćƒ³ć‚¹é¢ć®čŖ²é”Œć‚‚ę®‹ć£ć¦ć„ć¾ć™ć€‚ ćć—ć¦ä½•ć‚ˆć‚Šć€ä»Šå¾ŒęŒ‘ęˆ¦ć—ćŸć„ć®ćÆć€ć€Œä½•ćŒęµč”Œć£ć¦ć„ć‚‹ć‹ć€ć‚’ęē¤ŗć™ć‚‹ć ć‘ć§ćŖćć€ć€ŒćŖćœćć‚ŒćŒęµč”Œć£ć¦ć„ć‚‹ć®ć‹ć€ćØć„ć†ć‚¤ćƒ³ć‚µć‚¤ćƒˆć¾ć§ęä¾›ć™ć‚‹ć“ćØć§ć™ć€‚ ä¾‹ćˆć°ć€ć€Œ"é¶ć‚€ć­č‚‰"ć®č¦–č“ę•°ćŒę€„å¢—ć—ćŸć€ćØć„ć†äŗ‹å®Ÿć ć‘ć§ćŖćć€ć€Œćć‚ŒćÆć€äŗŗę°—ćƒ†ćƒ¬ćƒ“ē•Ŗēµ„ć§"節瓄"ć‚’ćƒ†ćƒ¼ćƒžć«ć—ćŸē‰¹é›†ćŒēµ„ć¾ć‚Œć€SNS恧"ē°”å˜ć§ćŠć„ć—ć„é¶ć‚€ć­č‚‰ćƒ¬ć‚·ćƒ”"ćØć„ć†ęŠ•ēØæćŒę‹”ę•£ć•ć‚ŒćŸć‹ć‚‰ć ć€ćØć„ć†čƒŒę™Æć¾ć§ć‚’č§£ćę˜Žć‹ć—ćŸć„ć€‚ć„ć‚ć°ć€ ćƒć‚ŗć®å› ę•°åˆ†č§£ 恧恙怂 ć“ć®ć€ŒćŖćœć€ć‚’č§£ćę˜Žć‹ć™ć“ćØćŒć§ćć‚Œć°ć€ćƒ‡ćƒŖćƒƒć‚·ćƒ„ćƒŖć‚µćƒ¼ćƒćÆćƒ”ćƒ¼ć‚«ćƒ¼ćƒ»å°å£²ć«åÆ¾ć—ć¦ć€ć‚ˆć‚Šē²¾åŗ¦ć®é«˜ć„éœ€č¦äŗˆęø¬ć‚„ć€åŠ¹ęžœēš„ćŖč²©å£²ęˆ¦ē•„ć®ē«‹ę”ˆć‚’ć‚µćƒćƒ¼ćƒˆć§ćć‚‹ćÆćšć§ć™ć€‚ ęœ€å¾Œć«ćŖć‚Šć¾ć™ćŒć€1ćƒ¶ęœˆé–“ęø©ć‹ćć€ćć—ć¦ē†±åæƒć«ć”ęŒ‡å°Žćć ć•ć£ćŸćƒ”ćƒ³ć‚æćƒ¼ć®ēš†ę§˜ć€ćƒ‡ćƒŖćƒƒć‚·ćƒ„ćƒŖć‚µćƒ¼ćƒéƒØé–€ć®ēš†ę§˜ć€ęœ¬å½“ć«ć‚ć‚ŠćŒćØć†ć”ć–ć„ć¾ć—ćŸļ¼
ćÆć˜ć‚ć« ć“ć‚“ć«ć”ćÆć€‚ę Ŗå¼ä¼šē¤¾ć‚Øćƒ–ćƒŖćƒ¼ć®é–‹ē™ŗ1éƒØć§éƒØé•·ć‚’å‹™ć‚ć¦ć„ć‚‹ę‘äøŠć§ć™ć€‚ 弊社は2023幓のGithub CopilotとChatGPT plusć®åˆ©ē”Øć‚µćƒćƒ¼ćƒˆć‹ć‚‰å§‹ć¾ć‚Šć€ē›“čæ‘ć§ćÆ4꜈恔悍恋悉Cursorć‚’å°Žå…„ć—ć€ē”ŸęˆAIę“»ē”Øć«åŠ›ć‚’å…„ć‚Œć¦ć„ć¾ć™ć€‚ä»Šå›žćÆć€ē›“čæ‘ć®ē”ŸęˆAIę“»ē”ØęŽØé€²ć§å–ć‚Šēµ„ć‚“ć å†…å®¹ć‚„ć€ē¤¾å†…ć§å®Ÿę–½ć—ćŸē”ŸęˆAIę“»ē”ØēŠ¶ę³ć‚¢ćƒ³ć‚±ćƒ¼ćƒˆć®ēµęžœć€ćć—ć¦ä»Šå¾Œć®å–ć‚Šēµ„ćæć«ć¤ć„ć¦ćŠč©±ć—ć—ć¾ć™ć€‚ ē›“čæ‘ć®å–ć‚Šēµ„ćæ ćƒ„ćƒ¼ćƒ«ć®å°Žå…„ć ć‘ć§ćÆćŖćć€ēµ„ē¹”å…Øä½“ć§AIć‚’ä½æć„ć“ćŖć—ć¦ć„ććŸć‚ć«ć€ē›“čæ‘ć§ćÆä»„äø‹ć®ć‚ˆć†ćŖå–ć‚Šēµ„ćæć‚’č”Œć£ć¦ć„ć¾ć™ć€‚ ē”ŸęˆAIę“»ē”Øć®ēŸ„č¦‹ć‚’å…±ęœ‰ć™ć‚‹slackćƒćƒ£ćƒ³ćƒćƒ«ć®é–‹čØ­ AIę“»ē”Øå‹‰å¼·ä¼šć®é–‹å‚¬ AIćƒ†ćƒ¼ćƒžć«ēµžć£ćŸęŒ‘ęˆ¦WEEKの開催 Figma Devć‚·ćƒ¼ćƒˆć‚’é…åøƒć—ć€MCPć‚’ę“»ē”Øć—ćŸćƒ‡ć‚¶ć‚¤ćƒ³ć‹ć‚‰ć‚³ćƒ¼ćƒ‰ē”Ÿęˆć®ę¤œčØ¼ ä»•ę§˜ę›øć‚’Githubē®”ē†ć«ē§»č”Œć—ćŸé–‹ē™ŗćƒ•ćƒ­ćƒ¼ć®ę¤œčØ¼ ć“ć‚Œć‚‰ć®å–ć‚Šēµ„ćæć«é–¢ć—ć¦ē°”å˜ć«čŖ¬ę˜Žć—ć¾ć™ć€‚ ē”ŸęˆAIę“»ē”Øć®ēŸ„č¦‹ć‚’å…±ęœ‰ć™ć‚‹slackćƒćƒ£ćƒ³ćƒćƒ«ć®é–‹čØ­ Cursorć®é…åøƒćØåˆć‚ć›ć¦ć€å„č‡ŖćŒåˆ©ē”Øć—ć¦ć„ć‚‹AIćƒ„ćƒ¼ćƒ«ć®ēŸ„č¦‹ć‚’ēµ„ē¹”ć§č“„ē©ć—ć¦ć„ććŸć‚ć€ę°—č»½ć«ć‚·ć‚§ć‚¢ć§ćć‚‹slackćƒćƒ£ćƒ³ćƒćƒ«ć‚’é–‹čØ­ć—ć¾ć—ćŸć€‚å®Ÿéš›ć«å€‹äŗŗć®ćƒ—ćƒ­ćƒ³ćƒ—ćƒˆć‚„ćƒ«ćƒ¼ćƒ«ć€AIćƒ„ćƒ¼ćƒ«ć®ęœ€ę–°ęƒ…å ±ćŒå…±ęœ‰ć•ć‚Œć€ćƒ”ćƒ³ćƒćƒ¼ć®ę“»ē”Øćƒ¬ćƒ™ćƒ«å‘äøŠć«ć‚‚ć¤ćŖćŒć£ć¦ć„ć¾ć™ć€‚ ē¾åœØć“ć®å–ć‚Šēµ„ćæćÆå…Øē¤¾ć«åŗƒćŒć‚Šć€ē‰¹å®šć®ć‚¹ć‚æćƒ³ćƒ—ć‚’ęŠ¼ć™ćØē¤¾å†…ć®AIę“»ē”Øå…±ęœ‰ē”Øćƒćƒ£ćƒ³ćƒćƒ«ć«ęŠ•ēØæć•ć‚Œć‚‹ä»•ēµ„ćæć§ć€ä¼šē¤¾å…Øä½“ć®AIę“»ē”ØēŸ„č¦‹ć‚’č“„ē©ć—ć¦ć„ć¾ć™ć€‚ AIę“»ē”Øå‹‰å¼·ä¼šć®é–‹å‚¬ 社内でAIę“»ē”Øć‚’ē©ę„µēš„ć«å–ć‚Šēµ„ć‚“ć§ć„ć‚‹ćƒ”ćƒ³ćƒćƒ¼ćŒę•°äŗŗé›†ć¾ć‚Šć€AIćƒ„ćƒ¼ćƒ«ć‚’ę“»ē”Øć—ćŸé–‹ē™ŗåŠ¹ēŽ‡åŒ–å‹‰å¼·ä¼šć‚’äø»å‚¬ć—ć¦ćć‚Œć¦ć„ć¾ć™ć€‚éš”é€±ć§é–‹å‚¬ć•ć‚Œć€ęŒć”å›žć‚Šć§ē™ŗč”Øć‚„ćƒćƒ³ć‚ŗć‚Ŗćƒ³ć‚’č”Œć£ć¦ć„ć¾ć™ć€‚ ć“ć‚Œć¾ć§ć®ē™ŗč”Øć§ćÆå„AIćƒ„ćƒ¼ćƒ«ć®ä½æć„ć“ćŖć—ę–¹ć‚„MCPć®ä½œć‚Šę–¹ć«ć¤ć„ć¦ć®å†…å®¹ćŒå¤šć„ć§ć™ć€‚ å‹‰å¼·ä¼šć®ę§˜å­ AIćƒ†ćƒ¼ćƒžć«ēµžć£ćŸęŒ‘ęˆ¦WEEKの開催 ęŒ‘ęˆ¦WEEKćÆć€é€šåøøć®äŗ‹ę„­éƒØćƒ­ćƒ¼ćƒ‰ćƒžćƒƒćƒ—ć‹ć‚‰é›¢ć‚Œć€ć‚Øćƒ³ć‚øćƒ‹ć‚¢ćŒč‡Ŗć‚‰ęę”ˆć—ćŸćƒ†ćƒ¼ćƒžć«åŸŗć„ć„ć¦ęŠ€č”“ēš„ćŖęŒ‘ęˆ¦ć«é›†äø­ć™ć‚‹ē¤¾å†…ć®å–ć‚Šēµ„ćæć§ć™ć€‚ę™®ę®µćÆč‡Ŗē”±ćŖćƒ†ćƒ¼ćƒžć§é–‹å‚¬ć—ć¦ć„ć¾ć™ćŒć€ä»Šå›žćÆć‚ćˆć¦ćƒ†ćƒ¼ćƒžć‚’AIę“»ē”Øć«ēµžć£ć¦å–ć‚Šēµ„ć‚€ć‚¢ć‚¤ćƒ‡ć‚¢ć‚’å‹Ÿé›†ć—å®Ÿę–½ć—ć¾ć—ćŸć€‚č‡Ŗä½œRedash MCPć‚µćƒ¼ćƒćƒ¼ć®ä½œęˆć‚„Sentryć®ć‚Øćƒ©ćƒ¼é€šēŸ„ć‚’Devinć§čŖæęŸ»ć—å®Ÿč”ŒčØˆē”»ć‚’ē«‹ć¦ć‚‹ä»•ēµ„ćæć‚’ä½œć‚‹ćŖć©ć€ä»Šå¾Œć®ć•ć‚‰ćŖć‚‹AIę“»ē”Øć«ć¤ćŖćŒć‚‹ęˆęžœćŒå‡ŗć¾ć—ćŸć€‚ č©³ć—ććÆćƒ†ćƒƒć‚Æćƒ–ćƒ­ć‚°ć«ć‚‚ę›øć„ć¦ć„ć‚‹ć®ć§ćœć²ć”č¦§ćć ć•ć„ć€‚ Figma Devć‚·ćƒ¼ćƒˆć‚’é…åøƒć—ć€MCPć‚’ę“»ē”Øć—ćŸćƒ‡ć‚¶ć‚¤ćƒ³ć‹ć‚‰ć‚³ćƒ¼ćƒ‰ē”Ÿęˆć®ę¤œčØ¼ ć¾ć ćƒ™ćƒ¼ć‚æć§ćÆć‚ć‚Šć¾ć™ćŒć€ä»Šå¹“ć®6月にFigmaå…¬å¼ć®MCPćŒē™ŗč”Øć•ć‚Œć¾ć—ćŸć€‚ 恓恮MCPはFigma恮Devć‚·ćƒ¼ćƒˆć§ćŖć„ćØć‚¢ć‚Æć‚»ć‚¹ć§ććŖć„ćŸć‚ć€ć‚·ćƒ¼ćƒˆć®é…åøƒć‚’č”Œć„ćŖćŒć‚‰ć€ćƒ‡ć‚¶ć‚¤ćƒ³ć‹ć‚‰ć‚¢ćƒ—ćƒŖē”»é¢ć®ć‚³ćƒ¼ćƒ‰ē”Ÿęˆć¾ć§ć‚’ę¤œčØ¼ć—ć¾ć—ćŸć€‚ćƒ‡ć‚¶ć‚¤ćƒŠćƒ¼ćØć‚Øćƒ³ć‚øćƒ‹ć‚¢ćŒå”åŠ›ć—ćŖćŒć‚‰åŠ¹ęžœēš„ćŖę“»ē”Øę–¹ę³•ć‚’å·„å¤«ć—ć¦ćć‚ŒćŸć“ćØć§ē”Ÿē”£ę€§ć‚‚å‘äøŠć—ć€ē¾åœØćÆę­£å¼å°Žå…„ć—ć¦ē¾å “ć§ä½æē”Øć—ć¦ć„ć¾ć™ć€‚ ä»•ę§˜ę›øć‚’Githubē®”ē†ć«ē§»č”Œć—ćŸé–‹ē™ŗćƒ•ćƒ­ćƒ¼ć®ę¤œčØ¼ å¼Šē¤¾ć§ćÆć“ć‚Œć¾ć§ä»•ę§˜ę›øć«Confluenceć‚’ę“»ē”Øć—ć¦ćć¾ć—ćŸćŒć€ę”¹ć‚ć¦AIę™‚ä»£ć«ćŠć‘ć‚‹ä»•ę§˜ę›øē®”ē†ć®åœØć‚Šę–¹ć‚’č¦‹ē›“ć—ć€PdMć®ē”Ÿē”£ę€§å‘äøŠć‚„AIć«ć‚ˆć‚‹ē²¾åŗ¦ć®é«˜ć„å®Ÿč£…ć‚’å®Ÿē¾ć™ć‚‹ę–¹ę³•ć‚’ę¤œčØŽć™ć‚‹ć‚ˆć†ć«ćŖć‚Šć¾ć—ćŸć€‚ ć‚‚ć”ć‚ć‚“Confluence恧悂MCPć‚’ä½æē”Øć—ć¦ć‚³ćƒ³ćƒ†ć‚­ć‚¹ćƒˆć‚’čŖ­ćæč¾¼ć‚€ć“ćØćÆåÆčƒ½ć§ć—ćŸćŒć€äø€éƒØć®ćƒćƒ¼ćƒ ć§ćÆä»„äø‹ć®ć‚ˆć†ćŖćƒ”ćƒŖćƒƒćƒˆćŒć‚ć‚‹ć®ć§ćÆćŖć„ć‹ćØć„ć†ä»®čŖ¬ć®ć‚‚ćØć€é–‹ē™ŗę‰‹ę³•ć‚’å¤‰ę›“ć—ć¦ę¤œčØ¼ć‚’č”Œć£ć¦ć„ć¾ć™ć€‚ PdMがCursorć‚’ä½æć£ć¦ćƒžćƒ¼ć‚Æćƒ€ć‚¦ćƒ³ć§ć®ä»•ę§˜ę›øä½œęˆć€ē·Øé›†ć‚’ć‚¹ćƒ ćƒ¼ć‚ŗć«č”Œćˆć‚‹ ćƒ‰ć‚­ćƒ„ćƒ”ćƒ³ćƒˆć®ćƒŖćƒć‚øćƒˆćƒŖć‚’ä½µē”Øć™ć‚‹ć“ćØć§AIćøä»•ę§˜ć‚’ē°”å˜ć«ä¼ćˆć‚‹ć“ćØćŒć§ćć€čØ­čØˆć‚„ć‚³ćƒ¼ćƒ‰ē”Ÿęˆć®ē²¾åŗ¦ćŒå‘äøŠć™ć‚‹ ćƒ‰ć‚­ćƒ„ćƒ”ćƒ³ćƒˆć®ę›“ę–°ć€ćƒ¬ćƒ“ćƒ„ćƒ¼ć«AIćƒ„ćƒ¼ćƒ«ļ¼ˆDevin, CodeRabbitē­‰ļ¼‰ć‚’ę“»ē”Øć§ćć‚‹ ć“ć®ę¤œčØ¼ćÆć¾ć é€”äø­ę®µéšŽć§ę­£å¼å°Žå…„ć®åˆ¤ę–­ć«ćÆč‡³ć£ć¦ć„ć¾ć›ć‚“ćŒć€ęŒÆć‚Ščæ”ć‚Šć®ć‚æć‚¤ćƒŸćƒ³ć‚°ć§ēµęžœć‚’ē™ŗäæ”ć—ć¦ć„ććŸć„ćØę€ć£ć¦ć„ć¾ć™ć€‚ ē¾åœØć®ē”ŸęˆAIć®ę“»ē”ØēŠ¶ę³ ć“ć†ć—ćŸå–ć‚Šēµ„ćæć‚’é€²ć‚ć‚‹äø­ć§ć€ē¾åœØć®ć‚Øćƒ³ć‚øćƒ‹ć‚¢ēµ„ē¹”ć«ćŠć‘ć‚‹ē”ŸęˆAIę“»ē”ØēŠ¶ę³ćÆć©ć†ćŖć£ć¦ć„ć‚‹ć§ć—ć‚‡ć†ć‹ć€‚ē›“čæ‘ć§å®Ÿę–½ć—ćŸē”ŸęˆAIę“»ē”Øć‚¢ćƒ³ć‚±ćƒ¼ćƒˆć®ēµęžœć‚’å…±ęœ‰ć—ć¾ć™ć€‚ ę„­å‹™ć§ęœ€ć‚‚åˆ©ē”Øć—ć¦ć„ć‚‹AIć‚³ćƒ¼ćƒ‡ć‚£ćƒ³ć‚°ćƒ„ćƒ¼ćƒ« ć‚¢ćƒ³ć‚±ćƒ¼ćƒˆēµęžœ ćƒ„ćƒ¼ćƒ«å 割合 äŗŗę•° Cursor 55.6% 15 Claude Code 40.7% 11 Android Studio Gemini 3.7% 1 éøęŠžč‚¢ć«ćÆć€ŽAIćƒ„ćƒ¼ćƒ«ć‚’ä½æć£ć¦ć„ćŖć„ć€ćØć„ć†å›žē­”ć‚‚ć§ćć‚‹ć‚ˆć†ć«ć—ć¦ć„ć¾ć—ćŸćŒć€å…Øć‚Øćƒ³ć‚øćƒ‹ć‚¢ćŒAIć‚³ćƒ¼ćƒ‡ć‚£ćƒ³ć‚°ćƒ„ćƒ¼ćƒ«ć‚’åˆ©ē”Øć—ć¦ć„ć‚‹ēµęžœćØćŖć‚Šć¾ć—ćŸć€‚ åˆ©ē”Øć—ć¦ć„ć‚‹AIć‚³ćƒ¼ćƒ‡ć‚£ćƒ³ć‚°ćƒ„ćƒ¼ćƒ«ć§ćÆć€ä¼šē¤¾ćŒć‚µćƒćƒ¼ćƒˆć—ć¦ć„ć‚‹CursorćŒęœ€ć‚‚å¤šćåˆ©ē”Øć•ć‚Œć¦ć„ć‚‹ć‚‚ć®ć®ć€Claude Codeć®åˆ©ē”Øć‚‚ę“»ē™ŗć«ćŖć£ć¦ćć¦ć„ć¾ć™ć€‚CursorとClaude Codećć‚Œćžć‚Œć®éøęŠžē†ē”±ćÆä»„äø‹ć®é€šć‚Šć§ć™ć€‚ Cursor ä¼šē¤¾ć§ć‚µćƒćƒ¼ćƒˆć‚’ć‚‚ć‚‰ćˆć¦ćŠć‚Šć€č¦ä»¶ć‚’ęŗ€ćŸć—ć¦ć„ć‚‹ ę„­å‹™ć®äø­ć§ć‚·ćƒ¼ćƒ ćƒ¬ć‚¹ć«åˆ©ē”Øć§ćć‚‹ćŸć‚ ē§˜ä¼ć®ć‚æćƒ¬ćŒå®Œęˆć—ć¦ć„ć¦ć‚€ć—ć‚ä»–ć®ćƒ„ćƒ¼ćƒ«ć«ē§»č”Œć™ć‚‹ć‚³ć‚¹ćƒˆćŒé«˜ć„ ć‚³ćƒ¼ćƒ‰č£œå®ŒćŒę„½ č¤‡ę•°ć®ć‚Øćƒ³ć‚øćƒ‹ć‚¢ćŒå…±é€šć—ć¦ä½æćˆć‚‹ćƒ„ćƒ¼ćƒ«ć‚’ć€ć¾ćšä½æćˆć‚‹ć‚‚ć®ć«ć—ćŖć„ćØć€ēµå±€å…Øä½“ć®ē”Ÿē”£ę€§ćÆäøŠćŒć‚‰ćŖć„ Claude Code åæ…č¦ćŖęƒ…å ±ć‚’čŖ­ćæč¾¼ć‚“ć§ćć‚Œć¦ć€ć‚³ćƒ³ćƒ†ć‚­ć‚¹ćƒˆćŒåŸ‹ć¾ć‚‹ćØåœ§ēø®ć—ć¦ć†ć¾ćęƒ…å ±ć‚’ę‰±ć£ć¦ćć‚Œć‚‹ ć‚æć‚¹ć‚Æē®”ē†ćØć‹ć‚’č‡Ŗåˆ†ć§ToDoć‚’ē«‹ć¦ć¦ć†ć¾ćé€²ć‚ć¦ćć‚Œć‚‹ hooks悄custom commandćŒå„Ŗē§€ OpusćŒč³¢ć„ć®ć§ä½æć„ćŸć„ TerminaläøŠć§å®Œēµć§ćć‚‹ć®ćŒč‰Æć„ cursorć‚ˆć‚Šē²˜ć£ć¦ćć‚Œć‚‹ äø€ē•Ŗå®‰å®šć—ćŸęˆęžœå‡ŗć‚‹å°č±”ćŒć‚ć‚‹ å„½ććŖIDEć§åˆ©ē”Øć§ćć‚‹ AIć‚³ćƒ¼ćƒ‡ć‚£ćƒ³ć‚°ćƒ„ćƒ¼ćƒ«ć®åˆ©ē”Øé »åŗ¦ ć‚¢ćƒ³ć‚±ćƒ¼ćƒˆēµęžœ 頻度 割合 äŗŗę•° ć»ć¼ęÆŽę—„ć€åøøć«åˆ©ē”Øć—ć¦ć„ć‚‹ 77.8% 21 週恫2怜3å›žēØ‹åŗ¦ 18.5% 5 週恫1å›žēØ‹åŗ¦ 3.7% 1 ē“„8å‰²ć®ć‚Øćƒ³ć‚øćƒ‹ć‚¢ćŒęÆŽę—„åˆ©ē”Øć—ć¦ć„ć‚‹ēŠ¶ę³ć§ć—ćŸć€‚ē¾åœØć€ć‚Øćƒ³ć‚øćƒ‹ć‚¢ć«ćØć£ć¦AIć‚³ćƒ¼ćƒ‡ć‚£ćƒ³ć‚°ćƒ„ćƒ¼ćƒ«ćŒåæ…č¦äøåÆę¬ ćŖå­˜åœØć«ćŖć£ć¦ć„ć‚‹ć“ćØćŒåˆ†ć‹ć‚Šć¾ć™ć€‚ AIć‚³ćƒ¼ćƒ‡ć‚£ćƒ³ć‚°ćƒ„ćƒ¼ćƒ«ć®åˆ©ē”Øē›®ēš„ č¤‡ę•°éøęŠžć®å½¢å¼ć§ć©ć‚“ćŖē›®ēš„ć§åˆ©ē”Øć—ć¦ć„ć‚‹ć‹ć‚’å›žē­”ć—ć¦ć‚‚ć‚‰ć„ć¾ć—ćŸć€‚ ē›®ēš„ 割合 äŗŗę•° ć‚³ćƒ¼ćƒ‰ć®ę–°č¦ä½œęˆćƒ»ć‚¹ćƒ‹ćƒšćƒƒćƒˆē”Ÿęˆ 96.3% 26 ę—¢å­˜ć‚³ćƒ¼ćƒ‰ć®äæ®ę­£ćƒ»ćƒŖćƒ•ć‚”ć‚Æć‚æćƒŖćƒ³ć‚° 77.8% 21 ć‚³ćƒ¼ćƒ‰ć®ćƒ‡ćƒćƒƒć‚°ćƒ»ć‚Øćƒ©ćƒ¼åŽŸå› ć®ē‰¹å®š 74.1% 20 ćƒ‰ć‚­ćƒ„ćƒ”ćƒ³ćƒˆć‚„ć‚³ćƒ”ćƒ³ćƒˆć®č‡Ŗå‹•ē”Ÿęˆ 59.3% 16 ä»•ę§˜ć‹ć‚‰ć®ć‚³ćƒ¼ćƒ‰å®Ÿč£… 48.1% 13 ęŠ€č”“ēš„ćŖčŖæęŸ»ć‚„å­¦ēæ’ć®č£œåŠ© 59.3% 16 ćƒ†ć‚¹ćƒˆć‚³ćƒ¼ćƒ‰ć®ä½œęˆ 48.1% 13 å¤šå°‘ć®å·®ćÆć‚ć‚Šć¾ć™ćŒć€å…Øä½“ćØć—ć¦ć‚Øćƒ³ć‚øćƒ‹ć‚¢ć®ć»ćØć‚“ć©ć®ę„­å‹™ć‚·ćƒ¼ćƒ³ć§AIć‚’ä½æć£ć¦ć„ć‚‹ć“ćØćŒåˆ†ć‹ć‚Šć¾ć™ć€‚ é–‹ē™ŗå…Øä½“ć®ē”Ÿē”£ę€§ćÆć©ć®ēØ‹åŗ¦å¤‰åŒ–ć—ćŸć‹ ē”Ÿē”£ę€§ 割合 äŗŗę•° ē”Ÿē”£ę€§2å€ä»„äøŠå‘äøŠć—ćŸ 48.1% 13 ē”Ÿē”£ę€§1.5å€ēØ‹åŗ¦å‘äøŠć—ćŸ 33.3% 9 ē”Ÿē”£ę€§1.2~1.3å€ēØ‹åŗ¦å‘äøŠć—ćŸ 14.8% 4 å¤‰ć‚ć‚‰ćŖć„ 3.7% 1 ē“„åŠę•°ć®ć‚Øćƒ³ć‚øćƒ‹ć‚¢ćŒć€Žē”Ÿē”£ę€§ćŒ2å€ä»„äøŠå‘äøŠć—ćŸć€ćØå›žē­”ć—ć¦ćŠć‚Šć€å¤§ććŖåŠ¹ęžœćŒč¦‹ć‚‰ć‚Œć¦ć„ć¾ć™ć€‚å…Øä½“ć‚’č¦‹ć¦ć‚‚2å€ä»„äøŠć«ćÆč‡³ć‚‰ćŖć„ć‚‚ć®ć®ć€ć»ćØć‚“ć©ć®ć‚Øćƒ³ć‚øćƒ‹ć‚¢ćŒē”ŸęˆAIć«ć‚ˆć£ć¦ē”Ÿē”£ę€§ćŒå‘äøŠć—ćŸćØå›žē­”ć—ć¾ć—ćŸć€‚ć“ć‚ŒćÆå®šę€§é¢ć ć‘ć§ćŖćć€ä»„å‰ć«CTOć®ä»Šäŗ•ćŒåŸ·ē­†ć—ćŸćƒ–ćƒ­ć‚°ć§ć‚‚PRꕰ恌2å€ć«ćŖć£ćŸć“ćØćŒå ±å‘Šć•ć‚Œć¦ćŠć‚Šć€ē”Ÿē”£ę€§ęø¬å®šć®å®Œå…ØćŖęŒ‡ęØ™ć§ćÆćŖć„ć‚‚ć®ć®ć€å®šé‡é¢ć§ć‚‚č£ä»˜ć‘ćØćŖć‚‹ēµęžœćŒå‡ŗć¦ć„ć¾ć™ć€‚ č©¦ć—ć¦ćæćŸć„AIćƒ„ćƒ¼ćƒ«ć‚„ä¼šē¤¾ć«ę±‚ć‚ć‚‹AIć‚µćƒćƒ¼ćƒˆ åˆ©ē”ØēŠ¶ę³ć‚¢ćƒ³ć‚±ćƒ¼ćƒˆć®ä»–ć«ć€ä»Šå¾Œć®AIę“»ē”Øć‚’č€ƒćˆć‚‹äøŠć§č©¦ć—ć¦ćæćŸć„ćƒ„ćƒ¼ćƒ«ć‚„ä¼šē¤¾ć«ę±‚ć‚ć‚‹ć‚µćƒćƒ¼ćƒˆć«ć¤ć„ć¦ć‚‚ć€ä»»ę„ć®č‡Ŗē”±čØ˜čæ°ć‚¢ćƒ³ć‚±ćƒ¼ćƒˆć‚’å®Ÿę–½ć—ć¾ć—ćŸć€‚ č©¦ć—ć¦ćæćŸć„AIćƒ„ćƒ¼ćƒ«ć§ćÆä»„äø‹ć®ć‚ˆć†ćŖćƒ„ćƒ¼ćƒ«ćŒäøŠćŒć£ć¦ćć¾ć—ćŸć€‚ē‰¹ć«AIćƒ¬ćƒ“ćƒ„ćƒ¼ē³»ć®ćƒ„ćƒ¼ćƒ«ć‚’ä½æć£ć¦ćæćŸć„ćØć„ć†å£°ćŒå¤šć‹ć£ćŸć§ć™ć€‚ CodeRabbit などのAIćƒ¬ćƒ“ćƒ„ćƒ¼ē³»ćƒ„ćƒ¼ćƒ« Autify态MagicPodなどのQAćƒ„ćƒ¼ćƒ« ļ¼ˆćƒŖćƒŖćƒ¼ć‚¹ć•ć‚ŒćŸć‚‰ļ¼‰Xcodeć®ć‚³ćƒ¼ćƒ‡ć‚£ćƒ³ć‚°ć‚¤ćƒ³ćƒ†ćƒŖć‚øć‚§ćƒ³ć‚¹ę©Ÿčƒ½ ć¾ćŸć€ä¼šē¤¾ć«ę±‚ć‚ć‚‹ć‚µćƒćƒ¼ćƒˆćØć—ć¦åœ§å€’ēš„ć«å¤šć‹ć£ćŸć®ćŒć€åˆ©ē”Øćƒ„ćƒ¼ćƒ«éøå®šć«ćŠć„ć¦å€‹äŗŗć«äø€å®šć®č‡Ŗē”±åŗ¦ć‚’čØ­ć‘ć€ę§˜ć€…ćŖćƒ„ćƒ¼ćƒ«ć‚’ć‚µćƒćƒ¼ćƒˆć—ć¦ć»ć—ć„ćØć„ć†å£°ć§ć—ćŸć€‚ć“ć®čƒŒę™Æć«ćÆć€å¼Šē¤¾ćŒCursorć‚µćƒćƒ¼ćƒˆć«é™å®šć—ć¦ć‚¹ć‚æćƒ¼ćƒˆć—ćŸć“ćØćŒć‚ć‚Šć€ć‚Øćƒ³ć‚øćƒ‹ć‚¢ēµ„ē¹”ćØć—ć¦ć‚‚ć‚µćƒćƒ¼ćƒˆć®åœØć‚Šę–¹ć‚„äŗˆē®—ć®ēµ„ćæę–¹ć‚’č¦‹ē›“ć™åæ…č¦ę€§ć‚’ę„Ÿć˜ć¾ć—ćŸć€‚ ēµęžœć‚’čøć¾ćˆćŸē¾åœØć®å–ć‚Šēµ„ćæ ē¾åœØćÆć‚¢ćƒ³ć‚±ćƒ¼ćƒˆēµęžœć‚„ē¾å “ć§ć®čŖ²é”Œę„Ÿć‚’čøć¾ćˆć€ä»„äø‹ć®ć‚ˆć†ćŖå–ć‚Šēµ„ćæć‚’å§‹ć‚ć¦ć„ć¾ć™ć€‚ AIćƒ¬ćƒ“ćƒ„ćƒ¼ćƒ„ćƒ¼ćƒ«ć®ę¤œčØ¼ ć‚¢ćƒ³ć‚±ćƒ¼ćƒˆć§å¤šćć®äŗŗćŒAIćƒ¬ćƒ“ćƒ„ćƒ¼ē³»ćƒ„ćƒ¼ćƒ«ć«é«˜ć„é–¢åæƒć‚’ē¤ŗć—ć¦ć„ć‚‹ć“ćØćŒåˆ†ć‹ć‚Šć¾ć—ćŸć€‚ć¾ćŸć€ä»Šå¾ŒAIć«ć‚ˆć‚‹ć‚¢ć‚¦ćƒˆćƒ—ćƒƒćƒˆé‡ćŒå¢—åŠ ć™ć‚‹äø­ć§ć€äŗŗé–“ć®ćƒ¬ćƒ“ćƒ„ćƒ¼č² č·č»½ęø›ćÆéžåøøć«é‡č¦ćŖćƒ†ćƒ¼ćƒžćØćŖć‚‹ćŸć‚ć€ć‚Øćƒ³ć‚øćƒ‹ć‚¢ēµ„ē¹”ćØć—ć¦ć‚‚ć“ć®åˆ†é‡Žćøć®ęŠ•č³‡ć‚’åŠ é€Ÿć™ć¹ćę¤œčØ¼ć‚’å§‹ć‚ć¦ć„ć¾ć™ć€‚ 恙恧恫CodeRabbitć®ę¤œčØ¼ćÆå®Œäŗ†ć—ć¦ćŠć‚Šć€Github Copilot悄Cursor Bugbotć®ćƒ¬ćƒ“ćƒ„ćƒ¼ćØęÆ”č¼ƒć—ć¦ć‚‚ē²¾åŗ¦ćŒé«˜ćć€å…Øćƒćƒ¼ćƒ ć‹ć‚‰å°Žå…„åøŒęœ›ć®å£°ć‚’ć‚‚ć‚‰ć£ć¦ć„ć¾ć™ć€‚AIćƒ¬ćƒ“ćƒ„ćƒ¼ē‰¹åŒ–ćƒ„ćƒ¼ćƒ«ćŖć‚‰ć§ćÆć®å¼·ćæć‚’å®Ÿę„Ÿć—ć¦ćŠć‚Šć€ä»Šå¾ŒćÆē«¶åˆćƒ„ćƒ¼ćƒ«ć§ć‚ć‚‹ Greptile ć®ę¤œčØ¼ć‚‚č”Œć„ć€ęÆ”č¼ƒę¤œčØŽć‚’é€²ć‚ć‚‹äŗˆå®šć§ć™ć€‚ ć‚ˆć‚ŠęŸ”č»ŸćŖAIćƒ„ćƒ¼ćƒ«ć®ć‚µćƒćƒ¼ćƒˆ ä¼šē¤¾ćøć®ć‚µćƒćƒ¼ćƒˆč¦ęœ›ć§ć‚‚ć‚ć‚Šć¾ć—ćŸćŒć€ä»Šå¾ŒAIćƒ„ćƒ¼ćƒ«é–“ć®ē«¶äŗ‰ćŒęæ€åŒ–ć—ć€ć‚ˆć‚Šå„Ŗć‚ŒćŸę–°ć—ć„ćƒ„ćƒ¼ćƒ«ćŒē™»å “ć™ć‚‹äø­ć§ć€ē‰¹å®šćƒ„ćƒ¼ćƒ«ć«é™å®šć—ćŸć‚µćƒćƒ¼ćƒˆćÆč‰Æć„ęˆ¦ē•„ć§ćÆćŖć„ć®ć§ćÆćŖć„ć‹ćØć„ć†čŖ²é”Œę„č­˜ćŒć‚ć‚Šć¾ć—ćŸć€‚ć¾ćŸć€ē¾ēŠ¶ć®Cursoråˆ©ē”Øå®Ÿę…‹ć‚’č¦‹ć‚‹ćØć€AIć‚’ē©ę„µę“»ē”Øć™ć‚‹ć‚Øćƒ³ć‚øćƒ‹ć‚¢ćÆć™ćć«é«˜é€ŸćƒŖć‚Æć‚Øć‚¹ćƒˆć®äøŠé™ć«é”ć—ć¦ć—ć¾ć†ćŸć‚ć€å¾“é‡čŖ²é‡‘ć§ć®ć‚µćƒćƒ¼ćƒˆčæ½åŠ ć®åæ…č¦ę€§ć‚‚ę„Ÿć˜ć¦ć„ć¾ć—ćŸć€‚ ćć“ć§ć‚µćƒćƒ¼ćƒˆč²»ē”Øć‚’ę‹”å¤§ć—ć€å€‹äŗŗćŒåˆ©ē”Øć™ć‚‹AIćƒ„ćƒ¼ćƒ«ć‚’éøęŠžć§ćć‚‹ć‚ˆć†ć«ć—ć¤ć¤ć€å‰²ć‚Šå½“ć¦ēÆ„å›²å†…ć§ć®å¾“é‡čŖ²é‡‘ć‚‚åæ…č¦ć«åæœć˜ć¦ć‚µćƒćƒ¼ćƒˆć—ć¦ć„ćę–¹é‡ć§ć™ć€‚ēµ„ē¹”ćØć—ć¦åˆ©ē”Øć™ć‚‹Devin悄CodeRabbitćØć„ć£ćŸćƒ„ćƒ¼ćƒ«ćÆåˆ„é€”å°Žå…„ć™ć‚‹å‰ęć§ć€å€‹äŗŗå‘ć‘ć«ćÆä»„äø‹ć®ćƒ„ćƒ¼ćƒ«ć‚’ć‚µćƒćƒ¼ćƒˆć—ć¦ć„ćć¾ć™ć€‚ Cursor Claude Code Firebender ćƒ„ćƒ¼ćƒ«ćÆć“ć‚Œć‚‰ć«é™å®šć›ćšć€ę–°ć—ćęœ‰ē”ØćŖćƒ„ćƒ¼ćƒ«ćŒē™»å “ć™ć‚Œć°éšę™‚å°Žå…„ę¤œčØŽć‚„čæ½åŠ ć‚’č”Œć„ć€åøøć«ćć®ę™‚ē‚¹ć§ęœ€ć‚‚ē†ęƒ³ēš„ćŖéøęŠžč‚¢ć‚’ęä¾›ć§ćć‚‹ēŠ¶ę…‹ć‚’ē›®ęŒ‡ć—ć¦ć„ćć¾ć™ć€‚ ēµ‚ć‚ć‚Šć« ä»Šå›žćÆć€ē”ŸęˆAIę“»ē”Øć«ćŠć‘ć‚‹ä¼šē¤¾ć§ć®å–ć‚Šēµ„ćæćØē¾åœØć®ę“»ē”ØēŠ¶ę³ć«ć¤ć„ć¦ćŠč©±ć—ć—ć¾ć—ćŸć€‚ ä»Šå¾Œć‚‚ē§ćŸć”ćÆć€Œé–‹ē™ŗē”Ÿē”£ę€§10å€ć€ć‚’ē›®ęØ™ć«ęŽ²ć’ć€ć‚Øćƒ³ć‚øćƒ‹ć‚¢ćŒć‚ˆć‚Šęœ¬č³Ŗēš„ćŖä¾”å€¤å‰µé€ ć«é›†äø­ć§ćć‚‹ć‚ˆć†ć€AIę“»ē”Øć‚‚ē©ę„µēš„ć«ęŽØé€²ć—ć¦ć„ćć¾ć™ć€‚AIć®ē™ŗå±•ć«ć‚ˆć‚Š2-3ćƒ¶ęœˆå…ˆć«ćÆå®Ÿē¾åÆčƒ½ćŖć“ćØć‚„åˆ©ē”Øćƒ„ćƒ¼ćƒ«ć‚‚å¤‰ć‚ć£ć¦ć„ć‚‹åÆčƒ½ę€§ćŒć‚ć‚Šć¾ć™ćŒć€ä»Šå›žē“¹ä»‹ć—ćŸć‚ˆć†ć«ęŠ€č”“ć®å¤‰åŒ–ć‚„ē¤¾å†…ć®ę“»ē”ØēŠ¶ę³ć‚’ęŠŠę”ć—ćŖćŒć‚‰ć€ęŸ”č»Ÿć«é©åæœć—ē¶šć‘ć‚‹ēµ„ē¹”ć§ć‚ć‚ŠćŸć„ćØę€ć„ć¾ć™ć€‚ ē”ŸęˆAIć§ćƒ—ćƒ­ćƒ€ć‚Æćƒˆé–‹ē™ŗć‚’ć‚¢ćƒƒćƒ—ćƒ‡ćƒ¼ćƒˆć—ćŸć„ę–¹ć€ ē”ŸęˆAIć‚’ę“»ē”Øć—ćŸćƒ—ćƒ­ćƒ€ć‚Æćƒˆć‚’ä½œć‚ŠćŸć„ę–¹ć€ ćœć²å¼Šē¤¾ć§äø€ē·’ć«åƒćć¾ć—ć‚‡ć†ļ¼
ćÆć˜ć‚ć« ćƒ‡ćƒŖćƒƒć‚·ćƒ„ć‚­ćƒƒćƒćƒ³ć®iOSć‚¢ćƒ—ćƒŖć‚’é–‹ē™ŗć—ć¦ć„ć‚‹ęˆē”°ć§ć™ć€‚ ć‚¢ćƒ—ćƒŖć§ē”»åƒå‡¦ē†ć‚’č”Œć†å “åˆć€ćć®ćƒ‘ćƒ•ć‚©ćƒ¼ćƒžćƒ³ć‚¹ćÆéžåøøć«é‡č¦ć§ć™ć€‚ ē”»åƒå‡¦ē†ć‚’č”Œć†ć‚±ćƒ¼ć‚¹ćØć—ć¦ć€ä¾‹ćˆć°ćƒ¦ćƒ¼ć‚¶ćƒ¼ćŒé€äæ”ć—ćŸē”»åƒć‚’ä½æć£ć¦ä½•ć‹ć—ćŸć„ćØć„ć£ćŸę™‚ć«ć€ć€Œäŗˆć‚ćƒ­ćƒ¼ć‚«ćƒ«ć§ē”»åƒć®å¹³å‡č¼åŗ¦ć‚’ē®—å‡ŗć—ć€č§£ęžć§ććŖć„ć»ć©ęš—ć™ćŽć‚‹ē”»åƒćÆå¼¾ćć€ćØć„ć£ćŸćƒ¦ćƒ¼ć‚¹ć‚±ćƒ¼ć‚¹ćŒć‚ć‚Šć¾ć™ć€‚ ćć—ć¦ć€å¹³å‡č¼åŗ¦ć‚’čØˆē®—ć™ć‚‹ę™‚ć«å˜ē“”ć«CPUで処理するか、GPUć§å‡¦ē†ć™ć‚‹ć®ć‹ć§å¤§ćććƒ‘ćƒ•ć‚©ćƒ¼ćƒžćƒ³ć‚¹ćŒē•°ćŖć‚Šć¾ć™ć€‚ ęœ¬čØ˜äŗ‹ć§ćÆć€ä»„äø‹ć®3ć¤ć®ć‚¢ćƒ—ćƒ­ćƒ¼ćƒć§å®Ÿéš›ć«iOSć‚¢ćƒ—ćƒŖć§ē”»åƒå‡¦ē†ć®ćƒ‘ćƒ•ć‚©ćƒ¼ćƒžćƒ³ć‚¹ęÆ”č¼ƒć‚’ć—ć¦ćæćŸć„ćØę€ć„ć¾ć™ć€‚ CPUćƒ™ćƒ¼ć‚¹ć®é€ę¬”å‡¦ē† Core怀Imagećƒ•ćƒ¬ćƒ¼ćƒ ćƒ¼ć‚Æć‚’åˆ©ē”Øć—ćŸGPU処理 Metalć‚’ē›“ęŽ„åˆ©ē”Øć—ćŸGPU処理(ćŠć¾ć‘) ćć‚Œćžć‚Œć®ć‚¢ćƒ—ćƒ­ćƒ¼ćƒć‚’ē“¹ä»‹ć—ćŸå¾Œć€ęœ€å¾Œć«ć¾ćØć‚ć¦ćƒ‡ćƒ¢ć‚¢ćƒ—ćƒŖć‚’ä½æć£ć¦ć‚µćƒ³ćƒ—ćƒ«ē”»åƒć®å‡¦ē†ę™‚é–“ć‚’ęÆ”č¼ƒć—ć¦ćæć‚ˆć†ćØę€ć„ć¾ć™ć€‚ ä»Šå›žē®—å‡ŗć™ć‚‹č¼åŗ¦ć«ć¤ć„ć¦ č¼åŗ¦ćØćÆęœ¬ę„ć€å…‰ć®å¼·ć•ćŖć©ę˜Žć‚‹ć•ć‚’č”Øć™ē‰©ē†é‡ć‚’ę„å‘³ć—ć¾ć™ćŒć€ē”»åƒć§č¼åŗ¦ć‚’ę±‚ć‚ć‚‹å “åˆćÆć€sRGBč‰²ē©ŗé–“ć«ćŠć‘ć‚‹ē›øåÆ¾č¼åŗ¦ć‚’ē”Øć„ć¾ć™ć€‚ sRGBč‰²ē©ŗé–“ć«ćŠć‘ć‚‹ē›øåÆ¾č¼åŗ¦ćØćÆć€ęœ€ć‚‚ęš—ć„é»’ć‚’0ć€ęœ€ć‚‚ę˜Žć‚‹ć„ē™½ć‚’1ćØć—ć¦ę­£č¦åŒ–ć—ćŸć€č‰²ē©ŗé–“å†…ć«ćŠć‘ć‚‹ē›øåÆ¾ēš„ćŖę˜Žć‚‹ć•ć®ć“ćØć§ć™ć€‚ 恓恓恧ꉱ恆 RGB 値は 8bit(0怜255) ć§č”Øē¾ć•ć‚Œć‚‹ćƒ”ć‚Æć‚»ćƒ«å€¤ć§ć™ć€‚ 恓悌悒0怜1ć«ę­£č¦åŒ–ć—ć¦ć€ę¬”ć®é‡ćæä»˜ćå’Œć§čØˆē®—ć—ć¾ć™ć€‚ ć“ć®å¼ć‚’ē”»åƒå…Øä½“ć«é©ē”Øć—ć¦å¹³å‡ć‚’å–ć£ćŸć‚‚ć®ćŒć€Œå¹³å‡č¼åŗ¦ć€ćØćŖć‚Šć¾ć™ć€‚ 1.CPUでの処理 ęœ€ć‚‚ć‚·ćƒ³ćƒ—ćƒ«ćŖć®ćÆć€forę–‡ć§ćƒ«ćƒ¼ćƒ—ć‚’å›žć—ć¦ćƒ”ć‚Æć‚»ćƒ«ć”ćØć«č¼åŗ¦ć‚’čØˆē®—ć™ć‚‹ę–¹ę³•ć§ć™ć€‚ 恗恋恗态iPhone恮CPUćÆć‚·ćƒ³ć‚°ćƒ«ć‚¹ćƒ¬ćƒƒćƒ‰ēš„ć«å‡¦ē†ć—ć¦ć„ććŸć‚ć€ę•°ē™¾äø‡ē”»ē“ ä»„äøŠćŖć©ć®ē”»åƒć‚’ę‰±ć£ćŸć‚Šć™ć‚‹ćØäø€ę°—ć«å‡¦ē†ćŒé‡ććŖć£ć¦ć„ćć¾ć™ć€‚ func calculateAverageLuminanceCPU (from uiImage : UIImage ) -> ( result: Float , time: TimeInterval ) { // å‡¦ē†é–‹å§‹ę™‚é–“ć‚’čØ˜éŒ² let startTime = CFAbsoluteTimeGetCurrent() // UIImage恋悉CGImageć‚’å–å¾— guard let cgImage = uiImage.cgImage else { return ( 0 , 0 ) } // ē”»åƒć®å¹…ćƒ»é«˜ć•ć‚’å–å¾— let width = cgImage.width let height = cgImage.height // ćƒ”ć‚Æć‚»ćƒ«ć‚ćŸć‚Šć®ćƒć‚¤ćƒˆę•°ļ¼ˆRGBAなので4) let bytesPerPixel = 4 // 1č”Œć‚ćŸć‚Šć®ćƒć‚¤ćƒˆę•° let bytesPerRow = bytesPerPixel * width // 1č‰²ęˆåˆ†ć‚ćŸć‚Šć®ćƒ“ćƒƒćƒˆę•°ļ¼ˆ8bit) let bitsPerComponent = 8 // ćƒ”ć‚Æć‚»ćƒ«ćƒ‡ćƒ¼ć‚æć‚’ę ¼ē“ć™ć‚‹é…åˆ—ć‚’ē”Øę„ļ¼ˆRGBA順) var pixelData = [UInt8](repeating : 0 , count : width * height * bytesPerPixel) // RGBč‰²ē©ŗé–“ć‚’ä½œęˆ let colorSpace = CGColorSpaceCreateDeviceRGB() // CGContext ć‚’ä½œęˆć—ć¦ćƒ”ć‚Æć‚»ćƒ«ćƒ‡ćƒ¼ć‚æé…åˆ—ć«ęē”» let context = CGContext(data : & pixelData, width : width , height : height , bitsPerComponent : bitsPerComponent , bytesPerRow : bytesPerRow , space : colorSpace , bitmapInfo : CGImageAlphaInfo.noneSkipLast.rawValue ) // CGImage 悒 CGContext ć«ęē”» context?.draw(cgImage, in : CGRect (x : 0 , y : 0 , width : width , height : height )) // č¼åŗ¦ć®åˆčØˆå€¤ć‚’ę ¼ē“ć™ć‚‹å¤‰ę•° var totalLuminance : Float = 0.0 let pixelCount = width * height // ćƒ«ćƒ¼ćƒ—ć§ē”»åƒć®å…Øć¦ć®ćƒ”ć‚Æć‚»ćƒ«ć‚’å‡¦ē† for i in 0 ..< pixelCount { let pixelIndex = i * bytesPerPixel // RGB値を0怜1ć«ę­£č¦åŒ–ć—ć€sRGB → ē·šå½¢č¼åŗ¦ć«å¤‰ę› let r = sRGBToLinear(Float(pixelData[pixelIndex]) / 255.0 ) let g = sRGBToLinear(Float(pixelData[pixelIndex + 1 ]) / 255.0 ) let b = sRGBToLinear(Float(pixelData[pixelIndex + 2 ]) / 255.0 ) // č¼åŗ¦ć‚’čØˆē®— let luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b totalLuminance += luminance } // å¹³å‡č¼åŗ¦ć‚’čØˆē®— let averageLuminance = totalLuminance / Float(pixelCount) // å‡¦ē†ēµ‚äŗ†ę™‚é–“ć‚’å–å¾—ć—ć¦ēµŒéŽę™‚é–“ć‚’čØˆē®— let endTime = CFAbsoluteTimeGetCurrent() // å¹³å‡č¼åŗ¦ćØå‡¦ē†ę™‚é–“ć‚’čæ”ć™ return (averageLuminance, endTime - startTime) } 2. Core Image恧恮GPU処理 Core ImageはApplećŒęä¾›ć™ć‚‹ē”»åƒå‡¦ē†ćƒ•ćƒ¬ćƒ¼ćƒ ćƒÆćƒ¼ć‚Æć§ć™ć€‚ å¤šę•°ć®ēµ„ćæč¾¼ćæćƒ•ć‚£ćƒ«ć‚æć‚’ä½æē”Øć—ć¦ē”»åƒć‚„å‹•ē”»ć‚’å‡¦ē†ć§ćć€ć¾ćŸćƒ•ć‚£ćƒ«ć‚æć‚’é€£ęŗć•ć›ć‚‹ć“ćØć§č¤‡é›‘ćŖć‚Øćƒ•ć‚§ć‚Æćƒˆć‚‚ę§‹ēÆ‰ć§ćć¾ć™ć€‚ē”»åƒć®ć¼ć‹ć—ć‚„ć‚·ćƒ£ćƒ¼ćƒ—åŒ–ć€č‰²å‘³ć‚„å½©åŗ¦ć®čŖæę•“ē”ØćŖć©å¤šćć®ćƒ•ć‚£ćƒ«ć‚æćŒē”Øę„ć•ć‚Œć¦ć„ć¾ć™ć€‚ å¹³å‡č¼åŗ¦ć‚’ę±‚ć‚ć‚‹å “åˆć‚‚ CIAreaAverage ćƒ•ć‚£ćƒ«ć‚æć‚’ä½æćˆć°ćƒÆćƒ³ćƒ©ć‚¤ćƒŠćƒ¼ć§čØˆē®—åÆčƒ½ć§ć™ć€‚ ć“ć®ćƒ•ć‚£ćƒ«ć‚æćƒ¼ć‚’ä½æć†ćØå…Øćƒ”ć‚Æć‚»ćƒ«ć®å¹³å‡č‰²ć‚’čØˆē®—ć—ć¦1Ɨ1ćƒ”ć‚Æć‚»ćƒ«ć®ē”»åƒćØć—ć¦čæ”ć—ć¦ćć‚Œć¾ć™ć€‚ å†…éƒØć§ćÆGPUć®å·Øå¤§ćŖć‚¹ćƒ¬ćƒƒćƒ‰ē¾¤ć§å…Øćƒ”ć‚Æć‚»ćƒ«ć‚’č¤‡ę•°ć®ć‚°ćƒ«ćƒ¼ćƒ—ć§äø¦åˆ—ć«å‡¦ē†ć—ć¦å¹³å‡č‰²ć‚’ē®—å‡ŗć—ć¦ć„ć‚‹ćØč€ƒćˆć‚‰ć‚Œć¾ć™ć€‚ func calculateAverageLuminanceCoreImage (from uiImage : UIImage ) -> ( result: Float , time: TimeInterval ) { // å‡¦ē†é–‹å§‹ę™‚é–“ć‚’čØ˜éŒ² let startTime = CFAbsoluteTimeGetCurrent() // UIImage 恋悉 CGImage ć‚’å–å¾— guard let cgImage = uiImage.cgImage else { return ( 0 , 0 ) } // CGImage 恋悉 CIImage ć‚’ä½œęˆļ¼ˆCore Image ćƒ•ć‚£ćƒ«ć‚æć§å‡¦ē†ć™ć‚‹ćŸć‚ļ¼‰ let ciImage = CIImage(cgImage : cgImage ) // CIAreaAverage ćƒ•ć‚£ćƒ«ć‚æćƒ¼ć‚’ä½æē”Øć—ć¦å¹³å‡č‰²ć‚’å–å¾— // CIAreaAverage ćÆęŒ‡å®šé ˜åŸŸć®å¹³å‡č‰²ć‚’čØˆē®—ć—ć¦ 1x1 ćƒ”ć‚Æć‚»ćƒ«ē”»åƒć‚’čæ”ć™ćƒ•ć‚£ćƒ«ć‚æ guard let areaAverageFilter = CIFilter(name : "CIAreaAverage" ) else { return ( 0 , 0 ) } // ćƒ•ć‚£ćƒ«ć‚æćƒ¼ć«å…„åŠ›ē”»åƒć‚’ć‚»ćƒƒćƒˆ areaAverageFilter.setValue(ciImage, forKey : kCIInputImageKey ) // ćƒ•ć‚£ćƒ«ć‚æćƒ¼ć‚’é©ē”Øć™ć‚‹ēÆ„å›²ć‚’ē”»åƒå…Øä½“ć«čØ­å®š areaAverageFilter.setValue(CIVector(cgRect : ciImage.extent ), forKey : kCIInputExtentKey ) // ćƒ•ć‚£ćƒ«ć‚æćƒ¼ć®å‡ŗåŠ›ē”»åƒć‚’å–å¾— guard let outputImage = areaAverageFilter.outputImage else { return ( 0 , 0 ) } // å‡ŗåŠ›ē”»åƒćÆ 1x1 ćƒ”ć‚Æć‚»ćƒ«ćŖć®ć§ć€ć“ć“ć‹ć‚‰ RGBA ćƒ‡ćƒ¼ć‚æć‚’å–å¾— var pixelData = [UInt8](repeating : 0 , count : 4 ) ciContext.render(outputImage, toBitmap : & pixelData, rowBytes : 4 , bounds : CGRect (x : 0 , y : 0 , width : 1 , height : 1 ), format : .RGBA8, colorSpace : CGColorSpaceCreateDeviceRGB ()) // ćƒ”ć‚Æć‚»ćƒ«å€¤ćÆ UInt8 (0怜255) なので 0怜1 ć«ę­£č¦åŒ–ć—ć€ // sRGB → ē·šå½¢č¼åŗ¦ć«å¤‰ę›ļ¼ˆć‚¬ćƒ³ćƒžč£œę­£ć®é€†å¤‰ę›ļ¼‰ let r = sRGBToLinear(Float(pixelData[ 0 ]) / 255.0 ) let g = sRGBToLinear(Float(pixelData[ 1 ]) / 255.0 ) let b = sRGBToLinear(Float(pixelData[ 2 ]) / 255.0 ) // ē›øåÆ¾č¼åŗ¦ć‚’čØˆē®— let luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b // å‡¦ē†ēµ‚äŗ†ę™‚é–“ć‚’čØ˜éŒ² let endTime = CFAbsoluteTimeGetCurrent() // å¹³å‡č¼åŗ¦ćØå‡¦ē†ę™‚é–“ć‚’čæ”ć™ return (luminance, endTime - startTime) } 3. Metal恧恮GPU処理 MetalとはApplećŒęä¾›ć™ć‚‹ä½Žćƒ¬ćƒ™ćƒ«ć®API恧态GPUć«ć‚¢ć‚Æć‚»ć‚¹ć—ć¦ē‹¬č‡Ŗć®ć‚°ćƒ©ćƒ•ć‚£ćƒƒć‚Æå‡¦ē†ćŖć©ć‚’č”Œć†ć“ćØćŒć§ćć¾ć™ć€‚ Metalć‚’ē›“ęŽ„åˆ©ē”Øć™ć‚‹ćØć€ć‚¹ćƒ¬ćƒƒćƒ‰ę•°ć‚„äø¦åˆ—åŒ–å˜ä½ć‚’č‡Ŗåˆ†ć§čØ­čØˆć—ćŸć‚Šć§ćć¾ć™ć€‚ ć“ć‚Œć«ć‚ˆć‚Šć€GPUć®äø¦åˆ—ę€§čƒ½ć‚’å¤§ććå¼•ćå‡ŗć™ć“ćØćŒåÆčƒ½ć§ć™ć€‚ ē‹¬č‡Ŗć®ć‚«ć‚¹ć‚æćƒ ćŒć—ć‚„ć™ć„åˆ†ć€å®Ÿč£…é‡ćÆć‹ćŖć‚Šå¤§ćć„ć§ć™ć€‚ ć¾ćšć€GPUć§å¹³å‡č¼åŗ¦ć‚’čØˆē®—ć™ć‚‹ćŸć‚ć®é–¢ę•°ć‚’å®šē¾©ć™ć‚‹åæ…č¦ćŒć‚ć‚Šć¾ć™ć€‚ ćƒ•ć‚”ć‚¤ćƒ«ć®ę‹”å¼µå­ćÆmetal恧恙怂 Swiftć§ćÆćŖćć€Metal Shading LanguageというMetalē”Øć®čØ€čŖžć§ę›øćć¾ć™ć€‚ 恓恓恧GPUäøŠć§å‹•ććƒ—ćƒ­ć‚°ćƒ©ćƒ ć‚’å®šē¾©ć—ć¾ć™ć€‚ #include < metal_stdlib > using namespace metal; // å¹³å‡č¼åŗ¦ć‚’čØˆē®—ć™ć‚‹é–¢ę•° kernel void calculateAverageLuminanceLinear(texture2d < float, access :: read > inputTexture [[texture( 0 )]], device atomic < float >* resultBuffer [[buffer( 0 )]], uint2 gid [[thread_position_in_grid]]) { uint width = inputTexture.get_width(); uint height = inputTexture.get_height(); // ćƒ†ć‚Æć‚¹ćƒćƒ£ć®å¢ƒē•Œćƒć‚§ćƒƒć‚Æ if (gid.x >= width || gid.y >= height) { return ; } // ćƒ”ć‚Æć‚»ćƒ«ć®č‰²ć‚’čŖ­ćæå–ć‚Š float4 color = inputTexture.read(gid); // sRGB -> Linearå¤‰ę› float r = sRGBToLinear(color.r); float g = sRGBToLinear(color.g); float b = sRGBToLinear(color.b); float luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b; // ē“Æē©č¼åŗ¦ć‚’čØˆē®— atomic_fetch_add_explicit(resultBuffer, luminance, memory_order_relaxed); } ę¬”ć«äøŠčØ˜ć§å®šē¾©ć—ćŸć‚‚ć®ć‚’ä½æć£ć¦Metalć®åˆęœŸåŒ–ć—ć¾ć™ć€‚ init () { setupMetal() } private func setupMetal () { // ćƒ‡ćƒ•ć‚©ćƒ«ćƒˆć®Metalćƒ‡ćƒć‚¤ć‚¹ļ¼ˆGPUļ¼‰ć‚’å–å¾— metalDevice = MTLCreateSystemDefaultDevice() // GPUć«é€ć‚‹ć‚³ćƒžćƒ³ćƒ‰ć‚­ćƒ„ćƒ¼ć‚’ä½œęˆ metalCommandQueue = metalDevice?.makeCommandQueue() guard let device = metalDevice else { print( "Metal device not available" ) return } do { // ćƒ—ćƒ­ć‚øć‚§ć‚Æćƒˆå†…ć®Metalć‚·ć‚§ćƒ¼ćƒ€ćƒ©ć‚¤ćƒ–ćƒ©ćƒŖć‚’å–å¾— let library = device.makeDefaultLibrary() // ćƒ©ć‚¤ćƒ–ćƒ©ćƒŖć‹ć‚‰čØˆē®—ē”Øé–¢ę•°ć‚’å–å¾— let function = library?.makeFunction(name : "calculateAverageLuminanceLinear" ) // å–å¾—ć—ćŸé–¢ę•°ć‚’GPUć§å®Ÿč”ŒåÆčƒ½ćŖćƒ‘ć‚¤ćƒ—ćƒ©ć‚¤ćƒ³ć«å¤‰ę› metalComputePipelineState = try device.makeComputePipelineState(function : function! ) } catch { print( "Failed to create Metal pipeline state: \( error ) " ) } } func calculateAverageLuminanceMetal (from uiImage : UIImage ) -> ( result: Float , time: TimeInterval ) { // å‡¦ē†é–‹å§‹ę™‚é–“ć‚’čØ˜éŒ² let startTime = CFAbsoluteTimeGetCurrent() guard let device = metalDevice, let commandQueue = metalCommandQueue, let pipelineState = metalComputePipelineState, let cgImage = uiImage.cgImage else { return ( 0 , 0 ) } let width = cgImage.width let height = cgImage.height let bytesPerPixel = 4 // ćƒ†ć‚Æć‚¹ćƒćƒ£ä½œęˆ // MetaläøŠć§ē”»åƒćƒ‡ćƒ¼ć‚æć‚’ę‰±ć†ćŸć‚ć®ćƒ†ć‚Æć‚¹ćƒćƒ£ć‚’ä½œęˆ let textureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat : .rgba8Unorm, width : width , height : height , mipmapped : false ) textureDescriptor.usage = [.shaderRead] guard let inputTexture = device.makeTexture(descriptor : textureDescriptor ) else { return ( 0 , 0 ) } // CGImageć‹ć‚‰ćƒ†ć‚Æć‚¹ćƒćƒ£ć«ćƒ‡ćƒ¼ć‚æć‚³ćƒ”ćƒ¼ // CGContextを使ってCGImageć®ćƒ”ć‚Æć‚»ćƒ«ćƒ‡ćƒ¼ć‚æć‚’é…åˆ—ć«čŖ­ćæč¾¼ćæ let colorSpace = CGColorSpaceCreateDeviceRGB() var pixelData = [UInt8](repeating : 0 , count : width * height * bytesPerPixel) let context = CGContext(data : & pixelData, width : width , height : height , bitsPerComponent : 8 , bytesPerRow : bytesPerPixel * width, space : colorSpace , bitmapInfo : CGImageAlphaInfo.noneSkipLast.rawValue ) context?.draw(cgImage, in : CGRect (x : 0 , y : 0 , width : width , height : height )) // ćƒ”ć‚Æć‚»ćƒ«ćƒ‡ćƒ¼ć‚æć‚’ćƒ†ć‚Æć‚¹ćƒćƒ£ć«ć‚³ćƒ”ćƒ¼ inputTexture.replace(region : MTLRegionMake2D ( 0 , 0 , width, height), mipmapLevel : 0 , withBytes : & pixelData, bytesPerRow : bytesPerPixel * width) // ēµęžœē”Øćƒćƒƒćƒ•ć‚”ä½œęˆ // å¹³å‡č¼åŗ¦ć‚’ę ¼ē“ć™ć‚‹ćŸć‚ć®Floatćƒćƒƒćƒ•ć‚” guard let resultBuffer = device.makeBuffer(length : MemoryLayout < Float > .size, options : [] ) else { return ( 0 , 0 ) } // ćƒćƒƒćƒ•ć‚”ć‚’0ć§åˆęœŸåŒ– let bufferPointer = resultBuffer.contents().bindMemory(to : Float.self , capacity : 1 ) bufferPointer[ 0 ] = 0.0 //ć€€ć‚³ćƒžćƒ³ćƒ‰ćƒćƒƒćƒ•ć‚”ćØć‚³ćƒ³ćƒ”ćƒ„ćƒ¼ćƒˆć‚Øćƒ³ć‚³ćƒ¼ćƒ€ä½œęˆ guard let commandBuffer = commandQueue.makeCommandBuffer(), let computeEncoder = commandBuffer.makeComputeCommandEncoder() else { return ( 0 , 0 ) } // ćƒ‘ć‚¤ćƒ—ćƒ©ć‚¤ćƒ³ć‚¹ćƒ†ćƒ¼ćƒˆć€ćƒ†ć‚Æć‚¹ćƒćƒ£ć€ēµęžœćƒćƒƒćƒ•ć‚”ć‚’ć‚»ćƒƒćƒˆ computeEncoder.setComputePipelineState(pipelineState) computeEncoder.setTexture(inputTexture, index : 0 ) computeEncoder.setBuffer(resultBuffer, offset : 0 , index : 0 ) // ć‚¹ćƒ¬ćƒƒćƒ‰ć‚°ćƒ«ćƒ¼ćƒ—ć®čØ­å®š // 16x16ć®ć‚¹ćƒ¬ćƒƒćƒ‰ćƒ–ćƒ­ćƒƒć‚Æć§åˆ†å‰²ć—ć¦čØˆē®— let threadgroupSize = MTLSize(width : 16 , height : 16 , depth : 1 ) let threadgroupCount = MTLSize(width : ( width + threadgroupSize.width - 1 ) / threadgroupSize.width, height : ( height + threadgroupSize.height - 1 ) / threadgroupSize.height, depth : 1 ) // ć€Œć“ć®ć‚¹ćƒ¬ćƒƒćƒ‰ć‚°ćƒ«ćƒ¼ćƒ—ć§čØˆē®—ć—ć¦ć­ć€ćØć„ć†å‘½ä»¤ć‚’ć‚³ćƒžćƒ³ćƒ‰ćƒćƒƒćƒ•ć‚”ć«čØ˜éŒ² computeEncoder.dispatchThreadgroups(threadgroupCount, threadsPerThreadgroup : threadgroupSize ) computeEncoder.endEncoding() // GPUć§ć®å‡¦ē†å®Ÿč”Œ commandBuffer.commit() commandBuffer.waitUntilCompleted() // ēµęžœå–å¾— let resultPointer = resultBuffer.contents().bindMemory(to : Float.self , capacity : 1 ) // ē“Æē©č¼åŗ¦ć‚’ćƒ”ć‚Æć‚»ćƒ«ę•°ć§å‰²ć£ć¦å¹³å‡č¼åŗ¦ć‚’ē®—å‡ŗ let result = resultPointer[ 0 ] / Float(width * height) // å‡¦ē†ēµ‚äŗ†ę™‚é–“ć‚’čØ˜éŒ² let endTime = CFAbsoluteTimeGetCurrent() // å¹³å‡č¼åŗ¦ćØå‡¦ē†ę™‚é–“ć‚’čæ”ć™ return (result, endTime - startTime) } å®Ÿéš›ć«å‡¦ē†é€Ÿåŗ¦ć‚’ęÆ”č¼ƒć—ć¦ćæć‚‹ äøŠčØ˜ļ¼“ć¤ć®å‡¦ē†é€Ÿåŗ¦ć‚’å®Ÿéš›ć«čØˆē®—ć™ć‚‹ćŸć‚ć«ćƒ‡ćƒ¢ć‚¢ćƒ—ćƒŖć‚’ä½œć£ć¦ćæć¾ć—ćŸć€‚ ē”»åƒć‚’éøęŠžć—ć¦å®Ÿč”Œćƒœć‚æćƒ³ć‚’ęŠ¼ć™ćØ3é€šć‚Šé †ę¬”å¹³å‡č¼åŗ¦ć‚’ē®—å‡ŗć—ć¦å…Øć¦ćŒå®Œäŗ†ć—ćŸć‚‰ēµęžœćŒå‡ŗåŠ›ć•ć‚Œć¾ć™ć€‚ å®Ÿč”ŒēµęžœćÆć“ć”ć‚‰ć«ćŖć‚Šć¾ć—ćŸć€‚ ä»Šå›žä½æē”Øć—ćŸē”»åƒćÆē“„1200äø‡ē”»ē“ ć§ć™ćŒć€CPUとGPUć‚’ä½æć£ćŸå “åˆć§å‡¦ē†é€Ÿåŗ¦ć«ę˜Žē¢ŗćŖå·®ć‚’ē¢ŗčŖć™ć‚‹ć“ćØćŒć§ćć¾ć—ćŸļ¼ ć¾ćŸć€Core Imageć‚’ä½æć£ćŸå “åˆćØMetalć‚’ē›“ęŽ„ä½æć£ćŸå “åˆć§ćÆå¤§ććŖå·®ćÆćæć‚‰ć‚Œć¾ć›ć‚“ć§ć—ćŸć€‚ ä»Šå›žćÆć‚·ćƒ³ćƒ—ćƒ«ćŖå¹³å‡č¼åŗ¦čØˆē®—ć ć£ćŸć®ć§Core ImageとMetalć§ćÆćć“ć¾ć§å·®ćŒć¤ć‹ćŖć„ć®ć‹ć‚‚ć—ć‚Œć¾ć›ć‚“ć€‚ まとめ CPU処理 : å®Ÿč£…ćÆē°”å˜ć ćŒć‚·ćƒ³ć‚°ćƒ«ć‚¹ćƒ¬ćƒƒćƒ‰ēš„å‡¦ē†ć«ćŖć‚‹ć®ć§éžåøøć«é…ć„ Core Image : ęŠ½č±”åŗ¦ćŒé«˜ćć€ē°”å˜ć‹ć¤é«˜é€Ÿć€‚ęØ™ęŗ–ēš„ćŖå‡¦ē†ćŖć‚‰ćƒ™ć‚¹ćƒˆ Metal : ęœ€ć‚‚ęŸ”č»Ÿć§é€Ÿć„ćŒć€å­¦ēæ’ć‚³ć‚¹ćƒˆćƒ»å®Ÿč£…ć‚³ć‚¹ćƒˆćÆé«˜ć„ iPhoneē«Æęœ«ć®ć‚¹ćƒšćƒƒć‚Æć‚‚å¹“ć€…å‘äøŠć—ć¦ć„ć‚‹ćŸć‚ć€ę™®ę®µć®ć‚¢ćƒ—ćƒŖé–‹ē™ŗć§ćÆGPUć‚’ć‚ć¾ć‚Šę„č­˜ć—ćŖćć¦ć‚‚å•é”ŒćŖć„ć‚±ćƒ¼ć‚¹ćŒå¤šć„ćØę€ć„ć¾ć™ć€‚ ć—ć‹ć—ć€ć‚°ćƒ©ćƒ•ć‚£ćƒƒć‚Æå‡¦ē†ćØć„ć£ćŸåˆ†é‡Žć§ćÆć€äø€åŗ¦ć«ę‰±ć†ćƒ‡ćƒ¼ć‚æé‡ćŒč†Øå¤§ć«ćŖć‚Šć€å‡¦ē†ćŒé‡ććŖć‚Šć‚„ć™ć„ć®ć‚‚äŗ‹å®Ÿć§ć™ć€‚ 恠恋悉恓恝态CPUとGPUć®å½¹å‰²ć‚„å¾—ę„åˆ†é‡Žć‚’ē†č§£ć—ć€åæ…č¦ć«åæœć˜ć¦ć†ć¾ćä½æć„åˆ†ć‘ć‚‹ć“ćØćŒć€åæ«é©ćŖć‚¢ćƒ—ćƒŖä½“éØ“ć‚’å®Ÿē¾ć™ć‚‹äøŠć§å¤§åˆ‡ć«ćŖć‚Šć¾ć™ć€‚
ćÆć˜ć‚ć« ć“ć‚“ć«ć”ćÆć€ćƒŖćƒ†ćƒ¼ćƒ«ćƒćƒ–é–‹ē™ŗéƒØć®ę‰ę£®ć§ć™ć€‚ ē§ćŸć”ć®ćƒćƒ¼ćƒ ć§ćÆć€ę—¢å­˜ć®ćƒćƒƒćƒć‚µćƒ¼ćƒćƒ¼ć«åÆ¾ć—ć¦ć€EC2ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ć‹ć‚‰ECS on Fargatećøć®ē§»č”Œć‚’ę¤œčØŽć—ć¦ć„ć¾ć™ć€‚ ć—ć‹ć—ć€ć€Œć‚³ć‚¹ćƒˆé¢ć§å¤§ććŖćƒ‡ćƒ”ćƒŖćƒƒćƒˆćŒć‚ć‚‹ć®ć§ćÆćŖć„ć‹ļ¼Ÿć€ćØć„ć†ē–‘å•ćŒęŒ™ćŒć£ć¦ć„ć¾ć—ćŸć€‚ ęœ¬čØ˜äŗ‹ć§ćÆć€å®Ÿéš›ć®ćƒćƒƒćƒć‚µćƒ¼ćƒćƒ¼ć®é‹ē”Øćƒ‘ć‚æćƒ¼ćƒ³ć‚’ęƒ³å®šć—ć€EC2ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ćØECS on Fargateć®ę–™é‡‘ć«é–¢ć—ć¦ć€ć‚³ć‚¹ćƒˆé¢ć§ć®ęÆ”č¼ƒć‚’å®Ÿę–½ć—ć¾ć™ć€‚ ć¾ćŸć€AWS Pricing MCP Serverć‚’ę“»ē”Øć—ćŸAIć«ć‚ˆć‚‹ę–™é‡‘čØˆē®—ć§åŒę§˜ć®ēµęžœćŒē®—å‡ŗć§ćć‚‹ć‹ć®ę¤œčØ¼ć‚‚č”Œć„ć¾ć™ć€‚ EC2ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ćØECSć®ę–™é‡‘ęÆ”č¼ƒ åŸŗęœ¬ćØćŖć‚‹ä»•ę§˜ ćƒćƒƒćƒć‚µćƒ¼ćƒćƒ¼ć®ä»•ę§˜ ćƒćƒƒćƒå†…å®¹ : 15åˆ†ć«1å›žå®Ÿč”Œć•ć‚Œć‚‹ēŸ­ę™‚é–“å®šęœŸå®Ÿč”Œćƒćƒƒćƒ å®Ÿč”Œę™‚é–“ : 5åˆ†ēØ‹åŗ¦(ć‚æć‚¹ć‚Æčµ·å‹•ć‹ć‚‰åœę­¢ć¾ć§ć‚’å«ć‚€) ※EC2ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ć®å “åˆåøøę™‚ēØ¼åƒć‚’å‰ęć«ć—ć¦ć„ć¾ć™ ć‚¤ćƒ³ćƒ•ćƒ©å‘Øć‚Šć®ä»•ę§˜ タスク要件 : CPU 2vCPUć€ćƒ”ćƒ¢ćƒŖ4GB(t4g.medium 1å°åˆ†ć®ćƒŖć‚½ćƒ¼ć‚¹ļ¼‰ ECSć‚æć‚¹ć‚Æå®šē¾© : 1ć‚æć‚¹ć‚Æć‚ćŸć‚Š2vCPU态4GBćƒ”ćƒ¢ćƒŖć‚’ä½æē”Ø OS : Linux CPU ć‚¢ćƒ¼ć‚­ćƒ†ć‚Æćƒćƒ£ : ARM ćƒŖćƒ¼ć‚øćƒ§ćƒ³ : ć‚¢ć‚øć‚¢ćƒ‘ć‚·ćƒ•ć‚£ćƒƒć‚Æ(ę±äŗ¬) ć‚¹ćƒˆćƒ¬ćƒ¼ć‚ø : 20GB(EC2ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ć®å “åˆćÆEBS gp3态Fargateć®å “åˆćÆćƒ‡ćƒ•ć‚©ćƒ«ćƒˆć®ēÆ„å›²ļ¼‰ ćƒŖćƒ¼ć‚øćƒ§ćƒ³ : ę±äŗ¬ļ¼ˆap-northeast-1) EC2ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ćØECS on Fargateの料金体系 (2025幓8月21旄時点の料金) EC2ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹(ć‚Ŗćƒ³ćƒ‡ćƒžćƒ³ćƒ‰ć§t4g.medium):$0.0432/Ꙃ間 EBS: $0.096/GB/月 ECS on Fargate vCPU: $0.04045/Ꙃ間 ピモリ: $0.00442/GB/Ꙃ間 ę–™é‡‘ęÆ”č¼ƒēµęžœ EC2ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ć®å “åˆ ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹å: t4g.medium (2vCPU, 4GB) EBS: 20GB gp3 ēØ¼åƒę™‚é–“: 24Ꙃ間 Ɨ 30ę—„ = 720Ꙃ間 ę–™é‡‘čØˆē®—: - ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹: $0.0432 Ɨ 720Ꙃ間 = $31.104 - EBS: $0.096 Ɨ 20GB = $1.92 ęœˆé”åˆčØˆ: $33.024 å‚č€ƒ: https://aws.amazon.com/jp/ec2/pricing/on-demand/ Fargateの堓合 ć‚æć‚¹ć‚Æę§‹ęˆ: 2vCPU, 4GB ピモリ å®Ÿč”Œå›žę•°: 96回/ę—„ Ɨ 30ę—„ = 2,880回/月 å®Ÿč”Œę™‚é–“: 5分/回 = 0.0833Ꙃ間/回 ē·ēØ¼åƒę™‚é–“: 0.0833Ꙃ間 Ɨ 2,880回 = 240Ꙃ間/月 ę–™é‡‘čØˆē®—: - vCPU: $0.04045 Ɨ 2 Ɨ 240Ꙃ間 = $19.416 - ピモリ: $0.00442 Ɨ 4 Ɨ 240Ꙃ間 = $4.2432 ęœˆé”åˆčØˆ: $23.6592 å‚č€ƒ: https://calculator.aws/#/createCalculator/Fargate (ćŠć¾ć‘)ēØ¼åƒę™‚é–“åˆ„ć®ę–™é‡‘ęÆ”č¼ƒ ćƒćƒƒćƒć®å®Ÿč”Œę™‚é–“ć«ćŠć„ć¦ć€ć©ć®ēØ‹åŗ¦ć§ć‚ć‚Œć°FaragatećŒć‚³ć‚¹ćƒˆé¢ć§å„Ŗä½ćŖć®ć‹ć‚’čØˆē®—ć—ć¾ć—ćŸć€‚ ēØ¼åƒę™‚é–“/ę—„ EC2ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ęœˆé” Fargateęœˆé” 差锍 å„Ŗä½ćŖę§‹ęˆ 8ę™‚é–“ļ¼ˆęœ¬ć‚±ćƒ¼ć‚¹ļ¼‰ $33.02 $23.66 -$9.36 Fargate 11Ꙃ間 $33.02 $32.53 -$0.49 Fargate 12Ꙃ間 $33.02 $35.49 +$2.47 EC2ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ 24Ꙃ間 $33.02 $70.98 +$37.96 EC2ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ ę–™é‡‘čØˆē®—ć«ćŠć‘ć‚‹AI擻用 äøŠčØ˜ć®å†…å®¹ć‚’ę‰‹å‹•ć§ć¾ćØć‚ć‚‹ć®ćÆć‹ćŖć‚Šć®ę‰‹é–“ć§ć™ć€‚ ē”Ÿē”£ę€§åŠ¹ēŽ‡åŒ–ć®ćŸć‚ć€AIć‚’ę“»ē”Øć—ć¦č‡Ŗå‹•åŒ–ć§ććŖć„ć‹ę¤œčØ¼ć—ć¾ć™ć€‚ ę¤œčØ¼å†…å®¹ Claude Codeć‚’åˆ©ē”Øć—ć€č‡Ŗē„¶čØ€čŖžćƒ™ćƒ¼ć‚¹ć§ć‚ˆć‚Šę­£ē¢ŗćŖę–™é‡‘ęÆ”č¼ƒć®å†…å®¹ć‚’å‡ŗåŠ›ć§ććŖć„ć‹ć‚’ę¤œčØ¼ć—ć¾ć™ć€‚ ä»Šå›žć®ę¤œčØ¼ć§AWS Pricing MCP Serverć‚’åˆ©ē”Øć—ć¾ć™ć€‚ AWS Pricing MCP Server とは? AWS Pricing MCP Serverは、Model Context Protocol (MCP) ęŗ–ę‹ ć®ćƒ„ćƒ¼ćƒ«ć‚µćƒ¼ćƒćƒ¼ć§ć™ć€‚ MCPćŒåˆ©ē”ØåÆčƒ½ćŖAIćƒ„ćƒ¼ćƒ«ć‹ć‚‰ć€EC2ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹/ECS/EBSćŖć©ć®ęœ€ę–°ć®å…¬é–‹ä¾”ę ¼ć‚’å–å¾—ć§ćć¾ć™ć€‚ å®Ÿč”Œē’°å¢ƒ AIćƒ„ćƒ¼ćƒ«ļ¼šClaude Code ćƒ¢ćƒ‡ćƒ«ļ¼šOpus4.1 MCPć‚µćƒ¼ćƒćƒ¼ć®čØ­å®š äø‹čØ˜å†…å®¹ć‚’ .claude/setting.local.json ć«äæå­˜ { " mcpServers ": { " aws-pricing-mcp-server ": { " command ": " uvx ", " args ": [ " awslabs.aws-pricing-mcp-server@latest " ] , " env ": { " FASTMCP_LOG_LEVEL ": " ERROR ", " AWS_PROFILE ": " ${{AWSćƒ—ćƒ­ćƒ•ć‚”ć‚¤ćƒ«}} ", " AWS_REGION ": " ${{ćƒŖćƒ¼ć‚øćƒ§ćƒ³}} " } } } } ę¤œčØ¼ēµęžœ ćƒ—ćƒ­ćƒ³ćƒ—ćƒˆ EC2ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ t4g.mediumć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ć‚’ę±äŗ¬ćƒŖćƒ¼ć‚øćƒ§ćƒ³ć§ęœˆ720ę™‚é–“ēØ¼åƒć•ć›ćŸå “åˆćØć€ECS Fargate(2vCPU态4GBćƒ”ćƒ¢ćƒŖć€ARM)を15åˆ†ć«1å›žć€å„5åˆ†é–“å®Ÿč”Œć§ęœˆ240ę™‚é–“ēØ¼åƒć•ć›ćŸå “åˆć®ę–™é‡‘ć‚’ęÆ”č¼ƒć—ć¦ćć ć•ć„ć€‚ ć‚¹ćƒˆćƒ¬ćƒ¼ć‚øć«é–¢ć—ć¦ćÆć€EC2ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ć®å “åˆć€EBSは20GB gp3を使用し、ECSć®å “åˆē„”ę–™ć®ēÆ„å›²ć§é‹ē”Øć—ć¾ć™ć€‚ AWS Pricing MCP Server ć‚’åˆ©ē”Øć—ć¦ćć ć•ć„ć€‚ ę·±ćč€ƒćˆć¦ć€‚ å‡ŗåŠ›ć•ć‚ŒćŸå†…å®¹ ā€»äø€éƒØēœē•„ć—ć¦ć„ć¾ć™ ć‚µćƒ¼ćƒ“ć‚¹ ęœˆé”ę–™é‡‘ļ¼ˆUSD) ęœˆé”ę–™é‡‘ļ¼ˆå††ļ¼‰ å‰Šęø›ēŽ‡ EC2ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ t4g.medium (ARM) $33.024 ē“„4,953円 ćƒ™ćƒ¼ć‚¹ćƒ©ć‚¤ćƒ³ ECS Fargate (ARM) $23.659 ē“„3,549円 28.4%å‰Šęø› čæ½åŠ ć®ćƒ—ćƒ­ćƒ³ćƒ—ćƒˆ ēØ¼åƒę™‚é–“ć§ % ćŒå¤‰å‹•ć™ć‚‹ć‹ćØę€ć„ć¾ć™ćŒć€| ēØ¼åƒę™‚é–“/ę—„ | EC2ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ęœˆé” | Fargateęœˆé” | 差锍 | å„Ŗä½ćŖę§‹ęˆ | ć®é …ē›®ć«ę²æć£ćŸč”Øć‚’ē”Ÿęˆć—ć¦ć€‚ å‡ŗåŠ›ć•ć‚ŒćŸå†…å®¹ ā€»äø€éƒØēœē•„ć—ć¦ć„ć¾ć™ ēØ¼åƒę™‚é–“/ę—„ EC2ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ęœˆé” Fargateęœˆé” 差锍 å„Ŗä½ćŖę§‹ęˆ 1Ꙃ間 $33.02 $2.96 -$30.06 Fargate(91%å‰Šęø›ļ¼‰ 2Ꙃ間 $33.02 $5.91 -$27.11 Fargate(82%å‰Šęø›ļ¼‰ 3Ꙃ間 $33.02 $8.87 -$24.15 Fargate(73%å‰Šęø›ļ¼‰ 4Ꙃ間 $33.02 $11.83 -$21.19 Fargate(64%å‰Šęø›ļ¼‰ 5Ꙃ間 $33.02 $14.79 -$18.23 Fargate(55%å‰Šęø›ļ¼‰ 6Ꙃ間 $33.02 $17.74 -$15.28 Fargate(46%å‰Šęø›ļ¼‰ 7Ꙃ間 $33.02 $20.70 -$12.32 Fargate(37%å‰Šęø›ļ¼‰ 8Ꙃ間 $33.02 $23.66 -$9.36 Fargate(28%å‰Šęø›ļ¼‰ 9Ꙃ間 $33.02 $26.62 -$6.40 Fargate(19%å‰Šęø›ļ¼‰ 10Ꙃ間 $33.02 $29.57 -$3.45 Fargate(10%å‰Šęø›ļ¼‰ 11Ꙃ間 $33.02 $32.53 -$0.49 Fargate(1%å‰Šęø›ļ¼‰ 12Ꙃ間 $33.02 $35.49 +$2.47 EC2ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ļ¼ˆ7%å‰Šęø›ļ¼‰ 13Ꙃ間 $33.02 $38.45 +$5.43 EC2ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ļ¼ˆ16%å‰Šęø›ļ¼‰ 14Ꙃ間 $33.02 $41.40 +$8.38 EC2ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ļ¼ˆ25%å‰Šęø›ļ¼‰ 15Ꙃ間 $33.02 $44.36 +$11.34 EC2ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ļ¼ˆ34%å‰Šęø›ļ¼‰ 16Ꙃ間 $33.02 $47.32 +$14.30 EC2ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ļ¼ˆ43%å‰Šęø›ļ¼‰ 18Ꙃ間 $33.02 $53.23 +$20.21 EC2ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ļ¼ˆ61%å‰Šęø›ļ¼‰ 20Ꙃ間 $33.02 $59.15 +$26.13 EC2ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ļ¼ˆ79%å‰Šęø›ļ¼‰ 22Ꙃ間 $33.02 $65.07 +$32.05 EC2ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ļ¼ˆ97%å‰Šęø›ļ¼‰ 24Ꙃ間 $33.02 $70.98 +$37.96 EC2ć‚¤ćƒ³ć‚¹ć‚æćƒ³ć‚¹ļ¼ˆ115%å‰Šęø›ļ¼‰ ę‰‹å‹•ć§čØˆē®—ć—ćŸå†…å®¹ćØåŒć˜ēµęžœć‚’å¾—ć‚‹ć“ćØćŒć§ćć¾ć—ćŸļ¼ MCPć‚µćƒ¼ćƒćƒ¼ć‚’ęŽ„ē¶šć—ćŖć„ēŠ¶ę…‹ć§ę¤œčØ¼ć—ćŸćØć“ć‚ć€ćƒŖć‚½ćƒ¼ć‚¹ć”ćØć®é‡‘é”ćŒé©åˆ‡ć§ćÆćŖć„å€¤ć§čØˆē®—ć•ć‚Œć¦ć„ćŸćŸć‚ć€AWS Pricing MCP Serverć‚’ē¹‹ćć“ćØćÆęœ‰åŠ¹ć§ć‚ć‚‹ć“ćØćŒć‚ć‹ć‚Šć¾ć—ćŸć€‚ (ē”ŸęˆAIć‚’ä½æć†ä»„äøŠåæ…ćšę­£ć—ć„å€¤ćŒē®—å‡ŗć•ć‚Œć‚‹ć‚ć‘ć§ćÆćŖć„ć§ć™ćŒć€č©¦ē®—ēØ‹åŗ¦ć§ć‚ć‚Œć°ć‹ćŖć‚Šęœ‰ē”Øć§ć‚ć‚‹ćØę„Ÿć˜ć¾ć—ćŸ) まとめ ćƒćƒƒćƒć‚µćƒ¼ćƒćƒ¼ć®ć‚¤ćƒ³ćƒ•ćƒ©éøå®šć«ćŠć„ć¦ć®ę–™é‡‘ęÆ”č¼ƒćØAWS Pricing MCP Serverć‚’åˆ©ē”Øć—ćŸåŠ¹ēŽ‡åŒ–ć®ę¤œčØ¼ć‚’å®Ÿę–½ć™ć‚‹ć“ćØćŒć§ćć¾ć—ćŸć€‚ ä»–ć«ć‚‚ć€ć€Œć‚·ć‚¹ćƒ†ćƒ č¦ä»¶ć«ć‚ˆć‚‹åˆ¶ē“„ć€ć€ć€Œćć®ä»–ć®AWSćƒŖć‚½ćƒ¼ć‚¹ć®ę–™é‡‘ć€ć€ć€Œäŗŗé–“ć«ć‚ˆć‚‹é‹ē”Øć‚³ć‚¹ćƒˆć€ など éøå®šć«ćŠć‘ć‚‹č¦³ē‚¹ćÆć•ć¾ć–ć¾ć‚ć‚Šć¾ć™ćŒć€ć‚¤ćƒ³ćƒ•ćƒ©č²»ć®ē®—å‡ŗć§å‚č€ƒć«ćŖć‚Œć°å¹øć„ć§ć™ć€‚