save more stats
This commit is contained in:
parent
88eabcdc15
commit
fcb141996e
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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}`;
|
||||||
|
}
|
||||||
|
|
|
@ -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) : [];
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user