@@ -52,9 +52,25 @@ def Create(self, restore: bool = False) -> None:
5252 self .inference_server .Create ()
5353
5454 def _PostCreate (self ):
55+ """Starts the event poller after the cluster has been created."""
5556 super ()._PostCreate ()
56- if self .cluster_spec .poll_for_events :
57- self .event_poller .StartPolling ()
57+ if self .event_poller :
58+ try :
59+ self .event_poller .StartPolling ()
60+ except Exception as exc : # pylint: disable=broad-except
61+ # Python 3.14 tightened pickling rules for multiprocessing — local
62+ # functions passed to Process cannot be pickled. Rather than crashing
63+ # PKB entirely (which prevents cleanup and orphans cloud resources),
64+ # log a warning and continue without the event poller.
65+ # Impact: no Kubernetes event streaming during the run — benchmark
66+ # metrics are unaffected.
67+ logging .warning (
68+ 'Event poller failed to start (non-fatal, continuing without '
69+ + 'event polling): %s. This is a known Python 3.14 pickling '
70+ + 'issue — switch to Python 3.13 to enable event polling.' ,
71+ exc ,
72+ )
73+ self .event_poller = None
5874
5975 def Delete (self , freeze : bool = False ) -> None :
6076 if self .inference_server :
@@ -316,32 +332,36 @@ def CreateNodePool(
316332 nodepool_config : container_lib .BaseNodePoolConfig ,
317333 node_version : str | None = None ,
318334 ) -> None :
319- """Creates a single named node pool on the cluster ( blocks until ready).
335+ """Creates a node pool and blocks until ready (sync wrapper ).
320336
321337 Args:
322338 nodepool_config: Node pool definition (name, machine type, node count).
323339 node_version: Optional Kubernetes version to pin the node pool to. None
324340 means use the cluster default.
325341 """
326- raise NotImplementedError
342+ op = self .CreateNodePoolAsync (nodepool_config , node_version )
343+ self .WaitForOperation (op )
327344
328345 def DeleteNodePool (self , name : str ) -> None :
329- """Deletes the named node pool (blocks until removed)."""
330- raise NotImplementedError
346+ """Deletes the named node pool and blocks until removed (sync wrapper)."""
347+ op = self .DeleteNodePoolAsync (name )
348+ self .WaitForOperation (op )
331349
332350 def UpgradeNodePool (self , name : str , target_version : str ) -> None :
333- """Upgrades the named node pool to the given Kubernetes version."""
334- raise NotImplementedError
351+ """Upgrades the named node pool and blocks until done (sync wrapper)."""
352+ op = self .UpgradeNodePoolAsync (name , target_version )
353+ self .WaitForOperation (op )
335354
336355 def UpdateCluster (self ) -> None :
337- """Performs a lightweight cluster-level update operation ( blocks).
356+ """Performs a cluster-level update and blocks until done (sync wrapper ).
338357
339358 Intended for management-plane benchmarks that need to overlap a real
340359 cluster-level operation with a node-pool operation. The implementation
341360 should issue a control-plane mutation (so an actual operation runs) that
342361 is non-destructive and idempotent across repeated invocations.
343362 """
344- raise NotImplementedError
363+ op = self .UpdateClusterAsync ()
364+ self .WaitForOperation (op )
345365
346366 def CreateNodePoolAsync (
347367 self ,
0 commit comments