summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/src/meshbay_node/transport
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-14 02:35:45 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-14 02:35:53 +0200
commit211ace6cc0168647e00bcc20a909aead50b8ad0a (patch)
treeef218ea44c91580b0daffc9eaa70270238598b97 /packages/meshbay-node/src/meshbay_node/transport
parenta1aaf31a27d1c1b65efc3c6a25fc6cc8771578ea (diff)
downloadmeshbay-211ace6cc0168647e00bcc20a909aead50b8ad0a.tar.gz
fix(node): an added root is served before it is scanned
Adding a large directory to a running group made the reload await the scan of the new root before putting the new RootSet in the group's context, holding _reload_lock the whole time. For the hours a large drive takes to hash, the node served the old set: - a file request under the new root got None from entry_abs_path and the handler died on None.exists() without replying; - a writable/removable toggle answered with the live table, still the old one, so the directory vanished from the operator's settings; - reconcile saw every file the scan had not reached as a missed event and hashed it again on the same executor, rewriting progress under the scan. retarget now applies the set, the roots table and the watcher first, and with wait=False scans the added roots in the background; the daemon swaps ctx["roots"] before calling it. A scan lock shared by the initial scan, added-root scans and reconcile makes the reconcile loop sit out a running scan without backing off. Every transport site that resolves an entry answers ROOT_NOT_SERVED instead of crashing, and a delete keeps the entry. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T6jPTeocXA1BePekdsgPya
Diffstat (limited to 'packages/meshbay-node/src/meshbay_node/transport')
-rw-r--r--packages/meshbay-node/src/meshbay_node/transport/quic_server.py5
-rw-r--r--packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py16
2 files changed, 19 insertions, 2 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/transport/quic_server.py b/packages/meshbay-node/src/meshbay_node/transport/quic_server.py
index 7b9d094..2a2b07a 100644
--- a/packages/meshbay-node/src/meshbay_node/transport/quic_server.py
+++ b/packages/meshbay-node/src/meshbay_node/transport/quic_server.py
@@ -34,7 +34,7 @@ from aioquic.quic.events import QuicEvent, StreamDataReceived, StreamReset
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from meshbay_common import MNP_VERSION
-from meshbay_node.roots import RootSet, entry_abs_path
+from meshbay_node.roots import ROOT_NOT_SERVED, RootSet, entry_abs_path
from meshbay_common.handshake import (
MNP_MIN_SUPPORTED,
NONCE_LEN,
@@ -418,6 +418,9 @@ class _MNPServerProtocol(QuicConnectionProtocol):
return
file_path = entry_abs_path(ctx["roots"], entry)
+ if file_path is None:
+ self._send(stream_id, {"type": "error", "detail": ROOT_NOT_SERVED})
+ return
if not file_path.exists():
self._send(stream_id, {"type": "error", "detail": "File not on disk"})
return
diff --git a/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py b/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py
index bc78644..eff40f1 100644
--- a/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py
+++ b/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py
@@ -137,7 +137,7 @@ from meshbay_node.media_probe import (
probe_video as _probe_video,
)
from meshbay_node.roots import (
- RootSet, entry_abs_path, SAFE_UPLOAD_NAME, safe_subdir, _free_name,
+ ROOT_NOT_SERVED, RootSet, entry_abs_path, SAFE_UPLOAD_NAME, safe_subdir, _free_name,
)
log = logging.getLogger(__name__)
@@ -3643,6 +3643,9 @@ class WebRTCPeerSession:
return
file_path = entry_abs_path(ctx["roots"], entry)
+ if file_path is None:
+ self._send({"type": "error", "detail": ROOT_NOT_SERVED})
+ return
if not file_path.exists():
self._send({"type": "error", "detail": "File not on disk"})
return
@@ -3770,6 +3773,9 @@ class WebRTCPeerSession:
self._send({"type": "error", "detail": "File not found"})
return
file_path = entry_abs_path(ctx["roots"], entry)
+ if file_path is None:
+ self._send({"type": "error", "detail": ROOT_NOT_SERVED})
+ return
if not file_path.exists():
self._send({"type": "error", "detail": "File not on disk"})
return
@@ -5524,6 +5530,11 @@ class WebRTCPeerSession:
def _exec_file_delete(self, ctx: dict, file_id: str, entry) -> None:
file_path = entry_abs_path(ctx["roots"], entry)
+ if file_path is None:
+ # Frozen, not gone: removing the entry would lose a file that is
+ # still on a drive the node cannot read right now.
+ self._send({"type": "error", "detail": ROOT_NOT_SERVED})
+ return
if file_path.exists():
file_path.unlink()
log.info("File deleted: %s", entry.name)
@@ -5723,6 +5734,9 @@ class WebRTCPeerSession:
return
file_path = entry_abs_path(ctx["roots"], entry)
+ if file_path is None:
+ self._send({"type": "error", "detail": ROOT_NOT_SERVED})
+ return
if not file_path.exists():
self._send({"type": "error", "detail": "File not on disk"})
return