|
7 | 7 | from sqlalchemy.orm import joinedload
|
8 | 8 |
|
9 | 9 | from dstack._internal.core.models.backends.base import BackendType
|
| 10 | +from dstack._internal.core.models.configurations import TaskConfiguration |
10 | 11 | from dstack._internal.core.models.instances import (
|
11 | 12 | InstanceAvailability,
|
12 | 13 | InstanceOfferWithAvailability,
|
@@ -536,6 +537,99 @@ async def test_assigns_job_to_shared_instance(self, test_db, session: AsyncSessi
|
536 | 537 | assert instance.total_blocks == 4
|
537 | 538 | assert instance.busy_blocks == 2
|
538 | 539 |
|
| 540 | + @pytest.mark.asyncio |
| 541 | + @pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True) |
| 542 | + async def test_assigns_multi_node_job_to_shared_instance(self, test_db, session: AsyncSession): |
| 543 | + project = await create_project(session) |
| 544 | + user = await create_user(session) |
| 545 | + repo = await create_repo( |
| 546 | + session=session, |
| 547 | + project_id=project.id, |
| 548 | + ) |
| 549 | + offer = get_instance_offer_with_availability(gpu_count=8, cpu_count=64, memory_gib=128) |
| 550 | + instance = await create_instance( |
| 551 | + session=session, |
| 552 | + project=project, |
| 553 | + status=InstanceStatus.IDLE, |
| 554 | + backend=BackendType.AWS, |
| 555 | + offer=offer, |
| 556 | + total_blocks=4, |
| 557 | + busy_blocks=0, |
| 558 | + ) |
| 559 | + configuration = TaskConfiguration(image="debian", nodes=2) |
| 560 | + run_spec = get_run_spec(run_name="run", repo_id=repo.name, configuration=configuration) |
| 561 | + run = await create_run( |
| 562 | + session=session, |
| 563 | + run_name="run", |
| 564 | + project=project, |
| 565 | + repo=repo, |
| 566 | + user=user, |
| 567 | + run_spec=run_spec, |
| 568 | + ) |
| 569 | + job = await create_job( |
| 570 | + session=session, |
| 571 | + run=run, |
| 572 | + instance_assigned=False, |
| 573 | + ) |
| 574 | + await process_submitted_jobs() |
| 575 | + await session.refresh(job) |
| 576 | + await session.refresh(instance) |
| 577 | + res = await session.execute(select(JobModel).options(joinedload(JobModel.instance))) |
| 578 | + job = res.unique().scalar_one() |
| 579 | + assert job.status == JobStatus.SUBMITTED |
| 580 | + assert job.instance_assigned |
| 581 | + assert job.instance is not None |
| 582 | + assert job.instance.id == instance.id |
| 583 | + assert instance.total_blocks == 4 |
| 584 | + assert instance.busy_blocks == 4 |
| 585 | + |
| 586 | + @pytest.mark.asyncio |
| 587 | + @pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True) |
| 588 | + async def test_cannot_assign_multi_node_job_to_partially_busy_shared_instance( |
| 589 | + self, test_db, session: AsyncSession |
| 590 | + ): |
| 591 | + project = await create_project(session) |
| 592 | + user = await create_user(session) |
| 593 | + repo = await create_repo( |
| 594 | + session=session, |
| 595 | + project_id=project.id, |
| 596 | + ) |
| 597 | + offer = get_instance_offer_with_availability(gpu_count=8, cpu_count=64, memory_gib=128) |
| 598 | + instance = await create_instance( |
| 599 | + session=session, |
| 600 | + project=project, |
| 601 | + status=InstanceStatus.IDLE, |
| 602 | + backend=BackendType.AWS, |
| 603 | + offer=offer, |
| 604 | + total_blocks=4, |
| 605 | + busy_blocks=1, |
| 606 | + ) |
| 607 | + configuration = TaskConfiguration(image="debian", nodes=2) |
| 608 | + run_spec = get_run_spec(run_name="run", repo_id=repo.name, configuration=configuration) |
| 609 | + run = await create_run( |
| 610 | + session=session, |
| 611 | + run_name="run", |
| 612 | + project=project, |
| 613 | + repo=repo, |
| 614 | + user=user, |
| 615 | + run_spec=run_spec, |
| 616 | + ) |
| 617 | + job = await create_job( |
| 618 | + session=session, |
| 619 | + run=run, |
| 620 | + instance_assigned=False, |
| 621 | + ) |
| 622 | + await process_submitted_jobs() |
| 623 | + await session.refresh(job) |
| 624 | + await session.refresh(instance) |
| 625 | + res = await session.execute(select(JobModel).options(joinedload(JobModel.instance))) |
| 626 | + job = res.unique().scalar_one() |
| 627 | + assert job.status == JobStatus.SUBMITTED |
| 628 | + assert job.instance_assigned |
| 629 | + assert job.instance is None |
| 630 | + assert instance.total_blocks == 4 |
| 631 | + assert instance.busy_blocks == 1 |
| 632 | + |
539 | 633 | @pytest.mark.asyncio
|
540 | 634 | @pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
541 | 635 | async def test_assigns_job_to_specific_fleet(self, test_db, session: AsyncSession):
|
|
0 commit comments