# FinagleStatsReceiverWrapper Metrics: Monitoring Graph Operations in Twitter's Algorithm

> Discover FinagleStatsReceiverWrapper metrics like poll, pollLatency, failure, and queryTweetDegree used in Twitter's algorithm for monitoring graph operations and performance. Optimize your GraphJet services today.

- Repository: [X (fka Twitter)/the-algorithm](https://github.com/twitter/the-algorithm)
- Tags: performance
- Published: 2026-03-03

---

**FinagleStatsReceiverWrapper exposes a dynamic metrics interface through `scope()` and `counter()` methods that enables GraphJet-based services to emit performance counters like `poll`, `pollLatency`, `failure`, and domain-specific stats such as `queryTweetDegree` and `response_size` in the `twitter/the-algorithm` repository.**

The `FinagleStatsReceiverWrapper` serves as the telemetry bridge between Twitter's **Finagle** statistics framework and the **GraphJet** graph processing library. This lightweight adapter, located in [`src/scala/com/twitter/recos/graph_common/FinagleStatsReceiverWrapper.scala`](https://github.com/twitter/the-algorithm/blob/main/src/scala/com/twitter/recos/graph_common/FinagleStatsReceiverWrapper.scala), allows recommendation services to instrument graph operations without hard-coding metric definitions, enabling granular observability into queue latencies, computation failures, and recommendation quality across the user-user and user-tweet entity graphs.

## Core Architecture of the Wrapper

`FinagleStatsReceiverWrapper` implements the GraphJet `StatsReceiver` interface while delegating to Finagle's native `StatsReceiver`. The adaptor provides two primary operations for metric creation:

| Method | Signature | Purpose |
|--------|-----------|---------|
| **scope** | `scope(namespace: String): FinagleStatsReceiverWrapper` | Returns a new wrapper instance whose metrics are prefixed with the given namespace, creating hierarchical metric paths like `UserGraph/poll`. |
| **counter** | `counter(name: String): Counter` | Returns a Finagle counter that can be incremented to track discrete events such as failures or queue operations. |

In practice, services typically access the underlying **Finagle** `StatsReceiver` via the `statsReceiver` field to utilize both counters and statistics (histograms). This pattern allows components to define custom metrics while the wrapper handles the Finagle-specific implementation details.

## Graph-Specific Metrics by Service

The wrapper itself does not ship with hard-coded metrics. Instead, each recommendation service injects the wrapper and defines domain-specific counters and stats. Below are the specific metrics exposed by the major GraphJet services.

### User-User Graph Operations (RecommendUsersHandler)

Located in [`src/scala/com/twitter/recos/user_user_graph/RecommendUsersHandler.scala`](https://github.com/twitter/the-algorithm/blob/main/src/scala/com/twitter/recos/user_user_graph/RecommendUsersHandler.scala), the user-user graph service tracks queue performance and recommendation quality through the wrapper:

- **`failure`** (counter): Incremented when GraphJet computation throws an exception (lines 42-49).
- **`recs_count`** (stat): Histogram tracking the number of recommendations returned per request.
- **`empty`** (counter): Tracks requests that produce no recommendations.
- **`poll`** (counter): Counts how many times a GraphJet runner is retrieved from the queue.
- **`pollTimeout`** (counter): Incremented when queue polling exceeds the configured timeout (lines 78-87).
- **`offer`** (counter): Tracks when a runner is returned to the queue after use.
- **`pollLatency`** (stat): Measures latency in milliseconds for each queue poll operation.

### User-Tweet Entity Graph (TweetRecommendationsRunner)

The `TweetRecommendationsRunner` in [`src/scala/com/twitter/recos/user_tweet_entity_graph/TweetRecommendationsRunner.scala`](https://github.com/twitter/the-algorithm/blob/main/src/scala/com/twitter/recos/user_tweet_entity_graph/TweetRecommendationsRunner.scala) manages MagicRecs computation with these metrics:

- **`failure`** (counter): Tracks exceptions thrown during GraphJet MagicRecs computation.
- **`magicRecsFailureCounter`** (counter): An alias scoped inside the runner that tracks the same failures.
- **`poll`** / **`offer`** (counters): Track runner acquisition and return to the `AsyncQueue`.
- **`pollTimeout`** (counter): Counts queue polling timeouts.
- **`pollLatency`** (stat): Records queue poll latency in milliseconds.
- **Filter-specific counters**: Result filtering components like `RecentTweetFilter` and `TweetAuthorFilter` create scoped counters (e.g., `RecentTweetFilter.filterPass`) through the wrapper.

### Tweet-Based Related Tweet Service (TweetBasedRelatedTweetsHandler)

For the related-tweet endpoints defined in [`src/scala/com/twitter/recos/user_video_graph/relatedTweetHandlers/TweetBasedRelatedTweetsHandler.scala`](https://github.com/twitter/the-algorithm/blob/main/src/scala/com/twitter/recos/user_video_graph/relatedTweetHandlers/TweetBasedRelatedTweetsHandler.scala), the wrapper exposes:

- **`queryTweetDegree`** (stat): Histogram of the source tweet's degree (connection count) used for scoring.
- **`requestTweetDegreeLessThanMinQueryDegree`** (counter): Tracks when a query tweet's degree falls below the minimum threshold required for processing (lines 24-30).
- **`response_size`** (stat): Measures the number of related tweets returned in responses.
- **`requestLatency`** (stat): Implicitly tracked via `trackFutureBlockStats`, measuring end-to-end request latency.

## Implementation Patterns and Code Examples

Services follow a consistent pattern: inject the wrapper, create a scoped namespace using the class name, then define counters and stats.

### Creating Scoped Metrics

```scala
import com.twitter.recos.graph_common.FinagleStatsReceiverWrapper
import com.twitter.finagle.stats.StatsReceiver

// Wrap the injected Finagle StatsReceiver
val finagleReceiver: StatsReceiver = ???
val wrapper = FinagleStatsReceiverWrapper(finagleReceiver)

// Create a scoped namespace for the component
val userGraphStats = wrapper.scope("UserGraph").statsReceiver

// Define specific metrics
val pollCounter = userGraphStats.counter("poll")
val pollLatency = userGraphStats.stat("pollLatency")
val failureCounter = userGraphStats.counter("failure")

// Record metrics during operation
pollCounter.incr()
pollLatency.add(42.0)

```

*Source: [`FinagleStatsReceiverWrapper.scala`](https://github.com/twitter/the-algorithm/blob/main/FinagleStatsReceiverWrapper.scala), lines 12-16*

### Injecting into Recommendation Handlers

The `RecommendUsersHandler` demonstrates dependency injection of the wrapper:

```scala
case class RecommendUsersHandlerImpl(
  bipartiteGraph: NodeMetadataLeftIndexedPowerLawMultiSegmentBipartiteGraph,
  salsaRunnerConfig: SalsaRunnerConfig,
  decider: UserUserGraphDecider,
  statsReceiverWrapper: FinagleStatsReceiverWrapper
) {
  private val stats = statsReceiverWrapper.statsReceiver
    .scope(this.getClass.getSimpleName)
  private val pollCounter = stats.counter("poll")
  private val pollLatency = stats.stat("pollLatency")
}

```

*Source: [`RecommendUsersHandler.scala`](https://github.com/twitter/the-algorithm/blob/main/RecommendUsersHandler.scala), lines 38-44*

### Queue-Based GraphJet Runners

For async queue management in the tweet-entity graph:

```scala
private val magicRecsQueue = new AsyncQueue[TopSecondDegreeByCountForTweet]

(0 until salsaRunnerConfig.numSalsaRunners).foreach { _ =>
  magicRecsQueue.offer(
    new TopSecondDegreeByCountForTweet(
      bipartiteGraph,
      salsaRunnerConfig.expectedNodesToHitInSalsa,
      // Scoped StatsReceiver allows GraphJet algorithms to emit metrics
      statsReceiverWrapper.scope(this.getClass.getSimpleName)
    )
  )
}

```

*Source: [`TweetRecommendationsRunner.scala`](https://github.com/twitter/the-algorithm/blob/main/TweetRecommendationsRunner.scala), lines 14-22*

## Summary

- **`FinagleStatsReceiverWrapper`** acts as a bridge between GraphJet's statistics interface and Finagle's metrics infrastructure, defined in [`src/scala/com/twitter/recos/graph_common/FinagleStatsReceiverWrapper.scala`](https://github.com/twitter/the-algorithm/blob/main/src/scala/com/twitter/recos/graph_common/FinagleStatsReceiverWrapper.scala).
- The wrapper exposes **`scope()`** and **`counter()`** methods, but services typically access the underlying **`statsReceiver`** field to create hierarchical metric namespaces using class names as prefixes.
- **User-User Graph** services track queue performance (`poll`, `pollLatency`, `pollTimeout`) and recommendation quality (`recs_count`, `empty`) via the wrapper.
- **User-Tweet Entity Graph** services emit MagicRecs-specific metrics including failure counters and filter-pass statistics scoped to individual runner instances.
- **Tweet-Based services** expose domain-specific histograms like `queryTweetDegree` and `response_size` to monitor data quality and response volumes.

## Frequently Asked Questions

### How does FinagleStatsReceiverWrapper differ from Finagle's native StatsReceiver?

`FinagleStatsReceiverWrapper` implements GraphJet's `StatsReceiver` interface while internally delegating to Finagle's native implementation. This allows GraphJet algorithms—which expect a specific interface—to emit metrics that are actually recorded by Finagle's statistics system, enabling seamless integration of the graph processing library into Twitter's service infrastructure.

### Where are the metric names defined in the Twitter algorithm source code?

Metric names are not hard-coded in [`FinagleStatsReceiverWrapper.scala`](https://github.com/twitter/the-algorithm/blob/main/FinagleStatsReceiverWrapper.scala). Instead, each service defines its own metrics where the wrapper is injected. For example, [`RecommendUsersHandler.scala`](https://github.com/twitter/the-algorithm/blob/main/RecommendUsersHandler.scala) defines `failure` and `poll` counters (lines 42-87), while [`TweetBasedRelatedTweetsHandler.scala`](https://github.com/twitter/the-algorithm/blob/main/TweetBasedRelatedTweetsHandler.scala) defines `queryTweetDegree` stats (lines 24-30). This decentralized approach allows each component to expose relevant operational metrics.

### What is the difference between a counter and a stat in FinagleStatsReceiverWrapper?

**Counters** track discrete, incrementing events such as `failure` or `pollTimeout` occurrences. **Stats** (statistics) track distributions of values over time, such as `pollLatency` or `response_size`, which Finagle exports as histograms percentiles (p50, p90, p99). Services access these via `stats.counter("name")` and `stats.stat("name")` respectively on the scoped `StatsReceiver` instance.

### Why are metrics scoped by class name in the recommendation services?

Services call `scope(this.getClass.getSimpleName)` to create a namespace hierarchy that prevents metric collisions between different graph components. This results in metric paths like `RecommendUsersHandler/poll` or `TweetRecommendationsRunner/failure`, making it easier to isolate performance data by service component in monitoring dashboards.