增加分布式锁

This commit is contained in:
CounterFire2023 2024-04-23 17:35:36 +08:00
parent 1a4da3ca73
commit 37aca45c9d
14 changed files with 166 additions and 29 deletions

View File

@ -58,7 +58,7 @@
#### 20240423 #### 20240423
1. 签到列表接口(11)增加返回字段: 是否已领取奖励 1. 签到列表接口(11)增加返回字段: 是否已领取奖励
1. 检查签到并领取奖励(23), 增加post参数, 可以领取指定天数的奖励 1. 检查签到并领取奖励(23), 增加post参数, 可以领取指定天数的奖励
1. 宝箱助力(18), 增加返回scoreBonus, 表示当前宝箱已得到的助力积分 1. 宝箱助力(18), 宝箱助力状态查询(26) 增加返回scoreBonus, 表示当前宝箱已得到的助力积分
### 1. 钱包预登录 ### 1. 钱包预登录
@ -803,6 +803,7 @@ body:
chestMax: 10, // 宝箱最大可助力次数 chestMax: 10, // 宝箱最大可助力次数
nickname: '11', // 宝箱所有者昵称 nickname: '11', // 宝箱所有者昵称
avatar: '', // 宝箱所有者头像, 可能为空 avatar: '', // 宝箱所有者头像, 可能为空
scoreBonus: 1 //当前箱子已助力积分
} }
``` ```

View File

@ -33,10 +33,12 @@
"fastify": "^4.15.0", "fastify": "^4.15.0",
"fastify-plugin": "^3.0.0", "fastify-plugin": "^3.0.0",
"fastify-xml-body-parser": "^2.2.0", "fastify-xml-body-parser": "^2.2.0",
"ioredis": "^5.4.1",
"mongodb-extended-json": "^1.11.1", "mongodb-extended-json": "^1.11.1",
"mongoose": "8.2.3", "mongoose": "8.2.3",
"nanoid": "^3.1.23", "nanoid": "^3.1.23",
"node-schedule": "^2.0.0", "node-schedule": "^2.0.0",
"redlock": "^5.0.0-beta.2",
"siwe": "^2.1.4", "siwe": "^2.1.4",
"tracer": "^1.1.6", "tracer": "^1.1.6",
"zutils": "link:packages/zutils" "zutils": "link:packages/zutils"

View File

@ -7,8 +7,8 @@ import { IncomingMessage, Server, ServerResponse } from 'http'
import { mongoose } from '@typegoose/typegoose' import { mongoose } from '@typegoose/typegoose'
import logger from 'logger/logger' import logger from 'logger/logger'
import NonceRecordSchedule from 'schedule/noncerecord.schedule' import NonceRecordSchedule from 'schedule/noncerecord.schedule'
import { RouterMap, SyncLocker, ZRedisClient } from 'zutils' import { RouterMap, ZRedisClient } from 'zutils'
import CacheSchedule from 'schedule/cache.schedule' import { SyncLocker } from 'common/SyncLocker'
const zReqParserPlugin = require('plugins/zReqParser') const zReqParserPlugin = require('plugins/zReqParser')

66
src/common/SyncLocker.ts Normal file
View File

@ -0,0 +1,66 @@
import { singleton } from 'zutils'
import Client from 'ioredis'
import Redlock, { Lock, ResourceLockedError } from 'redlock'
import logger from 'logger/logger'
interface IRequest {
method: string
url: string
user?: {
id: string
}
lock?: Lock
}
const redisA = new Client(process.env.REDIS)
const redlock = new Redlock(
// You should have one client for each independent redis node
// or cluster.
[redisA],
{
// The expected clock drift; for more details see:
// http://redis.io/topics/distlock
driftFactor: 0.01, // multiplied by lock ttl to determine drift time
// The max number of times Redlock will attempt to lock a resource
// before erroring.
retryCount: 10,
// the time in ms between attempts
retryDelay: 200, // time in ms
// the max time in ms randomly added to retries
// to improve performance under high contention
// see https://www.awsarchitectureblog.com/2015/03/backoff.html
retryJitter: 200, // time in ms
// The minimum remaining time on a lock before an extension is automatically
// attempted with the `using` API.
automaticExtensionThreshold: 500, // time in ms
},
)
redlock.on('error', error => {
// Ignore cases where a resource is explicitly marked as locked on a client.
if (error instanceof ResourceLockedError) {
return
}
// Log all other errors.
logger.error(error)
})
@singleton
export class SyncLocker {
public unlock(req: IRequest) {
if (req.lock) {
req.lock.release()
}
}
public async checkLock(req: IRequest) {
const key = `${req.method}:${req.url}:${req.user?.id || ''}`
let lock = await redlock.acquire([key], 3000)
req.lock = lock
return true
}
}

View File

@ -1,7 +1,8 @@
import { ActivityInfo } from 'models/ActivityInfo' import { ActivityInfo } from 'models/ActivityInfo'
import { ActivityUser } from 'models/ActivityUser' import { ActivityUser } from 'models/ActivityUser'
import { rankKey, rankLevel } from 'services/rank.svr' import { rankKey, rankLevel } from 'services/rank.svr'
import { BaseController, ROLE_ANON, SyncLocker, ZError, ZRedisClient, role, router } from 'zutils' import { BaseController, ROLE_ANON, ZError, ZRedisClient, role, router } from 'zutils'
import { SyncLocker } from 'common/SyncLocker'
import { ScoreRecord } from 'models/ScoreRecord' import { ScoreRecord } from 'models/ScoreRecord'
import { formatAddress } from 'zutils/utils/chain.util' import { formatAddress } from 'zutils/utils/chain.util'
import { formatNumShow, isValidShareCode } from 'common/Utils' import { formatNumShow, isValidShareCode } from 'common/Utils'
@ -32,7 +33,7 @@ export default class ActivityController extends BaseController {
*/ */
@router('post /api/activity/upload_invite_code') @router('post /api/activity/upload_invite_code')
async uploadInviteCode(req) { async uploadInviteCode(req) {
new SyncLocker().checkLock(req) await new SyncLocker().checkLock(req)
logger.db('upload_invite_code', req) logger.db('upload_invite_code', req)
await checkReCaptcha(req, 'invite_user') await checkReCaptcha(req, 'invite_user')
let { code } = req.params let { code } = req.params

View File

@ -1,4 +1,5 @@
import { ZError, SyncLocker, BaseController, router } from 'zutils' import { ZError, BaseController, router } from 'zutils'
import { SyncLocker } from 'common/SyncLocker'
import { FastifyRequest } from 'fastify' import { FastifyRequest } from 'fastify'
import { ActivityItem } from 'models/ActivityItem' import { ActivityItem } from 'models/ActivityItem'
import { TokenClaimHistory } from 'models/TokenClaimHistory' import { TokenClaimHistory } from 'models/TokenClaimHistory'
@ -16,7 +17,7 @@ export default class ChainController extends BaseController {
// @router('post /api/lottery/claim_usdt') // @router('post /api/lottery/claim_usdt')
async preClaimUsdt(req: FastifyRequest) { async preClaimUsdt(req: FastifyRequest) {
new SyncLocker().checkLock(req) await new SyncLocker().checkLock(req)
let user = req.user let user = req.user
if (user.address) { if (user.address) {
throw new ZError(11, 'no address') throw new ZError(11, 'no address')

View File

@ -1,4 +1,5 @@
import { ZError, SyncLocker, ZRedisClient, BaseController, ROLE_ANON, role, router } from 'zutils' import { ZError, ZRedisClient, BaseController, ROLE_ANON, role, router } from 'zutils'
import { SyncLocker } from 'common/SyncLocker'
import { ActivityChest, ChestStatusEnum } from 'models/ActivityChest' import { ActivityChest, ChestStatusEnum } from 'models/ActivityChest'
import { ActivityUser } from 'models/ActivityUser' import { ActivityUser } from 'models/ActivityUser'
import { rankKey, rankLevel, updateRankScore } from 'services/rank.svr' import { rankKey, rankLevel, updateRankScore } from 'services/rank.svr'
@ -185,6 +186,7 @@ class BoxController extends BaseController {
avatar: chestOwner?.twitterAvatar || '', avatar: chestOwner?.twitterAvatar || '',
chestCurrent: chest.bonusUsers.length, chestCurrent: chest.bonusUsers.length,
chestMax: chest.maxBounsCount, chestMax: chest.maxBounsCount,
scoreBonus: chest.scoreBonus,
} }
} else { } else {
return { return {
@ -196,6 +198,7 @@ class BoxController extends BaseController {
? formatAddress(chestOwner.address) ? formatAddress(chestOwner.address)
: 'unknown', : 'unknown',
avatar: chestOwner?.twitterAvatar || '', avatar: chestOwner?.twitterAvatar || '',
scoreBonus: chest.scoreBonus,
} }
} }
} }
@ -204,7 +207,7 @@ class BoxController extends BaseController {
*/ */
@router('post /api/chest/enhance') @router('post /api/chest/enhance')
async enhance(req) { async enhance(req) {
new SyncLocker().checkLock(req) await new SyncLocker().checkLock(req)
logger.db('enhance', req) logger.db('enhance', req)
await checkReCaptcha(req, 'chest_share') await checkReCaptcha(req, 'chest_share')
const { code } = req.params const { code } = req.params
@ -307,7 +310,7 @@ class BoxController extends BaseController {
*/ */
@router('post /api/chest/open') @router('post /api/chest/open')
async openChest(req) { async openChest(req) {
new SyncLocker().checkLock(req) await new SyncLocker().checkLock(req)
logger.db('open_chest', req) logger.db('open_chest', req)
const user = req.user const user = req.user
const { chestId } = req.params const { chestId } = req.params

View File

@ -3,7 +3,8 @@ import { ActivityGame } from 'models/ActivityGame'
import { DAILY_SIGN, SIGN_SEQ, SIGN_TOTAL, TicketRecord, USE_TICKET } from 'models/TicketRecord' import { DAILY_SIGN, SIGN_SEQ, SIGN_TOTAL, TicketRecord, USE_TICKET } from 'models/TicketRecord'
import { queryCheckInList } from 'services/chain.svr' import { queryCheckInList } from 'services/chain.svr'
import { checkInToday, seqSignCfg, seqSignScore, totalSignCfg, totalSignScore } from 'services/sign.svr' import { checkInToday, seqSignCfg, seqSignScore, totalSignCfg, totalSignScore } from 'services/sign.svr'
import { ZError, SyncLocker, BaseController, router } from 'zutils' import { ZError, BaseController, router } from 'zutils'
import { SyncLocker } from 'common/SyncLocker'
import { formatDate } from 'utils/utcdate.util' import { formatDate } from 'utils/utcdate.util'
import { generateStepReward } from 'services/game.svr' import { generateStepReward } from 'services/game.svr'
import { updateRankScore } from 'services/rank.svr' import { updateRankScore } from 'services/rank.svr'
@ -23,7 +24,7 @@ class GameController extends BaseController {
*/ */
@router('post /api/user/checkin') @router('post /api/user/checkin')
async checkIn(req) { async checkIn(req) {
new SyncLocker().checkLock(req) await new SyncLocker().checkLock(req)
logger.db('checkin', req) logger.db('checkin', req)
const user = req.user const user = req.user
const { address } = user const { address } = user
@ -99,7 +100,7 @@ class GameController extends BaseController {
*/ */
@router('post /api/user/checkin/claim') @router('post /api/user/checkin/claim')
async claimCheckResult(req) { async claimCheckResult(req) {
new SyncLocker().checkLock(req) await new SyncLocker().checkLock(req)
logger.db('claim_checkin_total', req) logger.db('claim_checkin_total', req)
const user = req.user const user = req.user
let { days } = req.params let { days } = req.params
@ -150,7 +151,7 @@ class GameController extends BaseController {
*/ */
@router('post /api/user/checkin/claim_seq') @router('post /api/user/checkin/claim_seq')
async claimCheckSeqResult(req) { async claimCheckSeqResult(req) {
new SyncLocker().checkLock(req) await new SyncLocker().checkLock(req)
logger.db('claim_checkin_seq', req) logger.db('claim_checkin_seq', req)
const user = req.user const user = req.user
let { days } = req.params let { days } = req.params
@ -302,7 +303,7 @@ class GameController extends BaseController {
@router('post /api/game/pre_step') @router('post /api/game/pre_step')
async gameChest(req) { async gameChest(req) {
const user = req.user const user = req.user
new SyncLocker().checkLock(req) await new SyncLocker().checkLock(req)
let { step } = req.params let { step } = req.params
step = step || '1' step = step || '1'
if (isNaN(step)) { if (isNaN(step)) {
@ -335,7 +336,7 @@ class GameController extends BaseController {
*/ */
@router('post /api/game/step') @router('post /api/game/step')
async gameStep(req, res) { async gameStep(req, res) {
new SyncLocker().checkLock(req) await new SyncLocker().checkLock(req)
logger.db('step', req) logger.db('step', req)
const user = req.user const user = req.user
const { id } = req.params const { id } = req.params

View File

@ -1,6 +1,7 @@
import { EMPTY_REWARD, SCORE_DRAW } from 'common/Constants' import { EMPTY_REWARD, SCORE_DRAW } from 'common/Constants'
import { LotteryCache } from 'common/LotteryCache' import { LotteryCache } from 'common/LotteryCache'
import { ZError, SyncLocker, BaseController, router } from 'zutils' import { ZError, BaseController, router } from 'zutils'
import { SyncLocker } from 'common/SyncLocker'
import { FUSION_CFG } from 'configs/fusion' import { FUSION_CFG } from 'configs/fusion'
import { ALL_ITEMS } from 'configs/items' import { ALL_ITEMS } from 'configs/items'
import { LOTTERY_CFG } from 'configs/lottery' import { LOTTERY_CFG } from 'configs/lottery'
@ -80,7 +81,7 @@ export default class LotteryController extends BaseController {
// @router('get /api/lottery/draw') // @router('get /api/lottery/draw')
async draw(req) { async draw(req) {
new SyncLocker().checkLock(req) await new SyncLocker().checkLock(req)
let user = req.user let user = req.user
const { start, end, rewards } = LOTTERY_CFG const { start, end, rewards } = LOTTERY_CFG
const startTime = new Date(start).getTime() const startTime = new Date(start).getTime()
@ -144,7 +145,7 @@ export default class LotteryController extends BaseController {
// @router('get /api/lottery/fusion') // @router('get /api/lottery/fusion')
async fusion(req) { async fusion(req) {
new SyncLocker().checkLock(req) await new SyncLocker().checkLock(req)
let user = req.user let user = req.user
let items = await ActivityItem.find({ user: user.id, activity: user.activity }) let items = await ActivityItem.find({ user: user.id, activity: user.activity })
let itemCountMap = new Map() let itemCountMap = new Map()

View File

@ -5,7 +5,8 @@ import { NFTHolderRecord } from 'models/NFTHodlerRecord'
import { queryNftBalance } from 'services/chain.svr' import { queryNftBalance } from 'services/chain.svr'
import { generateChestLevel, generateNewChest } from 'services/game.svr' import { generateChestLevel, generateNewChest } from 'services/game.svr'
import { checkDiscordRole } from 'services/oauth.svr' import { checkDiscordRole } from 'services/oauth.svr'
import { SyncLocker, BaseController, router, role, ROLE_ANON, ZError } from 'zutils' import { BaseController, router, role, ROLE_ANON, ZError } from 'zutils'
import { SyncLocker } from 'common/SyncLocker'
const sourceList = require('../../configs/partner_nft_list.json') const sourceList = require('../../configs/partner_nft_list.json')
const tierMap = { const tierMap = {
@ -103,7 +104,7 @@ class NftController extends BaseController {
*/ */
// @router('post /api/partner/claim') // @router('post /api/partner/claim')
async claimNftHolderReward(req) { async claimNftHolderReward(req) {
new SyncLocker().checkLock(req) await new SyncLocker().checkLock(req)
const user = req.user const user = req.user
let { contract } = req.params let { contract } = req.params
if (!contract) { if (!contract) {
@ -142,7 +143,7 @@ class NftController extends BaseController {
@router('post /api/partner/claim') @router('post /api/partner/claim')
async claimNftHolderRewardDC(req) { async claimNftHolderRewardDC(req) {
new SyncLocker().checkLock(req) await new SyncLocker().checkLock(req)
// throw new ZError(20, 'maintance now, please try later') // throw new ZError(20, 'maintance now, please try later')
logger.db('claim_partner', req) logger.db('claim_partner', req)

View File

@ -1,4 +1,5 @@
import { ZError, SyncLocker, ZRedisClient, BaseController, ROLE_ANON, role, router } from 'zutils' import { ZError, ZRedisClient, BaseController, ROLE_ANON, role, router } from 'zutils'
import { SyncLocker } from 'common/SyncLocker'
import { BOOST_CFG } from 'configs/boost' import { BOOST_CFG } from 'configs/boost'
import { ActivityUser } from 'models/ActivityUser' import { ActivityUser } from 'models/ActivityUser'
import { DEFAULT_EXPIRED, NonceRecord } from 'models/NonceRecord' import { DEFAULT_EXPIRED, NonceRecord } from 'models/NonceRecord'
@ -163,7 +164,7 @@ class SignController extends BaseController {
// @router('get /api/user/state/boost') // @router('get /api/user/state/boost')
async boost(req) { async boost(req) {
new SyncLocker().checkLock(req) await new SyncLocker().checkLock(req)
const user = req.user const user = req.user
if (user.boost > 1 && user.boostExpire && user.boostExpire > Date.now()) { if (user.boost > 1 && user.boostExpire && user.boostExpire > Date.now()) {
throw new ZError(11, 'already boosted') throw new ZError(11, 'already boosted')

View File

@ -1,4 +1,5 @@
import { ZError, SyncLocker, BaseController, router } from 'zutils' import { ZError, BaseController, router } from 'zutils'
import { SyncLocker } from 'common/SyncLocker'
import { TaskCfg, TaskTypeEnum } from 'models/ActivityInfo' import { TaskCfg, TaskTypeEnum } from 'models/ActivityInfo'
import { TaskStatus, TaskStatusEnum } from 'models/ActivityUser' import { TaskStatus, TaskStatusEnum } from 'models/ActivityUser'
import { join } from 'path' import { join } from 'path'
@ -77,7 +78,7 @@ export default class TasksController extends BaseController {
@router('post /api/tasks/begin_task') @router('post /api/tasks/begin_task')
async beginTask(req) { async beginTask(req) {
new SyncLocker().checkLock(req) await new SyncLocker().checkLock(req)
logger.db('begin_task', req) logger.db('begin_task', req)
let user = req.user let user = req.user
let activity = req.activity let activity = req.activity
@ -175,7 +176,7 @@ export default class TasksController extends BaseController {
@router('post /api/tasks/claim') @router('post /api/tasks/claim')
async claimTask(req) { async claimTask(req) {
new SyncLocker().checkLock(req) await new SyncLocker().checkLock(req)
logger.db('claim_task', req) logger.db('claim_task', req)
const user = req.user const user = req.user
const activity = req.activity const activity = req.activity

View File

@ -3,7 +3,8 @@ import logger from 'logger/logger'
import { ChestStatusEnum } from 'models/ActivityChest' import { ChestStatusEnum } from 'models/ActivityChest'
import { SourceEnum, VoucherRecord, VoucherStatusEnum } from 'models/VoucherRecord' import { SourceEnum, VoucherRecord, VoucherStatusEnum } from 'models/VoucherRecord'
import { generateNewChest } from 'services/game.svr' import { generateNewChest } from 'services/game.svr'
import { SyncLocker, BaseController, router, ZError } from 'zutils' import { BaseController, router, ZError } from 'zutils'
import { SyncLocker } from 'common/SyncLocker'
import { customAlphabet } from 'nanoid' import { customAlphabet } from 'nanoid'
import { BASE52_ALPHABET } from 'common/Constants' import { BASE52_ALPHABET } from 'common/Constants'
/** /**
@ -90,7 +91,7 @@ class VoucherController extends BaseController {
@router('post /api/voucher/claim') @router('post /api/voucher/claim')
async claimVoucherReward(req) { async claimVoucherReward(req) {
new SyncLocker().checkLock(req) await new SyncLocker().checkLock(req)
logger.db('claim_voucher', req) logger.db('claim_voucher', req)
const user = req.user const user = req.user
const { id } = req.params const { id } = req.params

View File

@ -815,6 +815,11 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz#d9fae00a2d5cb40f92cfe64b47ad749fbc38f917" resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz#d9fae00a2d5cb40f92cfe64b47ad749fbc38f917"
integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw== integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==
"@ioredis/commands@^1.1.1":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@ioredis/commands/-/commands-1.2.0.tgz#6d61b3097470af1fdbbe622795b8921d42018e11"
integrity sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==
"@istanbuljs/load-nyc-config@^1.0.0": "@istanbuljs/load-nyc-config@^1.0.0":
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
@ -2231,6 +2236,11 @@ clone-response@^1.0.2:
dependencies: dependencies:
mimic-response "^1.0.0" mimic-response "^1.0.0"
cluster-key-slot@^1.1.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac"
integrity sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==
co@^4.6.0: co@^4.6.0:
version "4.6.0" version "4.6.0"
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
@ -2507,6 +2517,11 @@ denque@^1.5.0:
resolved "https://registry.yarnpkg.com/denque/-/denque-1.5.1.tgz#07f670e29c9a78f8faecb2566a1e2c11929c5cbf" resolved "https://registry.yarnpkg.com/denque/-/denque-1.5.1.tgz#07f670e29c9a78f8faecb2566a1e2c11929c5cbf"
integrity sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw== integrity sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==
denque@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1"
integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==
depd@2.0.0: depd@2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
@ -3801,6 +3816,21 @@ inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4:
resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
ioredis@^5.4.1:
version "5.4.1"
resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-5.4.1.tgz#1c56b70b759f01465913887375ed809134296f40"
integrity sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==
dependencies:
"@ioredis/commands" "^1.1.1"
cluster-key-slot "^1.1.0"
debug "^4.3.4"
denque "^2.1.0"
lodash.defaults "^4.2.0"
lodash.isarguments "^3.1.0"
redis-errors "^1.2.0"
redis-parser "^3.0.0"
standard-as-callback "^2.1.0"
ipaddr.js@1.9.1: ipaddr.js@1.9.1:
version "1.9.1" version "1.9.1"
resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz"
@ -4508,6 +4538,16 @@ locate-path@^6.0.0:
dependencies: dependencies:
p-locate "^5.0.0" p-locate "^5.0.0"
lodash.defaults@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
integrity sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==
lodash.isarguments@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a"
integrity sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==
lodash.isfunction@^3.0.6: lodash.isfunction@^3.0.6:
version "3.0.9" version "3.0.9"
resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051" resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051"
@ -4908,6 +4948,11 @@ next-tick@^1.1.0:
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb"
integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==
node-abort-controller@^3.0.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548"
integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==
node-addon-api@^2.0.0: node-addon-api@^2.0.0:
version "2.0.2" version "2.0.2"
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32"
@ -5427,6 +5472,13 @@ redis@^3.1.2:
redis-errors "^1.2.0" redis-errors "^1.2.0"
redis-parser "^3.0.0" redis-parser "^3.0.0"
redlock@^5.0.0-beta.2:
version "5.0.0-beta.2"
resolved "https://registry.yarnpkg.com/redlock/-/redlock-5.0.0-beta.2.tgz#a629c07e07d001c0fdd9f2efa614144c4416fe44"
integrity sha512-2RDWXg5jgRptDrB1w9O/JgSZC0j7y4SlaXnor93H/UJm/QyDiFgBKNtrh0TI6oCXqYSaSoXxFh6Sd3VtYfhRXw==
dependencies:
node-abort-controller "^3.0.1"
reflect-metadata@^0.2.1: reflect-metadata@^0.2.1:
version "0.2.1" version "0.2.1"
resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.2.1.tgz#8d5513c0f5ef2b4b9c3865287f3c0940c1f67f74" resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.2.1.tgz#8d5513c0f5ef2b4b9c3865287f3c0940c1f67f74"
@ -5868,6 +5920,11 @@ stack-utils@^2.0.3:
dependencies: dependencies:
escape-string-regexp "^2.0.0" escape-string-regexp "^2.0.0"
standard-as-callback@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/standard-as-callback/-/standard-as-callback-2.1.0.tgz#8953fc05359868a77b5b9739a665c5977bb7df45"
integrity sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==
statuses@2.0.1: statuses@2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"