Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -452,8 +452,6 @@ async def _provision_gateway_replica(
replica_model: GatewayReplicaModel,
) -> _GatewayReplicaUpdateMap:
try:
if replica_model.backend_id is None: # unexpected
raise BackendNotAvailable()
(_, backend) = await backends_services.get_project_backend_with_model_by_id_or_error(
project=gateway_model.project, backend_id=replica_model.backend_id
)
Expand Down Expand Up @@ -614,8 +612,6 @@ async def _register_replica_with_load_balancer(
if replica_model.instance_id is None:
return "instance_id is None, cannot register with load balancer"
try:
if replica_model.backend_id is None:
raise BackendNotAvailable()
(_, backend) = await backends_services.get_project_backend_with_model_by_id_or_error(
project=gateway_model.project, backend_id=replica_model.backend_id
)
Expand Down Expand Up @@ -1565,8 +1561,6 @@ async def _process_terminating_item(item: GatewayReplicaPipelineItem):
status=GatewayReplicaStatus.TERMINATED, active=False, deleted=True
)
try:
if replica_model.backend_id is None: # unexpected
raise BackendNotAvailable()
(_, backend) = await backends_services.get_project_backend_with_model_by_id_or_error(
project=gateway_model.project,
backend_id=replica_model.backend_id,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"""Require GatewayReplicaModel.backend_id

Revision ID: eee3e79f29e9
Revises: 04126c7ea0c8
Create Date: 2026-08-18 10:51:19.731160+00:00

"""

import sqlalchemy as sa
import sqlalchemy_utils
from alembic import op

# revision identifiers, used by Alembic.
revision = "eee3e79f29e9"
down_revision = "04126c7ea0c8"
branch_labels = None
depends_on = None

# partial definition for queries
gateway_computes = sa.table(
"gateway_computes",
sa.column("id"),
sa.column("backend_id"),
)


def upgrade() -> None:
# Gateway computes with backend_id=None were only possible in dstack Sky and were already
# removed from all significant environments before this migration. So this cleanup query is
# expected to be a NOOP in most cases, except for, possibly, some local dev databases.
op.execute(sa.delete(gateway_computes).where(gateway_computes.c.backend_id.is_(None)))

with op.batch_alter_table("gateway_computes", schema=None) as batch_op:
batch_op.alter_column(
"backend_id",
existing_type=sqlalchemy_utils.types.uuid.UUIDType(binary=False),
nullable=False,
)


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("gateway_computes", schema=None) as batch_op:
batch_op.alter_column(
"backend_id",
existing_type=sqlalchemy_utils.types.uuid.UUIDType(binary=False),
nullable=True,
)

# ### end Alembic commands ###
6 changes: 2 additions & 4 deletions src/dstack/_internal/server/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -741,10 +741,8 @@ class GatewayReplicaModel(PipelineModelMixin, BaseModel):
Use `gateway or legacy_gateway` to get the gateway regardless of version.
"""

backend_id: Mapped[Optional[uuid.UUID]] = mapped_column(
ForeignKey("backends.id", ondelete="CASCADE")
)
backend: Mapped[Optional["BackendModel"]] = relationship()
backend_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("backends.id", ondelete="CASCADE"))
backend: Mapped["BackendModel"] = relationship()

ssh_private_key: Mapped[str] = mapped_column(Text)
"""`ssh_private_key` is the key used to authorize the server with the gateway."""
Expand Down
4 changes: 2 additions & 2 deletions src/dstack/_internal/server/services/gateways/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -948,7 +948,7 @@ def gateway_model_to_gateway(
all_replica_models = sorted(
get_gateway_replica_models(gateway_model), key=lambda r: r.replica_num
)
relevant_replica_models = []
relevant_replica_models: list[GatewayReplicaModel] = []
for replica_num, replica_models_for_num in itertools.groupby(
all_replica_models, key=lambda r: r.replica_num
):
Expand All @@ -959,7 +959,7 @@ def gateway_model_to_gateway(
GatewayReplica(
hostname=replica_model.ip_address,
replica_num=replica_model.replica_num,
backend=replica_model.backend.type if replica_model.backend else None,
backend=replica_model.backend.type,
region=replica_model.region,
created_at=replica_model.created_at,
status=replica_model.status,
Expand Down
11 changes: 3 additions & 8 deletions src/dstack/_internal/server/testing/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -711,8 +711,8 @@ async def create_gateway(

async def create_gateway_replica(
session: AsyncSession,
backend: BackendModel,
gateway_id: Optional[UUID] = None,
backend_id: Optional[UUID] = None,
ip_address: Optional[str] = "1.1.1.1",
region: Optional[str] = "us",
instance_id: Optional[str] = "i-1234567890",
Expand All @@ -734,24 +734,19 @@ async def create_gateway_replica(
testing against both in major test cases.
"""
if configuration is None and populate_configuration:
backend_type = BackendType.AWS
if backend_id is not None:
backend = await session.get(BackendModel, backend_id)
assert backend is not None
backend_type = backend.type
assert region is not None
configuration = GatewayReplicaConfiguration(
project_name="test-project",
instance_name=instance_id or "test-instance",
backend=backend_type,
backend=backend.type,
region=region,
public_ip=True,
ssh_key_pub=ssh_public_key,
certificate=None,
).model_dump_json()
gateway_replica = GatewayReplicaModel(
gateway_id=gateway_id,
backend_id=backend_id,
backend_id=backend.id,
ip_address=ip_address,
region=region,
instance_id=instance_id,
Expand Down
Loading
Loading