7979AUTOSCALER_PVC_SUFFIX = "-block-data"
8080AUTOSCALER_WAL_PVC_SUFFIX = "-pg-wal"
8181PITR_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 )
@@ -98,6 +99,10 @@ def branch_storage_class_name(branch_id: Identifier) -> str:
9899 return f"sc-{ str (branch_id ).lower ()} "
99100
100101
102+ def branch_wal_storage_class_name (branch_id : Identifier ) -> str :
103+ return f"sc-{ str (branch_id ).lower ()} -wal"
104+
105+
101106def deployment_branch (namespace : str ) -> ULID :
102107 """Return the branch ULID for a given deployment namespace."""
103108
@@ -355,14 +360,28 @@ async def resolve_branch_database_volume_size(branch_id: Identifier) -> int:
355360async def update_branch_volume_iops (branch_id : Identifier , iops : int ) -> None :
356361 namespace = deployment_namespace (branch_id )
357362
363+ data_iops = max (1 , round (iops * (1 - WAL_IOPS_FRACTION )))
364+ wal_iops = max (1 , round (iops * WAL_IOPS_FRACTION ))
365+
358366 volume , _ = await resolve_autoscaler_volume_identifiers (namespace )
359367 try :
360368 async with create_simplyblock_api () as sb_api :
361- await sb_api .update_volume (volume = volume , payload = {"max_rw_iops" : iops })
369+ await sb_api .update_volume (volume = volume , payload = {"max_rw_iops" : data_iops })
362370 except VelaSimplyblockAPIError as exc :
363371 raise VelaDeploymentError ("Failed to update volume" ) from exc
364372
365- logger .info ("Updated Simplyblock volume %s IOPS to %s" , volume , iops )
373+ logger .info ("Updated Simplyblock data volume %s IOPS to %s" , volume , data_iops )
374+
375+ try :
376+ wal_volume , _ = await resolve_autoscaler_wal_volume_identifiers (namespace )
377+ try :
378+ async with create_simplyblock_api () as sb_api :
379+ await sb_api .update_volume (volume = wal_volume , payload = {"max_rw_iops" : wal_iops })
380+ except VelaSimplyblockAPIError as exc :
381+ raise VelaDeploymentError ("Failed to update WAL volume" ) from exc
382+ logger .info ("Updated Simplyblock WAL volume %s IOPS to %s" , wal_volume , wal_iops )
383+ except VelaDeploymentError as exc :
384+ logger .info ("WAL volume not found for branch %s; skipping WAL IOPS update: %s" , branch_id , exc )
366385
367386
368387async def ensure_branch_storage_class (branch_id : Identifier , * , iops : int ) -> str :
@@ -384,6 +403,43 @@ async def ensure_branch_storage_class(branch_id: Identifier, *, iops: int) -> st
384403 return storage_class_name
385404
386405
406+ async def ensure_branch_wal_storage_class (branch_id : Identifier , * , iops : int ) -> str :
407+ wal_sc_name = branch_wal_storage_class_name (branch_id )
408+ try :
409+ await kube_service .get_storage_class (wal_sc_name )
410+ logger .info ("WAL StorageClass %s already exists; reusing" , wal_sc_name )
411+ return wal_sc_name
412+ except VelaKubernetesError :
413+ pass
414+
415+ base_storage_class = await kube_service .get_storage_class (SIMPLYBLOCK_CSI_STORAGE_CLASS )
416+ wal_iops = max (1 , round (iops * WAL_IOPS_FRACTION ))
417+ storage_class_manifest = _build_storage_class_manifest (
418+ storage_class_name = wal_sc_name ,
419+ iops = wal_iops ,
420+ base_storage_class = base_storage_class ,
421+ )
422+ await kube_service .apply_storage_class (storage_class_manifest )
423+ return wal_sc_name
424+
425+
426+ def _load_compose_manifest () -> dict [str , Any ]:
427+ compose_resource = resources .files (__package__ ).joinpath ("compose.yml" )
428+ compose_content = yaml .safe_load (compose_resource .read_text ())
429+ if not isinstance (compose_content , dict ):
430+ raise VelaDeploymentError ("docker-compose manifest must be a mapping" )
431+ return compose_content
432+
433+
434+ def _configure_compose_storage (compose : dict [str , Any ], * , enable_file_storage : bool ) -> dict [str , Any ]:
435+ services = compose .get ("services" )
436+ if not isinstance (services , dict ):
437+ raise VelaDeploymentError ("docker-compose manifest missing 'services' mapping" )
438+ if not enable_file_storage :
439+ services .pop ("storage" , None )
440+ return compose
441+
442+
387443def _load_chart_values (chart_root : Any ) -> dict [str , Any ]:
388444 values_content = yaml .safe_load ((chart_root / "values.yaml" ).read_text ())
389445 if not isinstance (values_content , dict ):
@@ -399,6 +455,7 @@ def _configure_vela_values(
399455 database_admin_password : str ,
400456 pgbouncer_admin_password : str ,
401457 storage_class_name : str ,
458+ wal_storage_class_name : str ,
402459 use_existing_db_pvc : bool ,
403460 pgbouncer_config : Mapping [str , int ] | None ,
404461 enable_file_storage : bool ,
@@ -453,7 +510,7 @@ def _configure_vela_values(
453510 wal_persistence = pg_wal_spec .setdefault ("persistence" , {})
454511 wal_persistence ["create" ] = not use_existing_db_pvc
455512 wal_persistence ["size" ] = PITR_WAL_PVC_SIZE
456- wal_persistence ["storageClassName" ] = storage_class_name
513+ wal_persistence ["storageClassName" ] = wal_storage_class_name
457514 wal_persistence ["claimName" ] = wal_persistence .get ("claimName" ) or (
458515 f"{ _autoscaler_vm_name ()} { AUTOSCALER_WAL_PVC_SUFFIX } "
459516 )
@@ -521,14 +578,17 @@ async def create_vela_config(
521578 postgresql_resource = resources .files (__package__ ).joinpath ("postgresql.conf" )
522579 values_content = _load_chart_values (chart )
523580
524- storage_class_name = await ensure_branch_storage_class (branch_id , iops = parameters .iops )
581+ data_iops = max (1 , round (parameters .iops * (1 - WAL_IOPS_FRACTION )))
582+ storage_class_name = await ensure_branch_storage_class (branch_id , iops = data_iops )
583+ wal_storage_class_name = await ensure_branch_wal_storage_class (branch_id , iops = parameters .iops )
525584 values_content = _configure_vela_values (
526585 values_content ,
527586 parameters = parameters ,
528587 jwt_secret = jwt_secret ,
529588 database_admin_password = database_admin_password ,
530589 pgbouncer_admin_password = pgbouncer_admin_password ,
531590 storage_class_name = storage_class_name ,
591+ wal_storage_class_name = wal_storage_class_name ,
532592 use_existing_db_pvc = use_existing_db_pvc ,
533593 pgbouncer_config = pgbouncer_config ,
534594 enable_file_storage = parameters .enable_file_storage ,
@@ -608,6 +668,7 @@ async def _delete_autoscaler_vm(namespace: str) -> None:
608668async def delete_deployment (branch_id : Identifier ) -> None :
609669 namespace , _ = get_autoscaler_vm_identity (branch_id )
610670 storage_class_name = branch_storage_class_name (branch_id )
671+ wal_sc_name = branch_wal_storage_class_name (branch_id )
611672 await cleanup_branch_dns (branch_id )
612673 await _delete_autoscaler_vm (namespace )
613674 try :
@@ -628,6 +689,13 @@ async def delete_deployment(branch_id: Identifier) -> None:
628689 logger .info ("StorageClass %s not found" , storage_class_name )
629690 else :
630691 raise
692+ try :
693+ await kube_service .delete_storage_class (wal_sc_name )
694+ except ApiException as exc :
695+ if exc .status == 404 :
696+ logger .info ("WAL StorageClass %s not found" , wal_sc_name )
697+ else :
698+ raise
631699
632700
633701def get_autoscaler_vm_identity (branch_id : Identifier ) -> tuple [str , str ]:
0 commit comments