Skip to content

perf(diagnostics): ограничение пула JLanguageTool#4260

Merged
nixel2007 merged 4 commits into
developfrom
perf/jlanguagetool-pool-cap
Jul 11, 2026
Merged

perf(diagnostics): ограничение пула JLanguageTool#4260
nixel2007 merged 4 commits into
developfrom
perf/jlanguagetool-pool-cap

Conversation

@sfaqer

@sfaqer sfaqer commented Jul 11, 2026

Copy link
Copy Markdown
Member

Суть

JLanguageToolPool рос неограниченно — до числа потоков анализа: при parallelStream на 24-ядерной машине создавалось ~20 инстансов JLanguageTool, каждый при создании загружает собственную копию полного набора правил языка (~35 МБ на инстанс; последующий disableRule память не освобождает). На analyze УПП это давало ~680 МБ live heap только под инфраструктуру диагностики Typo.

AbstractObjectPool получил опциональный maxSize: при исчерпании лимита checkOut() блокируется до возврата объекта через checkIn() (wait/notify; прерывание потока не срывает выдачу — флаг прерывания восстанавливается перед возвратом). JLanguageToolPool ограничен min(4, доступных ядер). Конкуренция за пул низкая: статусы слов кэшируются в CheckedWordsHolder (персистентный EhCache), к LT уходят только ещё не проверенные слова.

Замер

analyze УПП (~9 500 модулей), холодный кэш слов, jcmd GC.class_histogram (live-объекты):

Метрика До После
Инстансов JLanguageTool 20 4
Прямые объекты LanguageTool 359 МБ 80 МБ
DisambiguationPatternRule 974 тыс. 217 тыс.

С учётом шаренных структур (CHM/массивы/строки словарей) экономия — примерно −400 МБ на 24-ядерной машине (на машинах с меньшим числом ядер эффект меньше — пул и раньше не вырастал большим). Диагностики эквивалентны (SARIF с идентичными счётчиками правил), время анализа не выросло (2:34 → 2:21).

Проверки

  • полный ./gradlew test: 3 582 теста, 0 упавших;
  • функциональная сверка SARIF на УПП (см. выше).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Performance

    • Introduced bounded pooling for language-analysis resources with an explicit maximum parallelism limit to control concurrency.
    • Pooling now reuses returned instances and scales up to the configured cap based on available CPU resources.
  • Reliability

    • When the pool is exhausted, checkout requests now wait for an available instance instead of growing unbounded.
    • Interrupts are handled correctly while waiting, and returned instances reliably wake waiting checkouts.
  • Tests

    • Added JUnit coverage for reuse, bounded vs. unbounded behavior, interruption handling, invalid configuration, and pool state reporting.

JLanguageToolPool рос неограниченно — до числа потоков анализа: при
parallelStream на 24-ядерной машине создавалось ~20 инстансов JLanguageTool,
каждый при создании загружает собственную копию полного набора правил языка
(~35 МБ на инстанс; последующий disableRule память не освобождает). На analyze
УПП это давало ~680 МБ live heap только под инфраструктуру диагностики Typo.

AbstractObjectPool получил опциональный maxSize: при исчерпании лимита
checkOut() блокируется до возврата объекта через checkIn() (wait/notify;
прерывание потока не срывает выдачу — флаг прерывания восстанавливается).
JLanguageToolPool ограничен min(4, доступных ядер). Конкуренция за пул низкая:
статусы слов кэшируются в CheckedWordsHolder, к LT уходят только ещё не
проверенные слова.

Замер (analyze УПП, холодный кэш слов, jcmd GC.class_histogram, live):
JLanguageTool 20 → 4 инстансов, прямые объекты LanguageTool 359 → 80 МБ
(~-400 МБ с учётом шаренных структур); диагностики эквивалентны (SARIF с
идентичными счётчиками правил), время анализа не выросло (2:34 → 2:21).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 52566231-dc89-49f6-a188-7a2fb6b6840d

📥 Commits

Reviewing files that changed from the base of the PR and between bb25dbd and b13b486.

📒 Files selected for processing (2)
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/AbstractObjectPool.java
  • src/test/java/com/github/_1c_syntax/bsl/languageserver/utils/AbstractObjectPoolTest.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/AbstractObjectPool.java
  • src/test/java/com/github/_1c_syntax/bsl/languageserver/utils/AbstractObjectPoolTest.java

📝 Walkthrough

Walkthrough

AbstractObjectPool now supports bounded, blocking checkout with interruption handling and wake-up on check-in. JLanguageToolPool explicitly configures a processor-limited capacity, and tests cover reuse, concurrency, interruption, and state reporting.

Changes

Bounded object pooling

Layer / File(s) Summary
Bounded checkout and return behavior
src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/AbstractObjectPool.java, src/test/java/com/github/_1c_syntax/bsl/languageserver/utils/AbstractObjectPoolTest.java
AbstractObjectPool accepts a maximum size, waits when capacity is reached, preserves interruption status, notifies waiting threads on check-in, and is validated across reuse, concurrency, interruption, construction, and state-reporting scenarios.
Language-tool pool configuration
src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/typo/JLanguageToolPool.java
JLanguageToolPool replaces Lombok construction with an explicit constructor and sets capacity to the lower of four and the available processor count.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CheckoutThread
  participant AbstractObjectPool
  participant ReturningThread
  CheckoutThread->>AbstractObjectPool: checkOut()
  AbstractObjectPool-->>CheckoutThread: wait at maxSize
  ReturningThread->>AbstractObjectPool: checkIn(instance)
  AbstractObjectPool-->>CheckoutThread: notifyAll()
  AbstractObjectPool-->>CheckoutThread: return available or newly created object
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Title is concise and accurately describes the main change: limiting the JLanguageTool pool size for diagnostics performance.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch perf/jlanguagetool-pool-cap

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/AbstractObjectPool.java (1)

74-76: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Guard against non-positive maxSize in the bounded constructor.

If a subclass passes maxSize <= 0, checkOut() enters the wait loop (available.isEmpty() && inUse.size() >= maxSize is true with an empty pool), create() is never reached, and nothing can ever checkIn() — the caller hangs forever with no diagnostic. The only current caller is safe, but this is a protected base-class API, so a cheap precondition prevents a silent deadlock for future subclasses.

🛡️ Proposed guard
   protected AbstractObjectPool(int maxSize) {
+    if (maxSize < 1) {
+      throw new IllegalArgumentException("maxSize must be >= 1, got: " + maxSize);
+    }
     this.maxSize = maxSize;
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/AbstractObjectPool.java`
around lines 74 - 76, Add a constructor precondition in AbstractObjectPool(int
maxSize) that rejects non-positive values, throwing an appropriate
argument-validation exception with a clear diagnostic before assigning maxSize;
preserve valid positive sizes and leave unbounded construction behavior
unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/AbstractObjectPool.java`:
- Around line 74-76: Add a constructor precondition in AbstractObjectPool(int
maxSize) that rejects non-positive values, throwing an appropriate
argument-validation exception with a clear diagnostic before assigning maxSize;
preserve valid positive sizes and leave unbounded construction behavior
unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a136dc9b-af93-432d-918e-0d0c0c17c08a

📥 Commits

Reviewing files that changed from the base of the PR and between 61640a0 and 6031af2.

📒 Files selected for processing (2)
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/typo/JLanguageToolPool.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/AbstractObjectPool.java

@github-actions

github-actions Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Test Results

 3 618 files  + 6   3 618 suites  +6   1h 37m 20s ⏱️ -3s
 3 588 tests + 6   3 570 ✅ + 6   18 💤 ±0  0 ❌ ±0 
21 528 runs  +36  21 416 ✅ +36  112 💤 ±0  0 ❌ ±0 

Results for commit b13b486. ± Comparison against base commit 61640a0.

♻️ This comment has been updated with latest results.

sfaqer and others added 2 commits July 11, 2026 17:46
java:S2142 — флаг прерывания восстанавливается сразу в catch; прерванный
ожидающий поток выходит из ожидания и получает объект сверх лимита
(временное превышение maxSize на число прерванных ожидающих), вместо
отложенного восстановления флага после цикла ожидания.
java:S2446 — notifyAll() вместо notify().

Юнит-тесты AbstractObjectPool: переиспользование возвращённого экземпляра,
рост неограниченного пула, блокировка на лимите с передачей экземпляра,
поведение при прерывании, toString.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
public synchronized void checkIn(T instance) {
inUse.remove(instance);
available.add(instance);
notifyAll();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Зачем?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

До ограничения пула checkOut() никогда не ждал: при пустом available сразу создавался новый экземпляр. С появлением maxSize поток при исчерпании лимита встаёт в wait() — без notify* в checkIn() он не проснётся никогда.

notifyAll вместо notify — по замечанию Sonar (java:S2446): notify будит произвольный поток. Ожидающие здесь однородны, так что технически хватило бы и notify, но notifyAll — стандартный безопасный паттерн, а лишних пробуждений при размере пула ≤4 единицы.

По замечанию ревью: maxSize < 1 приводил бы к вечному ожиданию в checkOut
без диагностики — теперь конструктор бросает IllegalArgumentException.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@nixel2007 nixel2007 merged commit c0d5747 into develop Jul 11, 2026
37 checks passed
@nixel2007 nixel2007 deleted the perf/jlanguagetool-pool-cap branch July 11, 2026 19:12
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.

2 participants