<feed xmlns='http://www.w3.org/2005/Atom'>
<title>meshbay.git/packages/meshbay-node/tests/test_lease_enforcement.py, branch 0.16</title>
<subtitle>MeshBay — read-only public mirror</subtitle>
<id>https://git.meshbay.org/meshbay.git/atom?h=0.16</id>
<link rel='self' href='https://git.meshbay.org/meshbay.git/atom?h=0.16'/>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/'/>
<updated>2026-09-18T13:59:24Z</updated>
<entry>
<title>fix(node): take the availability poll and every upload write off the loop</title>
<updated>2026-09-18T13:59:24Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-18T13:59:24Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=5fa158fab709d3d24a33318b3d910f75c051af2e'/>
<id>urn:sha1:5fa158fab709d3d24a33318b3d910f75c051af2e</id>
<content type='text'>
The rest of AV9's disk half. Serving a file left the loop in the commit before
this one; two paths were still on it.

**The availability poll.** `RootSet.refresh_availability` stats every root, and
eleven call sites reached it from `async def` — the reconcile loop among them, on
a timer. On a sleeping disk that is a stall once per tick, and the stat is also
what keeps the disk awake, so a node paid spin-up for a library nobody was
reading. All eleven now go through `off_disk`, `Root.is_live` included.

**The upload write.** `open`/`write`, and the resolve, the stat, the free-name
search, the rename and the unlink around it. This one could not simply be
awaited: the handler was synchronous, so nothing could come between the
`chunk_index != state.next_index` check and the `advance` that answers it, and
that is the whole of the chunk-ordering rule. Awaiting the write opens the gap —
chunk 1 arriving while chunk 0 is in the disk thread reads a position that has
not moved and is refused as out of order, so an upload would fail on a slow disk
and nowhere else. Verified, not assumed: without the lock the new ordering test
refuses three chunks of four.

So the check, the write and the advance are one critical section again, under a
lock held **per group**. Not per session: `partial_uploads` lives in the group
context so a reconnecting client finds its upload where it left it, which means
two sessions of one member share the position of one `.part` file. Arrival order
is preserved by construction — the dispatcher creates one task per message as it
arrives, tasks start in creation order, and the lock is the first thing each one
waits on, so its waiters queue in arrival order too.

`_do_file_upload` is a coroutine now, which is why forty-two test call sites gain
an `await`. Their outcomes are unchanged, file by file, against the run before
the change.

`test_ops.py` asked which public coroutines `ops` exposes and got `off_disk`,
imported rather than defined there. It now asks for the ones written in the
module, which is what its own docstring means; all forty-three operations are
still checked.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>fix(node): a transfer id names a lease, or it names nothing</title>
<updated>2026-09-13T14:06:57Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-13T14:06:57Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=6a3f927413d4fd44b708a306e5e053f6660ec357'/>
<id>urn:sha1:6a3f927413d4fd44b708a306e5e053f6660ec357</id>
<content type='text'>
`_do_file_request` read `tr` as a boolean. Present meant "this is a leased
transfer, skip the leaseless ceiling", and nothing asked whether this node had
ever granted such a lease — `slots.touch(tr)` was called beside it and its
answer, `False` if it is not granted, was discarded. So any non-empty string
bought the whole library with no ceiling of any kind: not the per-member cap,
not the node-wide one, not the leaseless bound that exists to bound a client
claiming to be browsing. The queue held only the clients that chose to wait.

`_lease_of` decides it now, and the three answers differ on purpose:

  - **granted**, and of *this* session — served, and touched so the sweeper
    does not reclaim a transfer that is plainly moving. The session is checked
    as well as the id, because touching another connection's lease refreshed
    its idle timer.
  - **queued** — refused with `lease_not_granted`, on the upload path too,
    before anything reaches the operator's disk. A member reading while queued
    is the cap not applying.
  - **unknown** — bounded by the leaseless ceiling rather than refused. That is
    also what a reconnect looks like from here, where the session's leases died
    with the old connection and the client is re-opening them, and it leaves
    the residual §5.5 already states: a client that lies gets that bound's
    worth of files at a time, not the group. Noted once per connection so the
    residual is visible rather than merely documented.

Nothing changes for the shipped client: the transfer store awaits
`lease.acquire()` before it reads a byte, so the refused case is one it never
enters. §5.5 gains a paragraph saying the node decides which of the two a
request is — the document described the accounting without ever saying it was
enforced, which is how it came not to be.

`test_lease_enforcement.py` drives the real handlers over a real index; six of
its nine cases fail against the previous source, each on the property.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01UMxEQadpzPkYLFf5CYKhpW
</content>
</entry>
</feed>
