Skip to content

Deadlock when using Family::get_or_create #231

@sfleen

Description

@sfleen

It's fairly common practice for us to create a set of metrics from a metric family given a set of labels, and store them for later use. However, this can cause a deadlock when Family::get_or_create is called multiple times on the same instance within the same statement:

struct Metrics {
  inbound: Counter,
  outbound: Counter,
}

let family = Family::<Vec<(String, String)>, Counter>::default();

Metrics {
  inbound: family.get_or_create(&vec!["dir", "inbound"]).clone(),
  outbound: family.get_or_create(&vec!["dir", "outbound"]).clone(), // deadlocks
}

This is due to the RwLockReadGuard returned by each call to Family::get_or_create being temporaries that are not dropped until the end of the statement.

The user-facing solution is to create separate bindings for each metric:

// ...

let inbound - family.get_or_create(&vec!["dir", "inbound"]).clone();
let outbound = family.get_or_create(&vec!["dir", "outbound"]).clone(); // no deadlock
Metrics { inbound, outbound }

However, this is a footgun that can be pretty tricky to debug. Is there an alternative API here that would work, perhaps just returning the metric directly instead of a mutex guard?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions