summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/src/meshbay_node/transfers.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-node/src/meshbay_node/transfers.py')
-rw-r--r--packages/meshbay-node/src/meshbay_node/transfers.py34
1 files changed, 32 insertions, 2 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/transfers.py b/packages/meshbay-node/src/meshbay_node/transfers.py
index 0689ec8..533e985 100644
--- a/packages/meshbay-node/src/meshbay_node/transfers.py
+++ b/packages/meshbay-node/src/meshbay_node/transfers.py
@@ -104,8 +104,13 @@ class TransferSlots:
caps: dict[str, int] = field(
default_factory=lambda: {k: DEFAULT_MAX_CONCURRENT for k in KINDS})
+ # The node-wide default per member, per kind.
per_member: dict[str, int] = field(
default_factory=lambda: {k: DEFAULT_MAX_PER_MEMBER for k in KINDS})
+ # Per-group overrides: {group_id: {kind: n}}. The cap is a group's setting
+ # (its operator signs it), while the pools are the machine's — so this is
+ # the one dimension that is not node-wide, and a lookup rather than a field.
+ group_limits: dict[str, dict[str, int]] = field(default_factory=dict)
leases: dict[str, Lease] = field(default_factory=dict)
# FIFO of `tr`, per kind. Order is arrival; a member at their own cap is
# skipped rather than blocking the head, or one member's limit would stall
@@ -135,10 +140,22 @@ class TransferSlots:
except ValueError:
return 0
+ def member_cap(self, kind: str, member: tuple[str, str]) -> int:
+ """This member's cap in this group: the group's own, else the default.
+
+ Absent means the default, never "unlimited" — a group that predates the
+ setting coming back unlimited would leave the node-wide cap as the only
+ control, which is the situation slots exist to end.
+ """
+ group_id = member[0]
+ override = self.group_limits.get(group_id, {}).get(kind)
+ if override is not None:
+ return int(override)
+ return self.per_member.get(kind, DEFAULT_MAX_PER_MEMBER)
+
def _has_room(self, kind: str, member: tuple[str, str]) -> bool:
# Per-member first: see the module docstring.
- if self.member_in_use(kind, member) >= self.per_member.get(
- kind, DEFAULT_MAX_PER_MEMBER):
+ if self.member_in_use(kind, member) >= self.member_cap(kind, member):
return False
return self.in_use(kind) < self.caps.get(kind, DEFAULT_MAX_CONCURRENT)
@@ -292,6 +309,19 @@ class TransferSlots:
# ── what the operator sees ──────────────────────────────────────────────
+ def set_group_limits(self, group_id: str, limits: dict[str, int],
+ now: float | None = None) -> list[Lease]:
+ """One group's per-member caps, as its operator signed them."""
+ current = dict(self.group_limits.get(group_id, {}))
+ for kind, value in limits.items():
+ if kind in KINDS:
+ current[kind] = max(1, int(value))
+ self.group_limits[group_id] = current
+ granted: list[Lease] = []
+ for kind in KINDS:
+ granted.extend(self._pump(kind, now))
+ return granted
+
def set_caps(self, *, node: dict[str, int] | None = None,
per_member: dict[str, int] | None = None,
now: float | None = None) -> list[Lease]: