Skip to content

Commit bc49250

Browse files
perf: don't use 2 threads to start FastAPI server
1 parent 7ccb07d commit bc49250

File tree

2 files changed

+46
-10
lines changed

2 files changed

+46
-10
lines changed

bittensor/core/axon.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -142,14 +142,6 @@ def run_in_thread(self):
142142
self.should_exit = True
143143
thread.join()
144144

145-
def _wrapper_run(self):
146-
"""
147-
A wrapper method for the :func:`run_in_thread` context manager. This method is used internally by the ``start`` method to initiate the server's execution in a separate thread.
148-
"""
149-
with self.run_in_thread():
150-
while not self.should_exit:
151-
time.sleep(1e-3)
152-
153145
def start(self):
154146
"""
155147
Starts the FastAPI server in a separate thread if it is not already running. This method sets up the server to handle HTTP requests concurrently, enabling the Axon server to efficiently manage incoming network requests.
@@ -158,7 +150,7 @@ def start(self):
158150
"""
159151
if not self.is_running:
160152
self.should_exit = False
161-
thread = threading.Thread(target=self._wrapper_run, daemon=True)
153+
thread = threading.Thread(target=self.run, daemon=True)
162154
thread.start()
163155
self.is_running = True
164156

tests/unit_tests/test_axon.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,23 @@
1717

1818

1919
import re
20+
import threading
2021
import time
2122
from dataclasses import dataclass
2223
from typing import Any, Optional, Tuple
2324
from unittest import IsolatedAsyncioTestCase
2425
from unittest.mock import AsyncMock, MagicMock, patch
2526

2627
import fastapi
28+
import httpx
2729
import netaddr
2830
import pydantic
2931
import pytest
32+
import uvicorn
3033
from fastapi.testclient import TestClient
3134
from starlette.requests import Request
3235

33-
from bittensor.core.axon import AxonMiddleware, Axon
36+
from bittensor.core.axon import Axon, AxonMiddleware, FastAPIThreadedServer
3437
from bittensor.core.errors import RunException
3538
from bittensor.core.settings import version_as_int
3639
from bittensor.core.stream import StreamingSynapse
@@ -785,3 +788,44 @@ async def forward_fn(synapse: streaming_synapse_cls):
785788
"computed_body_hash": "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a",
786789
},
787790
)
791+
792+
793+
def test_threaded_fastapi():
794+
server_started = threading.Event()
795+
server_stopped = threading.Event()
796+
797+
async def lifespan(app):
798+
server_started.set()
799+
yield
800+
server_stopped.set()
801+
802+
app = fastapi.FastAPI(
803+
lifespan=lifespan,
804+
)
805+
app.get("/")(lambda: "Hello World")
806+
807+
server = FastAPIThreadedServer(
808+
uvicorn.Config(
809+
app,
810+
),
811+
)
812+
server.start()
813+
814+
server_started.wait()
815+
816+
assert server.is_running is True
817+
818+
client = httpx.Client(
819+
base_url="http://127.0.0.1:8000",
820+
)
821+
822+
assert client.get("/").text == '"Hello World"'
823+
824+
server.stop()
825+
826+
assert server.should_exit is True
827+
828+
server_stopped.wait()
829+
830+
with pytest.raises(httpx.ConnectError):
831+
client.get("/")

0 commit comments

Comments
 (0)