Skip to content

[Feature] Implement Compare-Version facility for update_if_present - #4049

Open
coder-jayp wants to merge 7 commits into
pytorch:mainfrom
coder-jayp:feat/compare-version
Open

[Feature] Implement Compare-Version facility for update_if_present#4049
coder-jayp wants to merge 7 commits into
pytorch:mainfrom
coder-jayp:feat/compare-version

Conversation

@coder-jayp

Copy link
Copy Markdown
Contributor

Description

Implements the optional compare-version facility inside ReplayBuffer.update_if_present. This allows asynchronous updates to be safely ignored if they carry an older model/state version than what is currently stored in a live Replay Buffer slot.

Motivation and Context

This prevents an older asynchronous computation from overwriting a newer refresh of mutable replay fields. It guarantees generation-safe and version-safe updates.

Part of #4040. (Builds on top of the base API drafted in #4043).

  • I have raised an issue to propose this change (required for new features and bug fixes)

Types of changes

  • New feature (non-breaking change which adds core functionality)

Checklist

  • I have read the CONTRIBUTION guide (required)
  • My change requires a change to the documentation.
  • I have updated the tests accordingly (required for a bug fix or a new feature).
  • I have updated the documentation accordingly.

theap06 and others added 6 commits July 24, 2026 00:21
Executable spec for step 1 of the conditional replay-update RFC:
round-robin writers stamp each slot with an int64 generation exposed
via writer.generations_of(index); first write is generation 0, every
slot reuse increments it, empty() never revives old handles, and
generations persist through state_dict and dumps with legacy
checkpoints still loading. Sampling exposes the stamps as an
index_generation info entry / TensorDict key aligned with index.

Tests are expected to fail until the implementation lands.
Round-robin writers now stamp each storage slot with an int64
generation counter: the first write of a slot has generation 0 and
every round-robin reuse increments it, so a captured
(index, generation) pair identifies one specific record and becomes
detectably stale once the slot is recycled or the buffer is emptied.
Generations are exposed through writer.generations_of(index), persist
through state_dict and dumps/loads (legacy checkpoints still load),
and are reported at sampling time as an index_generation info entry,
which TensorDict buffers surface as a sample key next to index.
Writers that do not track reuse report -1. Positional write_at and
raw __setitem__ writes patch records in place and do not advance
generations. A wraparound-heavy extend benchmark covers the hot-path
cost.

First step of the conditional replay-update RFC (pytorch#4040).
Addresses pytorch#4041.
Executable spec for step 2 of the conditional replay-update RFC:
rb.update_if_present(index=, generation=, patch=) applies every patch
key to records whose (index, generation) is still live, skips reused
or emptied slots without touching their content, and returns a result
with an updated mask aligned to the input order plus updated/stale
counts. The whole patch is validated before any write (KeyError for
unknown keys, ValueError for shape or dtype mismatches, storage
untouched in both cases), handles survive repeated updates, nested
keys are supported, ListStorage raises a capability error, multidim
storages round-trip, sampled handles flow straight into the call, and
a concurrent writer/updater stress test pins non-torn multi-key
visibility. RayReplayBuffer delegates the call to the actor.

Tests are expected to fail until the implementation lands.
…itional updates

Adds a best-effort conditional mutation API for stored replay fields.
update_if_present(index=, generation=, patch=) applies a patch only to
records whose (index, generation) pair still matches the writer's
current slot generation, skipping records whose slot was recycled or
emptied instead of corrupting them, and returns a
ConditionalUpdateResult with a per-record updated mask plus
updated/stale counts. The whole patch is validated (key existence,
shape, dtype) before any write; validation failures leave storage
untouched. The generation comparison and the patch write share one
replay-lock acquisition, giving per-record atomicity against
concurrent extends, and updating a record does not consume its handle.

Tensor storages advertise supports_conditional_update; unsupported
backends such as ListStorage raise a capability error instead of
performing an unsafe raw-index write. RayReplayBuffer delegates the
call to the actor in a single RPC (validation and write run inside the
actor under its own lock); the distributed transport raises a clear
capability error. Nested keys and multidimensional storages are
supported.

Second step of the conditional replay-update RFC. Closes pytorch#4040.
Executable contract for the optional versioning extension of pytorch#4040:
activation requires version_key and version together; generation-live
records apply iff version > stored (require_newer=True) or >= stored
(default); accepted records get the version written back atomically
with the patch; version_key may not appear in the patch; staleness
takes precedence and updated/version_rejected/stale partition the
batch; rejection is deterministic and side-effect free; version
accepts a scalar or a per-record int64 tensor; validation failures
leave storage and the version column untouched; without the facility
behavior is unchanged and version_rejected is None.

Tests are expected to fail until the implementation lands.
@pytorch-bot

pytorch-bot Bot commented Jul 25, 2026

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/rl/4049

Note: Links to docs will display an error until the docs builds have been completed.

❌ 41 New Failures, 18 Unrelated Failures, 1 Unclassified Failure

As of commit b2f4b09 with merge base ae421b9 (image):

NEW FAILURES - The following jobs have failed:

UNCLASSIFIED FAILURE - DrCI could not classify the following job because the workflow did not run on the merge base. The failure may be pre-existing on trunk or introduced by this PR:

BROKEN TRUNK - The following jobs failed but were present on the merge base:

👉 Rebase onto the `viable/strict` branch to avoid these failures

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jul 25, 2026
@github-actions github-actions Bot added Feature New feature Documentation Improvements or additions to documentation Benchmarks rl/benchmark changes ReplayBuffers and removed Feature New feature labels Jul 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Benchmarks rl/benchmark changes CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Documentation Improvements or additions to documentation Feature New feature ReplayBuffers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants