Skip to content

Refuse a pending dataset write while one is in place - #275

Merged
agners merged 1 commit into
mainfrom
guard-pending-dataset-create
Sep 3, 2026
Merged

Refuse a pending dataset write while one is in place#275
agners merged 1 commit into
mainfrom
guard-pending-dataset-create

Conversation

@agners

@agners agners commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Follow-up to #269, which added the in-flight refusal to set_pending_dataset_tlvs(). create_pending_dataset() writes the same endpoint — and is what set_channel() goes through — but had no such guard.

Superseding an in-flight pending dataset races the delay timer on every device that already holds it, so a late replacement can split the mesh, and it silently undoes whatever the in-flight dataset was doing, such as a channel change. That is a property of the endpoint, not of the encoding the caller happens to use, so the guard belongs on both writes.

create_pending_dataset() now does the local check and sends If-None-Match: *, raising PendingDatasetConflictError for both the local refusal and a 412. openthread/ot-br-posix#3552 put the precondition in SetDataset(), which is shared by both endpoints and both content types, so the JSON path gets the atomic version for free.

set_channel() drops its own check from #272, which would otherwise read the pending dataset twice per channel change.

Behavior changes

  • create_pending_dataset() refuses while a pending dataset is in place; it previously replaced it. This is the point of the PR, but it is a change for existing callers.
  • set_channel() makes the same three requests as before, but reads the active dataset first. A router with no active dataset now reports that rather than the pending conflict.
  • Both raise PendingDatasetConflictError instead of a plain OTBRError. It subclasses OTBRError, so existing except OTBRError handlers are unaffected.

No version bump — that follows the usual separate bump PR.

🤖 Generated with Claude Code

set_pending_dataset_tlvs() refuses a write while a pending dataset is in
flight, but create_pending_dataset() -- the other way to write that same
endpoint, and the one set_channel() uses -- did not. Superseding an
in-flight dataset races the delay timer on every device that already
holds it, so a late replacement can split the mesh, and it silently
undoes whatever the in-flight dataset was doing, such as a channel
change. That is a property of the endpoint, not of the encoding the
caller happens to use.

Move the guard into create_pending_dataset(), where both a local check
and the If-None-Match header the border router evaluates atomically with
the write (openthread/ot-br-posix#3552, which covers the endpoint rather
than the content type, so the JSON path gets it too) now apply. It
raises PendingDatasetConflictError, as the TLV write does.

set_channel() drops its own check, which would otherwise read the
pending dataset twice per channel change. It keeps refusing, with the
same request count as before; what changes is that the active dataset is
read first, so a router with no active dataset reports that rather than
the conflict, and the error is now the PendingDatasetConflictError
subclass instead of a plain OTBRError.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@agners

agners commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator Author

@LorbusChris can you have a look at this PR? It is essentially a follow up to your #269 and #272 PRs.

I intend to release a new version after that.

@LorbusChris LorbusChris left a comment

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.

LGTM, thanks!

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.

🟢 Approval recommended

The reviewed changes are well-covered with no unresolved issues.

Pull request overview

Prevents unsafe replacement of in-flight pending datasets.

Changes:

  • Adds local and atomic conflict checks.
  • Raises PendingDatasetConflictError on conflicts.
  • Centralizes channel-change protection.
File summaries
File Description
tests/test_init_legacy.py Updates request expectations and conflict coverage.
python_otbr_api/__init__.py Implements conflict-safe pending-dataset creation.
Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 0
  • Review effort level: Balanced

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

@agners
agners merged commit 58d6c26 into main Sep 3, 2026
2 checks passed
@agners
agners deleted the guard-pending-dataset-create branch September 3, 2026 15:43
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.

3 participants