[O] Ranking optimizations

This commit is contained in:
Azalea
2026-08-02 17:38:23 +00:00
parent 05e613d667
commit 67971e14d5
3 changed files with 332 additions and 10 deletions

View File

@@ -69,10 +69,18 @@ abstract class GameApiController<T : IUserData>(val name: String, userDataClass:
fun rankingCacheInit() = thread { rankingCacheRun() }
// Every 20 minutes
@Scheduled(fixedRate = 20, timeUnit = TimeUnit.MINUTES)
@Scheduled(fixedRate = 20, initialDelay = 20, timeUnit = TimeUnit.MINUTES)
fun rankingCacheRun() = rankingCacheLock.maybeLock { rankingCacheCompute() }
private val tableName = when (name) { "mai2" -> "maimai2"; "chu3" -> "chusan"; else -> name }
private val userDataTable = when (name) {
"mai2" -> "maimai2_user_detail"
"chu3" -> "chusan_user_data"
"ongeki" -> "ongeki_user_data"
else -> "wacca_user"
}
private val userCardColumn = if (name == "chu3") "card_id" else "aime_card_id"
fun rankingCacheCompute() {
val time = millis()
rankingCache = us.em.createNativeQuery(
@@ -82,20 +90,21 @@ abstract class GameApiController<T : IUserData>(val name: String, userDataClass:
u.user_name,
${if (name == "ongeki") "u.new_player_rating" else "u.player_rating"} AS rating,
u.last_play_date,
AVG(p.achievement) / 10000.0 AS acc,
SUM(p.is_full_combo) AS fc,
SUM(p.is_all_perfect) AS ap,
r.achievement_sum / NULLIF(r.play_count, 0) / 10000.0 AS acc,
r.full_combo_count AS fc,
r.all_perfect_count AS ap,
c.ranking_banned or COALESCE(a.opt_out_of_leaderboard, 0) or c.status = 12 AS hide,
a.username
FROM ${tableName}_user_playlog_view p
JOIN ${tableName}_user_data_view u ON p.user_id = u.id
JOIN sega_card c ON u.aime_card_id = c.id
FROM ${tableName}_user_ranking_cache r
JOIN $userDataTable u ON r.user_id = u.id
JOIN sega_card c ON u.$userCardColumn = c.id
LEFT JOIN aqua_net_user a ON c.net_user_id = a.au_id
GROUP BY p.user_id, u.player_rating
HAVING NOT hide ${if (name == "ongeki") "AND rating > 0" else "" /* Hide users on Ongeki 1.45 and below */}
WHERE r.play_count > 0
AND NOT (c.ranking_banned or COALESCE(a.opt_out_of_leaderboard, 0) or c.status = 12)
${if (name == "ongeki") "AND u.new_player_rating > 0" else "" /* Hide users on Ongeki 1.45 and below */}
ORDER BY rating DESC;
"""
).exec.mapIndexed { i, it ->
).setHint("jakarta.persistence.query.timeout", 60_000).exec.mapIndexed { i, it ->
GenericRankingPlayer(
rank = i + 1,
id = it[0]!!.long,

View File

@@ -0,0 +1,309 @@
-- Ranking previously aggregated every playlog every 20 minutes. Keep exact,
-- disposable aggregates in game-specific cache tables with real foreign keys.
CREATE TABLE maimai2_user_ranking_cache
(
user_id BIGINT NOT NULL,
achievement_sum BIGINT NOT NULL DEFAULT 0,
play_count BIGINT NOT NULL DEFAULT 0,
full_combo_count BIGINT NOT NULL DEFAULT 0,
all_perfect_count BIGINT NOT NULL DEFAULT 0,
CONSTRAINT pk_maimai2_user_ranking_cache PRIMARY KEY (user_id),
CONSTRAINT fk_maimai2_user_ranking_cache_user FOREIGN KEY (user_id)
REFERENCES maimai2_user_detail (id) ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE TABLE chusan_user_ranking_cache
(
user_id BIGINT NOT NULL,
achievement_sum BIGINT NOT NULL DEFAULT 0,
play_count BIGINT NOT NULL DEFAULT 0,
full_combo_count BIGINT NOT NULL DEFAULT 0,
all_perfect_count BIGINT NOT NULL DEFAULT 0,
CONSTRAINT pk_chusan_user_ranking_cache PRIMARY KEY (user_id),
CONSTRAINT fk_chusan_user_ranking_cache_user FOREIGN KEY (user_id)
REFERENCES chusan_user_data (id) ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE TABLE ongeki_user_ranking_cache
(
user_id BIGINT NOT NULL,
achievement_sum BIGINT NOT NULL DEFAULT 0,
play_count BIGINT NOT NULL DEFAULT 0,
full_combo_count BIGINT NOT NULL DEFAULT 0,
all_perfect_count BIGINT NOT NULL DEFAULT 0,
CONSTRAINT pk_ongeki_user_ranking_cache PRIMARY KEY (user_id),
CONSTRAINT fk_ongeki_user_ranking_cache_user FOREIGN KEY (user_id)
REFERENCES ongeki_user_data (id) ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE TABLE wacca_user_ranking_cache
(
user_id BIGINT NOT NULL,
achievement_sum BIGINT NOT NULL DEFAULT 0,
play_count BIGINT NOT NULL DEFAULT 0,
full_combo_count BIGINT NOT NULL DEFAULT 0,
all_perfect_count BIGINT NOT NULL DEFAULT 0,
CONSTRAINT pk_wacca_user_ranking_cache PRIMARY KEY (user_id),
CONSTRAINT fk_wacca_user_ranking_cache_user FOREIGN KEY (user_id)
REFERENCES wacca_user (id) ON DELETE CASCADE ON UPDATE CASCADE
);
INSERT INTO maimai2_user_ranking_cache
(user_id, achievement_sum, play_count, full_combo_count, all_perfect_count)
SELECT
user_id,
SUM(achievement),
COUNT(*),
SUM(IF(max_combo = total_combo, 1, 0)),
SUM(IF(
(tap_miss + tap_good + tap_great = 0) AND
(hold_miss + hold_good + hold_great = 0) AND
(slide_miss + slide_good + slide_great = 0) AND
(touch_miss + touch_good + touch_great = 0) AND
(break_miss + break_good + break_great = 0),
1, 0
))
FROM maimai2_user_playlog
GROUP BY user_id;
INSERT INTO chusan_user_ranking_cache
(user_id, achievement_sum, play_count, full_combo_count, all_perfect_count)
SELECT user_id, SUM(score), COUNT(*), SUM(is_full_combo), SUM(is_all_justice)
FROM chusan_user_playlog
GROUP BY user_id;
INSERT INTO ongeki_user_ranking_cache
(user_id, achievement_sum, play_count, full_combo_count, all_perfect_count)
SELECT user_id, SUM(tech_score), COUNT(*), SUM(is_full_combo), SUM(is_all_break)
FROM ongeki_user_playlog
GROUP BY user_id;
INSERT INTO wacca_user_ranking_cache
(user_id, achievement_sum, play_count, full_combo_count, all_perfect_count)
SELECT user_id, SUM(achievement), COUNT(*), SUM(is_full_combo), SUM(is_all_perfect)
FROM wacca_user_playlog
GROUP BY user_id;
CREATE TRIGGER trg_maimai2_rank_insert
AFTER INSERT ON maimai2_user_playlog
FOR EACH ROW
INSERT INTO maimai2_user_ranking_cache
(user_id, achievement_sum, play_count, full_combo_count, all_perfect_count)
VALUES
(NEW.user_id, NEW.achievement, 1,
IF(NEW.max_combo = NEW.total_combo, 1, 0),
IF(
(NEW.tap_miss + NEW.tap_good + NEW.tap_great = 0) AND
(NEW.hold_miss + NEW.hold_good + NEW.hold_great = 0) AND
(NEW.slide_miss + NEW.slide_good + NEW.slide_great = 0) AND
(NEW.touch_miss + NEW.touch_good + NEW.touch_great = 0) AND
(NEW.break_miss + NEW.break_good + NEW.break_great = 0),
1, 0
))
ON DUPLICATE KEY UPDATE
achievement_sum = achievement_sum + NEW.achievement,
play_count = play_count + 1,
full_combo_count = full_combo_count + IF(NEW.max_combo = NEW.total_combo, 1, 0),
all_perfect_count = all_perfect_count + IF(
(NEW.tap_miss + NEW.tap_good + NEW.tap_great = 0) AND
(NEW.hold_miss + NEW.hold_good + NEW.hold_great = 0) AND
(NEW.slide_miss + NEW.slide_good + NEW.slide_great = 0) AND
(NEW.touch_miss + NEW.touch_good + NEW.touch_great = 0) AND
(NEW.break_miss + NEW.break_good + NEW.break_great = 0),
1, 0
);
CREATE TRIGGER trg_maimai2_rank_delete
AFTER DELETE ON maimai2_user_playlog
FOR EACH ROW
UPDATE maimai2_user_ranking_cache SET
achievement_sum = achievement_sum - OLD.achievement,
play_count = play_count - 1,
full_combo_count = full_combo_count - IF(OLD.max_combo = OLD.total_combo, 1, 0),
all_perfect_count = all_perfect_count - IF(
(OLD.tap_miss + OLD.tap_good + OLD.tap_great = 0) AND
(OLD.hold_miss + OLD.hold_good + OLD.hold_great = 0) AND
(OLD.slide_miss + OLD.slide_good + OLD.slide_great = 0) AND
(OLD.touch_miss + OLD.touch_good + OLD.touch_great = 0) AND
(OLD.break_miss + OLD.break_good + OLD.break_great = 0),
1, 0
)
WHERE user_id = OLD.user_id;
CREATE TRIGGER trg_maimai2_rank_update_remove
AFTER UPDATE ON maimai2_user_playlog
FOR EACH ROW
UPDATE maimai2_user_ranking_cache SET
achievement_sum = achievement_sum - OLD.achievement,
play_count = play_count - 1,
full_combo_count = full_combo_count - IF(OLD.max_combo = OLD.total_combo, 1, 0),
all_perfect_count = all_perfect_count - IF(
(OLD.tap_miss + OLD.tap_good + OLD.tap_great = 0) AND
(OLD.hold_miss + OLD.hold_good + OLD.hold_great = 0) AND
(OLD.slide_miss + OLD.slide_good + OLD.slide_great = 0) AND
(OLD.touch_miss + OLD.touch_good + OLD.touch_great = 0) AND
(OLD.break_miss + OLD.break_good + OLD.break_great = 0),
1, 0
)
WHERE user_id = OLD.user_id;
CREATE TRIGGER trg_maimai2_rank_update_add
AFTER UPDATE ON maimai2_user_playlog
FOR EACH ROW FOLLOWS trg_maimai2_rank_update_remove
INSERT INTO maimai2_user_ranking_cache
(user_id, achievement_sum, play_count, full_combo_count, all_perfect_count)
VALUES
(NEW.user_id, NEW.achievement, 1,
IF(NEW.max_combo = NEW.total_combo, 1, 0),
IF(
(NEW.tap_miss + NEW.tap_good + NEW.tap_great = 0) AND
(NEW.hold_miss + NEW.hold_good + NEW.hold_great = 0) AND
(NEW.slide_miss + NEW.slide_good + NEW.slide_great = 0) AND
(NEW.touch_miss + NEW.touch_good + NEW.touch_great = 0) AND
(NEW.break_miss + NEW.break_good + NEW.break_great = 0),
1, 0
))
ON DUPLICATE KEY UPDATE
achievement_sum = achievement_sum + NEW.achievement,
play_count = play_count + 1,
full_combo_count = full_combo_count + IF(NEW.max_combo = NEW.total_combo, 1, 0),
all_perfect_count = all_perfect_count + IF(
(NEW.tap_miss + NEW.tap_good + NEW.tap_great = 0) AND
(NEW.hold_miss + NEW.hold_good + NEW.hold_great = 0) AND
(NEW.slide_miss + NEW.slide_good + NEW.slide_great = 0) AND
(NEW.touch_miss + NEW.touch_good + NEW.touch_great = 0) AND
(NEW.break_miss + NEW.break_good + NEW.break_great = 0),
1, 0
);
CREATE TRIGGER trg_chusan_rank_insert
AFTER INSERT ON chusan_user_playlog
FOR EACH ROW
INSERT INTO chusan_user_ranking_cache
(user_id, achievement_sum, play_count, full_combo_count, all_perfect_count)
VALUES (NEW.user_id, NEW.score, 1, NEW.is_full_combo, NEW.is_all_justice)
ON DUPLICATE KEY UPDATE
achievement_sum = achievement_sum + NEW.score,
play_count = play_count + 1,
full_combo_count = full_combo_count + NEW.is_full_combo,
all_perfect_count = all_perfect_count + NEW.is_all_justice;
CREATE TRIGGER trg_chusan_rank_delete
AFTER DELETE ON chusan_user_playlog
FOR EACH ROW
UPDATE chusan_user_ranking_cache SET
achievement_sum = achievement_sum - OLD.score,
play_count = play_count - 1,
full_combo_count = full_combo_count - OLD.is_full_combo,
all_perfect_count = all_perfect_count - OLD.is_all_justice
WHERE user_id = OLD.user_id;
CREATE TRIGGER trg_chusan_rank_update_remove
AFTER UPDATE ON chusan_user_playlog
FOR EACH ROW
UPDATE chusan_user_ranking_cache SET
achievement_sum = achievement_sum - OLD.score,
play_count = play_count - 1,
full_combo_count = full_combo_count - OLD.is_full_combo,
all_perfect_count = all_perfect_count - OLD.is_all_justice
WHERE user_id = OLD.user_id;
CREATE TRIGGER trg_chusan_rank_update_add
AFTER UPDATE ON chusan_user_playlog
FOR EACH ROW FOLLOWS trg_chusan_rank_update_remove
INSERT INTO chusan_user_ranking_cache
(user_id, achievement_sum, play_count, full_combo_count, all_perfect_count)
VALUES (NEW.user_id, NEW.score, 1, NEW.is_full_combo, NEW.is_all_justice)
ON DUPLICATE KEY UPDATE
achievement_sum = achievement_sum + NEW.score,
play_count = play_count + 1,
full_combo_count = full_combo_count + NEW.is_full_combo,
all_perfect_count = all_perfect_count + NEW.is_all_justice;
CREATE TRIGGER trg_ongeki_rank_insert
AFTER INSERT ON ongeki_user_playlog
FOR EACH ROW
INSERT INTO ongeki_user_ranking_cache
(user_id, achievement_sum, play_count, full_combo_count, all_perfect_count)
VALUES (NEW.user_id, NEW.tech_score, 1, NEW.is_full_combo, NEW.is_all_break)
ON DUPLICATE KEY UPDATE
achievement_sum = achievement_sum + NEW.tech_score,
play_count = play_count + 1,
full_combo_count = full_combo_count + NEW.is_full_combo,
all_perfect_count = all_perfect_count + NEW.is_all_break;
CREATE TRIGGER trg_ongeki_rank_delete
AFTER DELETE ON ongeki_user_playlog
FOR EACH ROW
UPDATE ongeki_user_ranking_cache SET
achievement_sum = achievement_sum - OLD.tech_score,
play_count = play_count - 1,
full_combo_count = full_combo_count - OLD.is_full_combo,
all_perfect_count = all_perfect_count - OLD.is_all_break
WHERE user_id = OLD.user_id;
CREATE TRIGGER trg_ongeki_rank_update_remove
AFTER UPDATE ON ongeki_user_playlog
FOR EACH ROW
UPDATE ongeki_user_ranking_cache SET
achievement_sum = achievement_sum - OLD.tech_score,
play_count = play_count - 1,
full_combo_count = full_combo_count - OLD.is_full_combo,
all_perfect_count = all_perfect_count - OLD.is_all_break
WHERE user_id = OLD.user_id;
CREATE TRIGGER trg_ongeki_rank_update_add
AFTER UPDATE ON ongeki_user_playlog
FOR EACH ROW FOLLOWS trg_ongeki_rank_update_remove
INSERT INTO ongeki_user_ranking_cache
(user_id, achievement_sum, play_count, full_combo_count, all_perfect_count)
VALUES (NEW.user_id, NEW.tech_score, 1, NEW.is_full_combo, NEW.is_all_break)
ON DUPLICATE KEY UPDATE
achievement_sum = achievement_sum + NEW.tech_score,
play_count = play_count + 1,
full_combo_count = full_combo_count + NEW.is_full_combo,
all_perfect_count = all_perfect_count + NEW.is_all_break;
CREATE TRIGGER trg_wacca_rank_insert
AFTER INSERT ON wacca_user_playlog
FOR EACH ROW
INSERT INTO wacca_user_ranking_cache
(user_id, achievement_sum, play_count, full_combo_count, all_perfect_count)
VALUES (NEW.user_id, NEW.achievement, 1, NEW.is_full_combo, NEW.is_all_perfect)
ON DUPLICATE KEY UPDATE
achievement_sum = achievement_sum + NEW.achievement,
play_count = play_count + 1,
full_combo_count = full_combo_count + NEW.is_full_combo,
all_perfect_count = all_perfect_count + NEW.is_all_perfect;
CREATE TRIGGER trg_wacca_rank_delete
AFTER DELETE ON wacca_user_playlog
FOR EACH ROW
UPDATE wacca_user_ranking_cache SET
achievement_sum = achievement_sum - OLD.achievement,
play_count = play_count - 1,
full_combo_count = full_combo_count - OLD.is_full_combo,
all_perfect_count = all_perfect_count - OLD.is_all_perfect
WHERE user_id = OLD.user_id;
CREATE TRIGGER trg_wacca_rank_update_remove
AFTER UPDATE ON wacca_user_playlog
FOR EACH ROW
UPDATE wacca_user_ranking_cache SET
achievement_sum = achievement_sum - OLD.achievement,
play_count = play_count - 1,
full_combo_count = full_combo_count - OLD.is_full_combo,
all_perfect_count = all_perfect_count - OLD.is_all_perfect
WHERE user_id = OLD.user_id;
CREATE TRIGGER trg_wacca_rank_update_add
AFTER UPDATE ON wacca_user_playlog
FOR EACH ROW FOLLOWS trg_wacca_rank_update_remove
INSERT INTO wacca_user_ranking_cache
(user_id, achievement_sum, play_count, full_combo_count, all_perfect_count)
VALUES (NEW.user_id, NEW.achievement, 1, NEW.is_full_combo, NEW.is_all_perfect)
ON DUPLICATE KEY UPDATE
achievement_sum = achievement_sum + NEW.achievement,
play_count = play_count + 1,
full_combo_count = full_combo_count + NEW.is_full_combo,
all_perfect_count = all_perfect_count + NEW.is_all_perfect;

View File

@@ -0,0 +1,4 @@
-- V1000.7 left two foreign keys on user_card_id. The older restrictive key
-- prevents the newer cascading relationship from deleting print details.
ALTER TABLE maimai2_user_print_detail
DROP FOREIGN KEY IF EXISTS FKDjNkXby95DMyQ9RKem;