Skip to content

refactor(mds): unify inode/partition/dirshard version tracking - #1116

Merged
rock-git merged 2 commits into
dingodb:mainfrom
rock-git:fix/main_091701
Sep 20, 2026
Merged

rock-git merged 2 commits into
dingodb:mainfrom
rock-git:fix/main_091701

Conversation

@rock-git

Copy link
Copy Markdown
Contributor

Summary

A directory's version was tracked in 3 shapes at once: Inode held base_version_ plus a per-slot delta_versions_ vector plus a total_delta_version_ sum, ShardPartition held its own base_version_/delta_version_ pair, and DirShard held a bare uint64_t. Partition dentry ops carried a scalar version too, so an op produced by a shard mutation was recorded against a complete version rather than its own slot, and the op replay could not distinguish "this shard is already past op N" from "this shard is past op N in a different slot".

Version is now one type, AttrVersionVec (base version + one delta per mutation slot + cached total), merged monotonically per slot by PutIf. It is the only version representation in Inode, ShardPartition, DirShard, and partition dentry ops, and dentry ops carry an AttrVersion that is either a base version or an indexed delta.

Changes

  • src/mds/common/type.h adds AttrVersion and AttrVersionVec, plus AttrWithMutation::ToAttrVersion(). TotalDeltaVersion() now dedups mutations by slot (max per slot) instead of summing, so a mutation list that accumulated a duplicate slot no longer inflates the version. AttrVersionVec::PutIf has overloads for AttrMutationEntry, AttrVersion, AttrEntry, AttrWithMutation, and AttrVersionVec, and LessThanOrEqual/GreaterThanOrEqual compare a single version against the right slot.
  • Inode collapses base_version_/delta_versions_/total_delta_version_ into version_vec_. Version() becomes CompleteVersion(), and VersionVec() exposes a copy of the whole vector so a caller can compare an inode against a partition slot by slot.
  • ShardPartition/DirShard carry an AttrVersionVec. Put/Delete/RefreshVersion take an AttrVersion, DeltaVersion() becomes CompleteVersion() with VersionVec() alongside, and ApplyDeltaOpNoLock skips an op with shard->VersionVec().GreaterThanOrEqual(op.version) instead of a scalar compare, which makes the insertion sort of delta_dentry_ops_ unnecessary (AddDeltaOpNoLock drops it). ShardPartition::New gains an AttrWithMutation overload so DoFetchPartition no longer builds a ToCompleteAttr() copy just to construct the partition, and DoFetchDirShard builds the shard version from the raw AttrWithMutation for the same reason.
  • FileSystem passes the parent's AttrVersion (from AttrWithMutation::ToAttrVersion()) into AddDentryToPartition/DeleteDentryFromPartition/RefreshPartitionVersion instead of the in-memory inode's current version, so a partition dentry op is stamped with the exact version component the operation produced.
  • FileSystem::Rename now invalidates the old parent's partition cache only when the parent actually changes. A same-parent rename no longer tears down the entry it just wrote a DELETE op into.
  • Store operations (RenameOperation, ScanDirShardOperation, GetInodeAttrOperation) clear accumulated mutations at the start of Run. These operations run under RunAlone, which retries Run on conflict; without the clear, a retry appended every mutation a second time and ToCompleteAttr() doubled the version.
  • DummyStorage gains versioned reads and write-conflict detection at commit (ESTORE_MAYBE_RETRY for a key that changed since the txn read it). The in-memory backend used by the MDS unit tests now exercises the same optimistic concurrency as DingoStore and TiKV rather than silently doing last-write-wins, which is what let the retry bug above hide.
  • src/common/config_mapper.h and mds-cli/br.cc fill aws_sdk_config from the --s3_* gflags for S3 filesystems. Without it the AWS SDK fell back to an empty log prefix and wrote <cwd>/<YYYY-MM-DD-HH>.log.
  • Cache inspection output changes shape: DirShard::Dump/ShardPartition::Dump emit a single version string ("<base> <total>") instead of base_version/delta_version numbers, and the FsStat partition page renders it as one value. The inode cache page and partition list page keep the previous base/complete split under the new accessor names.

Two unrelated commits are folded in: automate-test absorbs the xfstests/vdbench guidance from the deleted dev-regression-test skill, and dev-deploy documents only the knobs the deploy scripts actually read.

Test plan

cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_UNIT_TESTS=ON .. && make -j12
./build/bin/test/test_mds

New and extended coverage:

  • test/unit/mds/common/test_type.cc (new) covers every AttrVersionVec constructor and PutIf overload, per-slot dedup of repeated mutations, and the range comparisons.
  • test/unit/mds/filesystem/test_filesystem_state.cc (new, largest addition) drives the real FileSystem against DummyStorage and asserts version arithmetic per operation: create/mkdir/batch variants advance the parent exactly once, nlink moves only where it should, symlink bumps the version but not nlink, read paths change nothing, setattr/setxattr/removexattr refresh the partition, rename advances one or both parents, and long-running concurrent mixes keep inode and partition versions consistent.
  • test/unit/mds/filesystem/test_inode.cc and test_partition.cc cover monotonic base/complete/delta versions, shard split preserving the version vector, partition cache merge of a newer vector, and delta op replay leaving covered ops behind.
  • test/unit/mds/storage/test_dummy_storage.cc covers the new txn conflict semantics: stale read-modify-write conflicts, blind writes do not, recreate after delete does not.
  • test/unit/common/test_config_mapper.cc covers the new S3 SDK config fill.

Compound Engineering
pi

Copilot AI lite review requested due to automatic review settings September 20, 2026 05:44
…ev-deploy

dev-regression-test duplicated the test-loop guidance already in
automate-test while also hand-holding each tool's CLI. Fold the
script-uncovered tools (xfstests, vdbench) into automate-test with
explicit pass/fail criteria and drop the redundant skill.

dev-deploy is rewritten to document only the knobs the scripts actually
consume (mds_deploy_parameters.local, which is gitignored), replace the
unreliable `ps -ef | grep` checks with pgrep/mountpoint, and add the
missing create_fs step and smoke test.
@rock-git
rock-git enabled auto-merge September 20, 2026 05:49
@rock-git
rock-git added this pull request to the merge queue Sep 20, 2026

Copilot AI 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.

Copilot review overview

🟡 Changes recommended

Unresolved critical and moderate findings affect version correctness, cache rendering, retry safety, storage conflicts, and test reliability.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 4 High severity · 3 Medium severity

Open (7)
What changed in this PR

This PR unifies MDS version tracking with per-slot AttrVersionVec, adds optimistic conflict handling and retry-safe mutations, and updates S3 configuration, diagnostics, tests, and tooling.

Changes:

  • Replaces scalar inode, partition, shard, and dentry-operation versions with version vectors.
  • Adds DummyStorage conflict detection and expanded filesystem/version tests.
  • Updates cache rendering, S3 flag wiring, scripts, and development guidance.
File Summary Review findings
test/​unit/​mds/​storage/​test_dummy_storage.cc Tests transaction conflict behavior. None.
test/​unit/​mds/​filesystem/​test_partition.cc Tests partition and shard version vectors. None.
test/​unit/​mds/​filesystem/​test_inode.cc Tests inode version merging. None.
test/​unit/​mds/​filesystem/​test_filesystem_state.cc Adds filesystem state and concurrency coverage. Moderate (2 votes, line 847): conflict retries can make the concurrent test flaky. Moderate (1 vote, line 122): SimpleWorkerSet teardown does not stop the worker before destruction.
test/​unit/​mds/​common/​test_type.cc Tests version-vector behavior. None.
test/​unit/​common/​test_config_mapper.cc Tests S3 SDK configuration mapping. None.
src/​tools/​mds-cli/​br.cc Applies AWS SDK flag configuration. None.
src/​mds/​storage/​dummy_storage.h Adds transaction version bookkeeping APIs. None.
src/​mds/​storage/​dummy_storage.cc Implements versioned reads and conflict checks. Critical (3 votes, line 240): direct Put/Delete paths do not update version bookkeeping. Moderate (1 vote, line 313): unconditional random delays make tests nondeterministic and slower.
src/​mds/​storage/​dingodb_storage.cc Handles failed transaction creation. None.
src/​mds/​service/​fsstat_service.cc Updates cache version rendering. Moderate (2 votes, line 745): string versions are converted with asUInt64(), rendering loaded shard versions as zero.
src/​mds/​filesystem/​store_operation.h Documents operation mutation behavior. None.
src/​mds/​filesystem/​store_operation.cc Resets retryable operation state. Critical (1 vote, line 3861): retry reset leaves stale attr_with_mutation.attr data.
src/​mds/​filesystem/​partition.h Defines vector-based partition and shard versions. None.
src/​mds/​filesystem/​partition.cc Implements version-vector merging and replay. Moderate (2 votes, line 392): shard-page rendering still reads removed base_version/delta_version keys. Moderate (1 vote, line 471): pending operation versions are strings but rendered with asUInt64().
src/​mds/​filesystem/​inode.h Stores inode version vectors. None.
src/​mds/​filesystem/​inode.cc Applies monotonic version updates. None.
src/​mds/​filesystem/​filesystem.h Updates partition version APIs. None.
src/​mds/​filesystem/​filesystem.cc Propagates operation versions and rename cache updates. Critical (1 vote, lines 3889/4094): scalar CompleteVersion() comparisons can collide across distinct version slots and reuse stale partition caches.
src/​mds/​common/​type.h Adds unified version types and mutation conversion. Critical (3 votes, lines 181/191): sparse mutation entries are incorrectly accessed as a slot-indexed vector, producing wrong values or out-of-bounds access.
src/​common/​config_mapper.h Fills AWS SDK options from flags. None.
src/​client/​vfs/​metasystem/​mds/​file_session.cc Removes an obsolete comment. None.
scripts/​dev-mds/​run_all_test.sh Adds repeated test rounds and result checks. None.
.agents/​skills/​dev-regression-test/​SKILL.md Removes superseded regression guidance. None.
.agents/​skills/​dev-deploy/​SKILL.md Updates deployment guidance. None.
.agents/​skills/​automate-test/​SKILL.md Consolidates testing guidance. None.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/mds/common/type.h
Comment on lines +181 to +185
uint64_t DeltaVersion(uint32_t index) const {
CHECK(index < mutations.size()) << "invalid mutation index(" << index << "), should be less than "
<< mutations.size();
return mutations[index].delta_version();
}
}

uint64_t cache_version = use_base_version ? partition->BaseVersion() : partition->DeltaVersion();
uint64_t cache_version = use_base_version ? partition->BaseVersion() : partition->CompleteVersion();
Status ScanDirShardOperation::Run(TxnUPtr& txn) {
// RunAlone retries Run on conflict; clear appended mutations so a retry does
// not accumulate duplicate slots.
result_.attr_with_mutation.mutations.clear();
Comment on lines +240 to +247
uint64_t new_version = ++commit_version_;
for (const auto& [key, kv] : writes) {
if (kv.opt_type == KeyValue::OpType::kPut) {
data_[key] = kv.value;
} else if (kv.opt_type == KeyValue::OpType::kDelete) {
data_.erase(key);
}
key_versions_[key] = new_version;
value["ino"] = ino_;
value["base_version"] = base_version_;
value["delta_version"] = delta_version_;
value["version"] = version_vec_.ToString();
RenderCachePageStart("Partition Cache", fs_id, os);
os << fmt::format("<h3>Partition ino {}: base version {}, delta version {}</h3>", ino,
value["base_version"].asUInt64(), value["delta_version"].asUInt64());
os << fmt::format("<h3>Partition ino {}: version {}</h3>", ino, value["version"].asString());
param.name = fmt::format("cc_{}_{}", t, i);
param.mode = 0777;
EntryWithPaOut out;
if (fs_->MkNod(ctx, param, out).ok()) ok_count.fetch_add(1);
Merged via the queue into dingodb:main with commit f9fff1e Sep 20, 2026
5 checks passed
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