[{"data":1,"prerenderedAt":336},["ShallowReactive",2],{"content-/nuxt-build-time-parallel-investigation":3,"all-pages-for-dir":334,"og-image-/nuxt-build-time-parallel-investigation":335},{"id":4,"title":5,"body":6,"category":316,"description":317,"extension":318,"meta":319,"navigation":276,"ogImage":320,"path":321,"project_name":322,"published":323,"publishedAt":324,"seo":325,"stem":326,"tags":327,"todo":320,"unpublished":323,"updatedAt":320,"__hash__":333},"pages/2026-06/2026-06-16/nuxt-build-time-parallel-investigation.md","Nuxt Content v3 でビルド時間20分の壁に並列化で挑んだが意味なかった話",{"type":7,"value":8,"toc":305},"minimark",[9,14,18,25,32,35,38,49,52,55,104,115,123,126,133,147,150,161,165,168,171,182,188,192,195,198,221,224,227,230,235,238,243,246,251,254,259,262,265,302],[10,11,13],"h2",{"id":12},"発端-ビルドが20分で終わらない","発端: ビルドが20分で終わらない",[15,16,17],"p",{},"朝、Cloudflare Pages へのデプロイログを眺めながら腕を組んだ。「あれ、また20分かかってる」。@nuxt/content v3 で生成している記事ページが 7,478 ルートに増え、prerender だけで 751 秒（約12.5分）。バンドル＋ post-generate の処理を足すと、合計でほぼ20分が溶ける構造になっていた。",[15,19,20,24],{},[21,22,23],"code",{},"pnpm generate"," を回すたびに10分以上席を立つ運用は続けたくない。並列度を上げれば縮むのではないかと、ぼんやり手を伸ばしかけた。",[15,26,27,28,31],{},"ただ、手を動かす前に思い出した。「並列度の話、前にも一度やった気がする」。git ログを掘ると、過去に ",[21,29,30],{},"concurrency: 10"," を入れて、その後外した形跡が残っていた。",[15,33,34],{},"そこで、まずは別の出血点から塞ぐことにした。",[10,36,37],{"id":37},"まず効いたのはタグページの削除",[15,39,40,41,44,45,48],{},"ビルドログを眺め直すと、prerender 件数のうち、結構な比率を ",[21,42,43],{},"/tags/\u003Cタグ名>"," のページが占めていた。タグページが何の役割を担っていたか、自分でも忘れていた。Claude Code に「タグページが何か実例を見せて」と頼んだら、",[21,46,47],{},"pages/tags/[tag].vue"," の中身と、各記事の上部に並ぶタグリンクが出てきた。",[15,50,51],{},"中身を確認した瞬間、判断は早かった。「これ、別にいらない」。タグ別の記事一覧は、トップページのカレンダーや /blog の月別ビューで代替できる。タグごとに別ルートを切るほどの価値はなかった。",[15,53,54],{},"タグページを消す作業は、Claude Code に派遣して以下を一気にやらせた。",[56,57,58,65,75,85,95],"ul",{},[59,60,61,64],"li",{},[21,62,63],{},"apps/web/app/pages/tags/[tag].vue"," の削除",[59,66,67,70,71,74],{},[21,68,69],{},"apps/web/e2e/related-articles-and-tags.spec.ts"," の削除と、タグ無し版 ",[21,72,73],{},"related-articles.spec.ts"," の新設",[59,76,77,80,81,84],{},[21,78,79],{},"ArticleTable.vue"," と ",[21,82,83],{},"DocPage.vue"," からタグリンクの除去",[59,86,87,90,91,94],{},[21,88,89],{},"nuxt.config.ts"," 側の ",[21,92,93],{},"getTagRoutes"," 参照の除去",[59,96,97,80,100,103],{},[21,98,99],{},"public/_redirects",[21,101,102],{},"scripts/generate-redirects.mjs"," の整理",[15,105,106,107,110,111,114],{},"dev サーバで ",[21,108,109],{},"/blog"," のカレンダー表示、記事ページの上部、",[21,112,113],{},"/tags/Vue"," が 404 になることをスクショで確認。問題なし。",[15,116,117,118,122],{},"デプロイログを送り返してもらったら、",[119,120,121],"strong",{},"約20分 → 13分40秒（約32%削減）","。タグ削除だけでこの効き目が出るとは、正直思っていなかった。",[10,124,125],{"id":125},"並列度8を入れてみたら逆に遅くなった",[15,127,128,129,132],{},"ここで本題の並列化に戻る。Nitro の prerender には ",[21,130,131],{},"nitro.prerender.concurrency"," という設定があり、デフォルトは 1。過去に 10 を入れて外した経緯がドキュメントに残っていたが、そこには「効かなかった」とまでは書かれていない。試す価値はある。",[15,134,135,136,138,139,142,143,146],{},"「8 で入れてみよう」と決めて、Claude Code に ",[21,137,89],{}," を編集させた。同時に ",[21,140,141],{},"measure-deploy.ps1"," の整形フィルタが ",[21,144,145],{},"concurrency: 8 (explicit)"," のログ行を握り潰していたので、表示パターンも追加させた。",[15,148,149],{},"再デプロイ。結果のログを眺めた瞬間、声が漏れた。「あ、これ、遅くなってる」。",[15,151,152,153,156,157,160],{},"prerender 時間は ",[119,154,155],{},"concurrency: 1 のとき"," と比べて、",[119,158,159],{},"concurrency: 8 にしたほうがむしろ長い","という結果。誤差ではなく、はっきり遅い。スレッドの取り合いで context switching が増えただけ、という挙動に見えた。",[10,162,164],{"id":163},"codex-に聞こうとしたが-web-で答えが出てしまった","Codex に聞こうとしたが Web で答えが出てしまった",[15,166,167],{},"ここで、判断を一段深くする。「並列化が効かないのは設定ミスか、それとも Nitro 側の構造の問題か」を切り分けたい。",[15,169,170],{},"ユーザーに「Codex にも一応真相を聞いてみてほしい」と頼まれたので、まず Codex CLI を叩いた。ところがモデル名のエラーで蹴られた。別モデルで再試行する前に、並行で Web 検索を回したら、決定的な答えが出てきた。",[15,172,173,174,181],{},"Nitro の GitHub Issue #1447 で議論されている通り、",[119,175,176,177,180],{},"Nitro の prerender は worker_threads を使っていないため、",[21,178,179],{},"concurrency"," を上げても並列化が効かない","。同一プロセス内の非同期処理が増えるだけで、CPU バウンドな Vue/Nuxt のレンダリング処理は順番待ちになる。",[15,183,184,185,187],{},"つまり、過去に ",[21,186,30],{}," を外したのは正解だった。Codex に追加で聞く必要もない、と判断して撤回。",[10,189,191],{"id":190},"a-プランで撤回ドキュメントに残す","A プランで撤回、ドキュメントに残す",[15,193,194],{},"「結局意味なかったってことですね。じゃあドキュメントに残して戻してくれてるってことですね」と、ユーザーから一言。撤回作業に入った。",[15,196,197],{},"撤回内容:",[56,199,200,209,215],{},[59,201,202,204,205,208],{},[21,203,89],{}," の ",[21,206,207],{},"concurrency: 8"," 行を削除",[59,210,211,214],{},[21,212,213],{},"memo/2026-06-16/build-time-and-tags-removal.md"," に「並列化を試した結果と、なぜ効かなかったか」を追記",[59,216,217,218],{},"Nitro Issue #1447 への参照を残し、",[119,219,220],{},"今後同じ罠を踏まないようにする",[15,222,223],{},"このドキュメントは、半年後の自分が「ビルド遅いな、並列化試すか」と思ったときに最初に当たる地雷探知機の役割を担う。試して、ダメで、戻した。ここまでを丸ごと残すから、次の自分は2度目の試行錯誤に時間を溶かさずに済む。",[10,225,226],{"id":226},"学び",[15,228,229],{},"今日の試行錯誤で、頭の中に固定された判断軸がいくつかある。",[15,231,232],{},[119,233,234],{},"1. ビルド時間の最適化は、まず「ルート数を減らす」が一番効く",[15,236,237],{},"Nitro の prerender が CPU バウンドかつ並列化が効かない以上、勝ち筋は「不要なルートを削る」しかない。タグページのように、需要が薄い割にルート数を膨張させているページを真っ先に疑う。",[15,239,240],{},[119,241,242],{},"2. 過去の試行錯誤の痕跡は git に残っているので、まず履歴を当たる",[15,244,245],{},"「前にも一度やった気がする」という違和感を拾ったら、即 git log と git diff で過去のコミットを掘る。今回も「過去に concurrency: 10 を入れて外した」事実が見つかり、最初から仮説が立てやすかった。",[15,247,248],{},[119,249,250],{},"3. ダメだった試行も「ダメだった」とドキュメントに残す",[15,252,253],{},"成功した変更だけドキュメントに残すと、半年後の自分が同じ罠に再突入する。「試したけど効かなかった」「Nitro Issue #1447 が根本原因」までセットで書く。",[15,255,256],{},[119,257,258],{},"4. Codex も Web 検索も、両方手駒として並列で走らせる",[15,260,261],{},"今回は Codex がモデル名で蹴られたが、その間に Web 検索で答えが出てしまった。AI への質問と Web 検索は、片方が詰まったときの保険として並列で回すのが結局速い。",[10,263,264],{"id":264},"明日以降の宿題",[56,266,269,283,293],{"className":267},[268],"contains-task-list",[59,270,273,278,279,282],{"className":271},[272],"task-list-item",[274,275],"input",{"disabled":276,"type":277},true,"checkbox"," ビルド時間をさらに削るなら、記事の生成スキーマや ",[21,280,281],{},"useAsyncData"," の payload サイズを点検する",[59,284,286,288,289,292],{"className":285},[272],[274,287],{"disabled":276,"type":277}," ",[21,290,291],{},"pages/[...slug].vue"," のレンダリングで重い処理がないか、prerender 1ルートあたりの所要時間を計測する",[59,294,296,298,299,301],{"className":295},[272],[274,297],{"disabled":276,"type":277}," 13分40秒からさらに削るアイデアが出たら、また ",[21,300,213],{}," に追記する",[15,303,304],{},"並列化に時間を溶かしかけたが、ドキュメントに「効かない理由」を残せたので、今日の時間は無駄ではなかったことにする。",{"title":306,"searchDepth":307,"depth":307,"links":308},"",2,[309,310,311,312,313,314,315],{"id":12,"depth":307,"text":13},{"id":37,"depth":307,"text":37},{"id":125,"depth":307,"text":125},{"id":163,"depth":307,"text":164},{"id":190,"depth":307,"text":191},{"id":226,"depth":307,"text":226},{"id":264,"depth":307,"text":264},"dev","Cloudflare Pages へのデプロイで Nuxt Content v3 のビルド時間が20分に膨らんだ。タグページ削除で13分台まで落とせたが、そこからさらに concurrency: 8 で並列化を試したら逆に遅くなった。Nitro の prerender が worker_threads 未対応で並列化が効かないと判明するまでの試行錯誤を記録する。","md",{},null,"/nuxt-build-time-parallel-investigation","mdx-playground",false,"2026-06-16T00:00:00.000Z",{"title":5,"description":317},"2026-06/2026-06-16/nuxt-build-time-parallel-investigation",[328,329,330,331,332],"Nuxt","Cloudflare Pages","ビルド最適化","SSG","並列化","SMOU9-54s4PfHZRBGgjE6qlrPNT1Y0CZ4Ks-FS88Tow",[],"https://log.eurekapu.com/og/blog/nuxt-build-time-parallel-investigation.png?v=2026-06-16T00%3A00%3A00.000Z&title=Nuxt%20Content%20v3%20%E3%81%A7%E3%83%93%E3%83%AB%E3%83%89%E6%99%82%E9%96%9320%E5%88%86%E3%81%AE%E5%A3%81%E3%81%AB%E4%B8%A6%E5%88%97%E5%8C%96%E3%81%A7%E6%8C%91%E3%82%93%E3%81%A0%E3%81%8C%E6%84%8F%E5%91%B3%E3%81%AA%E3%81%8B%E3%81%A3%E3%81%9F%E8%A9%B1&author=Kei%20Komatsu&sig=e808c0cd1c153b4b",1782176329019]