diff options
Diffstat (limited to 'packages/meshbay-node')
| -rw-r--r-- | packages/meshbay-node/src/meshbay_node/config.py | 35 | ||||
| -rw-r--r-- | packages/meshbay-node/tests/test_connection_processes.py | 20 |
2 files changed, 43 insertions, 12 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/config.py b/packages/meshbay-node/src/meshbay_node/config.py index c5596f1..d46ab8c 100644 --- a/packages/meshbay-node/src/meshbay_node/config.py +++ b/packages/meshbay-node/src/meshbay_node/config.py @@ -74,13 +74,14 @@ max_concurrent_uploads = 8 # the partial file is deleted. max_upload_gb = 8 -# How many processes carry the connections of members. At 1 the node does it +# How many processes carry the connections of members. Left unset: one per core +# of this machine, up to four, about 85 MB of memory each. At 1 the node does it # all in one process, on one core: about 35 MB/s summed over every transfer in -# progress. Above 1 the node starts that many processes for the connections and -# keeps the rest for itself, so transfers use that many cores, plus part of one -# for the node. Takes effect for new connections, without a restart. Not above -# the number of cores of this machine. -connection_processes = 1 +# progress on Linux, and much less on Windows. Above 1 the node starts that many +# processes for the connections and keeps the rest for itself, so transfers use +# that many cores, plus part of one for the node. Takes effect for new +# connections, without a restart. Not above the number of cores of this machine. +# connection_processes = 4 # HEVC sources have no browser decoder on most platforms, so streaming one is # transcoded to H264 rather than the usual free copy — real CPU per viewer. @@ -202,8 +203,9 @@ class NodeConfig: # §15.3), so this is what stands between a writable root and a full disk. max_upload_gb: float = 8.0 # How many processes hold the peer connections. One means this process - # holds them itself, as it always did. See transport/webrtc/workers.py. - connection_processes: int = 1 + # holds them itself. See transport/webrtc/workers.py, and + # `default_connection_processes` for why it is not one by default. + connection_processes: int = field(default_factory=lambda: default_connection_processes()) # HEVC (and any future codec in media_probe.py's # BROWSER_INCOMPATIBLE_VIDEO_CODECS) has no decoder in most browsers, so # streaming it needs a real re-encode to H264 rather than the usual free @@ -305,6 +307,23 @@ def max_processes() -> int: return max(1, os.cpu_count() or 1) +# Above four, a single download no longer gains (docs/MESHBAY_DESIGN.md §5.5: +# 131 MB/s at four, 150 at eight with the receiving browser the limit), and each +# process is 85 MB held for the life of the node. +DEFAULT_CONNECTION_PROCESSES_MAX = 4 + + +def default_connection_processes() -> int: + """ + One per core, up to four. It was 1, which starts no process at all: the + several-cores path was measured, shipped and reached by no node whose + operator had not found the setting, and the lanes of a download all landed + on the node's one core — on Windows, where Python's UDP costs most, a + download over eight connections was as slow as over one. + """ + return min(DEFAULT_CONNECTION_PROCESSES_MAX, max_processes()) + + def _positive(value: object, default: int, name: str) -> int: """A count that must be at least one, or the default with a word about it. diff --git a/packages/meshbay-node/tests/test_connection_processes.py b/packages/meshbay-node/tests/test_connection_processes.py index ab9dfd3..778da5a 100644 --- a/packages/meshbay-node/tests/test_connection_processes.py +++ b/packages/meshbay-node/tests/test_connection_processes.py @@ -13,6 +13,7 @@ not carry. """ import asyncio +import re import struct from pathlib import Path @@ -231,11 +232,21 @@ async def test_one_process_starts_none(): # ── the setting ───────────────────────────────────────────────────────────── -def test_the_default_is_one_process(tmp_path): - assert node_settings_defaults()["connection_processes"] == 1 +@pytest.mark.parametrize("cores, expected", [(1, 1), (2, 2), (4, 4), (28, 4)]) +def test_the_default_is_one_per_core_up_to_four(tmp_path, monkeypatch, cores, expected): + """It was 1, which starts none: the path was shipped and reached by no node + whose operator had not found the setting.""" + monkeypatch.setattr("meshbay_node.config.max_processes", lambda: cores) + assert node_settings_defaults()["connection_processes"] == expected conf = tmp_path / "node.toml" conf.write_text('[hub]\nurl = "https://example.invalid"\nusername = "op"\n') - assert load_config(conf).node.connection_processes == 1 + assert load_config(conf).node.connection_processes == expected + + +def test_a_new_node_toml_leaves_it_to_the_default(tmp_path): + from meshbay_node.config import EXAMPLE_CONFIG + assert not re.search(r"^connection_processes\s*=", EXAMPLE_CONFIG, re.M), ( + "a value written by `init` is a value no later default reaches") def test_node_toml_cannot_ask_for_more_than_the_cores(tmp_path): @@ -296,4 +307,5 @@ async def test_a_number_the_machine_cannot_honour_is_refused(state, monkeypatch, with pytest.raises(ops.OpError): await ops.set_node_settings(state, {"connection_processes": value}) assert state["webrtc"].asked == [] - assert (await ops.get_node_settings(state))["connection_processes"] == 1 + assert ((await ops.get_node_settings(state))["connection_processes"] + == state["config"].node.connection_processes) |