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 { import {
getRanks, getRanks,
resetDailyRanks, resetDailyRanks,
saveDailyTop, getPrevTop,
loadDailyTop, getOnlineUserStats,
storeOnlinUserAmount,
getCountryDailyHistory,
getTopDailyHistory,
} from '../data/redis/ranks'; } from '../data/redis/ranks';
import socketEvents from '../socket/socketEvents'; import socketEvents from '../socket/socketEvents';
import logger from './logger'; import logger from './logger';
import { MINUTE } from './constants'; import { MINUTE } from './constants';
import { DailyCron } from '../utils/cron'; import { DailyCron, HourlyCron } from '../utils/cron';
class Ranks { class Ranks {
constructor() { constructor() {
this.resetDailyRanking = this.resetDailyRanking.bind(this);
this.ranks = { this.ranks = {
dailyRanking: [], dailyRanking: [],
ranking: [], ranking: [],
prevTop: [], prevTop: [],
onlineStats: [],
cHistStats: [],
histStats: [],
}; };
/* /*
* we go through socketEvents for sharding * we go through socketEvents for sharding
@ -35,18 +40,34 @@ class Ranks {
} }
async initialize() { async initialize() {
this.ranks.prevTop = await loadDailyTop(); try {
await Ranks.updateRanking(); 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); 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() { static async updateRanking() {
/* // only main shard does it
* only main shard updates and sends it to others
*/
if (!socketEvents.amIImportant()) { if (!socketEvents.amIImportant()) {
return; return null;
} }
const ranking = await populateRanking( const ranking = await populateRanking(
await getRanks( await getRanks(
@ -60,21 +81,78 @@ class Ranks {
1, 1,
100, 100,
)); ));
socketEvents.rankingListUpdate({ ranking, dailyRanking }); const ret = {
ranking,
dailyRanking,
};
socketEvents.rankingListUpdate(ret);
return ret;
} }
async resetDailyRanking() { /*
/* * get online counter stats,
* only main shard updates and sends it to others * 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()) { if (!socketEvents.amIImportant()) {
return; return;
} }
const prevTop = await saveDailyTop(this.ranks.dailyRanking);
socketEvents.rankingListUpdate({ prevTop });
logger.info('Resetting Daily Ranking'); logger.info('Resetting Daily Ranking');
await resetDailyRanks(); await resetDailyRanks();
await Ranks.updateRanking(); await Ranks.dailyUpdateRanking();
} }
} }

View File

@ -125,7 +125,9 @@ export default async function drawByOffsets(
throw new Error(7); 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 // not in top ten
throw new Error(12); throw new Error(12);
} }

View File

@ -573,3 +573,16 @@ export function combineObjects(a, b) {
} }
return 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 * counter for daily and total pixels and ranking
*/ */
import client from './client'; import client from './client';
import { getDateKeyOfTs } from '../../core/utils';
import logger from '../../core/logger';
export const RANKED_KEY = 'rank'; export const RANKED_KEY = 'rank';
export const DAILY_RANKED_KEY = 'rankd'; export const DAILY_RANKED_KEY = 'rankd';
@ -11,7 +10,7 @@ export const DAILY_CRANKED_KEY = 'crankd';
const PREV_DAY_TOP_KEY = 'prankd'; const PREV_DAY_TOP_KEY = 'prankd';
const DAY_STATS_RANKS_KEY = 'ds'; const DAY_STATS_RANKS_KEY = 'ds';
const CDAY_STATS_RANKS_KEY = 'cds'; const CDAY_STATS_RANKS_KEY = 'cds';
const PREV_DAILY_TOP_KEY = 'prevtop'; const ONLINE_CNTR_KEY = 'tonl';
/* /*
* get pixelcount and ranking * get pixelcount and ranking
@ -81,6 +80,105 @@ export async function getRanks(daily, start, amount) {
return ret; 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 * reset daily ranks
* @return boolean for success * @return boolean for success
@ -91,57 +189,15 @@ export async function resetDailyRanks() {
REV: true, REV: true,
}); });
// store day // store day
const yesterday = new Date(Date.now() - 1000 * 3600 * 24); const dateKey = getDateKeyOfTs(
let day = yesterday.getUTCDate(); Date.now() - 1000 * 3600 * 24,
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
await client.rename( await client.rename(
DAILY_RANKED_KEY, DAILY_RANKED_KEY,
`${DAY_STATS_RANKS_KEY}:${dateKey}`, `${DAY_STATS_RANKS_KEY}:${dateKey}`,
); );
/*
await client.zUnionStore(
`${DAY_STATS_RANKS_KEY}:${dateKey}`,
DAILY_RANKED_KEY,
);
*/
await client.rename( await client.rename(
DAILY_CRANKED_KEY, DAILY_CRANKED_KEY,
`${CDAY_STATS_RANKS_KEY}:${dateKey}`, `${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) : [];
} }