2025 05 16.RuntimeMetricsSummary - SergeiGolos/wod-wiki GitHub Wiki

Summary of RuntimeMetrics Assembly and Relationships in wod.wiki

This document outlines how RuntimeMetric objects are created, assembled, and managed within the wod.wiki runtime system. It details the roles of different block types in generating and aggregating metrics.

1. RuntimeMetric Structure

  • Definition (effort, repetitions, resistance, distance) - as per memory 74eb0117-4e09-4991-bdfd-20d4a63f10cc.
  • Purpose: To capture quantifiable results of workout components.

2. Metric Creation

  • EffortBlock: Primary generator of RuntimeMetric instances for individual exercises.
    • Metrics are derived from PrecompiledNode sources passed during its construction, which contain parsed workout data (e.g., exercise name, repetitions, weight, distance).
    • Manages its metrics within its own MetricsContext, initialized with MetricsRelationshipType.INHERIT (via AbstractBlockLifecycle).
    • During its lifecycle, particularly in its overridden enhanceResultSpan and doLeave methods, it adds further details like 'Last Updated' timestamps, completion timestamps, and a 'Status: completed' metric to its ResultSpan (stored in BlockContext).
  • RuntimeJit: As per memory 74eb0117-4e09-4991-bdfd-20d4a63f10cc, RuntimeJit plays a role in the initial compilation of the workout. AbstractBlockLifecycle's initializeMetrics method, using MetricsFactory, then creates the RuntimeMetric structures from PrecompiledNodes for each block.

3. Metric Aggregation and Inheritance by Block Type

This section describes how different runtime blocks handle metrics, particularly in terms of merging metrics from child blocks or inheriting them. The core aggregation logic (ADD, MULTIPLY, INHERIT) resides in MetricsContext.getMetrics(), orchestrated by AbstractBlockLifecycle.

3.1. EffortBlock

  • Its MetricsContext (initialized by AbstractBlockLifecycle) contains base metrics from its PrecompiledNode sources.
  • Uses MetricsRelationshipType.INHERIT. It doesn't aggregate child metrics.
  • Its overridden enhanceResultSpan method dynamically adds detailed descriptions (e.g., "10 Pullups 50kg") and timestamps to its ResultSpan.

3.2. RepeatingBlock

  • Its MetricsContext is initialized with MetricsRelationshipType.MULTIPLY by AbstractBlockLifecycle.
    • When its getMetrics() is called (likely by a parent or for final summary), MetricsContext will handle multiplying the metrics of its children (representing one round) by the number of rounds completed (this.ctx.index).
  • ResultSpan Enhancements (via overridden enhanceResultSpan):
    • Updates the ResultSpan label with round progress.
    • Adds a 'Rounds' RuntimeMetric.
    • When a round completes (in doNext()), generates a 'Round Complete' metric and adds it to the current ResultSpan (via this.ctx.addMetricsToResultSpan()).

3.3. TimedGroupBlock

  • Its MetricsContext is initialized with MetricsRelationshipType.ADD by AbstractBlockLifecycle.
  • ResultSpan Enhancements (via overridden enhanceResultSpan):
    • This is where it aggregates metrics from its children. It calls getMetrics() on each child block (which in turn uses their MetricsContext).
    • The summed child metrics are then added to its own ResultSpan via this.ctx.addMetricsToResultSpan().
    • Manages child ResultSpans via its childSpanRegistry to facilitate this aggregation.

3.4. RootBlock

  • Its MetricsContext is initialized by AbstractBlockLifecycle, likely defaulting to MetricsRelationshipType.ADD.
  • When its doLeave() method is called:
    • Its ResultSpan (created on enter and stored in this.ctx) is finalized.
    • The call to ResultSpan.fromBlock(this) (or an equivalent this.getMetrics(true,true)) triggers its MetricsContext to aggregate metrics from all its direct children (top-level workout segments).
  • The final ResultSpan contains the grand total for the workout and is emitted by WriteResultAction.

3.5. Other Blocks

  • IdleRuntimeBlock, DoneRuntimeBlock: Typically do not generate significant performance metrics. Their MetricsContext would be minimal, and they wouldn't override enhanceResultSpan extensively.

4. Role of BlockContext

BlockContext serves as the stateful companion to RuntimeBlock instances, crucial for metric management by providing storage and basic manipulation tools for ITimeSpans and ResultSpans. It does not perform aggregation logic itself.

  • State Holding: Stores index, childIndex, spans (array of ITimeSpan), resultSpans (array of ResultSpan), blockKey, etc.
  • ResultSpan Management:
    • createResultSpan(): Called by AbstractBlockLifecycle.createResultSpan() to instantiate a new ResultSpan when a block begins execution. This span is stored in ctx.resultSpans.
    • getCurrentResultSpan(): Allows blocks to retrieve their active ResultSpan for updates during their lifecycle (e.g., in enhanceResultSpan).
    • addMetricsToResultSpan(): Key method used by block-specific enhanceResultSpan overrides to add generated or aggregated RuntimeMetric arrays to their ResultSpan.
    • completeResultSpan(): Marks a ResultSpan as complete, typically called by AbstractBlockLifecycle.leave().
  • Facilitator: BlockContext provides the mutable storage for ResultSpan objects that are populated by AbstractBlockLifecycle and its concrete subclasses.

5. Role of AbstractBlockLifecycle

AbstractBlockLifecycle implements the Template Method pattern and is central to orchestrating metric initialization, ResultSpan management, and providing access to aggregated metrics.

  • Initialization & MetricsContext Setup:
    • In its constructor, it creates a MetricsContext for the block, establishing its MetricsRelationshipType (ADD, MULTIPLY, INHERIT) and linking it to a parent MetricsContext if applicable.
    • Calls initializeMetrics(), which uses a MetricsFactory to create base RuntimeMetric objects from the PrecompiledNode sources and adds them to the block's MetricsContext.
  • Metric Calculation (getMetrics):
    • Implements IMetricsProvider.
    • Its getMetrics(includeChildren, inheritFromParent) method delegates directly to this.metricsContext.getMetrics(). This is where the actual hierarchical aggregation (ADD, MULTIPLY) or INHERITance logic defined by MetricsRelationshipType is executed by the MetricsContext.
  • ResultSpan Lifecycle Orchestration:
    • enter(): Calls its createResultSpan() helper, which in turn calls this.ctx.createResultSpan() to make a new ResultSpan for the current execution. It then calls this.enhanceResultSpan() for initial population.
    • enhanceResultSpan(span) (Protected Hook): This is a key extension point. The base version adds default metrics. Subclasses override it to:
      • Add their own specific metrics (e.g., 'Round Complete' for RepeatingBlock).
      • For aggregator blocks (like TimedGroupBlock), fetch aggregated metrics from children (by calling child.getMetrics()) and add them to their own span (using this.ctx.addMetricsToResultSpan()).
    • leave(): Performs a final call to enhanceResultSpan() on the current ResultSpan, then calls this.ctx.completeResultSpan(). It also calls updateMetricsContextFromSpans() to potentially sync the MetricsContext with data from the ResultSpans (though primary aggregation for getMetrics() relies on the MetricsContext hierarchy).
  • Overall Flow: AbstractBlockLifecycle sets up the MetricsContext (which knows how to calculate its total metrics). It also manages the ResultSpan for an execution instance, allowing subclasses to fill this ResultSpan with the relevant metrics that will eventually be reported.

6. Conclusion

RuntimeMetrics are initialized from script definitions by AbstractBlockLifecycle (using MetricsFactory) and stored in a block's MetricsContext. The MetricsContext itself, governed by MetricsRelationshipType, is responsible for calculating a block's total metrics, including aggregation from children when its getMetrics() method is called. During runtime, each block execution gets a ResultSpan (managed via BlockContext and AbstractBlockLifecycle). Blocks populate their ResultSpan (via overridden enhanceResultSpan methods) with their specific metrics and/or metrics aggregated from children (obtained by calling child.getMetrics()). This ResultSpan is then emitted upon block completion, providing a snapshot of that block's performance for that specific run.

⚠️ **GitHub.com Fallback** ⚠️