Cache parsed sources#3407
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds an opt-in ChangesParser Source Caching Feature
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
🚥 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 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 |
|
📚 Docs Preview: https://pr-3407.datamodel-code-generator.pages.dev |
Merging this PR will not alter performance
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/datamodel_code_generator/parser/xmlschema.py (1)
332-335: ⚖️ Poor tradeoffConsider moving freshness check outside the lock to reduce contention.
_xml_schema_cache_entry_is_fresh(entry)performs file I/O (path.is_file()and_digest_path()) for each dependency while holding_xml_schema_data_cache_lock. For schemas with many includes/imports, this could cause lock contention and block concurrent cache access.Consider restructuring to:
- Retrieve entry inside lock
- Release lock
- Check freshness outside lock
- Reacquire lock only if cache update needed
The race condition this introduces (another thread updating cache between steps 2-4) is benign for a cache — worst case is redundant computation.
🤖 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/datamodel_code_generator/parser/xmlschema.py` around lines 332 - 335, The `_xml_schema_cache_entry_is_fresh()` function performs file I/O operations while the `_xml_schema_data_cache_lock` is held, causing unnecessary lock contention. Restructure the code to retrieve the cache entry and release the lock first, then perform the freshness check outside the critical section. Only reacquire the lock if the cache needs to be updated (i.e., if the entry is not fresh or not found). This reduces lock duration and allows concurrent cache access without blocking on expensive file I/O operations.
🤖 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/datamodel_code_generator/parser/base.py`:
- Around line 966-970: The issue is that within the enable_parser_cache branch,
the file is read twice separately: once via path.read_text() for the text field
and once via _load_parser_source_data_from_path() for the raw_data field. A
concurrent file modification between these two reads can cause text and raw_data
to contain inconsistent content from different file versions. To fix this, read
the file once to obtain the raw bytes, then derive both the text field and
raw_data from that single read operation. Refactor the code to call
path.read_bytes() once and then either decode the bytes for text and pass the
bytes to _load_parser_source_data_from_path, or restructure
_load_parser_source_data_from_path to accept pre-read bytes to avoid a second
read operation.
---
Nitpick comments:
In `@src/datamodel_code_generator/parser/xmlschema.py`:
- Around line 332-335: The `_xml_schema_cache_entry_is_fresh()` function
performs file I/O operations while the `_xml_schema_data_cache_lock` is held,
causing unnecessary lock contention. Restructure the code to retrieve the cache
entry and release the lock first, then perform the freshness check outside the
critical section. Only reacquire the lock if the cache needs to be updated
(i.e., if the entry is not fresh or not found). This reduces lock duration and
allows concurrent cache access without blocking on expensive file I/O
operations.
🪄 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: b15b3f6f-6657-4489-8e50-5a1c043ac7a4
📒 Files selected for processing (13)
src/datamodel_code_generator/__init__.pysrc/datamodel_code_generator/_types/generate_config_dict.pysrc/datamodel_code_generator/_types/parser_config_dicts.pysrc/datamodel_code_generator/config.pysrc/datamodel_code_generator/parser/avro.pysrc/datamodel_code_generator/parser/base.pysrc/datamodel_code_generator/parser/jsonschema.pysrc/datamodel_code_generator/parser/openapi.pysrc/datamodel_code_generator/parser/protobuf.pysrc/datamodel_code_generator/parser/xmlschema.pytests/main/jsonschema/test_main_jsonschema.pytests/main/test_public_api_signature_baseline.pytests/main/xmlschema/test_main_xmlschema.py
04be419 to
153e010
Compare
There was a problem hiding this comment.
♻️ Duplicate comments (1)
src/datamodel_code_generator/parser/base.py (1)
966-970:⚠️ Potential issue | 🟠 Major | ⚡ Quick winRead source bytes once when cache + text retention are both enabled.
Line 969 and Line 970 can observe different file revisions because this path performs two independent disk reads. Build both
textandraw_datafrom the same byte buffer to keepSourceinternally consistent.Suggested fix
def from_path( cls, path: Path, base_path: Path, encoding: str, *, enable_parsed_source_cache: bool = False, keep_text: bool = False, ) -> Source: """Create a Source from a file path relative to base_path.""" if enable_parsed_source_cache: + data = path.read_bytes() return cls( path=path.relative_to(base_path), - text=path.read_text(encoding=encoding) if keep_text else "", - raw_data=_load_parser_source_data_from_path(path, encoding), + text=data.decode(encoding) if keep_text else "", + raw_data=_load_parser_source_data_from_bytes(path, data, encoding), ) return cls( path=path.relative_to(base_path), text=path.read_text(encoding=encoding), )🤖 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/datamodel_code_generator/parser/base.py` around lines 966 - 970, When enable_parsed_source_cache is true, the code currently performs two independent disk reads: one via path.read_text() on line 969 and another via _load_parser_source_data_from_path() on line 970, which can observe different file revisions if the file is modified between reads. Refactor this section to read the source bytes once into a single buffer, then derive both the text and raw_data parameters from that same buffer, passing it to _load_parser_source_data_from_path() or decoding it directly for the text field based on the keep_text condition, ensuring the Source instance remains internally consistent.
🤖 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.
Duplicate comments:
In `@src/datamodel_code_generator/parser/base.py`:
- Around line 966-970: When enable_parsed_source_cache is true, the code
currently performs two independent disk reads: one via path.read_text() on line
969 and another via _load_parser_source_data_from_path() on line 970, which can
observe different file revisions if the file is modified between reads. Refactor
this section to read the source bytes once into a single buffer, then derive
both the text and raw_data parameters from that same buffer, passing it to
_load_parser_source_data_from_path() or decoding it directly for the text field
based on the keep_text condition, ensuring the Source instance remains
internally consistent.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: f7ddc8f7-0eeb-4bcf-9239-ece36404c733
📒 Files selected for processing (15)
src/datamodel_code_generator/__init__.pysrc/datamodel_code_generator/_types/generate_config_dict.pysrc/datamodel_code_generator/_types/parser_config_dicts.pysrc/datamodel_code_generator/config.pysrc/datamodel_code_generator/parser/avro.pysrc/datamodel_code_generator/parser/base.pysrc/datamodel_code_generator/parser/jsonschema.pysrc/datamodel_code_generator/parser/openapi.pysrc/datamodel_code_generator/parser/protobuf.pysrc/datamodel_code_generator/parser/xmlschema.pytests/data/expected/main/input_model/config_class.pytests/main/conftest.pytests/main/jsonschema/test_main_jsonschema.pytests/main/test_public_api_signature_baseline.pytests/main/xmlschema/test_main_xmlschema.py
🚧 Files skipped from review as they are similar to previous changes (7)
- src/datamodel_code_generator/_types/parser_config_dicts.py
- src/datamodel_code_generator/parser/protobuf.py
- src/datamodel_code_generator/parser/avro.py
- src/datamodel_code_generator/parser/jsonschema.py
- src/datamodel_code_generator/parser/openapi.py
- src/datamodel_code_generator/init.py
- src/datamodel_code_generator/parser/xmlschema.py
b749b49 to
a9ae3c4
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/datamodel_code_generator/parser/xmlschema.py (1)
347-350: 💤 Low valueFile I/O occurs while holding the cache lock.
_xml_schema_cache_entry_is_fresh(entry)calls_digest_path()for each dependency, performing file reads while_xml_schema_data_cache_lockis held. Under concurrent access this can cause contention.Consider extracting the entry under lock, releasing the lock, then performing the freshness check outside:
♻️ Suggested refactor
with _xml_schema_data_cache_lock: - if (entry := _xml_schema_data_cache.get(cache_key)) is not None and _xml_schema_cache_entry_is_fresh(entry): + entry = _xml_schema_data_cache.get(cache_key) + + if entry is not None and _xml_schema_cache_entry_is_fresh(entry): + with _xml_schema_data_cache_lock: + # Re-check entry is still present before returning + if _xml_schema_data_cache.get(cache_key) is entry: + _xml_schema_data_cache.move_to_end(cache_key) - _xml_schema_data_cache.move_to_end(cache_key) - return _copy_schema(entry.data) + return _copy_schema(entry.data)🤖 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/datamodel_code_generator/parser/xmlschema.py` around lines 347 - 350, The code performs file I/O through _xml_schema_cache_entry_is_fresh() while holding _xml_schema_data_cache_lock, causing potential contention under concurrent access. Extract the cache entry under the lock, then release the lock immediately before checking freshness. Move the _xml_schema_cache_entry_is_fresh(entry) check and the subsequent _xml_schema_data_cache.move_to_end() and _copy_schema() calls outside the lock context to avoid blocking other threads during file I/O operations.
🤖 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/datamodel_code_generator/parser/xmlschema.py`:
- Around line 347-350: The code performs file I/O through
_xml_schema_cache_entry_is_fresh() while holding _xml_schema_data_cache_lock,
causing potential contention under concurrent access. Extract the cache entry
under the lock, then release the lock immediately before checking freshness.
Move the _xml_schema_cache_entry_is_fresh(entry) check and the subsequent
_xml_schema_data_cache.move_to_end() and _copy_schema() calls outside the lock
context to avoid blocking other threads during file I/O operations.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: eabf5675-8c8a-444b-9be4-7c568f27ce38
📒 Files selected for processing (15)
src/datamodel_code_generator/__init__.pysrc/datamodel_code_generator/_types/generate_config_dict.pysrc/datamodel_code_generator/_types/parser_config_dicts.pysrc/datamodel_code_generator/config.pysrc/datamodel_code_generator/parser/avro.pysrc/datamodel_code_generator/parser/base.pysrc/datamodel_code_generator/parser/jsonschema.pysrc/datamodel_code_generator/parser/openapi.pysrc/datamodel_code_generator/parser/protobuf.pysrc/datamodel_code_generator/parser/xmlschema.pytests/data/expected/main/input_model/config_class.pytests/main/conftest.pytests/main/jsonschema/test_main_jsonschema.pytests/main/test_public_api_signature_baseline.pytests/main/xmlschema/test_main_xmlschema.py
🚧 Files skipped from review as they are similar to previous changes (12)
- tests/data/expected/main/input_model/config_class.py
- src/datamodel_code_generator/_types/parser_config_dicts.py
- src/datamodel_code_generator/_types/generate_config_dict.py
- src/datamodel_code_generator/config.py
- src/datamodel_code_generator/parser/avro.py
- tests/main/test_public_api_signature_baseline.py
- src/datamodel_code_generator/parser/openapi.py
- src/datamodel_code_generator/parser/protobuf.py
- tests/main/xmlschema/test_main_xmlschema.py
- src/datamodel_code_generator/parser/jsonschema.py
- src/datamodel_code_generator/parser/base.py
- src/datamodel_code_generator/init.py
a9ae3c4 to
33c0f2e
Compare
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/datamodel_code_generator/parser/jsonschema.py`:
- Around line 5356-5361: The `_load_source_dict` function returns the result of
`load_data(source.text)` without validating its type, while it properly
validates `source.raw_data` to ensure it is a dict. Since callers assume the
return value is a dict and call `.get(...)` on it, non-dict payloads loaded from
text will cause crashes. After calling `load_data(source.text)` when
`source.raw_data is None`, add a type check using `isinstance()` to validate
that the result is a dict. If the result is not a dict, raise a TypeError with a
message indicating the unexpected type (following the same pattern as the
validation for `source.raw_data`), before returning the data.
🪄 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: 3f4ffa1b-0a8d-41d6-a776-e1166a108d2c
📒 Files selected for processing (15)
src/datamodel_code_generator/__init__.pysrc/datamodel_code_generator/_types/generate_config_dict.pysrc/datamodel_code_generator/_types/parser_config_dicts.pysrc/datamodel_code_generator/config.pysrc/datamodel_code_generator/parser/avro.pysrc/datamodel_code_generator/parser/base.pysrc/datamodel_code_generator/parser/jsonschema.pysrc/datamodel_code_generator/parser/openapi.pysrc/datamodel_code_generator/parser/protobuf.pysrc/datamodel_code_generator/parser/xmlschema.pytests/data/expected/main/input_model/config_class.pytests/main/conftest.pytests/main/jsonschema/test_main_jsonschema.pytests/main/test_public_api_signature_baseline.pytests/main/xmlschema/test_main_xmlschema.py
🚧 Files skipped from review as they are similar to previous changes (13)
- src/datamodel_code_generator/_types/generate_config_dict.py
- src/datamodel_code_generator/parser/avro.py
- tests/main/test_public_api_signature_baseline.py
- src/datamodel_code_generator/config.py
- src/datamodel_code_generator/parser/protobuf.py
- src/datamodel_code_generator/parser/openapi.py
- tests/data/expected/main/input_model/config_class.py
- tests/main/jsonschema/test_main_jsonschema.py
- tests/main/conftest.py
- tests/main/xmlschema/test_main_xmlschema.py
- src/datamodel_code_generator/parser/base.py
- src/datamodel_code_generator/init.py
- src/datamodel_code_generator/parser/xmlschema.py
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3407 +/- ##
==========================================
Coverage 100.00% 100.00%
==========================================
Files 138 138
Lines 29734 30097 +363
Branches 3525 3556 +31
==========================================
+ Hits 29734 30097 +363
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This reverts commit a5d85d3.
Breaking Change AnalysisResult: No breaking changes detected Reasoning: PR #3407 "Cache parsed sources" is an internal performance optimization. It adds a new public function This analysis was performed by Claude Code Action |
|
🎉 Released in 0.64.0 This PR is now available in the latest release. See the release notes for details. |
No description provided.