Skip to content

Commit 3d8ab62

Browse files
authored
Make local code upload size limit configurable (#2673)
1 parent 07e1456 commit 3d8ab62

5 files changed

Lines changed: 24 additions & 20 deletions

File tree

docs/docs/reference/environment-variables.md

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ tasks, and services:
1212
```yaml
1313
type: task
1414
name: vscode
15-
15+
1616
commands:
1717
- echo $DSTACK_RUN_NAME
1818
```
@@ -25,11 +25,11 @@ tasks, and services:
2525
- `DSTACK_GPUS_NUM`{ #DSTACK_GPUS_NUM } – The total number of GPUs in the run.
2626

2727
Example:
28-
28+
2929
```yaml
3030
type: service
3131
name: llama31
32-
32+
3333
env:
3434
- HF_TOKEN
3535
commands:
@@ -39,7 +39,7 @@ tasks, and services:
3939
--tensor-parallel-size $DSTACK_GPUS_NUM
4040
port: 8000
4141
model: meta-llama/Meta-Llama-3.1-8B-Instruct
42-
42+
4343
resources:
4444
gpu: 24GB
4545
```
@@ -51,7 +51,7 @@ tasks, and services:
5151

5252
Below is an example of using `DSTACK_NODES_NUM`, `DSTACK_GPUS_PER_NODE`, `DSTACK_NODE_RANK`, and `DSTACK_MASTER_NODE_IP`
5353
for distributed training:
54-
54+
5555
```yaml
5656
type: task
5757
name: train-distrib
@@ -83,18 +83,18 @@ tasks, and services:
8383
The following environment variables are supported by the `dstack` server and can be specified whether the server is run
8484
via `dstack server` or deployed using Docker.
8585

86-
For more details on the options below, refer to the [server deployment](../guides/server-deployment.md) guide.
86+
For more details on the options below, refer to the [server deployment](../guides/server-deployment.md) guide.
8787

8888
- `DSTACK_SERVER_LOG_LEVEL`{ #DSTACK_SERVER_LOG_LEVEL } – Has the same effect as `--log-level`. Defaults to `INFO`.
8989

9090
Example:
91-
91+
9292
<div class="termy">
93-
93+
9494
```shell
9595
$ DSTACK_SERVER_LOG_LEVEL=debug dstack server
9696
```
97-
97+
9898
</div>
9999

100100
- `DSTACK_SERVER_LOG_FORMAT`{ #DSTACK_SERVER_LOG_FORMAT } – Sets format of log output. Can be `rich`, `standard`, `json`. Defaults to `rich`.
@@ -110,9 +110,10 @@ For more details on the options below, refer to the [server deployment](../guide
110110
- `DSTACK_ENABLE_PROMETHEUS_METRICS`{ #DSTACK_ENABLE_PROMETHEUS_METRICS } — Enables Prometheus metrics collection and export.
111111
- `DSTACK_DEFAULT_SERVICE_CLIENT_MAX_BODY_SIZE`{ #DSTACK_DEFAULT_SERVICE_CLIENT_MAX_BODY_SIZE } – Request body size limit for services running with a gateway, in bytes. Defaults to 64 MiB.
112112
- `DSTACK_FORBID_SERVICES_WITHOUT_GATEWAY`{ #DSTACK_FORBID_SERVICES_WITHOUT_GATEWAY } – Forbids registering new services without a gateway if set to any value.
113+
- `DSTACK_SERVER_CODE_UPLOAD_LIMIT`{ #DSTACK_SERVER_CODE_UPLOAD_LIMIT } - The repo size limit when uploading diffs or local repos, in bytes. Set to 0 to disable size limits. Defaults to 2MiB.
113114

114115
??? info "Internal environment variables"
115-
The following environment variables are intended for development purposes:
116+
The following environment variables are intended for development purposes:
116117

117118
* `DSTACK_SERVER_ROOT_LOG_LEVEL` – Sets root logger log level. Defaults to `ERROR`.
118119
* `DSTACK_SERVER_UVICORN_LOG_LEVEL` – Sets uvicorn logger log level. Defaults to `ERROR`.

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ dependencies = [
3434
"psutil",
3535
"gpuhunt==0.1.5",
3636
"argcomplete>=3.5.0",
37+
"humanize>=4.12.3",
3738
]
3839

3940
[project.urls]

src/dstack/_internal/server/routers/repos.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from typing import List, Tuple
22

33
from fastapi import APIRouter, Depends, Request, UploadFile
4+
from humanize import naturalsize
45
from sqlalchemy.ext.asyncio import AsyncSession
56

67
from dstack._internal.core.errors import ResourceNotExistsError, ServerClientError
@@ -14,9 +15,10 @@
1415
)
1516
from dstack._internal.server.security.permissions import ProjectMember
1617
from dstack._internal.server.services import repos
18+
from dstack._internal.server.settings import SERVER_CODE_UPLOAD_LIMIT
1719
from dstack._internal.server.utils.routers import (
1820
get_base_api_additional_responses,
19-
request_size_exceeded,
21+
get_request_size,
2022
)
2123

2224
router = APIRouter(
@@ -94,10 +96,12 @@ async def upload_code(
9496
session: AsyncSession = Depends(get_session),
9597
user_project: Tuple[UserModel, ProjectModel] = Depends(ProjectMember()),
9698
):
97-
if request_size_exceeded(request, limit=2 * 2**20):
99+
request_size = get_request_size(request)
100+
if SERVER_CODE_UPLOAD_LIMIT > 0 and request_size > SERVER_CODE_UPLOAD_LIMIT:
98101
raise ServerClientError(
99-
"Repo diff size exceeds the limit of 2MB. "
100-
"Use .gitignore to exclude large files from the repo."
102+
f"Repo diff size is {naturalsize(request_size)}, which exceeds the limit of "
103+
f"{naturalsize(SERVER_CODE_UPLOAD_LIMIT)}. Use .gitignore to exclude large files from the repo. This "
104+
f"limit can be modified by setting the DSTACK_SERVER_CODE_UPLOAD_LIMIT_BYTES environment variable"
101105
)
102106
_, project = user_project
103107
await repos.upload_code(

src/dstack/_internal/server/settings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
USER_PROJECT_DEFAULT_QUOTA = int(os.getenv("DSTACK_USER_PROJECT_DEFAULT_QUOTA", 10))
8686
FORBID_SERVICES_WITHOUT_GATEWAY = os.getenv("DSTACK_FORBID_SERVICES_WITHOUT_GATEWAY") is not None
8787

88+
SERVER_CODE_UPLOAD_LIMIT = int(os.getenv("DSTACK_SERVER_CODE_UPLOAD_LIMIT", 2 * 2**20))
8889

8990
# Development settings
9091

src/dstack/_internal/server/utils/routers.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,10 @@ def get_server_client_error_details(error: ServerClientError) -> List[Dict]:
9393
return details
9494

9595

96-
def request_size_exceeded(request: Request, limit: int) -> bool:
96+
def get_request_size(request: Request) -> int:
9797
if "content-length" not in request.headers:
98-
return True
99-
content_length = int(request.headers["content-length"])
100-
if content_length > limit:
101-
return True
102-
return False
98+
return 0
99+
return int(request.headers["content-length"])
103100

104101

105102
def check_client_server_compatibility(

0 commit comments

Comments
 (0)