Skip to content

Commit

Permalink
reorg checkboxes, add load testing (#929)
Browse files Browse the repository at this point in the history
* reorg checkboxes, add load testing

* integrate with CI

* back to 10k checkboxes

* remove stray load test files from WIP

* module-style, not script-style

* relative import of constants

* ignore locustfile in pytest

* absolute import for constants in locustfile
  • Loading branch information
charlesfrye authored Oct 17, 2024
1 parent d31377e commit a0c804c
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 5 deletions.
95 changes: 95 additions & 0 deletions 07_web_endpoints/fasthtml-checkboxes/checkboxes-load-test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import os
from datetime import datetime
from pathlib import Path

import modal

if modal.is_local():
workspace = modal.config._profile
else:
workspace = os.environ["MODAL_WORKSPACE"]


image = (
modal.Image.debian_slim(python_version="3.12")
.pip_install("locust~=2.29.1")
.env({"MODAL_WORKSPACE": workspace})
.copy_local_file(
Path(__file__).parent / "checkboxes-locustfile.py",
remote_path="/root/locustfile.py",
)
.copy_local_file(
Path(__file__).parent / "constants.py",
remote_path="/root/constants.py",
)
)
volume = modal.Volume.from_name(
"loadtest-checkboxes-results", create_if_missing=True
)
remote_path = Path("/root") / "loadtests"
OUT_DIRECTORY = (
remote_path / datetime.utcnow().replace(microsecond=0).isoformat()
)

app = modal.App("loadtest-checkbox", image=image, volumes={remote_path: volume})

workers = 8
host = f"https://{workspace}--example-checkboxes-web.modal.run"
csv_file = OUT_DIRECTORY / "stats.csv"
default_args = [
"-H",
host,
"--processes",
str(workers),
"--csv",
csv_file,
]

MINUTES = 60 # seconds


@app.function(allow_concurrent_inputs=1000, cpu=workers)
@modal.web_server(port=8089)
def serve():
run_locust.local(default_args)


@app.function(cpu=workers, timeout=60 * MINUTES)
def run_locust(args: list, wait=False):
import subprocess

process = subprocess.Popen(["locust"] + args)
if wait:
process.wait()
return process.returncode


@app.local_entrypoint()
def main(
r: float = 1.0,
u: int = 36,
t: str = "1m", # no more than the timeout of run_locust, one hour
):
args = default_args + [
"--spawn-rate",
str(r),
"--users",
str(u),
"--run-time",
t,
]

html_report_file = OUT_DIRECTORY / "report.html"
args += [
"--headless", # run without browser UI
"--autostart", # start test immediately
"--autoquit", # stop once finished...
"10", # ...but wait ten seconds
"--html", # output an HTML-formatted report
html_report_file, # to this location
]

if exit_code := run_locust.remote(args, wait=True):
SystemExit(exit_code)
else:
print("finished successfully")
50 changes: 50 additions & 0 deletions 07_web_endpoints/fasthtml-checkboxes/checkboxes-locustfile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# ---
# lambda-test: false
# pytest: false
# ---
import random

from constants import N_CHECKBOXES
from locust import HttpUser, between, task


class CheckboxesUser(HttpUser):
wait_time = between(0.01, 0.1) # Simulates a wait time between requests

def load_homepage(self):
"""
Simulates a user loading the homepage and fetching the state of the checkboxes.
"""
self.client.get("/")

@task(10)
def toggle_random_checkboxes(self):
"""
Simulates a user toggling a random checkbox.
"""
n_checkboxes = random.binomialvariate( # approximately poisson at 5
n=100,
p=0.1,
)
for _ in range(min(n_checkboxes, 1)):
checkbox_id = int(
N_CHECKBOXES * random.random() ** 2
) # Choose a random checkbox between 0 and 9999, more likely to be closer to 0
self.client.post(
f"/checkbox/toggle/{checkbox_id}/{self.id}",
name="/checkbox/toggle",
)

@task(1)
def poll_for_diffs(self):
"""
Simulates a user polling for any outstanding diffs.
"""
self.client.get(f"/diffs/{self.id}", name="/diffs")

def on_start(self):
"""
Called when a simulated user starts, typically used to initialize or login a user.
"""
self.id = str(random.randint(1, 9999))
self.load_homepage()
4 changes: 4 additions & 0 deletions 07_web_endpoints/fasthtml-checkboxes/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# ---
# lambda-test: false
# ---
N_CHECKBOXES = 10_000 # feel free to increase, if you dare!
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ---
# deploy: true
# cmd: ["modal", "serve", "07_web_endpoints/fasthtml_checkboxes.py"]
# cmd: ["modal", "serve", "07_web_endpoints.fasthtml-checkboxes.fasthtml_checkboxes"]
# mypy: ignore-errors
# ---

Expand All @@ -24,13 +24,13 @@

import modal

from .constants import N_CHECKBOXES

app = modal.App("example-checkboxes")
db = modal.Dict.from_name("example-checkboxes-db", create_if_missing=True)

css_path_local = Path(__file__).parent / "fasthtml_checkboxes.css"
css_path_remote = Path("/assets/fasthtml_checkboxes.css")

N_CHECKBOXES = 10_000 # feel free to increase, if you dare!
css_path_local = Path(__file__).parent / "styles.css"
css_path_remote = Path("/assets/styles.css")


@app.function(
Expand Down
File renamed without changes.

0 comments on commit a0c804c

Please sign in to comment.