save more stats
This commit is contained in:
parent
88eabcdc15
commit
fcb141996e
|
@ -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();
|
||||
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);
|
||||
}
|
||||
|
||||
static async updateRanking() {
|
||||
/*
|
||||
* only main shard updates and sends it to others
|
||||
* get daily and total ranking from database
|
||||
*/
|
||||
static async updateRanking() {
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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}`;
|
||||
}
|
||||
|
|
|
@ -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) : [];
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user