20
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

地獄のクローラー運用 ~クラウドから自宅物理サーバーへ。しかし~

Last updated at Posted at 2025-12-24

AI Coding.Infoの話

私はAI Coding.InfoというAI Coding Agentの利用動向を毎日集計しているサイトを公開しています。

最近は、こんな発表をしてきました。

image.png

このAI Coding.Infoは以下のような特徴があります。

  • Githubの公開リポジトリを9,000リポジトリを毎日調査
    • プログラミング言語毎のGithub スター数のTOP 300
    • プログラミング言語は30種類から調査
  • AI Coding Agent 16種類を対象
  • 各種AI Coding Agentの利用するルールファイルがGithubリポジトリにある場合のみ、AI Coding Agentを利用していると判断

このような特徴のため、毎日Githubのデータをクローリングする必要があります。このクローラー運用をしてみて色々なことが起こったことをお話ししようと思います。

Github Actionsでのschedule運用

 最初、自分がこのサイトを始めた。プロトタイプを作っていた当初は、クローリングのスクリプトの実行は手動で行っていました。自分が起きたタイミングで、スクリプトを実行し、その裏では適当にパソコンを触っている。というような簡単なものでした。しかし、パソコンも少しトイレで離れたりするとスリープをしてしまい、クローリングが中途半端な状態でエラーになる。といったことが起こってしまいます。そのため、何かしら、自分のパソコン以外の安定的な環境で動かしたい。という希望がでてきました。
 そこで、目をつけたのがGithub Actionsのschedule機能です。

いわゆる、linuxで使われるcronがGithub Actionsでも使うことが可能で、勝手に時間になったら発火してくれます。

 on:
   schedule:
     - cron: "15 4,5 * * *"

これでクローラーの実行を気にせず、自分のPCに負荷をかけたり作業をすることが可能になりました。

Github Actions無料使用枠が枯渇する

 Github Actionsには無料使用枠というものが存在します。基本的にpublicなリポジトリでは、この無料使用枠は消費せず、privateなリポジトリで無料使用枠を消費することになります。
この無料使用枠は、いろんな条件があるので一概には言えないのですが、私が使っていた標準のCPU構成のubuntuのlinuxイメージでは、おおむね2,000分利用することが出来ます。

 いわゆる入門者が普通に使うような利用の仕方です。AI Coding.Infoは毎日クローリングしている。ということを書きました。ということは、

2,000分 / 30日 = 66.7分/日

1日当たりのクロール時間を1時間未満に抑える必要があります。しかし、実際のクロール時間は当時は2時間程度ありました。そのため、運用を始めた月の半ばぐらいですべてのGithub Actionsが止まり自分の別のプライベートリポジトリのCI/CDも全部止まるという状況が発生しました。

Raspberry Pi 3という選択肢

GithubActionsの無料枠を使い切ってしまった。となると、どうなるか。というと、

  1. GithubActionsに課金する
  2. サーバーを有料で借りる
  3. 自分のメインPCで頑張る
  4. 新たにPCを調達する

という4種類の選択肢が生まれます。そもそも無料で提供しているサービスにお金をかける選択肢は除外すると、本質的には3の選択肢しかありません。そんなことを考えていると、Raspberry Pi 3が手持ちで余っていることを思い出しました。こいつを使おう。という発想になりました。

image.png

さきほどから、「Github Actionsの無料使用枠」という話をしていますが、これは厳密には「Github ActionsのGithub Hosted Runnerの無料使用枠」です。なにかプログラムを動かすということはコンピューターが必要になります。そのためのコンピューターをGithub社の物で動かす=Github Hosted Runnerとなります。この利用時間が2,000分となっています。逆を返せば、コンピューターを自前で用意すれば、利用時間は無限になります。このようにGithub Actionsの処理を自前のPCで行う方法を、Self Hosted Runnerと言います。

このように自宅でサーバーを用意すれば、電気代は目をつむるにしても、利用時間は無限で何も問題はないわけです。

ubuntu-latestってなんだ。

このself-hosted runner自体は、色々な記事があり、とても導入しやすいです。

なので、導入自体も簡単でSelf hosted runnerとして動作させること自体は特に問題がありません。

しかし、既存のworkflowは動きません。

仮に以下のようなGithub workflowがあったとしましょう。

name: Daily Batch

on:
  schedule:
    - cron: "15 4,5 * * *"

jobs:
  research:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout repository
      uses: actions/checkout@v4

    - uses: actions/cache@v4

 self Hosted Runnerに代える場合は、このruns-onという項目をself-hostedに書き換える必要がある。というのは、かなりの記事で書かれています。それ以上に必要な調整がある。ということです。
 まず、いわゆる上記のworkflowはかなり一般的でcheckoutとcacheぐらいは誰でも動かすだろう。と思います。しかし、動きません。理由はcheckoutやcacheのアクションが内部的にgitやzipコマンドを利用しており、RaspberryPiのデフォルトのインストールコマンドにgitとzipコマンドがないからです。
 今まで選択していたubuntu-latestですが、そこそこにデフォルトでコマンドが入っています。しかし、自分で組んだRaspberry Piのサーバーには、自分でコマンドをインストールする必要があります。簡単に思いつく方法は、

  1. ubuntu-latestのDocker Imageを利用する
  2. ubuntu-latestのコマンドを手動でインストールする

という2パターンがありえます。
 そもそものSelf Hosted RunnerをDockerコンテナで動かします。その時に、Githubが用意しているubuntu-latestのDocker Imageを利用すればよい。という発想です。しかし、そんなものはGithubから公開されていません。 自分としては、公開されてるんじゃない?ぐらいの感覚があったのですが、意外となくて驚きました。
 では、ubuntu-latestとは何なんだ。どんなコマンドがそもそも利用できるのだ。 という疑問があります。これは公式ドキュメントがあります。

 実は多くのコマンドがデフォルトの状態で提供されています。 そのため、ubuntu-latestの互換性のために、これらのコマンドを手動で全てインストールして、ubuntu-latestと互換な状況をRaspberry Pi上に作るのが無理筋である。という結論になりました。
 では、どうしたか。ということですが、結果としてgitとunzip程度をインストールすれば、自分のworkflowでは動くようになりました。そのため、最小限の環境構築で事なきを得ました。

2週間でRaspberry Pi環境が死ぬ

 RaspeberryPiにクローラー環境、CI/CD環境も移行し、ある程度、安定稼働をしていました。しかし、2週間程度で突然Raspbery Piの環境が動かなくなりました。
 そこで、今まで画面につないでいなかった、RaspberryPi3に画面をつなぎ、ブートシーケンスの画像を取り、ChatGPTさんに投げます。

image.png

image.png

 それで、ふと思い出したことがありました。

Raspberry PiのmicroSDは死にやすい

 私自身は、別にそのような経験はなかったのですが、Raspberry Piの検証記事を見ていたところ、microSDが壊れる。microSDの寿命を延ばす方法。と、なにかしら他の人がRaspberryPiのストレージのmicroSDの運用に苦労している。ということが頭に過ぎりました。しかし自分自身は、今まで壊れたことがなかったため、他人事だと思っていました。
 そこで、DiskGeniusというソフトを使い、microSDのチェックを行ったところ、実際にmicroSD内に不良セクタが発生していました。ということで、私は夜にドン・キホーテへ行き、microSDを調達してきて、RaspberryPiの環境構築を再度行いました。

冪等性という地獄

 いろいろと話を聞いてみると、RaspberryPiのストレージとしてmicroSDを採用しているのは、そもそも長期運用の耐久性として無理筋である。みたいな話も知り合いから聞きました。この辺りは、どのように調べるか。というと、

$ sudo cat /sys/block/mmcblk0/stat 
350801 89253 34064326 7400908 2318324 2185114 55592233 26078920 0 7540636 33479828 0 0 0 0 0 0

という風な形で、ディスクへの読み書き回数などを表示することで確認できます。しかし、見方が分からないので、ChatGPTに投げます。イメージとしては、

kotauchisunsun@raspberrypi:~/work/second-runner $ date Fri 1 Aug 01:51:27 JST 2025 
kotauchisunsun@raspberrypi:~/work/second-runner $ sudo cat /sys/block/mmcblk0/stat
1398111 635633 110717895 20836772 9467959 10271197 267476994 154423609 0 31830332 175283282 706 200 126171072 22900 0 0

みたいな形で、今が何日かで、その時、どれくらいの量だったかを明示的に分かりやすくしながら教え込みます。そうすると以下のような回答が返ってきました。

image.png

というわけで、数週間のうちにmicroSDが壊れるだろう。という所見が返ってきました。
 これは1つにCIの冪等性の話もあると思っています。Dockerをイメージすると分かりやすいですが、常にゼロベースから環境を構築するワークフローを維持することによって、いつ、どこででもソフトウェアがビルドできるような状態にする。というのが、現代的なサーバー系のプラクティスとしてあると思います。しかし、これはこの環境下においては罠で、ライブラリはnpmからダウンロードを行い、それをストレージに書き込み、それからプロダクトをビルドし、そして廃棄する。そして、別のタスクが走ったときにも同じデータであったとしても、ライブラリをnpmからダウンロードを行い、それをストレージに書き込み、タスクを行う。というのは、多量のIOがmicroSDに走り、結果、寿命を高速に縮める原因になります。しかし、よい解決策は思いつかない。というのが、その時の状態でした。
 microSD自体、32GBが1,500円くらいだったので安いVPSサーバーを借りるのと同じくらい。microSDの寿命次第では安いぐらい。程度で実に微妙なラインでした。

XServerの無料枠

そこで、ある日、大阪であったAI系の勉強会「AI駆動開発勉強会」へ参加しました。

これはXServerさんが協賛に入っていました。で、その中で、XServerのVPSが1ヵ月無料で使える。という告知がでていました。

 やったぜ!!ということで、いったんこちらにクローラーを逃がすことでmicroSDの負荷を減らし、VPSサーバーの方でSelf Hosted Runnerを動かすことにしました。
 実は、RaspberryPiの件は、もう1つ別の問題があり、難儀していました。それは 「CPU」 です。一般的なWindowsノートパソコンやサーバーは基本的にはintelのx86のCPUが搭載されています。しかし、Raspberry Pi系にはARMのCPUが搭載されています。しかもRaspberry Pi 3自体が2016年のモデルです。そう考えると、新しいソフトウェアが動かなくなってる。もしくは、動かすためには自前でソフトウェアのビルド環境から用意する必要がある。という極めてめんどくさい状態になっていました。そのため、比較的新しい環境や互換性の強いx86系のCPUの選定の方がよい。という感触がこの辺にありました。

miniPC購入を決意

 もともとはGithubの無料枠。その次は、RaspberryPi、そしてXServerと来ました。しかし、XServerの無料枠には1ヵ月という時間制限があり、この後どうするのか。という問題はあります。実は、すでに私は別の事業者でVPSを借りていました。そちらはそちらで使っていたため、重いクローラー作業等でシステムが圧迫されたり、逆にシステム負荷が高まったときにクローラーの挙動がおかしくなることは避けたい。ということがありました。
 今回無料提供があったXServerさんのサーバー(6GBプラン)を借りるとすると1か月の最低構成が約2,200円。これを借り続けるか?というところを考えていました。

 一方で、別の選択肢を考えていました。miniPCです。

 一部の界隈で、このような手のひらサイズのPCが流行っていることを知っていました。そのため、これってどうなんだろうな。と思って、見ていました。

FIREBATミニPC N150/16GB/512GB/WiFi/BT メモリとストレージはアップグレード可能で、超ポータブルでコストパフォーマンスが 静音ミニPC 家庭用ポータブルメディアミニコンピュータminipc【T2】
image.png

今は売り切れとなっていますが、当時は、17,858円で売られていました。ということは、

17,858[円] / 2,200[円/月] ≒ 7.9[月]

となり、電気代を無視すれば8ヵ月くらいあればペイする。という計算でした。「ならいけるやろ」。というノリで自分が欲しかったのも含めて、miniPCを購入し、Self Hosted Runner運用に切り替えました。
 もともとRaspberry Pi 3のメモリが1GBで、XServerのプランのメモリが8GBでした。それに対して、このminiPCはメモリが16GB、ストレージが500GBである。CPUもIntel N150ということでx86系のアーキテクチャなので、環境構築の辛さもRaspberry Piほどではない。ということを考えると、手軽で論理的に高スペックのものが使えるため、今までよりタスクがこなせるであろう。というヨミもありました。
 実際に買ったminiPCはこんな感じ

image.png

 感想としては、思ったよりかは大きいかな。という感想でした。miniのイメージが先行しすぎていたため、RaspberryPiぐらいかな。という先入観があったのですが、それよりかは一回り大きいぐらいのサイズ感です。しかし、手の上に乗るサイズのPCなので省スペースであることは事実です。
 こういったPCはWindowsが最初からプリインストールされていることが多いです。しかし、そこには若干ライセンスが怪しい問題があります。しかし、私の場合はOSを全部Ubuntuで吹き飛ばしたので、その辺の問題がなかったです。そして、Self Hosted Runnerの設定もすんなりと行き(4回目)、運用に乗せることが出来ました。

気づかない負債と運用

miniPCを運用し始めて数か月。あることに気づきました。

クロール時間が5時間を超えている。

 基本的には、Github ActionsのUIを見ており、1日のうちに何度かアクセスし、不慮の事故で止まってたときのみ、リスタートをする。という運用をしていました。そのため、ある程度、クロール時間を把握していたものの、日付によって上下することもあるので、さほど気にはしていませんでした。しかし、5時間を超え始めると、さすがになんかヤバいぞ。と感じ取りました。
 実際に、日付とクロール時間をプロットしたものが以下のようになっています。

image.png

 少し下がった時期もありますが、おおむね1ヵ月につき1時間増えています。これは非常に遅延しているという感覚になります。しかし、1ヵ月で1時間、つまり60分増えていることにはなるのですが、1日当たりに直すと2分になります。毎日監視していたとしても、1日2分程度のクロール時間の変動なら、誤差だと錯覚してしまった。 という問題でした。
 しかし、まぁいいだろう。という感覚がありました。理由は単純で、

Self-Hosted Runnerの実行時間は最大5日間だから

 仮にクロール時間が24時間を超える。となると、それはサービスとして意味がなくなるので問題です。しかし、7時間ぐらいだと、まだ耐えられる。というのが正直なところでした。一方で、Github Hosted Runnerは6時間以上のジョブは強制停止されます。

image.png

しかし、Self Hosted Runnerは最大5日間まで実行可能です。そのため、6時間以上かかっても、まぁまだ耐えられる。という感触でした。

image.png

嘘です。Self Hosted Runnerは普通に設定していると6時間以上ジョブが回せません。

これがキツかった。ある日、友人と外食をして家に帰ってきて、そういや「今日のジョブの結果みてなかったな」と思って確認したところ、「6時間でがっつりタスクが切られていた」時には絶望しました。
 いそいでChatGPT先生に聞いてみます。

image.png

ChatGPT先生も、そんなわけはない。と言います。しかし、別の聞き方をしてみます。

image.png

真相はこうです。

  • Github Hosted Runnerの1つのタスクの実行時間が6時間→正しい
  • Self Hosted Runnerの1つのタスクの実行時間は5日間→間違い
  • Self Hosted Runnerでtimeout-minutesを設定したときに1つのタスクの実行時間を5日間にできる→正しい

というわけで、

Self Hosted Runnerにしたら1つのタスクの実行時間の6時間の上限を自動で緩和してくれるわけではなく、timeout-minutesを設定しないと最大の実行時間の5日間にならない。

ということで、以下のようにtimeout-minutesを指定する必要があります。(下記設定では72時間にしています)

jobs:
  research:
    runs-on: [self-hosted]
    timeout-minutes: 4320

ソフトウェアの地獄の時間

 先ほどの章で、クローリングに6時間かかっている。ということを話しました。そもそもGithub Actionsは、クローラーをキックして動かすようなものではなく、そういう用途で作られていないんだから、お前の使い方が悪い。と言われればそれまでです。したがって、根本的な解決をする必要があります。
 今までのログを調査してみると、デプロイ時間の長大化も見られました。2025年8月1日のデプロイ時間は9分4秒であったのに対し、2025年11月25日のデプロイ時間は1時間28分50秒でした。機能追加やページ数が増えていたりするため、直接比較することはできないですが、それでも9倍近い時間になっていることは事実です。ここでは

「データベースの件数が肥大化したことによる速度低下だ」

が私の直感でした。これは、最初に書いた通り、1日当たり15万件のデータが増えています。そのため、1ヵ月で450万件近くデータが増えていきます。2025年12月24日現在では、2,800万件以上のレコードがあります。 これが悪いんだろう。という予測がありました。
 では、当たり前の話をすると、データベースのクエリチューニングという話になります。いわゆる、

  1. 時間のかかる実行しているクエリを特定
  2. クエリをEXPLAIN
  3. 適切なIndexを貼る

というのが、お手本の手法になります。

まぁそれが出来れば苦労はないんですよ。

 AI Coding.InfoはNext.jsを採用していますが、Next.jsではnext buildというコマンドでビルドします。これに対応するプロファイラがどうも見当たらなかった。例えば、lighthouseなどWebページの速度を計測するプロファイラは存在します。しかし、いわゆるgprofやPythonのcProfileに当たるようなもので、関数単位の実行時間や実行回数を可視化するプロファイラのTypeScript版が見当たりませんでした。そこで、プロファイラを自作することを考えました。
 というのは、そもそも小手先の高速化ということをやっていたことに起因しています。例えば、

export const cachedGetAgentRankingData = withCache(getAgentRankingData, (researchType = "Stars") => `getAgentRankingData-${researchType}`);

という風な関数を作っていました。getAgentRankingDataは最新の日時のAI Coding Agentのランキングデータを取得する関数です。withCacheという関数は第一引数にラップする関数を持っています。そして、2つ目の引数では、キーを生成する関数を定義し、内部的なinMemoryKVSでキーが合致したときには値を返す。そうでない場合は、関数を呼び出し、値を格納し、返す。ということをやっていました。(そう。何も対策を施していなかったわけではなく、対策を施したうえで、ビルド時間が1.5時間あった。というのがかなりきつかったポイントだったりします。)
 そのため、withCache内で、キャッシュ内で値が存在せず、関数を実行する前と後で、console.timeを呼び出すことで、簡単なプロファイラを自作できました。そのため、どの関数が遅いか。ということは、ある程度、当たりをつけることが可能でした。
 また、今回、ORMにはprismaというライブラリを利用していました。これは簡単にqueryログを出すことが出来ます。

const prisma = globalForPrisma.prisma || new PrismaClient({
    log: ["query"],
})

こんな感じでクエリログを出すことが出来ます。実際に出るログがこんな感じです。

SELECT `main`.`GithubRepository`.`id` FROM `main`.`GithubRepository` WHERE `main`.`GithubRepository`.`id` IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) LIMIT ? OFFSET ?

これだと全然分からん。というのが正直なところです。実は公式ドキュメントによると、もう少しパラメーターを含めた詳細なログも出せるそうですが、当時は、そこまで読み込めてませんでした。

しかし、先ほど検証のために動かしてみたところ、動きませんでした。

./src/lib/prisma.ts:9:12
Type error: Argument of type '"query"' is not assignable to parameter of type 'never'.

   7 | })
   8 |
>  9 | prisma.$on('query', (e) => {
     |            ^
  10 |   console.log('Query: ' + e.query)
  11 |   console.log('Params: ' + e.params)
  12 |   console.log('Duration: ' + e.duration + 'ms')
Next.js build worker exited with code: 1 and signal: null

というわけで、あとは勘で戦います。
AI Coding.Infoはおおむね以下のようなスキーマ構造をしています。

image.png

Researchのテーブルが調査全体を表し、「いつの調査結果か」を管理します。その中で、「調査結果の具体的な内容」については、ResearchRecordが担当します。この時に、どのGithubリポジトリで、どんなAgentに対する調査であったか。結果がどうであったか。を管理します。Githubのリポジトリでは、「リポジトリ名」と「どんなプログラミング言語が使われていたのか」を保持しています。
 AI Coding.Infoの表示として、「〇月〇日の調査結果」という表示を行っています。そう考えると、例えば、12月25日の調査結果を調べたいとき、ResearchのcreatedAtが2025/12/25 00:00:00から2025/12/26 00:00:00までの結果を調べ、Researchのidを調べます。そして、ResearchRecordからresearch_idが合致する結果を調べる必要があります。ということは、

SELECT `exists` from ResearchRecord where research_id = "XXXXXX"

のようなクエリは最低限必要であることが分かります。これをEXPLAINして実行計画を見てみました。そうするとフルスキャンが走っていました。 フルスキャンは雑に言うと、テーブルのデータを全走査して確認するデータの抽出方式です。したがって、データ量が多ければ多いほど遅くなります。先ほど、データ数が2,800万件あったテーブルは、このResearchRecordです。そのため、この一番レコード数の多いテーブルで、こんな最低限のクエリでもフルスキャンがかかるのは、さすがにまずい。と思いました。そのため、このResearchRecordのresearch_idに対して、インデックスを貼りました。
 また、AI Coding.Infoとして、「プログラミング言語ごとのAI Coding Agentの利用率」を計算しています。例えば、TypeScriptの2025年12月25日の利用率を調べたい。と考えると、まずTypeScriptというプログラミング言語が内部的にどのようなidで管理されているかを調べます(language_id)。そのプログラミング言語を利用しているリポジトリを特定します。そして、それをResearchRecordの結果と突合することで、プログラミング言語毎のAI Coding Agentの利用率を計算します。ということは、github_repository_idにも、インデックスを貼っておいた方がよさそうかな・・・?ぐらいでこちらはつけました。これは、prismaの集計クエリを考えたときに、ResearchRecordを起点に書いているもの、Languageを起点に書いているもの。と少し異なるため、こちらもindexをつけました。割と勘でやってしまっているのは、先にも書いた通り、そもそもprismaがどんなクエリを実際に吐いているのかを特定することが出来ない(具体的な値が分からない)ため、こうするしかなかった。というのが、その時の現状です。
 実際、どれくらい改善したかのデータもあります。

image.png

日付 デプロイ時間 クロール時間
2025/11/28 1:28:50 7:05:31
2025/11/29 0:10:48 1:53:01
改善率 87.84% 73.44%

デプロイ時間は87%削減。クロール時間は73%削減できた。という結果になりました。

実行時間のムラっけ

 AI Coding.InfoではPRごとにテストを行っています。しかし、このテストですが、実行時間に非常にムラがありました。先ほどのDBへのインデックスを作ったため、サイト自体のビルドのテスト自体は高速化しています。しかし、同じPRのテストでも、1時間以上かかったり、3分で終わったりという非常に極端な事象が起きています。
 原因を調査するために、topコマンドでminiPCの様子を見ていました。おおむね処理時間がかかる。ということはCPU利用率が100%になっている。メモリが枯渇しておりスラッシングしてる。の2パターンだと踏んでいました。そして、どちらも違いました。
 もともとのアーキテクチャは非常にシンプルでした。ここではサイトを実際にビルドする「build_test」、crawlが動くことを確認する「crawl_test」を題材に説明します。それぞれのクラウド上に保存されていたデータベースのデータを、それぞれのGithub Actionsのワークフローでダウンロードしてきます。そして、それぞれが、そのデータベースのデータを参照して、build_testとcrawl_testの両方を行います。

image.png

 このminiPCではgithub actionsのrunnerを2つ同時に動かしています。そのため、2つのrunnerが同時に動き出すと、データベースをダウンロードし、ストレージへの書き込みが同時に走ることでIOを占有し、テストが始まらない。という挙動でした。 そのため、ムラっけがあり、1つしかタスクが始まっていない。もしくはダウンロードが終わったタイミングで、他のテストが始まる。といったことが重なると早くテストが動きますが、同時に走ったときに、強烈にテスト時間が長くなる。という非常に厄介な挙動でした。
 では、どうしたか。というと、結局のところIOが発生する部分を極限まで減らす。ということをしました。

image.png

 build_test、crawl_testのそれぞれでクラウドにあるデータベースのハッシュ値を確認しに行きます。そして、このハッシュ値と同様のデータベースデータが/tmp配下に存在しないときのみ、/tmpへそのデータベースのデータをダウンロードします。これによって、タスク間でのダウンロードや、データベースが更新されない期間においては、余計なIOが発生しなくなります。そして、配置後、すぐにreadonlyの権限にします。その後、build_testにおいては、データベースのデータはsymbolic linkの形式で、crawl_testにおいては、rsyncでそれぞれの作業領域にコピーしてきます。
 これは「テストの性質の差」があり、build_testのような「サイトをビルドしてみる」というテストにおいては、データベースはreadonlyで十分であるため、symbolic linkで十分になります。しかし、crawl_testのように小規模なクロールを行い、「データベースの書き込み」が必要なテストにおいては、/tmpからrsyncしてきます。なぜ、先ほど「データベースのダウンロード後、すぐにreadonlyの権限にする」と書いたのかというと、このあたりのフェイルセーフのためです。仮に、crawl_testのような「データベースに書き込み」のあるテストを誤って、symoblic linkで配置してしまうと、ダウンロードしてきた元のデータベースのデータが書き換わってしまい、それ以降のビルド結果が、このcrawl_testで実行したクロール結果の影響を受けてしまいます。そのためにも、ダウンロードしてすぐのタイミングでreadonly化し、誤ってテストを動かした場合でもデータが破壊されないように工夫をしています。
 このように、hash値が変わらない間は、/tmp配下にreadonlyでデータベースのデータを配置することで、ダウンロード量やストレージの書き込みを減らします。そしてテストの性質によって、symbolic linkを用いることで、ストレージへの書き込み(コピー)回数を最低限にすることで、IOの競合を回避し、CIの実行時間の安定化を図っています。

地獄は続くよどこまでも。

 最近、残念なニュースが入ってきました。Self Hosted Runnerは、今まで無料枠を消費せず無制限に利用できていました。しかし、それが1分当たり0.002ドルのコストがかかる予告をした。 というニュースです。実際、Githubから告知はでたのですが、ユーザーからの反響の多さから、Self-Hosted Runnerの課金の延期が発表されたそうです。しかし、私見としてはおそらく有料課金されるだろう。と予測しています。似たような話として、Github Copilotの話もあり、あの時も、無料枠に上限を作る!という話を出した後、延期します。というニュースになり、そして、その後に、実際に無料枠に上限が作られました。そのため、今回も3~6か月以内にはSelf Hosted Runnerも無料ではなくなるという予感がしています。

感想

やはり感じたポイントとしては、

「表層的VibeCodingの限界」

でした。AI Coding.Info自体もAIを用いて開発していますが、ここに書かれている問題のほとんどは手動でしか解決できませんでした。それは、物理的な問題もありますが、ソフトウェアのレイヤーにおいても、「なぜかクロール時間が長くなる」「なぜかデプロイ時間が長くなる」といったものは、極めてややこしい問題でした。途中で、「一部の関数の結果をInMemoryKVSに入れてキャッシュ化する」という話も書きましたが、実は内部的にはもう少し複雑なことをしており、そういったコードも割とかけませんでした。このDBまわりのチューニングは、ツールがないためできない。という次元の話だったので、人間が出来ないものをAIにやらせるのはそもそも無理筋だと思いました。
 少し前に、こんなポストをしましたが、この内容を地で行っているような内容でした。

 これは仕事においてもそうですが、自分で解決できないなぁと思った仕事を他人に任せて、他人が完成しなかったとき、これが一番きつい。そして、チームのマネージャーで動いていると、解決できた仕事はバンバン上がってきて検品する必要がある。解決できなかった人に対しては、別の仕事を振る必要がある。そして、そういうタスクのパズルに時間を使いつつ、自分でも解決できない問題を自分で解決するしかない。という極限に追われていた時期もありました。そして、そういうタスクこそサービス開発のボトルネックになり、全体のスループットが落ちてしまう。という悪循環が発生してしまったりします。今回の件は本当にそうで、クロール時間がかかろうがテスト時間がかかろうが本質的には1円にもサービスには影響しません。しかし、開発効率は圧倒的に落ちるために、やるしかない。というしんどさのある内容でした。
 「実行時間のムラっけ」という話も書きましたが、今、改めて考えると難しい問題だったな。と振り返っています。だって、Github Hosted Runnerで動かしてたら必要なかったし。というのが正直な気持ちです。物理的なサーバーのスペック、IOの性能がボトルネックになったために、やらねばならなかった作業だったな。と改めて思いました。クロール自体はGithub Hosted Runnerで動かすのは難しいかもしれませんが、テスト周りはGithub Hosted Runnerで動かしてもいいのかも。と思い始めました。
 今後、Self Hosted Runnerが課金になったりすると問題も発生しますが、目下問題となっているのは、自宅に常時稼働しなければならない物理サーバーを置いてしまったがために、家庭内のネットワーク構成をいじりにくくなったな・・・というのが、ちょっとした悩みです。そろそろリプレイスしたいなぁ。

20
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?