2

I am using io.prometheus.client.Gauge to implement a thread safety counter to calculate the number of events processed in a time frame. Now there are several threads that are processing the events. And all can update the counter when it finishes the processing. My question ins is Gauge counters are thread safety in nature? Following is my implementation.

    private Map<String, Gauge> gaugeMap = new ConcurrentHashMap<>();
    
    // This method will be called to register the Gauge
    private void registerCounter(String metricName) {
        Gauge gauge = Gauge.build()
                .name(metricName)
                .help(metricName)
                .register(meterRegistry.getPrometheusRegistry());
        gaugeMap.put(metricName, gauge);
    }
    
    public void incrementCounter(String metricName) {
        if (isCounterPresent(metricName)) {
            gaugeMap.get(metricName).inc();
        }
    }
   public void incrementCounter(String metricName, long value) {
        if (isCounterPresent(metricName)) {
            gaugeMap.get(metricName).inc(value);
        }
    }

Following is my client code.

// on application startup I am calling registerCounter for all metrics
@PostConstruct
private void registerMetrics(List<String> metricList) {
    // for each of metricList --> call registerCounter(String metricName)
}


Thread1
-------------------
// process N events
// call incrementCounter("metric-1", N);

Thread2
-------------------
// process N events
// call incrementCounter("metric-1", N);

Thread3
-------------------
// process N events
// call incrementCounter("metric-1", N);

Now my question is as multiple threads are incrementing the same counter, then will it give me the correct value?

Arpan Das
  • 1,015
  • 3
  • 24
  • 57
  • `gaugeMap.put(metricName, gauge);` <- you should use `computeIfAbsent`, otherwise it's possible for multiple threads to put same key in `Map` – rkosegi Sep 23 '20 at 09:50
  • Yes, but that will not be the case, as the `registerCounter` is getting called from `@PostConstruct`, so it will be get called on application startup by the strat up thread only. And once the application is up, there is no registration call at all. – Arpan Das Sep 23 '20 at 14:55

2 Answers2

3

The gauge is thread safe and uses CAS operation to support non-blocking state update. All the metric collectors are. In the end they has to be exposed by the endpoint in separate thread. Hence the state should be consistent for read and writes. One particular thing to keep in mind is that the higher is contention the lower is efficiency of CAS operation. So make sure you don't expose it for tens of threads that are trying to update it at the same time.

Secondly, using gauge as counter is not a good idea as there is a special type for that - Counter. Some Prometheus functions are specifically implemented and optimized to be used for counters - rate, irate etc. So in case you need to collect and expose the number of events processed by several threads the recommendation is to use Counter.

1

Prometheus JVM Client README states that:

the default inc(), dec() and set() methods on Gauge take care of thread safety

Anar Sultanov
  • 3,016
  • 2
  • 17
  • 27