Skip to content

Commit

Permalink
feat: limit compile job concurrency (#3)
Browse files Browse the repository at this point in the history
chore: add pylint / black tests on PR
  • Loading branch information
rmoesbergen authored Sep 10, 2023
1 parent 4bc75ca commit f904941
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 9 deletions.
27 changes: 27 additions & 0 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: 'Run tests'

on:
pull_request:
branches:
- main

jobs:
run_tests:
name: Run Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Create venv
run: |
python3 -m venv venv
. ./venv/bin/activate
pip install -r requirements.txt
pip install black==23.7.0 pylint==2.17.5
- name: Run tests
run: |
set -e
. ./venv/bin/activate
black --check .
pylint *.py deps
5 changes: 5 additions & 0 deletions conf.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
""" Configuration settings """
from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
"""Settings class, loaded from .env or environment vars"""

model_config = SettingsConfigDict(env_file=".env")

arduino_cli_path: str = "arduino-cli"
Expand All @@ -15,6 +18,8 @@ class Settings(BaseSettings):
# Code cache settings
max_code_caches: int = 100
code_cache_duration: int = 3600
# Max number of concurrent compile tasks
max_concurrent_tasks: int = 10


settings = Settings()
1 change: 1 addition & 0 deletions deps/cache.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
""" Code for caching compiled C++ code """
from hashlib import md5

from cachetools import TTLCache
Expand Down
2 changes: 2 additions & 0 deletions deps/session.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
""" Manage session concurrency """
import uuid
from typing import Annotated

Expand All @@ -13,6 +14,7 @@
def get_session_id(
response: Response, session_id: Annotated[str | None, Cookie()] = None
):
"""Generate or get a consistent session ID for an anonymous user"""
if not session_id:
# First time user, create a new session
session_id = uuid.uuid4().hex
Expand Down
22 changes: 14 additions & 8 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
""" Leaphy compiler backend webservice """
import asyncio
import tempfile
from os import path
Expand All @@ -20,6 +21,9 @@
allow_headers=["*"],
)

# Limit compiler concurrency to prevent overloading the vm
semaphore = asyncio.Semaphore(settings.max_concurrent_tasks)


async def _install_libraries(libraries: list[Library]):
# Install required libraries
Expand All @@ -46,8 +50,8 @@ async def _compile_sketch(sketch: Sketch) -> dict[str, str]:
sketch_path = f"{dir_name}/{file_name}"

# Write the sketch to a temp .ino file
async with aiofiles.open(sketch_path, "w+") as f:
await f.write(sketch.source_code)
async with aiofiles.open(sketch_path, "w+") as _f:
await _f.write(sketch.source_code)

compiler = await asyncio.create_subprocess_exec(
settings.arduino_cli_path,
Expand All @@ -64,12 +68,13 @@ async def _compile_sketch(sketch: Sketch) -> dict[str, str]:
if compiler.returncode != 0:
raise HTTPException(500, stderr.decode() + stdout.decode())

async with aiofiles.open(f"{sketch_path}.hex", "r") as f:
return {"hex": await f.read()}
async with aiofiles.open(f"{sketch_path}.hex", "r") as _f:
return {"hex": await _f.read()}


@app.post("/compile/cpp")
async def compile_cpp(sketch: Sketch, session_id: Session) -> dict[str, str]:
"""Compile code and return the result in HEX format"""
# Make sure there's no more than X compile requests per user
sessions[session_id] += 1

Expand All @@ -81,9 +86,10 @@ async def compile_cpp(sketch: Sketch, session_id: Session) -> dict[str, str]:
return compiled_code

# Nope -> compile and store in cache
await _install_libraries(sketch.libraries)
result = await _compile_sketch(sketch)
code_cache[cache_key] = result
return result
async with semaphore:
await _install_libraries(sketch.libraries)
result = await _compile_sketch(sketch)
code_cache[cache_key] = result
return result
finally:
sessions[session_id] -= 1
4 changes: 3 additions & 1 deletion models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
""" FastAPI models """
from typing import Annotated

from pydantic import BaseModel, Field
Expand All @@ -7,7 +8,8 @@


class Sketch(BaseModel):
"""Model representing a arduino Sketch"""

source_code: str
# TODO: make this an enum with supported board types
board: str
libraries: list[Library] = []
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[tool.pylint.'MESSAGES CONTROL']
max-line-length = 120
disable = "too-few-public-methods, import-error"

0 comments on commit f904941

Please sign in to comment.