0

I have a custom micrometer metrics in spring boot application configured with Prometheus which scrapes the metrics every 15s.

The custom metrics is querying the db every 1 min. As I have 2 instances of this service running, both the instances tries to run the same query every 1 minute.

package com.test;

import com.entity.Foo;
import com.repo.FooRepository;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.function.Supplier;

@Component
public class MonitoringService {
    private final MeterRegistry meterRegistry;
    private final Gauge fooCount;
    private final FooRepository<Foo> fooRepository;

    
    @Autowired
    public MonitoringService(final FooRepository<Foo> fooRepository,
                             final MeterRegistry meterRegistry) {
        this.fooRepository = fooRepository;
        this.meterRegistry = meterRegistry;
        fooCount = Gauge.builder("foo_count", checkFooCount())
                .description("Number of foo count")
                .register(meterRegistry);
    }

    @Scheduled(fixedDelayString = "PT1M", initialDelayString = "PT1M")
    public Supplier<Number> checkFooCount() {
        return ()-> fooRepository.getTotalFooCount();
    }

}

Is there anyway I can configure to run this metrics in any 1 instance of my spring boot application?

DarkCrow
  • 785
  • 2
  • 8
  • 29
  • What is the point in making `checkFooCount()` a `@Scheduled` task? Also, suppose you managed to make only one instance of your service hit the database to get the metric value. What would other instances return to Prometheus as a value for this metric? – dekkard Dec 03 '22 at 21:48
  • The scheduled is not needed here, I added because my scrape time out is very less, And the metric value would be the same. I just waned to avoid running this multiple times. – DarkCrow Dec 06 '22 at 11:51
  • A kubernetes job would start, fetch the data and end itself. (You could set some delay to allow the data to be collected by prometheus before the pod stops.) – Sascha Doerdelmann Dec 06 '22 at 14:55
  • See [Kubernetes cron job to run a query on couchbase database](https://stackoverflow.com/a/74731435/11934850) for a solution with Couchbase and Kubernetes Jobs. – Sascha Doerdelmann Dec 08 '22 at 14:07

2 Answers2

0

Depending of your runtime environment you could either use Quartz with a persistent job store and clustering option or use a single App to get this job done. For the latter you might want to use something like Kubernetes Jobs.

Quartz can schedule the job on just one instance, while all instances are equally configured. The Kubernetes job would be a separate pod next to your application that does just this task. A kubernetes job would start, fetch the data and end itself. (You could set some delay to allow the data to be collected by prometheus before the pod stops.) Both job frameworks work with cron-like configuration.

But wouldn't it be a better solution to use database tools instead of some Java application for counting database rows:

  • what kind of database tools you mean? I explored mysqld-exporter for prometheus but I couldn't configure any custom queries with my application tables. – DarkCrow Nov 28 '22 at 15:13
  • I've edited the answer to cope with this issue, too. – Sascha Doerdelmann Nov 28 '22 at 16:36
  • Yes I have explored this option. I think there is no out of box support for custom metrics in mysql-exporter. We had to use some third party repo's like you mentioned above to get the custom metrics. It might not be acceptable solution to use third party repo in the organizations. – DarkCrow Nov 29 '22 at 10:52
  • Give Quartz a try. I did this in a similar situation and it works as expected. The only downside is, that you need to add some Quartz tables to the database. With the Spring Boot integration of Quartz this will be done automatically but you can also do it differently, for example you can also run the script with Liquibase. – Sascha Doerdelmann Nov 29 '22 at 11:45
0

Sharing the metric's query result among the instances will most likely require your services to interact with a third party that'll store the latest result. That might be a special table in a DB, a distributed configuration server like etcd or Zookeeper or a distributed cache like Apache Ignite. Obviously anything of this will add to complexity of the system and require more effort to setup and maintain then just letting all the instances run a counting query.

On the other hand you can try to optimize this query itself if it's really slow. For example if insertions to the queried table are relatively seldom you could setup an aggregation on the DB side - with each write to this table also update the total count in a separate table. This way your services will run a simple query that returns a single result of the already prepared total count.

dekkard
  • 6,121
  • 1
  • 16
  • 26
  • About "optimize this query itself". I think, slowness should not be the point. Counting rows can also be a burdon for the database. Although the question refers to mysql, I'd like to mention the function APPROX_COUNT_DISTINCT, which is available in Oracle DBMS and SQL Server. It does not deliver exact rowcounts but it is not as resource intensive as COUNT. – Sascha Doerdelmann Dec 06 '22 at 17:01