Before a NETCONF client edits the candidate datastore and commits it, why should it lock both candidate and running, and what does each lock guard?
answer
- the candidate is shared by default
- commit publishes the whole candidate
- dirty candidate refuses a lock
- releasing the candidate lock discards edits
basics
~20 sThe candidate is a scratch pad every session may share, and <commit> publishes all of it; locking candidate keeps other sessions' edits out of your commit, and locking running stops anyone changing or committing to running until you finish.
solid answer
~40 sRFC 6241 §8.3 says a client must assume the candidate is shared with other sessions and that it is prudent to lock it before editing. That matters because `<commit>` sets `running` to the **whole** candidate, so an unlocked candidate can carry another session's half-made edits into your commit. The candidate lock is refused while the candidate already holds uncommitted changes, and releasing it - by `<unlock>` or by the session dying - discards any outstanding changes. Locking `running` stops other sessions, SNMP and CLI from changing it, and makes any other session's `<commit>` fail with `in-use`. Inside that fence the commit itself is atomic: if the device cannot apply every change, `running` stays unchanged. So the sequence is lock running, lock candidate, edit, validate, commit, then unlock both.
go deeper
Recall that the candidate is a working copy and commit copies all of it into running, so other sessions' edits can ride along.
Explain what each of the two locks blocks, why a dirty candidate cannot be locked, and why commit fails with in-use when another session holds either lock.
Show the failure modes of locking only one datastore, and use the discard-on-unlock rule to argue that a crashed client leaves the candidate clean.
Discuss whether a shared candidate is a hazard worth designing around, versus devices that give each session its own working copy, and what that does to concurrency.
## The problem: a shared scratch pad A NETCONF server that advertises the `:candidate` capability keeps a **candidate datastore**: a full copy of the configuration that clients edit without touching the device's behaviour. Nothing takes effect until a client sends `<commit>`, which sets the **running datastore** - the configuration the device is actually using - to the contents of the candidate. RFC 6241 §8.3.1 adds a warning that is easy to miss: the candidate **can be shared among multiple sessions**. Unless a client knows otherwise, it MUST assume other sessions can modify the candidate at the same time, and the RFC calls it "prudent" to lock the candidate before modifying it. Take a router shared by a **CI pipeline** and an **operator's script**. The script has written two of the five edits it needs into the candidate when the pipeline, which has finished its own edits, sends `<commit>`. `<commit>` does not know whose edits are whose; it publishes the whole candidate. The script's half-built change goes live under the pipeline's name. ## What each lock guards | Lock | Stops | Why it matters for the commit | |---|---|---| | `<lock>` on `candidate` | other sessions editing the candidate; another session's `<commit>` | your commit publishes only what you put there | | `<lock>` on `running` | other sessions, SNMP and CLI changing `running`; another session's `<commit>` | `running` stays as you last saw it until your commit lands | RFC 6241 §8.3.4.1 ties the two together: if either `running` or `candidate` is locked by a **different** session, `<commit>` MUST fail with `error-tag` **`in-use`**. Holding both locks therefore makes your session the only one that can move configuration into `running` while you work. The lock on `running` also covers the people who never touch the candidate. A device with `:writable-running` accepts edits straight into `running`, and the CLI often writes there too. Without a `running` lock, such an edit can land between your `<validate>` and your `<commit>`. ## Two rules that only apply to the candidate **A dirty candidate cannot be locked.** RFC 6241 §7.5 refuses a lock on `candidate` when it has been modified and the changes have been neither committed nor rolled back. Granting it would hand the new owner edits it did not make. The client has to find out whose edits those are; clearing them is `<discard-changes>`, which resets the candidate to the contents of `running`. **Releasing the candidate lock discards outstanding changes.** RFC 6241 §8.3.5.2 says any uncommitted changes are discarded when the lock is released, whether by `<unlock>` or because the session failed. That gives two consequences: - a client that crashes mid-edit leaves a clean candidate behind, not a trap for the next committer; - a client that unlocks the candidate **before** committing loses its own edits. ## Atomicity: what the commit promises inside the fence Locks decide **who** may change the configuration. The commit decides **how** the change lands. RFC 6241 §8.3.4.1: if the device is unable to commit all of the changes in the candidate, `running` **MUST remain unchanged**; if it succeeds, `running` is updated with the contents of the candidate. There is no partial commit. That all-or-nothing rule is only as useful as the candidate is clean. Atomicity applied to a candidate full of someone else's edits is still an atomic mistake - which is why the locks come first. ## The sequence RFC 6241 Appendix E (non-normative) walks through the per-device steps. Reduced to the locking skeleton: 1. `<lock>` on `running`. 2. `<lock>` on `candidate` - refused if it is dirty or held. 3. `<edit-config>` requests against `candidate`. 4. `<validate>` the candidate. 5. `<commit>` - optionally a confirmed commit, so the change reverts unless confirmed. 6. Test the result; send the confirming commit if one was used. 7. `<unlock>` on `candidate`, then on `running`. If the client gives up at any step before the commit, unlocking the candidate throws its edits away, and `running` was never touched. ## Common mistakes - Locking only the candidate, so a CLI user or a `:writable-running` client changes `running` underneath. - Locking only `running`, so another session's edits ride along in the shared candidate. - Treating `lock-denied` on a dirty candidate as a reason to edit without the lock. - Unlocking the candidate as a tidy-up step before the commit, and losing the change.
- If the client holds both locks, can its own commit still fail and leave running half-changed?The commit can fail, but not halfway. RFC 6241 §8.3.4.1 requires `running` to remain unchanged when the device cannot commit all of the candidate's changes. The client gets an `<rpc-error>`, still holds both locks, and can correct the candidate and retry or discard the changes.
- Does a lock on the candidate stop other sessions from editing running directly?No. Each lock covers only its own datastore. A candidate lock keeps others out of the candidate and makes their commits fail, but on a device with `:writable-running` another session can still `<edit-config>` `running` directly, and a CLI user can still change it, unless `running` is locked as well.
saying these in an interview costs you the question
- The candidate is private to each NETCONF session, so locking it is unnecessary.
- A NETCONF commit publishes only the edits made by the session that sends it.
- Unlocking the candidate before commit keeps the edits for a later commit.
- Locking candidate also protects running, so one lock is enough.
- A failed commit can leave running with some of the candidate's changes applied.