aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/src/meshbay_node/ops.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-node/src/meshbay_node/ops.py')
-rw-r--r--packages/meshbay-node/src/meshbay_node/ops.py50
1 files changed, 18 insertions, 32 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/ops.py b/packages/meshbay-node/src/meshbay_node/ops.py
index 10f6a8f..5b10452 100644
--- a/packages/meshbay-node/src/meshbay_node/ops.py
+++ b/packages/meshbay-node/src/meshbay_node/ops.py
@@ -682,30 +682,21 @@ async def add_root(state: dict, group_id: str, path: str, *,
writable=added.writable, removable=added.removable,
direct=added.direct))
- # The *live* set, not only the config — the same thing `remove_root` and
- # `update_root` do, and the one this was missing.
+ # Deliberately *not* mutating the live RootSet in place.
#
- # `_retarget_indexer` (the MNP path) re-points the indexer at
- # `groups_ctx[gid]["roots"]`, so leaving that object untouched retargeted
- # it at exactly what it already had: node.toml gained the directory, the
- # index went on reporting the old set, and the next `index_sync` overwrote
- # whatever the ack had just told the client. The root was there, invisible,
- # until a restart — and adding it again was refused as a duplicate of
- # itself, which is the only reason anyone found out.
+ # `DirectoryIndexer.retarget` decides what to scan by diffing the names it
+ # already has against the ones it is given — so handing it the same object,
+ # edited, means the new root is in both sides of the comparison and is
+ # never scanned. It would appear in the table and stay permanently empty.
+ # `_reload_config_inner` diffs the same way and would likewise conclude
+ # nothing changed. The caller reloads instead, which builds a fresh set
+ # from the file this just wrote.
#
- # Safe to append rather than rebuild: `RootSet.build(specs)` above already
- # validated the whole set, this root included, for name collisions and
- # nesting.
- live_roots: RootSet | None = state.get("groups_ctx", {}).get(
- group_id, {}).get("roots")
- if live_roots is not None and not any(
- r.folded == added.folded for r in live_roots.roots):
- live_roots.roots.append(added)
-
+ # `built` is that set, computed here only to validate and to answer with;
+ # what the node serves comes from the reload.
log.info("Root added: %s → group %s", added.name, group_id[:8])
return {"status": "added", "name": added.name, "path": str(added.path),
- "group_id": group_id,
- "roots": (live_roots or built).describe()}
+ "group_id": group_id, "roots": built.describe()}
async def remove_root(state: dict, group_id: str, root_name: str) -> dict:
@@ -741,20 +732,15 @@ async def remove_root(state: dict, group_id: str, root_name: str) -> dict:
cfg.roots.pop(match_idx)
- # Update the live RootSet so GET /api/groups returns correct data
- # immediately, without waiting for the async reload.
- live_roots = state.get("groups_ctx", {}).get(
- group_id, {}).get("roots")
- if live_roots:
- live_roots.roots = [
- r for r in live_roots.roots if fold(r.name) != target]
-
- # Built from config when there is no live set, never returned empty: an
+ # Not mutating the live set here either — see `add_root`. Dropping the
+ # root from it would leave `retarget` unable to tell that its entries
+ # should go, so the removed directory's files would stay in the index.
+ #
+ # Built from the config this just edited, and never returned empty: an
# empty list is a *valid answer* meaning "this group has no directories",
- # and the client cannot tell it from "the node could not say". It would
+ # which the client cannot tell from "the node could not say" — it would
# blank the operator's table on an op that succeeded.
- result_roots = (live_roots.describe() if live_roots
- else RootSet.build([asdict(r) for r in cfg.roots]).describe())
+ result_roots = RootSet.build([asdict(r) for r in cfg.roots]).describe()
log.info("Root removed: %s from group %s", root_name, group_id[:8])
return {"status": "removed", "name": root_name, "group_id": group_id,