From ee0904d7a6425704eb1b3da3955d1536cb936a0c Mon Sep 17 00:00:00 2001 From: Akira Takahashi Date: Mon, 6 Jul 2026 12:54:45 +0900 Subject: [PATCH 1/6] =?UTF-8?q?=E5=A4=96=E9=83=A8=E3=83=AA=E3=83=B3?= =?UTF-8?q?=E3=82=AF=E3=83=81=E3=82=A7=E3=83=83=E3=82=AF=20:=20=E4=B8=80?= =?UTF-8?q?=E6=99=82=E7=9A=84=E3=81=AA=E6=8E=A5=E7=B6=9A=E4=B8=8D=E8=89=AF?= =?UTF-8?q?=E3=81=B8=E3=81=AE=E5=AF=BE=E7=AD=96=E3=81=AE=E3=81=9F=E3=82=81?= =?UTF-8?q?2=E6=AE=B5=E9=9A=8E=E3=81=AE=E3=83=81=E3=82=A7=E3=83=83?= =?UTF-8?q?=E3=82=AF=E3=82=92=E5=85=A5=E3=82=8C=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/script/link_check.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/script/link_check.py b/.github/workflows/script/link_check.py index f5320e9fca..a772012b77 100644 --- a/.github/workflows/script/link_check.py +++ b/.github/workflows/script/link_check.py @@ -169,11 +169,23 @@ def check(check_inner_link: bool, check_outer_link: bool, url: str) -> bool: if len(url) > 0: outer_link_dict[url] = "" + # 1パス目: 失敗したURLだけ集める + failed = [] for link, from_list in outer_link_dict.items(): exists, reason = check_url(link) if not exists: - print("URL {} not found. {} from:{}".format(link, reason, from_list), file=sys.stderr) - found_error = True + failed.append((link, from_list)) + + # 2パス目: 一時的な障害 (ランナーのネットワーク輻輳やサイトの瞬断) を + # 誤検出しないよう、間をあけて失敗したURLのみ再チェックし、 + # 両方のパスで失敗したものだけを報告する + if failed: + time.sleep(120) + for link, from_list in failed: + exists, reason = check_url(link) + if not exists: + print("URL {} not found. {} from:{}".format(link, reason, from_list), file=sys.stderr) + found_error = True return not found_error From 139e3d538d4e57bbcd59b5a0859017a322b17a20 Mon Sep 17 00:00:00 2001 From: Akira Takahashi Date: Mon, 6 Jul 2026 13:04:13 +0900 Subject: [PATCH 2/6] =?UTF-8?q?=E5=A4=96=E9=83=A8=E3=83=AA=E3=83=B3?= =?UTF-8?q?=E3=82=AF=E3=83=81=E3=82=A7=E3=83=83=E3=82=AF=20:=20=E3=83=AA?= =?UTF-8?q?=E3=83=80=E3=82=A4=E3=83=AC=E3=82=AF=E3=83=88=E3=82=92=E5=87=A6?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/script/link_check.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/script/link_check.py b/.github/workflows/script/link_check.py index a772012b77..33007868e5 100644 --- a/.github/workflows/script/link_check.py +++ b/.github/workflows/script/link_check.py @@ -18,13 +18,13 @@ def retry_sleep(): def check_url(url: str, retry: int = 5) -> tuple[bool, str]: try: headers = {'User-agent': 'Mozilla/5.0'} - res = requests.head(url, headers=headers, verify=False, timeout=60.0) - if res.url: - if res.url == url: - return res.status_code != 404, "404" - return check_url(res.url) - else: - return res.status_code != 404, "404" + # パフォーマンスのため本文は取得せずHEADで確認する。 + # ただしallow_redirects=Trueでリダイレクトはrequestsに追わせ、最終的な + # ステータスで判定する (手動でres.urlを辿る再帰をやめ、リトライ回数が + # 途中でリセットされる問題を避ける)。 + res = requests.head(url, headers=headers, verify=False, timeout=60.0, + allow_redirects=True) + return res.status_code != 404, str(res.status_code) except requests.exceptions.ConnectionError as e: if retry <= 0: return False, "requests.exceptions.ConnectionError : {} ".format(e) From 79a4e8deac650c560f0da4cce7dae90b2409da86 Mon Sep 17 00:00:00 2001 From: Akira Takahashi Date: Mon, 6 Jul 2026 13:37:18 +0900 Subject: [PATCH 3/6] =?UTF-8?q?=E5=A4=96=E9=83=A8=E3=83=AA=E3=83=B3?= =?UTF-8?q?=E3=82=AF=E3=83=81=E3=82=A7=E3=83=83=E3=82=AF=20:=20=E3=83=AA?= =?UTF-8?q?=E3=83=88=E3=83=A9=E3=82=A4=E6=99=82=E3=81=AEsleep=E6=99=82?= =?UTF-8?q?=E9=96=93=E3=82=92=E8=A6=8B=E7=9B=B4=E3=81=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/script/link_check.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/workflows/script/link_check.py b/.github/workflows/script/link_check.py index 33007868e5..26d1d59804 100644 --- a/.github/workflows/script/link_check.py +++ b/.github/workflows/script/link_check.py @@ -11,11 +11,16 @@ urllib3.disable_warnings() -def retry_sleep(): - sec = random.uniform(20, 30) - time.sleep(sec) +MAX_OUTER_LINK_RETRY = 3 -def check_url(url: str, retry: int = 5) -> tuple[bool, str]: +def retry_sleep(retry: int) -> None: + # 指数バックオフ + ジッタ。残りretry回数から試行回数を求めて待機時間を増やし、 + # ジッタで再試行タイミングを分散させる (レート制限中のサーバへの集中を避ける)。 + attempt = MAX_OUTER_LINK_RETRY - retry + 1 # 1, 2, 3, ... + backoff = min(15 * (2 ** (attempt - 1)), 60) # 15, 30, 60 秒 (上限60秒) + time.sleep(backoff + random.uniform(0, 15)) # 0〜15秒のジッタ + +def check_url(url: str, retry: int = MAX_OUTER_LINK_RETRY) -> tuple[bool, str]: try: headers = {'User-agent': 'Mozilla/5.0'} # パフォーマンスのため本文は取得せずHEADで確認する。 @@ -28,12 +33,12 @@ def check_url(url: str, retry: int = 5) -> tuple[bool, str]: except requests.exceptions.ConnectionError as e: if retry <= 0: return False, "requests.exceptions.ConnectionError : {} ".format(e) - retry_sleep() + retry_sleep(retry) return check_url(url, retry - 1) except requests.exceptions.RequestException as e: if retry <= 0: return False, "requests.exceptions.RequestException : {}".format(e) - retry_sleep() + retry_sleep(retry) return check_url(url, retry - 1) except Exception as e: return False, "unknown exception : {}".format(e) From 594ebb3d87d727e71200d1ea0839110ac2dfd76c Mon Sep 17 00:00:00 2001 From: Akira Takahashi Date: Mon, 6 Jul 2026 14:24:12 +0900 Subject: [PATCH 4/6] =?UTF-8?q?=E3=83=AA=E3=83=B3=E3=82=AF=E5=88=87?= =?UTF-8?q?=E3=82=8C=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lang/cpp17/extending_static_assert.md | 2 +- reference/random/bernoulli_distribution.md | 2 +- reference/random/cauchy_distribution.md | 2 +- reference/random/exponential_distribution.md | 2 +- reference/random/extreme_value_distribution.md | 1 - reference/random/lognormal_distribution.md | 2 +- reference/random/poisson_distribution.md | 2 +- reference/random/weibull_distribution.md | 2 +- 8 files changed, 7 insertions(+), 8 deletions(-) diff --git a/lang/cpp17/extending_static_assert.md b/lang/cpp17/extending_static_assert.md index b0d6ac3386..f1269930eb 100644 --- a/lang/cpp17/extending_static_assert.md +++ b/lang/cpp17/extending_static_assert.md @@ -52,7 +52,7 @@ example_static_assert.cpp:5:3: error: static_assert failed ## この機能が必要になった背景・経緯 `assert` は条件式のみを引数に取るのに対し、`static_assert` には診断メッセージを提供しなければならなかった。 -[Boost.StaticAssert](http://www.boost.org/doc/libs/release/doc/html/boost_staticassert.html) は以下のような `BOOST_STATIC_ASSERT` マクロを提供しており、 +[Boost.StaticAssert](https://www.boost.org/doc/libs/latest/libs/config/doc/html/index.html) は以下のような `BOOST_STATIC_ASSERT` マクロを提供しており、 `static_assert` の診断メッセージを省略できた: ```cpp #define BOOST_STATIC_ASSERT(B) static_assert(B, #B) diff --git a/reference/random/bernoulli_distribution.md b/reference/random/bernoulli_distribution.md index 0571fa9f52..d84492a77c 100644 --- a/reference/random/bernoulli_distribution.md +++ b/reference/random/bernoulli_distribution.md @@ -118,4 +118,4 @@ int main() ## 参照 - [ベルヌーイ分布 - Wikipedia](https://ja.wikipedia.org/wiki/ベルヌーイ分布) -- [ベルヌーイ分布(Bernoulli distribution) - NtRand](http://www.ntrand.com/jp/bernoulli-distribution/) +- [ベルヌーイ分布(Bernoulli distribution) - NtRand](https://www.ntrand.com/ja/docs/gallery-of-distributions/bernoulli-distribution) diff --git a/reference/random/cauchy_distribution.md b/reference/random/cauchy_distribution.md index 094c211178..afc7a2d058 100644 --- a/reference/random/cauchy_distribution.md +++ b/reference/random/cauchy_distribution.md @@ -125,4 +125,4 @@ int main() ### 参考 - [コーシー分布 - Wikipedia](https://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%BC%E3%82%B7%E3%83%BC%E5%88%86%E5%B8%83) -- [コーシー分布 - NtRand](http://www.ntrand.com/jp/cauchy-distribution/) +- [コーシー分布 - NtRand](https://www.ntrand.com/ja/docs/gallery-of-distributions/cauchy-distribution) diff --git a/reference/random/exponential_distribution.md b/reference/random/exponential_distribution.md index b8f051c5c3..74238337ea 100644 --- a/reference/random/exponential_distribution.md +++ b/reference/random/exponential_distribution.md @@ -188,6 +188,6 @@ Phone call after 2.60918 minute wait ### 参考 - [指数分布 - Wikipedia](https://ja.wikipedia.org/wiki/指数分布) -- [指数分布 - NtRand](http://www.ntrand.com/jp/exponential-distribution/) +- [指数分布 - NtRand](https://www.ntrand.com/ja/docs/gallery-of-distributions/exponential-distribution) - [指数分布 - 統計学自習ノート](https://web.archive.org/web/20241204102039/http://aoki2.si.gunma-u.ac.jp/lecture/Bunpu/exponential.html) - [指数分布とポアソン分布のいけない関係](http://www.slideshare.net/teramonagi/ss-11296227) diff --git a/reference/random/extreme_value_distribution.md b/reference/random/extreme_value_distribution.md index d2a843602b..023d00d241 100644 --- a/reference/random/extreme_value_distribution.md +++ b/reference/random/extreme_value_distribution.md @@ -128,4 +128,3 @@ int main() ### 参考 - [極値分布](https://ja.wikipedia.org/wiki/極値分布) - [一般化極値分布 - MATLAB & Simulink - MathWorks 日本](https://jp.mathworks.com/help/stats/generalized-extreme-value-distribution.html) -- [極値分布とその応用に関する研究](http://www.seto.nanzan-u.ac.jp/msie/gr-thesis/ms/2005/osaki/02mm042.pdf) diff --git a/reference/random/lognormal_distribution.md b/reference/random/lognormal_distribution.md index c114f3cf57..7559dcd886 100644 --- a/reference/random/lognormal_distribution.md +++ b/reference/random/lognormal_distribution.md @@ -125,5 +125,5 @@ int main() ### 参考 - [対数正規分布 - Wikipedia](https://ja.wikipedia.org/wiki/%E5%AF%BE%E6%95%B0%E6%AD%A3%E8%A6%8F%E5%88%86%E5%B8%83) -- [対数正規分布 - NtRand](http://www.ntrand.com/jp/log-normal-distribution/) +- [対数正規分布 - NtRand](https://www.ntrand.com/ja/docs/gallery-of-distributions/log-normal-distribution) - [対数正規分布の仕組み - 小人さんの妄想](http://d.hatena.ne.jp/rikunora/20100418/p1) diff --git a/reference/random/poisson_distribution.md b/reference/random/poisson_distribution.md index 3456778584..f462e43318 100644 --- a/reference/random/poisson_distribution.md +++ b/reference/random/poisson_distribution.md @@ -167,7 +167,7 @@ Month 12: 1 earthquake(s) ### 参考 - [ポワソン分布 - Wikipedia](https://ja.wikipedia.org/wiki/ポアソン分布) - [ポアソン分布 - 統計・データ解析](https://okumuralab.org/~okumura/stat/poisson.html) -- [ポアソン分布 - NtRand](http://www.ntrand.com/jp/poisson-distribution/) +- [ポアソン分布 - NtRand](https://www.ntrand.com/ja/docs/gallery-of-distributions/poisson-distribution) - [[データ分析]ポアソン分布 ~ 100年に1人の天才は何人現れる?:やさしい確率分布 - @IT](https://atmarkit.itmedia.co.jp/ait/articles/2407/11/news002.html) diff --git a/reference/random/weibull_distribution.md b/reference/random/weibull_distribution.md index b47b886653..9c83b3bfa4 100644 --- a/reference/random/weibull_distribution.md +++ b/reference/random/weibull_distribution.md @@ -129,5 +129,5 @@ int main() ### 参考 - [ワイブル分布 - Wikipedia](https://ja.wikipedia.org/wiki/ワイブル分布) -- [ワイブル分布 = NtRand](http://www.ntrand.com/jp/weibull-distribution/) +- [ワイブル分布 = NtRand](https://www.ntrand.com/ja/docs/gallery-of-distributions/weibull-distribution) - [疲労や破壊現象とワイブル分布](http://web.archive.org/web/20220706102605/http://www.mogami.com/notes/weibull.html) From b3502ee83ff98c7e72fa650137400cb5dfcb9987 Mon Sep 17 00:00:00 2001 From: Akira Takahashi Date: Mon, 6 Jul 2026 14:25:59 +0900 Subject: [PATCH 5/6] =?UTF-8?q?=E5=A4=96=E9=83=A8=E3=83=AA=E3=83=B3?= =?UTF-8?q?=E3=82=AF=E3=83=81=E3=82=A7=E3=83=83=E3=82=AF=20:=20=E4=B8=A6?= =?UTF-8?q?=E5=88=97=E5=8C=96=E3=81=97=E3=81=A615=E5=80=8D=E3=81=BB?= =?UTF-8?q?=E3=81=A9=E9=AB=98=E9=80=9F=E5=8C=96=E3=80=823=E5=88=86?= =?UTF-8?q?=E7=A8=8B=E5=BA=A6=E3=81=A7=E3=81=8A=E3=82=8F=E3=82=8B=E3=81=AF?= =?UTF-8?q?=E3=81=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/script/link_check.py | 37 +++++++++++++++++++------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/.github/workflows/script/link_check.py b/.github/workflows/script/link_check.py index 26d1d59804..8e5d71cdb0 100644 --- a/.github/workflows/script/link_check.py +++ b/.github/workflows/script/link_check.py @@ -8,9 +8,14 @@ import sys import time import random +from concurrent.futures import ThreadPoolExecutor urllib3.disable_warnings() +# 外部リンクチェックの並列数。ネットワークI/OバウンドなのでスレッドでOK。 +# open-std.org (全URLの過半) が同時16接続を問題なく捌けることを実測して決定。 +OUTER_LINK_WORKERS = 16 + MAX_OUTER_LINK_RETRY = 3 def retry_sleep(retry: int) -> None: @@ -30,6 +35,13 @@ def check_url(url: str, retry: int = MAX_OUTER_LINK_RETRY) -> tuple[bool, str]: res = requests.head(url, headers=headers, verify=False, timeout=60.0, allow_redirects=True) return res.status_code != 404, str(res.status_code) + except requests.exceptions.TooManyRedirects: + # リダイレクトループ (http↔httpsを行き来する等)。ブラウザではHSTS等で + # 到達できることが多く、リトライしても解消しないため、存在するものとして + # 扱う (ループ誤検出とムダな再試行を避ける)。 + # ※ TooManyRedirectsはRequestExceptionのサブクラスなので、下の汎用ハンドラ + # より前で捕捉する必要がある。 + return True, "redirect loop" except requests.exceptions.ConnectionError as e: if retry <= 0: return False, "requests.exceptions.ConnectionError : {} ".format(e) @@ -174,23 +186,28 @@ def check(check_inner_link: bool, check_outer_link: bool, url: str) -> bool: if len(url) > 0: outer_link_dict[url] = "" - # 1パス目: 失敗したURLだけ集める - failed = [] - for link, from_list in outer_link_dict.items(): + def check_one(item): + link, from_list = item exists, reason = check_url(link) - if not exists: - failed.append((link, from_list)) + return link, from_list, exists, reason + + # 1パス目: 失敗したURLだけ集める (ネットワークI/Oバウンドなのでスレッドで並列化) + failed = [] + with ThreadPoolExecutor(max_workers=OUTER_LINK_WORKERS) as executor: + for link, from_list, exists, reason in executor.map(check_one, outer_link_dict.items()): + if not exists: + failed.append((link, from_list)) # 2パス目: 一時的な障害 (ランナーのネットワーク輻輳やサイトの瞬断) を # 誤検出しないよう、間をあけて失敗したURLのみ再チェックし、 # 両方のパスで失敗したものだけを報告する if failed: time.sleep(120) - for link, from_list in failed: - exists, reason = check_url(link) - if not exists: - print("URL {} not found. {} from:{}".format(link, reason, from_list), file=sys.stderr) - found_error = True + with ThreadPoolExecutor(max_workers=OUTER_LINK_WORKERS) as executor: + for link, from_list, exists, reason in executor.map(check_one, failed): + if not exists: + print("URL {} not found. {} from:{}".format(link, reason, from_list), file=sys.stderr) + found_error = True return not found_error From 323aaa48092b5cd8e360baba0d1aa5e533810f35 Mon Sep 17 00:00:00 2001 From: Akira Takahashi Date: Mon, 6 Jul 2026 14:59:54 +0900 Subject: [PATCH 6/6] =?UTF-8?q?=E5=A4=96=E9=83=A8=E3=83=AA=E3=83=B3?= =?UTF-8?q?=E3=82=AF=E3=83=81=E3=82=A7=E3=83=83=E3=82=AFCI=20:=20=E5=AE=9F?= =?UTF-8?q?=E8=A1=8C=E6=99=82=E9=96=93=E3=81=8C=E7=9F=AD=E3=81=8F=E3=81=AA?= =?UTF-8?q?=E3=81=A3=E3=81=9F=E3=81=AE=E3=81=A7=E3=80=812=E5=9B=9E?= =?UTF-8?q?=E3=83=81=E3=82=A7=E3=83=83=E3=82=AF=E3=82=92=E5=AE=9F=E8=A1=8C?= =?UTF-8?q?=E3=81=97=E3=81=A6=E5=A4=B1=E6=95=97=E3=81=97=E3=81=9F=E3=82=82?= =?UTF-8?q?=E3=81=AE=E3=82=92=E5=A0=B1=E5=91=8A=E3=81=99=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/outer_link_check.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/outer_link_check.yml b/.github/workflows/outer_link_check.yml index 2ae2262183..7f541c197e 100644 --- a/.github/workflows/outer_link_check.yml +++ b/.github/workflows/outer_link_check.yml @@ -26,6 +26,17 @@ jobs: - name: check run: | { python3 .github/workflows/script/link_check.py --check-outer-link 3>&2 2>&1 1>&3 | tee check_result.txt; } 3>&2 2>&1 1>&3 + - name: recheck if the first check reported failures + # 1回目が失敗を報告した場合のみ、時間をあけてもう一度チェックし直す。 + # ランナー側のネットワーク不調など、1回の実行がまるごと一時障害に当たった + # ケースを誤検出しないためのワークフローレベルの再チェック。 + # 2回目の結果でcheck_result.txtを上書きし、両方で失敗したものだけを通知する。 + run: | + if [ -s check_result.txt ]; then + echo "First check reported failures. Waiting 5 minutes and rechecking to rule out transient/runner-side issues." + sleep 300 + { python3 .github/workflows/script/link_check.py --check-outer-link 3>&2 2>&1 1>&3 | tee check_result.txt; } 3>&2 2>&1 1>&3 + fi - name: create github issue when needed run: python3 .github/workflows/script/create_github_issue_when_not_empty.py check_result.txt ${{ secrets.GITHUB_TOKEN }} ${{ github.repository }} ${{ github.sha }} - name: detect link check check_result