CourseModel Context Protocol · Module 9: Production and Ecosystem · part 66 of 83
Part 66 · Module 9: Production and Ecosystem

Topic 9: Module 9 lab

4 min read·22 Sept 2026

The lab runs the whole module as one production rehearsal: two replicas with a shared requestState key, a rate limit, checkpointed imports, and a sticky balancer in front. It exercises every part and prints what it observed.

python
"""Module 9 lab: run the notes server as a small production fleet and exercise it end to end.

Two replicas with a shared requestState key, a per-client rate limit, and checkpointed
imports, behind a sticky round-robin balancer. Every step prints what it observed.
"""
from __future__ import annotations

import secrets
import shutil
import signal
from pathlib import Path

import anyio
import httpx2

from examples.m09_eras import one_call
from examples.m09_fleet import fleet
from examples.m09_import_client import main as import_notes
from examples.m09_loadtest import HEADERS, body
from examples.m09_registry_check import check

WORK = Path("/tmp/m09-lab")


async def burst(url: str, count: int) -> dict[int, int]:
    statuses: dict[int, int] = {}
    async with httpx2.AsyncClient() as http:
        for n in range(count):
            status = (await http.post(url, json=body(n), headers=HEADERS)).status_code
            statuses[status] = statuses.get(status, 0) + 1
    return statuses


async def lab(f) -> None:
    print("1. health")
    async with httpx2.AsyncClient() as http:
        for url in f.replica_urls:
            print("  ", (await http.get(url + "/health")).json())

    print("2. both protocol eras through the balancer")
    for mode in ("auto", "legacy"):
        print("  ", await one_call(f.url, mode))

    print("3. multi-round-trip import across replicas, then the same key again")
    await import_notes(f.url, "lab-batch-0001", 3)
    await import_notes(f.url, "lab-batch-0001", 3)

    print("4. burst of 40 calls from one client (limit: 10 per replica, refill 2/s)")
    await anyio.sleep(5)  # let the buckets refill after the steps above
    print("   statuses:", await burst(f.url, 40))

    print("5. SIGTERM replica-b, then call again")
    f.processes["replica-b"].send_signal(signal.SIGTERM)
    f.processes["replica-b"].wait(timeout=30)
    await anyio.sleep(5)
    for mode in ("auto", "legacy"):
        print("  ", await one_call(f.url, mode))

    print("6. registry metadata")
    problems = check(Path("registry/server.json"))
    print("   server.json:", "valid" if not problems else problems)


def main() -> None:
    shutil.rmtree(WORK, ignore_errors=True)
    shutil.copytree("notes", WORK / "notes")
    env = {"NOTES_DIR": str(WORK / "notes"), "NOTES_CHECKPOINT_DIR": str(WORK / "checkpoints"),
           "NOTES_REQUEST_STATE_KEY": secrets.token_hex(32), "NOTES_RATE_CAPACITY": "10", "NOTES_RATE_REFILL": "2"}
    with fleet(replicas=2, sticky=True, env=env) as f:
        anyio.run(lab, f)
        print("\nbalancer warnings:")
        print("\n".join(line for line in f.log("balancer").splitlines() if "refused" in line) or "   (none)")


if __name__ == "__main__":
    main()

Code explained

  • In simple words: a fire drill for the fleet: check health, serve old and new clients, run a confirmed import twice, flood it, kill a replica, and check the registry file.

The rest of this course is yours to keep

This course is bought on its own, once, and stays readable afterwards, including the parts added to it later.