Skip to content

feat(lmdb): align backend with DeePMD-kit#1012

Open
OutisLi wants to merge 3 commits into
deepmodeling:masterfrom
OutisLi:pr/lmdb
Open

feat(lmdb): align backend with DeePMD-kit#1012
OutisLi wants to merge 3 commits into
deepmodeling:masterfrom
OutisLi:pr/lmdb

Conversation

@OutisLi

@OutisLi OutisLi commented Jul 12, 2026

Copy link
Copy Markdown

Summary

  • replace the legacy LMDB backend with the per-frame msgpack schema consumed by DeePMD-kit, including global type maps, mixed compositions, and system partition metadata
  • add schema-driven custom field handling, safe staged publication, automatic LMDB map growth, reader lifecycle guards, and an ordered dump_systems API for prob_sys_size
  • document the format and add protocol-independent regression coverage for interoperability, atomic types, custom data, malformed metadata, and failure recovery

Test plan

  • python -m unittest test_lmdb test_lmdb_custom_dtype test_deepmd_comp test_deepmd_mixed test_multisystems (438 tests)
  • ruff check dpdata/ tests/test_lmdb.py tests/test_lmdb_custom_dtype.py
  • git diff --check
  • validate output with DeePMD-kit's LmdbDataReader
  • read 100-frame samples from MPTrj and OMat24 LMDB datasets

Full discovery ran 2,194 tests. The remaining 46 errors are pre-existing optional-dependency/environment failures (for example, unavailable parmed); no LMDB tests failed.

Summary by CodeRabbit

  • New Features
    • Added DeePMD-kit-compatible LMDB support with improved metadata handling and per-frame storage.
    • Introduced dump_systems to export without formula-based merging.
    • Enhanced loading/writing with mixed-type support, type_map overrides, batching, overwrite control, and max_frames limits.
  • Documentation
    • Added an lmdb systems documentation page and linked it in the systems navigation.
  • Bug Fixes
    • Strengthened validation for fields, element/type handling, and malformed data; improved concurrency safety during atomic database replacement.
  • Tests
    • Expanded LMDB interoperability and robustness coverage, including on-disk format checks and edge cases.
  • Chores
    • Switched dependency from msgpack-numpy to msgpack.

Replace the legacy LMDB schema with a safe, mixed-type format that preserves system partitions and interoperates with DeePMD-kit training datasets.
@dosubot dosubot Bot added size:XXL This PR changes 1000+ lines, ignoring generated files. deepmd DeePMD-kit format enhancement New feature or request labels Jul 12, 2026
@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f1e47c24-5166-456c-bcca-c015c5217612

📥 Commits

Reviewing files that changed from the base of the PR and between 5c34ca8 and 3590e02.

📒 Files selected for processing (3)
  • docs/systems/lmdb.md
  • dpdata/formats/lmdb/format.py
  • tests/test_lmdb.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • docs/systems/lmdb.md
  • tests/test_lmdb.py
  • dpdata/formats/lmdb/format.py

📝 Walkthrough

Walkthrough

The LMDB backend was rewritten around a flat DeePMD-kit-compatible database schema, staged batched writes, atomic publication, validated frame loading, composition grouping, atom-order canonicalization, and custom field protocols. Documentation, exports, dependencies, and extensive interoperability and robustness tests were updated.

Changes

LMDB backend rewrite

Layer / File(s) Summary
Protocol and public API
dpdata/formats/lmdb/format.py, dpdata/format.py, dpdata/formats/lmdb/__init__.py, pyproject.toml
Defines the flat LMDB metadata and frame protocol, portable array encoding, field validation, new dump APIs, exports, type annotations, and the msgpack dependency.
Staged writing and publication
dpdata/formats/lmdb/format.py, tests/test_lmdb.py
Adds batched staged writes, map growth, metadata/key validation, partition-preserving dumps, overwrite handling, atomic replacement, and reader-conflict protection.
Reading and frame aggregation
dpdata/formats/lmdb/format.py, tests/test_lmdb.py
Loads and validates global frame streams, resolves schemas and type maps, canonicalizes atom order, groups frames by composition, and enforces frame and field constraints.
Custom field protocols
dpdata/formats/lmdb/format.py, tests/test_lmdb_custom_dtype.py
Validates custom field names, shapes, axes, static data, virtual-atom removal, deepmd mappings, and automatic dtype registration.
Documentation and compatibility coverage
docs/systems/index.rst, docs/systems/lmdb.md, tests/test_lmdb.py
Documents the LMDB layout and workflows, and tests reference-format interoperability, encoded records, malformed metadata, frame limits, and runtime annotations.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

  • deepmodeling/dpdata#928: Earlier LMDB backend work extended by this format rewrite and its expanded dump/load semantics.

Suggested labels: dpdata

Suggested reviewers: wanghan-iapcm, njzjz, iProzd

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 38.38% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly captures the main change: aligning the LMDB backend with DeePMD-kit.
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.

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.

@codspeed-hq

codspeed-hq Bot commented Jul 12, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

⚠️ Unknown Walltime execution environment detected

Using the Walltime instrument on standard Hosted Runners will lead to inconsistent data.

For the most accurate results, we recommend using CodSpeed Macro Runners: bare-metal machines fine-tuned for performance measurement consistency.

✅ 2 untouched benchmarks


Comparing OutisLi:pr/lmdb (3590e02) with master (0416b54)

Open in CodSpeed

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/test_lmdb_custom_dtype.py (1)

313-324: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Use a context manager for the write env to avoid a leaked handle.

Unlike the other tests here (which use with lmdb.open(...) as env:), this block opens the environment manually and only closes it at Line 324. If msgpack.unpackb/packb raises inside the with env.begin(write=True) block, env is never closed, which can leak the handle and, on some platforms, block shutil.rmtree in tearDown.

♻️ Wrap the environment in a context manager
-        env = lmdb.open(self.lmdb_path, map_size=1 << 30)
-        with env.begin(write=True) as txn:
-            key = b"000000000001"
-            frame = msgpack.unpackb(txn.get(key), raw=False)
-            replacement = np.array([3.0, 4.0])
-            frame["static_data"] = {
-                "type": str(replacement.dtype),
-                "shape": list(replacement.shape),
-                "data": replacement.tobytes(),
-            }
-            txn.put(key, msgpack.packb(frame, use_bin_type=True))
-        env.close()
+        with lmdb.open(self.lmdb_path, map_size=1 << 30) as env:
+            with env.begin(write=True) as txn:
+                key = b"000000000001"
+                frame = msgpack.unpackb(txn.get(key), raw=False)
+                replacement = np.array([3.0, 4.0])
+                frame["static_data"] = {
+                    "type": str(replacement.dtype),
+                    "shape": list(replacement.shape),
+                    "data": replacement.tobytes(),
+                }
+                txn.put(key, msgpack.packb(frame, use_bin_type=True))
🤖 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 `@tests/test_lmdb_custom_dtype.py` around lines 313 - 324, Update the LMDB
environment setup in this test to use lmdb.open as a context manager around the
existing write transaction and frame replacement logic. Remove the manual
env.close call while preserving the current key update behavior and transaction
scope.
🤖 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 `@dpdata/formats/lmdb/format.py`:
- Around line 1061-1066: Update the publish flow around _open_publish_guard and
os.replace so the guard is closed before replacing staged_data with target_data,
ensuring overwrite=True works on Windows. Preserve cleanup of the guard and temp
directory, and avoid leaving the directory handle open during os.replace.

---

Nitpick comments:
In `@tests/test_lmdb_custom_dtype.py`:
- Around line 313-324: Update the LMDB environment setup in this test to use
lmdb.open as a context manager around the existing write transaction and frame
replacement logic. Remove the manual env.close call while preserving the current
key update behavior and transaction scope.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b43de27f-e10b-4eec-9e56-4a0e30df6de9

📥 Commits

Reviewing files that changed from the base of the PR and between 0416b54 and 5c34ca8.

📒 Files selected for processing (8)
  • docs/systems/index.rst
  • docs/systems/lmdb.md
  • dpdata/format.py
  • dpdata/formats/lmdb/__init__.py
  • dpdata/formats/lmdb/format.py
  • pyproject.toml
  • tests/test_lmdb.py
  • tests/test_lmdb_custom_dtype.py

Comment thread dpdata/formats/lmdb/format.py
Make protocol-metadata assertions independent of global plugin registration order and reject unsafe Windows overwrite operations explicitly.
@wanghan-iapcm wanghan-iapcm requested review from iProzd, njzjz and wanghan-iapcm and removed request for iProzd July 12, 2026 15:01
Comment thread docs/systems/lmdb.md
@@ -0,0 +1,100 @@
# LMDB Format

The format `lmdb` stores the frames of one or more systems in a single [LMDB](http://www.lmdb.tech/doc/) database, and can be loaded or dumped through {class}`dpdata.System`, {class}`dpdata.LabeledSystem`, and {class}`dpdata.MultiSystems`. The on-disk layout coincides with the LMDB datasets read by the DeePMD-kit data loader. Core fields and registered additional fields are therefore available to DeePMD-kit under their `deepmd_name`.

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.

The claim that core fields are available to DeePMD-kit "under their deepmd_name" is incorrect for core fields. The write path stores core fields under their plural dpdata names (coords, cells, energies, forces, virials) via _CORE_DISK_NAMES, not their singular deepmd_name (coord, box, energy, force, virial). Only additional registered fields fall back to deepmd_name (_disk_name = _CORE_DISK_NAMES.get(dtype.name, dtype.deepmd_name)).

This also contradicts the module docstring in format.py, which correctly states core keys use the plural names and only additional fields use DataType.deepmd_name. Suggest rewording so that core fields are described as using their plural on-disk names, and only additional registered fields as using their deepmd_name.

return lmdb.open(
str(path),
readonly=True,
lock=False,

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.

_open_publish_guard (and _open_read_env above) open with lock=False, which disables LMDB's reader-lock table — the only mechanism that would report a second in-process open. With lock=False, opening a path that another environment already has open succeeds, so this except lmdb.Error branch can never fire and the advertised "Close DeePMD-kit and other readers first" protection is effectively a no-op on POSIX.

Consequences: (a) the safety guarantee is not delivered, and (b) three tests fail locally — test_existing_external_reader_has_actionable_error, test_external_lmdb_reader_blocks_overwrite, and test_publish_guard_closes_external_reader_race. Note this PR has no unit-test CI job (only CodeRabbit / readthedocs / pre-commit), so those failures are not caught automatically. Recommend reworking the detection (e.g. rely on the reader-lock table instead of lock=False) or revisiting the tests/claims.

@codecov

codecov Bot commented Jul 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 87.57764% with 120 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.82%. Comparing base (0416b54) to head (3590e02).

Files with missing lines Patch % Lines
dpdata/formats/lmdb/format.py 87.50% 120 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1012      +/-   ##
==========================================
- Coverage   86.87%   86.82%   -0.06%     
==========================================
  Files          89       89              
  Lines        8266     9114     +848     
==========================================
+ Hits         7181     7913     +732     
- Misses       1085     1201     +116     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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

Labels

deepmd DeePMD-kit format enhancement New feature or request size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants