@@ -2200,6 +2200,14 @@ def query_plan(self):
22002200 plan_entries = self ._job_statistics ().get ('queryPlan' , ())
22012201 return [QueryPlanEntry .from_api_repr (entry ) for entry in plan_entries ]
22022202
2203+ @property
2204+ def timeline (self ):
2205+ """List(TimelineEntry): Return the query execution timeline
2206+ from job statistics.
2207+ """
2208+ raw = self ._job_statistics ().get ('timeline' , ())
2209+ return [TimelineEntry .from_api_repr (entry ) for entry in raw ]
2210+
22032211 @property
22042212 def total_bytes_processed (self ):
22052213 """Return total bytes processed from job statistics, if present.
@@ -2274,6 +2282,11 @@ def num_dml_affected_rows(self):
22742282 result = int (result )
22752283 return result
22762284
2285+ @property
2286+ def slot_millis (self ):
2287+ """Union[int, None]: Slot-milliseconds used by this query job."""
2288+ return _int_or_none (self ._job_statistics ().get ('totalSlotMs' ))
2289+
22772290 @property
22782291 def statement_type (self ):
22792292 """Return statement type from job statistics, if present.
@@ -2518,46 +2531,49 @@ def start(self):
25182531 if self ._properties .get ('startMs' ) is None :
25192532 return None
25202533 return _datetime_from_microseconds (
2521- self ._properties .get ('startMs' ) * 1000.0 )
2534+ int ( self ._properties .get ('startMs' ) ) * 1000.0 )
25222535
25232536 @property
25242537 def end (self ):
25252538 """Union[Datetime, None]: Datetime when the stage ended."""
25262539 if self ._properties .get ('endMs' ) is None :
25272540 return None
25282541 return _datetime_from_microseconds (
2529- self ._properties .get ('endMs' ) * 1000.0 )
2542+ int ( self ._properties .get ('endMs' ) ) * 1000.0 )
25302543
25312544 @property
25322545 def input_stages (self ):
25332546 """List(int): Entry IDs for stages that were inputs for this stage."""
2534- return self ._properties .get ('inputStages' , [])
2547+ if self ._properties .get ('inputStages' ) is None :
2548+ return []
2549+ return [_int_or_none (entry )
2550+ for entry in self ._properties .get ('inputStages' )]
25352551
25362552 @property
25372553 def parallel_inputs (self ):
25382554 """Union[int, None]: Number of parallel input segments within
25392555 the stage.
25402556 """
2541- return self ._properties .get ('parallelInputs' )
2557+ return _int_or_none ( self ._properties .get ('parallelInputs' ) )
25422558
25432559 @property
25442560 def completed_parallel_inputs (self ):
25452561 """Union[int, None]: Number of parallel input segments completed."""
2546- return self ._properties .get ('completedParallelInputs' )
2562+ return _int_or_none ( self ._properties .get ('completedParallelInputs' ) )
25472563
25482564 @property
25492565 def wait_ms_avg (self ):
25502566 """Union[int, None]: Milliseconds the average worker spent waiting to
25512567 be scheduled.
25522568 """
2553- return self ._properties .get ('waitMsAvg' )
2569+ return _int_or_none ( self ._properties .get ('waitMsAvg' ) )
25542570
25552571 @property
25562572 def wait_ms_max (self ):
25572573 """Union[int, None]: Milliseconds the slowest worker spent waiting to
25582574 be scheduled.
25592575 """
2560- return self ._properties .get ('waitMsMax' )
2576+ return _int_or_none ( self ._properties .get ('waitMsMax' ) )
25612577
25622578 @property
25632579 def wait_ratio_avg (self ):
@@ -2580,14 +2596,14 @@ def read_ms_avg(self):
25802596 """Union[int, None]: Milliseconds the average worker spent reading
25812597 input.
25822598 """
2583- return self ._properties .get ('readMsAvg' )
2599+ return _int_or_none ( self ._properties .get ('readMsAvg' ) )
25842600
25852601 @property
25862602 def read_ms_max (self ):
25872603 """Union[int, None]: Milliseconds the slowest worker spent reading
25882604 input.
25892605 """
2590- return self ._properties .get ('readMsMax' )
2606+ return _int_or_none ( self ._properties .get ('readMsMax' ) )
25912607
25922608 @property
25932609 def read_ratio_avg (self ):
@@ -2610,14 +2626,14 @@ def compute_ms_avg(self):
26102626 """Union[int, None]: Milliseconds the average worker spent on CPU-bound
26112627 processing.
26122628 """
2613- return self ._properties .get ('computeMsAvg' )
2629+ return _int_or_none ( self ._properties .get ('computeMsAvg' ) )
26142630
26152631 @property
26162632 def compute_ms_max (self ):
26172633 """Union[int, None]: Milliseconds the slowest worker spent on CPU-bound
26182634 processing.
26192635 """
2620- return self ._properties .get ('computeMsMax' )
2636+ return _int_or_none ( self ._properties .get ('computeMsMax' ) )
26212637
26222638 @property
26232639 def compute_ratio_avg (self ):
@@ -2640,14 +2656,14 @@ def write_ms_avg(self):
26402656 """Union[int, None]: Milliseconds the average worker spent writing
26412657 output data.
26422658 """
2643- return self ._properties .get ('writeMsAvg' )
2659+ return _int_or_none ( self ._properties .get ('writeMsAvg' ) )
26442660
26452661 @property
26462662 def write_ms_max (self ):
26472663 """Union[int, None]: Milliseconds the slowest worker spent writing
26482664 output data.
26492665 """
2650- return self ._properties .get ('writeMsMax' )
2666+ return _int_or_none ( self ._properties .get ('writeMsMax' ) )
26512667
26522668 @property
26532669 def write_ratio_avg (self ):
@@ -2668,12 +2684,12 @@ def write_ratio_max(self):
26682684 @property
26692685 def records_read (self ):
26702686 """Union[int, None]: Number of records read by this stage."""
2671- return self ._properties .get ('recordsRead' )
2687+ return _int_or_none ( self ._properties .get ('recordsRead' ) )
26722688
26732689 @property
26742690 def records_written (self ):
26752691 """Union[int, None]: Number of records written by this stage."""
2676- return self ._properties .get ('recordsWritten' )
2692+ return _int_or_none ( self ._properties .get ('recordsWritten' ) )
26772693
26782694 @property
26792695 def status (self ):
@@ -2685,14 +2701,14 @@ def shuffle_output_bytes(self):
26852701 """Union[int, None]: Number of bytes written by this stage to
26862702 intermediate shuffle.
26872703 """
2688- return self ._properties .get ('shuffleOutputBytes' )
2704+ return _int_or_none ( self ._properties .get ('shuffleOutputBytes' ) )
26892705
26902706 @property
26912707 def shuffle_output_bytes_spilled (self ):
26922708 """Union[int, None]: Number of bytes written by this stage to
26932709 intermediate shuffle and spilled to disk.
26942710 """
2695- return self ._properties .get ('shuffleOutputBytesSpilled' )
2711+ return _int_or_none ( self ._properties .get ('shuffleOutputBytesSpilled' ) )
26962712
26972713 @property
26982714 def steps (self ):
@@ -2703,6 +2719,66 @@ def steps(self):
27032719 for step in self ._properties .get ('steps' , [])]
27042720
27052721
2722+ class TimelineEntry (object ):
2723+ """TimelineEntry represents progress of a query job at a particular
2724+ point in time.
2725+
2726+ See
2727+ https://cloud.google.com/bigquery/docs/reference/rest/v2/jobs
2728+ for the underlying API representation within query statistics.
2729+
2730+ """
2731+
2732+ def __init__ (self ):
2733+ self ._properties = {}
2734+
2735+ @classmethod
2736+ def from_api_repr (cls , resource ):
2737+ """Factory: construct instance from the JSON repr.
2738+
2739+ Args:
2740+ resource(Dict[str: object]):
2741+ QueryTimelineSample representation returned from API
2742+
2743+ Returns:
2744+ google.cloud.bigquery.TimelineEntry:
2745+ Timeline sample parsed from ``resource``
2746+ """
2747+ entry = cls ()
2748+ entry ._properties = resource
2749+ return entry
2750+
2751+ @property
2752+ def elapsed_ms (self ):
2753+ """Union[int, None]: Milliseconds elapsed since start of query
2754+ execution."""
2755+ return _int_or_none (self ._properties .get ('elapsedMs' ))
2756+
2757+ @property
2758+ def active_units (self ):
2759+ """Union[int, None]: Current number of input units being processed
2760+ by workers, reported as largest value since the last sample."""
2761+ return _int_or_none (self ._properties .get ('activeUnits' ))
2762+
2763+ @property
2764+ def pending_units (self ):
2765+ """Union[int, None]: Current number of input units remaining for
2766+ query stages active at this sample time."""
2767+ return _int_or_none (self ._properties .get ('pendingUnits' ))
2768+
2769+ @property
2770+ def completed_units (self ):
2771+ """Union[int, None]: Current number of input units completed by
2772+ this query."""
2773+ return _int_or_none (self ._properties .get ('completedUnits' ))
2774+
2775+ @property
2776+ def slot_millis (self ):
2777+ """Union[int, None]: Cumulative slot-milliseconds consumed by
2778+ this query."""
2779+ return _int_or_none (self ._properties .get ('totalSlotMs' ))
2780+
2781+
27062782class UnknownJob (_AsyncJob ):
27072783 """A job whose type cannot be determined."""
27082784
0 commit comments