Skip to content

Commit 4f6aad6

Browse files
committed
iops split for PITR branches: 25% to WAL and 75% to DATA
1 parent f31cb27 commit 4f6aad6

2 files changed

Lines changed: 55 additions & 10 deletions

File tree

src/api/organization/project/branch/__init__.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from ....._util.crypto import encrypt_with_passphrase, generate_keys
2626
from .....database import AsyncSessionLocal, SessionDep
2727
from .....deployment import (
28+
WAL_IOPS_FRACTION,
2829
DeploymentParameters,
2930
ResizeParameters,
3031
branch_api_domain,
@@ -754,7 +755,8 @@ async def _clone_branch_environment_task(
754755
storage_class_name: str | None = None
755756
if copy_data:
756757
try:
757-
storage_class_name = await ensure_branch_storage_class(branch_id, iops=parameters.iops)
758+
data_iops = max(1, round(parameters.iops * (1 - WAL_IOPS_FRACTION))) if pitr_enabled else parameters.iops
759+
storage_class_name = await ensure_branch_storage_class(branch_id, iops=data_iops)
758760
await clone_branch_database_volume(
759761
source_branch_id=source_branch_id,
760762
target_branch_id=branch_id,
@@ -829,7 +831,8 @@ async def _restore_branch_environment_task(
829831
await _persist_branch_status(branch_id, BranchServiceStatus.CREATING)
830832
storage_class_name: str | None = None
831833
try:
832-
storage_class_name = await ensure_branch_storage_class(branch_id, iops=parameters.iops)
834+
data_iops = max(1, round(parameters.iops * (1 - WAL_IOPS_FRACTION))) if pitr_enabled else parameters.iops
835+
storage_class_name = await ensure_branch_storage_class(branch_id, iops=data_iops)
833836
await restore_branch_database_volume_from_snapshot(
834837
source_branch_id=source_branch_id,
835838
target_branch_id=branch_id,
@@ -894,6 +897,7 @@ async def _restore_branch_environment_in_place_task(
894897
snapshot_namespace: str,
895898
snapshot_name: str,
896899
snapshot_content_name: str | None,
900+
pitr_enabled: bool,
897901
recovery_target_time: datetime | None = None,
898902
) -> None:
899903
await _persist_branch_status(branch_id, BranchServiceStatus.RESTARTING)
@@ -910,7 +914,8 @@ async def _restore_branch_environment_in_place_task(
910914
return
911915

912916
try:
913-
storage_class_name = await ensure_branch_storage_class(branch_id, iops=parameters.iops)
917+
data_iops = max(1, round(parameters.iops * (1 - WAL_IOPS_FRACTION))) if pitr_enabled else parameters.iops
918+
storage_class_name = await ensure_branch_storage_class(branch_id, iops=data_iops)
914919
await restore_branch_database_volume_from_snapshot(
915920
source_branch_id=source_branch_id,
916921
target_branch_id=branch_id,
@@ -1600,6 +1605,7 @@ async def restore(
16001605
snapshot_namespace=snapshot_namespace,
16011606
snapshot_name=snapshot_name,
16021607
snapshot_content_name=snapshot_content_name,
1608+
pitr_enabled=branch.pitr_enabled,
16031609
recovery_target_time=restore_target if isinstance(restore_target, datetime) else None,
16041610
)
16051611
)

src/deployment/__init__.py

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
AUTOSCALER_PVC_SUFFIX = "-block-data"
8080
AUTOSCALER_WAL_PVC_SUFFIX = "-pg-wal"
8181
PITR_WAL_PVC_SIZE = "100Gi"
82+
WAL_IOPS_FRACTION = 0.25
8283
_LOAD_BALANCER_TIMEOUT_SECONDS = float(600)
8384
_LOAD_BALANCER_POLL_INTERVAL_SECONDS = float(2)
8485
_OVERLAY_IP_TIMEOUT_SECONDS = float(300)
@@ -356,13 +357,39 @@ async def update_branch_volume_iops(branch_id: Identifier, iops: int) -> None:
356357
namespace = deployment_namespace(branch_id)
357358

358359
volume, _ = await resolve_autoscaler_volume_identifiers(namespace)
360+
361+
# Detect PITR by checking whether the WAL PVC exists.
362+
wal_pvc_name = f"{_autoscaler_vm_name()}{AUTOSCALER_WAL_PVC_SUFFIX}"
363+
try:
364+
await kube_service.get_persistent_volume_claim(namespace, wal_pvc_name)
365+
pitr_active = True
366+
except VelaKubernetesError:
367+
pitr_active = False
368+
369+
if pitr_active:
370+
data_iops = max(1, round(iops * (1 - WAL_IOPS_FRACTION)))
371+
wal_iops = max(1, round(iops * WAL_IOPS_FRACTION))
372+
else:
373+
data_iops = iops
374+
359375
try:
360376
async with create_simplyblock_api() as sb_api:
361-
await sb_api.update_volume(volume=volume, payload={"max_rw_iops": iops})
377+
await sb_api.update_volume(volume=volume, payload={"max_rw_iops": data_iops})
362378
except VelaSimplyblockAPIError as exc:
363379
raise VelaDeploymentError("Failed to update volume") from exc
364380

365-
logger.info("Updated Simplyblock volume %s IOPS to %s", volume, iops)
381+
logger.info("Updated Simplyblock data volume %s IOPS to %s", volume, data_iops)
382+
383+
if pitr_active:
384+
wal_volume, _ = await resolve_autoscaler_wal_volume_identifiers(namespace)
385+
try:
386+
async with create_simplyblock_api() as sb_api:
387+
await sb_api.update_volume(volume=wal_volume, payload={"max_rw_iops": wal_iops})
388+
except VelaSimplyblockAPIError as exc:
389+
raise VelaDeploymentError("Failed to update WAL volume") from exc
390+
logger.info("Updated Simplyblock WAL volume %s IOPS to %s", wal_volume, wal_iops)
391+
else:
392+
logger.info("WAL PVC absent for branch %s; full IOPS budget applied to data volume", branch_id)
366393

367394

368395
async def ensure_branch_storage_class(branch_id: Identifier, *, iops: int) -> str:
@@ -397,15 +424,18 @@ async def _create_fresh_pvcs(
397424
parameters: DeploymentParameters,
398425
*,
399426
pitr_enabled: bool,
427+
wal_iops: int = 0,
400428
) -> None:
401429
"""Create empty block PVCs for a brand-new deployment before helm install."""
402430
from .kubernetes.pvc import create_pvc
403431

404432
access_modes = ["ReadWriteMany"]
405433

406-
def _pvc(name: str, size: str) -> kubernetes_client.V1PersistentVolumeClaim:
434+
def _pvc(
435+
name: str, size: str, annotations: dict[str, str] | None = None
436+
) -> kubernetes_client.V1PersistentVolumeClaim:
407437
return kubernetes_client.V1PersistentVolumeClaim(
408-
metadata=kubernetes_client.V1ObjectMeta(name=name),
438+
metadata=kubernetes_client.V1ObjectMeta(name=name, annotations=annotations),
409439
spec=kubernetes_client.V1PersistentVolumeClaimSpec(
410440
access_modes=access_modes,
411441
storage_class_name=storage_class_name,
@@ -419,7 +449,8 @@ def _pvc(name: str, size: str) -> kubernetes_client.V1PersistentVolumeClaim:
419449

420450
if pitr_enabled:
421451
wal_pvc_name = f"{_autoscaler_vm_name()}{AUTOSCALER_WAL_PVC_SUFFIX}"
422-
await create_pvc(namespace, _pvc(wal_pvc_name, PITR_WAL_PVC_SIZE))
452+
wal_annotations = {"simplybk/qos-rw-iops": str(wal_iops)} if wal_iops > 0 else None
453+
await create_pvc(namespace, _pvc(wal_pvc_name, PITR_WAL_PVC_SIZE, annotations=wal_annotations))
423454

424455

425456
def _configure_vela_values(
@@ -548,10 +579,18 @@ async def create_vela_config(
548579
postgresql_resource = resources.files(__package__).joinpath("postgresql.conf")
549580
values_content = _load_chart_values(chart)
550581

551-
storage_class_name = await ensure_branch_storage_class(branch_id, iops=parameters.iops)
582+
if pitr_enabled:
583+
data_iops = max(1, round(parameters.iops * (1 - WAL_IOPS_FRACTION)))
584+
wal_iops = max(1, round(parameters.iops * WAL_IOPS_FRACTION))
585+
else:
586+
data_iops = parameters.iops
587+
wal_iops = 0
588+
storage_class_name = await ensure_branch_storage_class(branch_id, iops=data_iops)
552589

553590
if not use_existing_db_pvc:
554-
await _create_fresh_pvcs(namespace, storage_class_name, parameters, pitr_enabled=pitr_enabled)
591+
await _create_fresh_pvcs(
592+
namespace, storage_class_name, parameters, pitr_enabled=pitr_enabled, wal_iops=wal_iops
593+
)
555594

556595
values_content = _configure_vela_values(
557596
values_content,

0 commit comments

Comments
 (0)