save more stats

This commit is contained in:
HF 2022-09-24 11:26:15 +02:00
parent 88eabcdc15
commit fcb141996e
4 changed files with 217 additions and 68 deletions

View File

@ -6,22 +6,27 @@ import { populateRanking } from '../data/sql/RegUser';
import {
getRanks,
resetDailyRanks,
saveDailyTop,
loadDailyTop,
getPrevTop,
getOnlineUserStats,
storeOnlinUserAmount,
getCountryDailyHistory,
getTopDailyHistory,
} from '../data/redis/ranks';
import socketEvents from '../socket/socketEvents';
import logger from './logger';
import { MINUTE } from './constants';
import { DailyCron } from '../utils/cron';
import { DailyCron, HourlyCron } from '../utils/cron';
class Ranks {
constructor() {
this.resetDailyRanking = this.resetDailyRanking.bind(this);
this.ranks = {
dailyRanking: [],
ranking: [],
prevTop: [],
onlineStats: [],
cHistStats: [],
histStats: [],
};
/*
* we go through socketEvents for sharding
@ -35,18 +40,34 @@ class Ranks {
}
async initialize() {
this.ranks.prevTop = await loadDailyTop();
await Ranks.updateRanking();
try {
let someRanks = await Ranks.dailyUpdateRanking();
this.ranks = {
...this.ranks,
...someRanks,
};
someRanks = await Ranks.hourlyUpdateRanking();
this.ranks = {
...this.ranks,
...someRanks,
};
await Ranks.updateRanking();
} catch (err) {
console.error(`Error initialize ranks: ${err.message}`);
}
setInterval(Ranks.updateRanking, 5 * MINUTE);
DailyCron.hook(this.resetDailyRanking);
DailyCron.hook(Ranks.setDailyRanking);
setInterval(Ranks.setHourlyRanking, 5 * MINUTE);
//HourlyCron.hook(Ranks.setHourlyRanking);
}
/*
* get daily and total ranking from database
*/
static async updateRanking() {
/*
* only main shard updates and sends it to others
*/
// only main shard does it
if (!socketEvents.amIImportant()) {
return;
return null;
}
const ranking = await populateRanking(
await getRanks(
@ -60,21 +81,78 @@ class Ranks {
1,
100,
));
socketEvents.rankingListUpdate({ ranking, dailyRanking });
const ret = {
ranking,
dailyRanking,
};
socketEvents.rankingListUpdate(ret);
return ret;
}
async resetDailyRanking() {
/*
* only main shard updates and sends it to others
*/
/*
* get online counter stats,
* list of users online per hour
*/
static async hourlyUpdateRanking() {
const onlineStats = await getOnlineUserStats();
const cHistStats = await getCountryDailyHistory();
const histStats = await getTopDailyHistory();
const ret = {
onlineStats,
cHistStats,
histStats,
};
if (socketEvents.amIImportant()) {
// only main shard sends to others
socketEvents.rankingListUpdate(ret);
}
return ret;
}
/*
* get prevTop from database
*/
static async dailyUpdateRanking() {
const prevTop = await populateRanking(
await getPrevTop(),
);
const ret = {
prevTop,
};
if (socketEvents.amIImportant()) {
// only main shard sends to others
socketEvents.rankingListUpdate(ret);
}
return ret;
}
/*
* get and store amount of online users into stats
*/
static async setHourlyRanking() {
if (!socketEvents.amIImportant()) {
return;
}
let amount;
try {
amount = socketEvents.onlineCounter.total;
await storeOnlinUserAmount(amount);
await Ranks.hourlyUpdateRanking();
} catch (err) {
console.error(`error on hourly ranking: ${err.message} / ${amount}`);
}
}
/*
* reset daily rankings, store previous rankings
*/
static async setDailyRanking() {
if (!socketEvents.amIImportant()) {
return;
}
const prevTop = await saveDailyTop(this.ranks.dailyRanking);
socketEvents.rankingListUpdate({ prevTop });
logger.info('Resetting Daily Ranking');
await resetDailyRanks();
await Ranks.updateRanking();
await Ranks.dailyUpdateRanking();
}
}

View File

@ -125,7 +125,9 @@ export default async function drawByOffsets(
throw new Error(7);
}
}
if (canvas.req === 'top' && !rankings.ranks.prevTop.includes(user.id)) {
if (canvas.req === 'top'
&& !rankings.ranks.prevTop.find((r) => r.id === user.id)
) {
// not in top ten
throw new Error(12);
}

View File

@ -573,3 +573,16 @@ export function combineObjects(a, b) {
}
return a + b;
}
/*
* get YYYYMMDD of timestamp
*/
export function getDateKeyOfTs(ts) {
const date = new Date(ts);
let day = date.getUTCDate();
if (day < 10) day = `0${day}`;
let month = date.getUTCMonth() + 1;
if (month < 10) month = `0${month}`;
const year = date.getUTCFullYear();
return `${year}${month}${day}`;
}

View File

@ -2,8 +2,7 @@
* counter for daily and total pixels and ranking
*/
import client from './client';
import logger from '../../core/logger';
import { getDateKeyOfTs } from '../../core/utils';
export const RANKED_KEY = 'rank';
export const DAILY_RANKED_KEY = 'rankd';
@ -11,7 +10,7 @@ export const DAILY_CRANKED_KEY = 'crankd';
const PREV_DAY_TOP_KEY = 'prankd';
const DAY_STATS_RANKS_KEY = 'ds';
const CDAY_STATS_RANKS_KEY = 'cds';
const PREV_DAILY_TOP_KEY = 'prevtop';
const ONLINE_CNTR_KEY = 'tonl';
/*
* get pixelcount and ranking
@ -81,6 +80,105 @@ export async function getRanks(daily, start, amount) {
return ret;
}
/*
* get top 10 from previous day
*/
export async function getPrevTop() {
let prevTop = await client.zRangeWithScores(PREV_DAY_TOP_KEY, 0, 9, {
REV: true,
});
prevTop = prevTop.map((r) => ({
id: Number(r.value),
px: Number(r.score),
}));
return prevTop;
}
/*
* store amount of online Users
*/
export async function storeOnlinUserAmount(amount) {
await client.lPush(ONLINE_CNTR_KEY, String(amount));
await client.lTrim(ONLINE_CNTR_KEY, 0, 14 * 24);
}
/*
* get list of online counters
*/
export async function getOnlineUserStats() {
const onlineStats = await client.lRange(ONLINE_CNTR_KEY, 0, -1);
console.log('STAAATTTSS', onlineStats);
return onlineStats;
}
/*
* get top 10 of daily pixels over the past days
*/
export async function getTopDailyHistory() {
const stats = [];
const users = [];
let ts;
let key;
for (let c = 0; c < 14; c += 1) {
if (!ts) {
ts = Date.now();
key = DAY_STATS_RANKS_KEY;
} else {
ts -= 1000 * 3600 * 24;
const dateKey = getDateKeyOfTs(ts);
key = `${DAY_STATS_RANKS_KEY}:${dateKey}`;
}
// eslint-disable-next-line no-await-in-loop
let dData = await client.zRangeWithScores(key, 0, 9, {
REV: true,
});
dData = dData.map((r) => {
const id = Number(r.value);
if (!users.some((q) => q.id === id)) {
users.push({ id });
}
return {
id,
px: Number(r.score),
};
});
stats.push(dData);
}
return {
users,
stats,
};
}
/*
* get top 10 countries over the past days
*/
export async function getCountryDailyHistory() {
const ret = [];
let ts;
let key;
for (let c = 0; c < 14; c += 1) {
if (!ts) {
ts = Date.now();
key = CDAY_STATS_RANKS_KEY;
} else {
ts -= 1000 * 3600 * 24;
const dateKey = getDateKeyOfTs(ts);
key = `${CDAY_STATS_RANKS_KEY}:${dateKey}`;
}
// eslint-disable-next-line no-await-in-loop
let dData = await client.zRangeWithScores(key, 0, 9, {
REV: true,
});
dData = dData.map((r) => ({
cc: r.value,
px: Number(r.score),
}));
ret.push(dData);
}
return ret;
}
/*
* reset daily ranks
* @return boolean for success
@ -91,57 +189,15 @@ export async function resetDailyRanks() {
REV: true,
});
// store day
const yesterday = new Date(Date.now() - 1000 * 3600 * 24);
let day = yesterday.getUTCDate();
if (day < 10) day = `0${day}`;
let month = yesterday.getUTCMonth() + 1;
if (month < 10) month = `0${month}`;
const year = yesterday.getUTCFullYear();
const dateKey = `${year}${month}${day}`;
// TODO check if this works
const dateKey = getDateKeyOfTs(
Date.now() - 1000 * 3600 * 24,
);
await client.rename(
DAILY_RANKED_KEY,
`${DAY_STATS_RANKS_KEY}:${dateKey}`,
);
/*
await client.zUnionStore(
`${DAY_STATS_RANKS_KEY}:${dateKey}`,
DAILY_RANKED_KEY,
);
*/
await client.rename(
DAILY_CRANKED_KEY,
`${CDAY_STATS_RANKS_KEY}:${dateKey}`,
);
/*
await client.zUnionStore(
`${CDAY_STATS_RANKS_KEY}:${dateKey}`,
DAILY_CRANKED_KEY,
);
// reset daily counter
await client.del(DAILY_RANKED_KEY);
await client.del(DAILY_CRANKED_KEY);
*/
}
/*
* saves the top 10 into redis
* @param dailyRanking Array of dailyRanking
*/
export async function saveDailyTop(dailyRanking) {
const top10 = dailyRanking.slice(0, 10).map((user) => user.id);
const jsonTop = JSON.stringify(top10);
logger.info(`Saving current daily top 10 into redis: ${jsonTop}`);
await client.set(PREV_DAILY_TOP_KEY, jsonTop);
return top10;
}
/*
* load top10 from redis
* @return Promis<Array> Array of user IDs of the top 10
*/
export async function loadDailyTop() {
const jsonTop = await client.get(PREV_DAILY_TOP_KEY);
logger.info(`Loaded current daily top 10 into redis: ${jsonTop}`);
return (jsonTop) ? JSON.parse(jsonTop) : [];
}