Skip to content

fix: OAI-PMH の scroll keep-alive 既定値を 24 時間から 1 時間に短縮 - #1943

Open
mhaya wants to merge 1 commit into
develop_v2.1.0from
fix/oaiserver-scroll-keepalive
Open

mhaya wants to merge 1 commit into
develop_v2.1.0from
fix/oaiserver-scroll-keepalive

Conversation

@mhaya

@mhaya mhaya commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

変更内容

  • OAISERVER_RESUMPTION_TOKEN_EXPIRE_TIME の既定値を 24 * 60 * 60(86400 秒)から 60 * 60(3600 秒)に変更
  • 実際の値と食い違っていた docstring(Default: 60 seconds)を直し、この値が scroll の keep-alive にも使われることと、ES の search.max_keep_alive との関係を追記
  • テストを 3 件追加
    • test_app.py: 既定値が 3600 であること、環境側の設定で上書きできること
    • test_query.py: 1 ページ目と 2 ページ目以降の両方で、設定値が ES に scroll の keep-alive として渡ること

目的

ES データノードのヒープが数日かけて増え続ける問題への対応です。

この設定値は resumptionToken の有効期限であると同時に、ListRecords / ListIdentifiers が開く ES scroll の keep-alive にも使われます(query.py:159,168)。scroll は最終ページを返したときにしか clear_scroll されません(query.py:265-270)。そのため、1 ページ目で止まるクローラーや、途中で中断したハーベストの scroll コンテキストは、24 時間ヒープに残っていました。

検証結果(ES 6.8.23)

  • WEKO の get_records() で、1 ページ目で放棄した scroll が残ること、最後まで取り切れば閉じることを再現した
  • keep-alive が切れると、clear_scroll しなくても scroll が使っていたヒープは解放される。1 本あたり約 78 KB(1 シャード、100 件、ソートあり)で、繰り返しても溜まらない
  • keep-alive は「次のページを取りに来るまでの猶予」で、ページを取るたびに延長される。keep-alive 5 秒・ページ間 3 秒の条件で 25,000 件をすべて取得できた(所要 16 秒)。短くしても、1 万件を超える結果を全件取得できる

影響

  • ハーベスタのページ間隔が 1 時間を超えると、badResumptionToken になります。24 時間のときより厳しくなりますが、1 ページに 1 時間以上かける実運用のハーベスタは想定しにくいです
  • 環境側(instance.cfg など)で値を設定している場合は、その値が優先されます(テストで確認済み)
  • ES 側の設定変更は不要です。search.max_keep_alive は既定の 24h のままで 3600 秒の要求を受け付け、期限切れの scroll は ES が自動で回収します
  • デプロイ前に 24 時間の keep-alive で開かれた scroll は、期限切れまで(最大 24 時間)残ります。すぐに解放したい場合は、ハーベスタが走っていない時間帯に DELETE /_search/scroll/_all を 1 回実行してください

範囲外

同じ resumptionToken で再送すると、ES の scroll が進んでしまい 1 ページ分を取りこぼす問題も見つかっています。挙動が変わる修正になるため、別の PR で扱います。

動作確認

  • scripts/ci/run-local.sh invenio-oaiserver: 104 passed, 3 skipped, 1 xfailed
  • flake8: 変更行への指摘なし。Black / isort は、ファイル全体が未適用で無関係な差分が大量に出るため、適用していません

🤖 Generated with Claude Code

Summary by Sourcery

Shorten the default OAI-PMH scroll keep-alive to reduce Elasticsearch heap usage while preserving configurable harvest behavior.

Bug Fixes:

  • Reduce the default OAI-PMH resumption-token and Elasticsearch scroll keep-alive from 24 hours to 1 hour to limit heap retained by abandoned harvests.

Enhancements:

  • Document the scroll keep-alive behavior, configuration override, and Elasticsearch max-keep-alive requirement.

Tests:

  • Add coverage for the new default, instance-level overrides, and keep-alive propagation across initial and subsequent result pages.

@sourcery-ai

sourcery-ai Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Reviewer's Guide

OAI-PMHのresumption tokenおよびElasticsearch scrollのkeep-alive既定値を24時間から1時間へ短縮し、設定の上書きと初回・継続ページでのscroll伝播をテストで担保する変更です。

Sequence diagram for OAI-PMH scroll keep-alive across pages

sequenceDiagram
    participant Harvester
    participant OAI_Server
    participant Elasticsearch

    Harvester->>OAI_Server: ListRecords / ListIdentifiers
    OAI_Server->>Elasticsearch: search(scroll=OAISERVER_RESUMPTION_TOKEN_EXPIRE_TIME)
    Elasticsearch-->>OAI_Server: First page and resumptionToken
    OAI_Server-->>Harvester: First page and resumptionToken
    Harvester->>OAI_Server: ListRecords / ListIdentifiers with resumptionToken
    OAI_Server->>Elasticsearch: scroll(keep_alive=OAISERVER_RESUMPTION_TOKEN_EXPIRE_TIME)
    Elasticsearch-->>OAI_Server: Next page and resumptionToken
    OAI_Server-->>Harvester: Next page and resumptionToken
Loading

File-Level Changes

Change Details Files
短縮した既定値と、resumption token・Elasticsearch scroll keep-aliveの関係を設定ドキュメントに反映
  • 既定値を86400秒から3600秒へ変更
  • ListRecords/ListIdentifiersのscrollで同じ値がページ間隔として更新されることを説明
  • 途中放棄時のscrollコンテキスト保持と、ESのsearch.max_keep_alive以上に設定すべき制約を追記
modules/invenio-oaiserver/invenio_oaiserver/config.py
既定値と環境設定による上書き動作を検証
  • 既定値が3600秒であることを追加検証
  • アプリケーション側で指定した600秒が維持されることを検証
modules/invenio-oaiserver/tests/test_app.py
初回検索および継続ページ取得に設定値がscroll keep-aliveとして渡ることを検証
  • 初回searchに3600sが渡ることを検証
  • 次ページのscroll更新にも3600sが渡ることを検証
  • 設定変更後の初回searchに600sが渡ることを検証
modules/invenio-oaiserver/tests/test_query.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Oct 20, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 387f378e-7c7e-4a6f-9dd9-6373a20e3995

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

🔍 Claude レビュー統合

指摘はありません。


次にすること: 裏を取れた問題はなく、このまま進めて問題ありません。外部データは要約と「レビュー省略」の通知だけで、裁定すべき指摘スレッドはありませんでした。設定値は query.py:159 の1か所から読まれ、初回の search(168行目)と継続ページの scroll(250行目)の両方で '{n}s' として使われます。追加テストはこの経路と整合しています。また es_app は関数スコープなので、設定の上書きが他のテストへ漏れることもありません。


モデル sonnet / 2 回実行して和集合 / コスト $0.2175。同じ入力でも結果が揺れるため複数回まわし、一部のパスでしか挙がらなかったものには回数を添えています

他レビューを踏まえた自動レビューです。誤りが含まれることがあります。

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="modules/invenio-oaiserver/invenio_oaiserver/config.py" line_range="38" />
<code_context>
 """

-OAISERVER_RESUMPTION_TOKEN_EXPIRE_TIME = 24 * 60 * 60
+OAISERVER_RESUMPTION_TOKEN_EXPIRE_TIME = 60 * 60
 """The expiration time of a resumption token in seconds.

</code_context>
<issue_to_address>
**Scrolls exceed cluster limit**

When an Elasticsearch deployment has `search.max_keep_alive` configured below 3600 seconds, `get_records` passes the configured value as the Elasticsearch scroll keep-alive on both the initial search and later scroll requests, with no cap or error handling. Elasticsearch rejects those requests, so OAI-PMH record and identifier harvesting fails.

Validate the effective keep-alive against the Elasticsearch limit at startup, or ensure the setting is kept within that limit before enabling this default.
</issue_to_address>

Sourcery assessment

Approval pending. 1 finding to address first.

Blocking findings: modules/invenio-oaiserver/invenio_oaiserver/config.py:38


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

"""

OAISERVER_RESUMPTION_TOKEN_EXPIRE_TIME = 24 * 60 * 60
OAISERVER_RESUMPTION_TOKEN_EXPIRE_TIME = 60 * 60

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High · Scrolls exceed cluster limit

When an Elasticsearch deployment has search.max_keep_alive configured below 3600 seconds, get_records passes the configured value as the Elasticsearch scroll keep-alive on both the initial search and later scroll requests, with no cap or error handling. Elasticsearch rejects those requests, so OAI-PMH record and identifier harvesting fails.

Validate the effective keep-alive against the Elasticsearch limit at startup, or ensure the setting is kept within that limit before enabling this default.

Prompt for AI agents
In `modules/invenio-oaiserver/invenio_oaiserver/config.py` at line 38:

**Scrolls exceed cluster limit**

When an Elasticsearch deployment has `search.max_keep_alive` configured below 3600 seconds, `get_records` passes the configured value as the Elasticsearch scroll keep-alive on both the initial search and later scroll requests, with no cap or error handling. Elasticsearch rejects those requests, so OAI-PMH record and identifier harvesting fails.

Validate the effective keep-alive against the Elasticsearch limit at startup, or ensure the setting is kept within that limit before enabling this default.

OAISERVER_RESUMPTION_TOKEN_EXPIRE_TIME は resumptionToken の有効期限であると同時に、
ListRecords / ListIdentifiers が開く Elasticsearch scroll の keep-alive にも使われる。
scroll は最終ページを返したときにしか clear_scroll されないため、途中でやめた
ハーベストの scroll コンテキストは keep-alive の間ヒープに残り続ける。既定値が
24 時間だったため、ES データノードのヒープが数日かけて増え続けていた。

keep-alive はページを取るたびに延長されるので、必要なのは「次のページを取りに
来るまでの間隔」をまかなう長さだけで、ハーベスト全体の所要時間ではない。
既定値を 3600 秒にし、実際の値と食い違っていた docstring(Default: 60 seconds)を直す。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

API インベントリ差分(件数のみ)

台帳ブランチ: develop_v2.1.0

明細は公開できないため件数のみ表示しています。該当箇所はプライベートリポジトリ側の台帳・レポートで確認してください。

ベースラインとの差分

API インベントリ差分レポート

  • 旧: b572c442d v2.0.4-577-gb572c442d (profile=default) endpoints=933 (外部ライブラリ由来 359)
  • 新: a407084f6 v2.0.4-675-ga407084f6 (profile=default) endpoints=933 (外部ライブラリ由来 359)

判定: ❌ FAIL (FAIL 1 / WARN 3)

サマリ

分類 件数
ADDED 0
REMOVED 0
RULE_CHANGED 0
METHODS_CHANGED 0
AUTH_CHANGED 35
IMPL_CHANGED 9
ATTRS_UNKNOWN_NEW 0
ModelView 追加 0
ModelView 削除 0
ModelView フラグ変化 0
config 変化 2
コメントアウト認証の増加 0
依存パッケージの版変化 2

[FAIL] G2 認証系デコレータが削除された — 3件

件数のみ。該当の経路名はプライベートリポジトリ側の完全版レポートを参照。

[WARN] W2 実装本体が変化(data_op / 情報露出を再確認) — 9件

件数のみ。該当の経路名はプライベートリポジトリ側の完全版レポートを参照。

[WARN] W6 依存パッケージの版が変化した — 2件

  • distlib — 0.4.3 -> 0.4.4
  • pbr — 7.0.3 -> 7.1.3

[WARN] W5 監視対象 config が変化した — 2件

件数のみ。該当の経路名はプライベートリポジトリ側の完全版レポートを参照。


台帳との突き合わせ

(生成されませんでした)

ソース由来の経路検知

(生成されませんでした)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant