swarmops op undo#
Move every ref operation OP moved back to what it named before, all or none.
Each ref must still name what the operation set it to (or what it named before, which is skipped): one that was moved since stops the undo with E_CAS_MISMATCH, and nothing is written. An undo run again is a replay (already_applied). Changesets are never removed.
- Group
operations- Since
0.1.0- Mutates
- yes
- Identity required
- yes
- MCP tool
undo- Envelope op
undo- Exit codes
- 016
Syntax#
swarmops op undo [OPTIONS] <OP>Required arguments#
Syntax: <OP>.
The operation, as op log numbers it.
Optional arguments#
The command also takes the global options.
Examples#
swarmops --repo [tmp]/store --as agent/worker-17 op undo 3{
"ok": true,
"op": "undo",
"api": 1,
"result": {
"undone": 3,
"op": 4,
"ref_moves": [
{
"name": "main",
"old": "[c2]",
"new": "[c1]",
"actor": "agent/worker-17",
"at": "[at]"
}
],
"already_applied": false
},
"meta": {
"repo": "[tmp]/store",
"elapsed_ms": "[elapsed_ms]",
"resolved": {},
"auth": "trust"
}
}Exit code 0. Recorded by the CLI tests. A name in square brackets, such as [tmp] or [c1], stands for what differs from run to run: the test's temporary directory, an id, a time.
No recorded example yet.
Details#
op undo takes back the ref moves of one operation: every ref that operation OP moved goes
back to what it named before OP, in one transaction of its own, all of them or none. It moves
refs only. Changesets are never removed, so whatever an undo takes off a ref stays in the store,
readable by id. Use it to take back a mistaken ref set, a publish or a land that should not
have reached main, a deletion or a creation of a ref.
What it restores#
For each ref, OP's net move counts: from the first old the operation recorded for the ref to
the last new. A ref the operation moved and moved back is left alone. The undo then moves each
ref, in the order of OP's moves, from OP's new back to its old:
What OP did to the ref | What the undo does |
|---|---|
Moved it from A to B (ref set, a publish, a land) | Moves it from B back to A |
Created it at B | Deletes it |
Deleted it, from A | Creates it again at A, with version 1 |
When it refuses, and when it is a replay#
The undo reads operation OP under the store's write lock, after the write has checked the
journal's tail, then compares each ref:
| The ref names now | Result |
|---|---|
What OP set it to (B; no ref, if OP deleted it) | Moved back |
What it named before OP (A; no ref, if OP created it) | Skipped: an earlier undo, or a later move, put it there already |
| Anything else | The whole undo is refused with E_CAS_MISMATCH (exit 4) and nothing is written, the other refs' moves included |
With every ref skipped, nothing is written either: the result says already_applied: true, has
no op, and lists no moves. So running the same undo again is safe.
Before the comparison:
E_OP_NOT_FOUND(exit 6): the store has no operationOP.details.lastis the newest operation's number,nullfor a store without one.E_NOTHING_TO_UNDO(exit 1):OPmoved no ref (acommitwithout--advance, a compaction), or moved each ref back to where it found it.E_IDENTITY_REQUIRED(exit 5): an undo is a write, so it needs--asorSWARMOPS_AGENT.
After a refusal#
E_CAS_MISMATCH here carries details.name (the ref), details.actual (what it names now,
null when it does not exist) and details.op; the hint is swarmops op log, which only reads.
A later operation moved the ref, so the message gives two ways on:
- Undo the later operations first, newest first, then
OPagain. Each undo is checked the same way, so this works only while nothing else moved the ref meanwhile. - Keep the later moves, and land a change that reverses
OP's. No command builds the reverse change for you (Known limitations).
The retry flag says that the undo can succeed once the ref is back where OP left it. Running
the same undo again without changing anything fails the same way.
The undo is an operation#
A successful undo records one operation of kind ref_move, its number in the result's op. Its
moves name the undoer as actor and carry the undo's time. Another op undo of that number
takes the undo back, which moves the refs forward again.
Patterns#
Take back the newest move of main:
OP=$(swarmops op log | jq -r '[.result.ops[] | select(any(.ref_moves[]; .name == "main"))][0].op')
swarmops op log --before $((OP + 1)) -n 1 | jq -c '.result.ops[0]'
swarmops op undo "$OP"The second line lists the operation once more, to check that its moves are the ones you mean before you undo it (below).
Read the result: already_applied: true means nothing was written because every ref was back
already; otherwise ref_moves lists each move made and op is the undo's own number.
Caveats#
- Check the number first.
op loglists an operation before its sync has finished. If the store takes that frame back (a failed sync, a power loss), the next write's frame gets the same number, and an undo of the number you read moves back that other operation's refs, with no error, as long as they hold what it set. List the operation again before you undo it, above all after an error (Known limitations). - It is not a revert. An undo moves refs; it does not record a change that reverses the
files. After it, the changesets
OPadded are on no ref, yet still in the store. - Land keys. A keyed land looks for an earlier land of its key on the target's
first-parent chain. A land that an undo took off
mainis no longer there, so the sameland --keyrun again lands once more (status: landed, notreplayed). - Durability and the lock work as for
ref set: the undo commits when its frame is durable,warningsreports a later step that failed, and a lock wait past 10 s isE_BUSY.
Through MCP#
The MCP tool is undo, with op. Its description says the same: a ref moved since stops it,
undo the later operations first, and a second run is a replay.
Guides#
- Reading and repairing history: Undo an operation
- Reading and repairing history: Recover from a refusal
- Concepts: Op log and undo
Return information#
result is a UndoResp (swarmops schema UndoResp prints its JSON Schema).
| Field | Type | Required | Description |
|---|---|---|---|
already_applied | boolean | yes | Every ref the operation moved already names what it named before, so nothing was written: an undo run again is a replay, and safe. |
op | integer or null | no | The operation this undo recorded, which another undo can take; omitted when it wrote nothing (already_applied). |
ref_moves | array of RefMoveOut | yes | The moves it made: each ref from what the operation set back to what it named before. |
undone | integer | yes | The operation undone. |
warnings | array of string | no | Problems found after the undo became durable, such as failed housekeeping; the undo itself succeeded. Omitted when empty. |
Envelope op: undo.
Exit codes:
0: Success.1: The operation failed.6: Something the request names does not exist.
Errors#
- E_NOTHING_TO_UNDO (exit 1): The operation moved no ref, so there is nothing to restore.
- E_OP_NOT_FOUND (exit 6): The store has no operation by that number.
Any command that opens a store may also fail with a store error, such as E_NOT_A_REPO, E_IO or E_STORE_CORRUPT; a command line the grammar refuses is E_INVALID_ARG (exit 2).