From e5e731149836aef52dd149e0664108d5e4688ef7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Vrba?= Date: Sat, 5 Oct 2024 16:59:14 +0200 Subject: [PATCH] Add a separate metric + db view for user score --- .../main/java/dev/vrba/dubs/domain/Match.java | 3 - .../main/java/dev/vrba/dubs/domain/Score.java | 43 ++++++++++++++ ...cConfiguration.java => MatchesMetric.java} | 8 +-- .../vrba/dubs/metrics/UserScoreMetric.java | 56 ++++++++++++++++++ .../vrba/dubs/repository/ScoreRepository.java | 10 ++++ .../V8__rework_score_leaderboard.sql | 58 +++++++++++++++++++ 6 files changed, 170 insertions(+), 8 deletions(-) create mode 100644 api/src/main/java/dev/vrba/dubs/domain/Score.java rename api/src/main/java/dev/vrba/dubs/metrics/{MatchesMetricConfiguration.java => MatchesMetric.java} (91%) create mode 100644 api/src/main/java/dev/vrba/dubs/metrics/UserScoreMetric.java create mode 100644 api/src/main/java/dev/vrba/dubs/repository/ScoreRepository.java create mode 100644 api/src/main/resources/db/migration/V8__rework_score_leaderboard.sql diff --git a/api/src/main/java/dev/vrba/dubs/domain/Match.java b/api/src/main/java/dev/vrba/dubs/domain/Match.java index fbe9785..157622d 100644 --- a/api/src/main/java/dev/vrba/dubs/domain/Match.java +++ b/api/src/main/java/dev/vrba/dubs/domain/Match.java @@ -49,7 +49,4 @@ public class Match { @MappedProperty("count") private BigInteger count; - - @MappedProperty("points") - private BigInteger points; } diff --git a/api/src/main/java/dev/vrba/dubs/domain/Score.java b/api/src/main/java/dev/vrba/dubs/domain/Score.java new file mode 100644 index 0000000..c514a84 --- /dev/null +++ b/api/src/main/java/dev/vrba/dubs/domain/Score.java @@ -0,0 +1,43 @@ +package dev.vrba.dubs.domain; + +import io.micronaut.data.annotation.Id; +import io.micronaut.data.annotation.MappedEntity; +import io.micronaut.data.annotation.MappedProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.math.BigInteger; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@MappedEntity("score") +public class Score { + @Id + @MappedProperty("row_id") + private Integer id; + + @MappedProperty("user_id") + private String userId; + + @MappedProperty("user_name") + private String userName; + + @MappedProperty("channel_id") + private String channelId; + + @MappedProperty("channel_name") + private String channelName; + + @MappedProperty("guild_id") + private String guildId; + + @MappedProperty("guild_name") + private String guildName; + + @MappedProperty("score") + private BigInteger score; +} diff --git a/api/src/main/java/dev/vrba/dubs/metrics/MatchesMetricConfiguration.java b/api/src/main/java/dev/vrba/dubs/metrics/MatchesMetric.java similarity index 91% rename from api/src/main/java/dev/vrba/dubs/metrics/MatchesMetricConfiguration.java rename to api/src/main/java/dev/vrba/dubs/metrics/MatchesMetric.java index 2b6fc3e..a5dc474 100644 --- a/api/src/main/java/dev/vrba/dubs/metrics/MatchesMetricConfiguration.java +++ b/api/src/main/java/dev/vrba/dubs/metrics/MatchesMetric.java @@ -13,7 +13,7 @@ import java.util.stream.Collectors; @Singleton -public class MatchesMetricConfiguration { +public class MatchesMetric { @NonNull private final MultiGauge gauge; @@ -21,7 +21,7 @@ public class MatchesMetricConfiguration { @NonNull private final MatchRepository repository; - public MatchesMetricConfiguration( + public MatchesMetric( final @NonNull MeterRegistry registry, final @NonNull MatchRepository repository ) { @@ -51,9 +51,7 @@ private MultiGauge.Row mapPatternToGaugeRow(final @NonNull Match match) Tag.of("guild.name", match.getGuildName()), Tag.of("pattern.name", match.getPatternName()), Tag.of("pattern.rare", match.getPatternIsRare().toString()), - Tag.of("pattern.points", match.getPatternPoints().toString()), - Tag.of("points", match.getPoints().toString()) - + Tag.of("pattern.points", match.getPatternPoints().toString()) ), match.getCount() ); diff --git a/api/src/main/java/dev/vrba/dubs/metrics/UserScoreMetric.java b/api/src/main/java/dev/vrba/dubs/metrics/UserScoreMetric.java new file mode 100644 index 0000000..778f70a --- /dev/null +++ b/api/src/main/java/dev/vrba/dubs/metrics/UserScoreMetric.java @@ -0,0 +1,56 @@ +package dev.vrba.dubs.metrics; + +import dev.vrba.dubs.domain.Score; +import dev.vrba.dubs.repository.ScoreRepository; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.MultiGauge; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.Tags; +import io.micronaut.core.annotation.NonNull; +import io.micronaut.scheduling.annotation.Scheduled; +import jakarta.inject.Singleton; + +import java.util.stream.Collectors; + +@Singleton +public class UserScoreMetric { + + @NonNull + private final MultiGauge gauge; + + @NonNull + private final ScoreRepository repository; + + public UserScoreMetric( + final @NonNull MeterRegistry registry, + final @NonNull ScoreRepository repository + ) { + this.gauge = MultiGauge.builder("users.score").register(registry); + this.repository = repository; + } + + @Scheduled(fixedRate = "PT1M") + public void refresh() { + gauge.register( + repository.findAll() + .stream() + .map(this::mapPatternToGaugeRow) + .collect(Collectors.toList()), + true + ); + } + + private MultiGauge.Row mapPatternToGaugeRow(final @NonNull Score score) { + return MultiGauge.Row.of( + Tags.of( + Tag.of("user.id", score.getUserId()), + Tag.of("user.name", score.getUserName()), + Tag.of("channel.id", score.getChannelId()), + Tag.of("channel.name", score.getChannelName()), + Tag.of("guild.id", score.getGuildId()), + Tag.of("guild.name", score.getGuildName()) + ), + score.getScore() + ); + } +} diff --git a/api/src/main/java/dev/vrba/dubs/repository/ScoreRepository.java b/api/src/main/java/dev/vrba/dubs/repository/ScoreRepository.java new file mode 100644 index 0000000..fd41edf --- /dev/null +++ b/api/src/main/java/dev/vrba/dubs/repository/ScoreRepository.java @@ -0,0 +1,10 @@ +package dev.vrba.dubs.repository; + +import dev.vrba.dubs.domain.Score; +import io.micronaut.data.jdbc.annotation.JdbcRepository; +import io.micronaut.data.model.query.builder.sql.Dialect; +import io.micronaut.data.repository.CrudRepository; + +@JdbcRepository(dialect = Dialect.POSTGRES) +public interface ScoreRepository extends CrudRepository { +} diff --git a/api/src/main/resources/db/migration/V8__rework_score_leaderboard.sql b/api/src/main/resources/db/migration/V8__rework_score_leaderboard.sql new file mode 100644 index 0000000..06ab5c5 --- /dev/null +++ b/api/src/main/resources/db/migration/V8__rework_score_leaderboard.sql @@ -0,0 +1,58 @@ +drop view if exists matches; +create or replace view matches as +( +select row_number() over (order by users.id) as row_id, + pattern_name, + pattern_points, + pattern_is_rare, + users.id as user_id, + users.name as user_name, + channels.id as channel_id, + channels.name as channel_name, + guilds.id as guild_id, + guilds.name as guild_name, + count(*) as count +from matched_patterns + left join users on matched_patterns.user_id = users.id + left join channels on matched_patterns.channel_id = channels.id + left join guilds on channels.guild_id = guilds.id +group by users.id, + channels.id, + guilds.id, + pattern_name, + pattern_points, + pattern_is_rare + ); + +create or replace view score as +( +select row_number() over (order by user_id) as row_id, + user_id, + user_name, + channel_id, + channel_name, + guild_id, + guild_name, + sum(pattern_points * count) as score +from (select users.id as user_id, + users.name as user_name, + channels.id as channel_id, + channels.name as channel_name, + guilds.id as guild_id, + guilds.name as guild_name, + pattern_points as pattern_points, + count(*) as count + from matched_patterns + left join users on matched_patterns.user_id = users.id + left join channels on matched_patterns.channel_id = channels.id + left join guilds on channels.guild_id = guilds.id + group by users.id, + channels.id, + guilds.id, + pattern_points) +group by user_id, + user_name, + channel_id, + channel_name, + guild_id, + guild_name);