0

I encountered an error while using timefold, and it seems to be related to registering the Collector. Below is my code and the error message.

 @Test
    public void testOptaPlanner() {
        List<Shift> shifts = Mock.generateShift();
        List<Employee> employees = scheduleForecastService.extractEmployees(1684765798377332736L, 11111, LocalDate.of(2023, 8, 7), LocalDate.of(2023, 8, 7));
        SolverConfig solverConfig = new SolverConfig()
                .withSolutionClass(TimeTable.class)
                .withEntityClasses(Shift.class)
                .withConstraintProviderClass(TimeTableConstraintProvider.class)
                .withTerminationConfig(new TerminationConfig()
                        .withBestScoreLimit("0hard/-2147483648soft")
                        .withSecondsSpentLimit(300L)
                )
                .withEnvironmentMode(EnvironmentMode.FAST_ASSERT);
        //设置线程数
//        solverConfig.setMoveThreadCount("16");
//        solverConfig.setThreadFactoryClass(Mock.MyThreadFactory.class);
        //随机种子
        solverConfig.setRandomType(RandomType.MERSENNE_TWISTER);
        solverConfig.setRandomSeed(RandomUtils.nextLong(new Random(), 2147483647L));

        SolverFactory<TimeTable> solverFactory = SolverFactory
                .create(solverConfig);
        // Load the problem
        TimeTable problem = new TimeTable(shifts, employees);
        // Solve the problem
        Solver<TimeTable> solver = solverFactory.buildSolver();
        TimeTable solution = solver.solve(problem);
        // Display the result
        Mock.printTimetable(solution);
    }
java.lang.IllegalArgumentException: Failed to register Collector of type MicrometerCollector: The Collector exposes the same name multiple times: timefold_solver_solve_duration_seconds

    at io.prometheus.client.CollectorRegistry.assertNoDuplicateNames(CollectorRegistry.java:71)
    at io.prometheus.client.CollectorRegistry.register(CollectorRegistry.java:51)
    at io.prometheus.client.Collector.register(Collector.java:308)
    at io.micrometer.prometheus.PrometheusMeterRegistry.lambda$applyToCollector$16(PrometheusMeterRegistry.java:479)
    at java.base/java.util.concurrent.ConcurrentHashMap.compute(ConcurrentHashMap.java:1916)
    at io.micrometer.prometheus.PrometheusMeterRegistry.applyToCollector(PrometheusMeterRegistry.java:475)
    at io.micrometer.prometheus.PrometheusMeterRegistry.newLongTaskTimer(PrometheusMeterRegistry.java:288)
    at io.micrometer.core.instrument.MeterRegistry$More.lambda$longTaskTimer$0(MeterRegistry.java:883)
    at io.micrometer.core.instrument.MeterRegistry.getOrCreateMeter(MeterRegistry.java:618)
    at io.micrometer.core.instrument.MeterRegistry.registerMeterIfNecessary(MeterRegistry.java:570)
    at io.micrometer.core.instrument.MeterRegistry.access$600(MeterRegistry.java:60)
    at io.micrometer.core.instrument.MeterRegistry$More.longTaskTimer(MeterRegistry.java:880)
    at io.micrometer.core.instrument.LongTaskTimer$Builder.register(LongTaskTimer.java:421)
    at io.micrometer.core.instrument.composite.CompositeLongTaskTimer.registerNewMeter(CompositeLongTaskTimer.java:102)
    at io.micrometer.core.instrument.composite.CompositeLongTaskTimer.registerNewMeter(CompositeLongTaskTimer.java:30)
    at io.micrometer.core.instrument.composite.AbstractCompositeMeter.add(AbstractCompositeMeter.java:68)
    at java.base/java.lang.Iterable.forEach(Iterable.java:75)
    at java.base/java.util.Collections$SetFromMap.forEach(Collections.java:5700)
    at io.micrometer.core.instrument.composite.CompositeMeterRegistry.lambda$new$0(CompositeMeterRegistry.java:67)
    at io.micrometer.core.instrument.composite.CompositeMeterRegistry.lock(CompositeMeterRegistry.java:189)
    at io.micrometer.core.instrument.composite.CompositeMeterRegistry.lambda$new$1(CompositeMeterRegistry.java:67)
    at io.micrometer.core.instrument.MeterRegistry.getOrCreateMeter(MeterRegistry.java:627)
    at io.micrometer.core.instrument.MeterRegistry.registerMeterIfNecessary(MeterRegistry.java:570)
    at io.micrometer.core.instrument.MeterRegistry.access$600(MeterRegistry.java:60)
    at io.micrometer.core.instrument.MeterRegistry$More.longTaskTimer(MeterRegistry.java:880)
    at io.micrometer.core.instrument.LongTaskTimer$Builder.register(LongTaskTimer.java:421)
    at io.micrometer.core.instrument.MeterRegistry$More.longTaskTimer(MeterRegistry.java:871)
    at io.micrometer.core.instrument.MeterRegistry$More.longTaskTimer(MeterRegistry.java:861)
    at io.micrometer.core.instrument.Metrics$More.longTaskTimer(Metrics.java:246)
    at ai.timefold.solver.core.impl.solver.DefaultSolver.solve(DefaultSolver.java:178)

springboot version: 2.6.13, timefold version: 1.0.0

I tried excluding the micrometer-core dependency in timefold and also attempted to use filters to exclude metric measurements, but it didn't take effect.

@Bean
    public MeterFilter customMeterFilter() {
        return new MeterFilter() {
            @Override
            public Meter.Id map(Meter.Id id) {
                // 在这里可以对指标进行过滤或修改
                // 返回null表示丢弃该指标,返回id表示保留该指标
                String name = id.getName();
                if (name.startsWith("optaplanner.solver.")) {
                    return id.withName(""); // 丢弃以 "optaplanner_solver_" 开头的指标
                }
                return id; // 保留其他指标
            }
        };
    }

I also tried switching back to OptaPlanner 9.41.0 version, but the same error still occurs.

1 Answers1

0

It seems you have inconsistencies within your tags across your time series: e.g.:

timefold_solver_solve_duration_seconds{tagA="a"} 5
timefold_solver_solve_duration_seconds{tagB="b"} 5

This is unsupported by the Prometheus Java client and discouraged by the Prometheus server. When you add tags, you make sure all the keys are always present, if you don't have a value, you can use none or unknown, e.g.:

timefold_solver_solve_duration_seconds{tagA="a",tagB="none"} 5
timefold_solver_solve_duration_seconds{tagA="none",tagB="b"} 5
Jonatan Ivanov
  • 4,895
  • 2
  • 15
  • 30