rename allowPlace to cooldown and batch incrementing pixelcounts into a
single transaction
This commit is contained in:
parent
100bdb17b5
commit
1fe9b70b9b
|
@ -7,7 +7,7 @@ import {
|
|||
} from './utils';
|
||||
import logger, { pixelLogger } from './logger';
|
||||
import RedisCanvas from '../data/redis/RedisCanvas';
|
||||
import allowPlace from '../data/redis/allowPlace';
|
||||
import allowPlace from '../data/redis/cooldown';
|
||||
import {
|
||||
setPixelByOffset,
|
||||
setPixelByCoords,
|
||||
|
@ -70,7 +70,7 @@ export async function drawByOffsets(
|
|||
let pxlCnt = 0;
|
||||
let rankedPxlCnt = 0;
|
||||
let needProxycheck = 0;
|
||||
const { ip } = user;
|
||||
const { ipSub: ip } = user;
|
||||
|
||||
try {
|
||||
const startTime = Date.now();
|
||||
|
@ -220,7 +220,7 @@ export async function drawByOffsets(
|
|||
}
|
||||
|
||||
if (rankedPxlCnt) {
|
||||
await user.incrementPixelcount(rankedPxlCnt);
|
||||
user.incrementPixelcount(rankedPxlCnt);
|
||||
}
|
||||
|
||||
if (retCode !== 13) {
|
||||
|
|
|
@ -7,11 +7,11 @@
|
|||
* */
|
||||
|
||||
import { QueryTypes, Utils } from 'sequelize';
|
||||
import redis from './redis/client';
|
||||
import logger from '../core/logger';
|
||||
|
||||
import sequelize from './sql/sequelize';
|
||||
import { RegUser, Channel, UserBlock } from './sql';
|
||||
import { incrementPixelcount } from './sql/RegUser';
|
||||
import { setCoolDown, getCoolDown } from './redis/cooldown';
|
||||
import { getIPv6Subnet } from '../utils/ip';
|
||||
import { ADMIN_IDS } from '../core/config';
|
||||
|
||||
|
@ -171,45 +171,16 @@ class User {
|
|||
return (this.regUser) ? this.regUser.name : null;
|
||||
}
|
||||
|
||||
async setWait(wait, canvasId) {
|
||||
// PX is milliseconds expire
|
||||
await redis.set(`cd:${canvasId}:ip:${this.ipSub}`, '', {
|
||||
PX: wait,
|
||||
});
|
||||
if (this.id) {
|
||||
await redis.set(`cd:${canvasId}:id:${this.id}`, '', {
|
||||
PX: wait,
|
||||
});
|
||||
}
|
||||
return true;
|
||||
setWait(wait, canvasId) {
|
||||
return setCoolDown(this.ipSub, this.id, canvasId, wait);
|
||||
}
|
||||
|
||||
async getWait(canvasId) {
|
||||
let ttl = await redis.pTTL(`cd:${canvasId}:ip:${this.ipSub}`);
|
||||
if (this.id) {
|
||||
const ttlid = await redis.pTTL(
|
||||
`cd:${canvasId}:id:${this.id}`,
|
||||
);
|
||||
ttl = Math.max(ttl, ttlid);
|
||||
}
|
||||
logger.debug('ererer', ttl, typeof ttl);
|
||||
|
||||
const wait = ttl < 0 ? 0 : ttl;
|
||||
return wait;
|
||||
getWait(canvasId) {
|
||||
return getCoolDown(this.ipSub, this.id, canvasId);
|
||||
}
|
||||
|
||||
async incrementPixelcount(amount = 1) {
|
||||
const { id } = this;
|
||||
if (!id) return false;
|
||||
try {
|
||||
await this.regUser.increment(
|
||||
['totalPixels', 'dailyTotalPixels'],
|
||||
{ by: amount },
|
||||
);
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
incrementPixelcount(amount = 1) {
|
||||
incrementPixelcount(this.regUser, amount);
|
||||
}
|
||||
|
||||
async getTotalPixels() {
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* redis script for user pixel placement
|
||||
* this does not set any pixels itself, see lua/placePixel.lua
|
||||
*/
|
||||
import client from './client';
|
||||
import { getIPv6Subnet } from '../../utils/ip';
|
||||
import { PREFIX as CAPTCHA_PREFIX } from './captcha';
|
||||
import { PREFIX as ALLOWED_PREFIX } from './isAllowedCache';
|
||||
import { CAPTCHA_TIME } from '../../core/config';
|
||||
|
||||
/*
|
||||
* gets pixels and chunk coords and checks if
|
||||
* and how many a user can set and sets the cooldown accordingly
|
||||
* @param ip ip of request
|
||||
* @param id userId
|
||||
* @param clrIgnore, bcd, pcd, cds incormations about canvas
|
||||
* @param i, j chunk coordinates
|
||||
* @param pxls Array with offsets of pixels
|
||||
* @return see lua/placePixel.lua
|
||||
*/
|
||||
export default function allowPlace(
|
||||
ip,
|
||||
id,
|
||||
canvasId,
|
||||
i, j,
|
||||
clrIgnore,
|
||||
bcd,
|
||||
pcd,
|
||||
cds,
|
||||
pxls,
|
||||
) {
|
||||
const ipn = getIPv6Subnet(ip);
|
||||
const isalKey = `${ALLOWED_PREFIX}:${ipn}`;
|
||||
const captKey = (CAPTCHA_TIME >= 0) ? `${CAPTCHA_PREFIX}:${ipn}` : 'nope';
|
||||
const ipCdKey = `cd:${canvasId}:ip:${ipn}`;
|
||||
const idCdKey = (id) ? `cd:${canvasId}:id:${id}` : 'nope';
|
||||
const chunkKey = `ch:${canvasId}:${i}:${j}`;
|
||||
return client.placePxl(
|
||||
isalKey, captKey, ipCdKey, idCdKey, chunkKey,
|
||||
clrIgnore, bcd, pcd, cds,
|
||||
...pxls,
|
||||
);
|
||||
}
|
90
src/data/redis/cooldown.js
Normal file
90
src/data/redis/cooldown.js
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* redis script for cooldown calculation
|
||||
* this does not set any pixels itself, see lua/placePixel.lua
|
||||
*/
|
||||
import client from './client';
|
||||
import { PREFIX as CAPTCHA_PREFIX } from './captcha';
|
||||
import { PREFIX as ALLOWED_PREFIX } from './isAllowedCache';
|
||||
import { CAPTCHA_TIME } from '../../core/config';
|
||||
|
||||
const PREFIX = 'cd';
|
||||
|
||||
/*
|
||||
* checks how many of the given pixels can be set,
|
||||
* sets user cooldown and returns the number of pixels to set
|
||||
* @param ip ip of request
|
||||
* @param id userId
|
||||
* @param clrIgnore, bcd, pcd, cds incormations about canvas
|
||||
* @param i, j chunk coordinates
|
||||
* @param pxls Array with offsets of pixels
|
||||
* @return see lua/placePixel.lua
|
||||
*/
|
||||
export default function allowPlace(
|
||||
ip,
|
||||
id,
|
||||
canvasId,
|
||||
i, j,
|
||||
clrIgnore,
|
||||
bcd,
|
||||
pcd,
|
||||
cds,
|
||||
pxls,
|
||||
) {
|
||||
const isalKey = `${ALLOWED_PREFIX}:${ip}`;
|
||||
const captKey = (CAPTCHA_TIME >= 0) ? `${CAPTCHA_PREFIX}:${ip}` : 'nope';
|
||||
const ipCdKey = `${PREFIX}:${canvasId}:ip:${ip}`;
|
||||
const idCdKey = (id) ? `${PREFIX}:${canvasId}:id:${id}` : 'nope';
|
||||
const chunkKey = `ch:${canvasId}:${i}:${j}`;
|
||||
return client.placePxl(
|
||||
isalKey, captKey, ipCdKey, idCdKey, chunkKey,
|
||||
clrIgnore, bcd, pcd, cds,
|
||||
...pxls,
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* get cooldown of specific user
|
||||
* @param ip ip of request
|
||||
* @param id userId
|
||||
* @param canvasId
|
||||
* @return cooldown
|
||||
*/
|
||||
export async function getCoolDown(
|
||||
ip,
|
||||
id,
|
||||
canvasId,
|
||||
) {
|
||||
let ttl = await client.pTTL(`${PREFIX}:${canvasId}:ip:${ip}`);
|
||||
if (id) {
|
||||
const ttlid = await client.pTTL(`${PREFIX}:${canvasId}:id:${id}`);
|
||||
ttl = Math.max(ttl, ttlid);
|
||||
}
|
||||
const cooldown = ttl < 0 ? 0 : ttl;
|
||||
return cooldown;
|
||||
}
|
||||
|
||||
/*
|
||||
* set cooldown of specific user
|
||||
* @param ip ip of request
|
||||
* @param id userId
|
||||
* @param canvasId
|
||||
* @param cooldown (in ms)
|
||||
* @return cooldown
|
||||
*/
|
||||
export async function setCoolDown(
|
||||
ip,
|
||||
id,
|
||||
canvasId,
|
||||
cooldown,
|
||||
) {
|
||||
// PX is milliseconds expire
|
||||
await client.set(`${PREFIX}:${canvasId}:ip:${ip}`, '', {
|
||||
PX: cooldown,
|
||||
});
|
||||
if (id) {
|
||||
await client.set(`${PREFIX}:${canvasId}:id:${id}`, '', {
|
||||
PX: cooldown,
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
|
@ -6,8 +6,9 @@
|
|||
*/
|
||||
|
||||
import { DataTypes, QueryTypes } from 'sequelize';
|
||||
import sequelize from './sequelize';
|
||||
|
||||
import logger from '../../core/logger';
|
||||
import sequelize from './sequelize';
|
||||
import { generateHash } from '../../utils/hash';
|
||||
|
||||
|
||||
|
@ -203,4 +204,42 @@ export async function getNamesToIds(ids) {
|
|||
return idToNameMap;
|
||||
}
|
||||
|
||||
/*
|
||||
* increment user pixelcount in a batched transaction
|
||||
*/
|
||||
const incrementQueue = [];
|
||||
let pushLoop = null;
|
||||
const incrementLoop = async () => {
|
||||
if (!incrementQueue.length) {
|
||||
clearInterval(pushLoop);
|
||||
pushLoop = null;
|
||||
}
|
||||
try {
|
||||
sequelize.transaction(async (t) => {
|
||||
while (incrementQueue.length) {
|
||||
const [model, amount] = incrementQueue.pop();
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await model.increment(
|
||||
['totalPixels', 'dailyTotalPixels'],
|
||||
{ by: amount, transaction: t },
|
||||
);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
} catch (err) {
|
||||
logger.warn(`Error on batched incrementing pixelcounts: ${err.message}`);
|
||||
}
|
||||
};
|
||||
// TODO remove this after testing
|
||||
setInterval(() => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('INCREMENTATION QUEUE SIZE', incrementQueue.length);
|
||||
}, 300000);
|
||||
export async function incrementPixelcount(model, amount) {
|
||||
incrementQueue.push([model, amount]);
|
||||
if (!pushLoop) {
|
||||
pushLoop = setInterval(incrementLoop, 250);
|
||||
}
|
||||
}
|
||||
|
||||
export default RegUser;
|
||||
|
|
Loading…
Reference in New Issue
Block a user