perf(types): идемпотентная регистрация member-source в MetadataCollectionSpecializer#4189
Conversation
…tionSpecializer Обход дерева метаданных приходит к одному и тому же per-owner synthetic-типу многократно (общие имена табличных частей, общий element-type), а TypeRegistry.registerMemberSource добавляет источник без дедупликации (computeIfAbsent(...).add(...)). В итоге ~429K списков member-source держали ~6.7M лямбд (~14 дублей на тип), а getMembers материализовал ~14-кратно избыточный граф членов, удерживаемый в membersCache. registerPerOwner теперь помечает уже обработанные ref в рамках одного specialize() и не регистрирует источники повторно. Регистрация детерминирована по path-кодированному имени ref, поэтому повторная обработка была чисто избыточной — выход getMembers и так дедуплицируется putIfAbsent по имени. Замер на nixel2007/cpm (analyze): MemberDescriptor 6.01M → 442K, TypeSet 6.04M → 475K, BilingualString 6.19M → 617K (ровно ~13.6×, совпало с оценкой дублей); live heap 5.04 ГБ → 2.95 ГБ (-41.5%); CPU на дублирующих member-source лямбдах (LambdaForm.invokeExact 7.6%) исчезает; Object[] allocation pressure 74% → 49%. JSON-отчёт побайтно идентичен baseline. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01EWA4hy5YvUBU1CXkdr4npc
📝 WalkthroughWalkthrough
ChangesPer-owner registration deduplication
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~3 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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.
Inline comments:
In
`@src/main/java/com/github/_1c_syntax/bsl/languageserver/types/registry/MetadataCollectionSpecializer.java`:
- Around line 316-324: The `registeredOwners` field is mutable shared state in a
WorkspaceScope component that lacks thread-safety guarantees. Since
WorkspaceScope does not serialize method invocations, the `specialize()` method
can be called concurrently by multiple threads, causing race conditions when
`clear()` and `add()` operations execute on this HashSet. Either add
synchronization and document that `specialize()` must be invoked serially within
a workspace, or refactor `registeredOwners` from a class-level field to a local
variable declared within the `specialize()` method scope so each invocation has
its own dedup state.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 5264b748-5668-4f78-8f56-7fa82eb72bf8
📒 Files selected for processing (1)
src/main/java/com/github/_1c_syntax/bsl/languageserver/types/registry/MetadataCollectionSpecializer.java
|
Прогнал |
Что и зачем
Обход дерева метаданных в
MetadataCollectionSpecializerприходит к одному и тому же per-owner synthetic-типу многократно (общие имена табличных частей, общий element-type), аTypeRegistry.registerMemberSourceдобавляет источник без дедупликации (computeIfAbsent(...).add(...)). В итоге ~429K списков member-source держат ~6.7M лямбд (~14 дублей на тип), аgetMembersматериализует ~14-кратно избыточный граф членов, удерживаемый вmembersCache.Профилирование (JFR + heap dump + Eclipse MAT) показало, что
TypeRegistryудерживает ~52% живого хипа, а весь 6-млн кластерMemberDescriptor/TypeSet/BilingualString/лямбд — это и есть эта дублирующая материализация.Изменение
registerPerOwnerпомечает уже обработанныеrefв рамках одногоspecialize()и не регистрирует источники повторно. Регистрация детерминирована по path-кодированному имениref, поэтому повторная обработка была чисто избыточной — выходgetMembersи так дедуплицируетсяputIfAbsentпо имени.Замер (analyze на
nixel2007/cpm)LambdaForm.invokeExact(CPU на дублях)Object[]allocation pressureСоотношение 6.01M/442K ≈ 13.6× — ровно фактор дублирования. JSON-отчёт
analyzeпобайтно идентичен baseline.✅ Валидация
MetadataCollectionSpecializerTest(12 тестов, HBK-gated черезBSL_LANGUAGE_SERVER_RUN_HBK_TESTS=true) прогнан локально на реальном HBK — 12/12 PASS на этой ветке и в комбинации A+B+C. Тесты покрывают per-MDO/per-collection специализацию, табличные части и рекурсию по под-владельцам (tabularSectionGetsRecursivePerSectionType) — ровно там, где работает гард дедупа. Плюс JSON-отчётanalyzeпобайтно идентичен baseline.🤖 Generated with Claude Code