Skip to content

Commit d2c2bb9

Browse files
authored
Merge pull request #586 from TaloDev/develop
Release 0.84.1
2 parents a48b736 + edeead4 commit d2c2bb9

30 files changed

+140
-95
lines changed

codecov.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
ignore:
2-
- '/src/config'
3-
- '/src/migrations'
2+
- "/src/config"
3+
- "/src/migrations"
4+
- "vitest.config.mts"

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "game-services",
3-
"version": "0.84.0",
3+
"version": "0.84.1",
44
"description": "",
55
"main": "src/index.ts",
66
"scripts": {

src/config/mikro-orm.config.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ import subscribers from '../entities/subscribers'
44
import migrationsList from '../migrations'
55
import { TsMorphMetadataProvider } from '@mikro-orm/reflection'
66
import { Migrator } from '@mikro-orm/migrations'
7-
import { defineConfig } from '@mikro-orm/mysql'
7+
import { defineConfig, MikroORM } from '@mikro-orm/mysql'
88
import { RedisCacheAdapter } from 'mikro-orm-cache-adapter-redis'
99
import redisConfig from './redis.config'
1010

11-
export default defineConfig({
11+
const ormConfig = defineConfig({
1212
entities,
1313
host: process.env.DB_HOST,
1414
port: Number(process.env.DB_PORT),
@@ -33,3 +33,13 @@ export default defineConfig({
3333
options: redisConfig
3434
}
3535
})
36+
37+
export default ormConfig // loaded in package.json
38+
39+
let orm: Awaited<ReturnType<typeof MikroORM.init>>
40+
export async function getMikroORM() {
41+
if (!orm || !(await orm.checkConnection())) {
42+
orm = await MikroORM.init(ormConfig)
43+
}
44+
return orm
45+
}

src/config/providers.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import Koa from 'koa'
2-
import ormConfig from './mikro-orm.config'
3-
import { MikroORM } from '@mikro-orm/mysql'
2+
import { getMikroORM } from './mikro-orm.config'
43
import createEmailQueue from '../lib/queues/createEmailQueue'
54
import createClickHouseClient from '../lib/clickhouse/createClient'
65
import { runClickHouseMigrations } from '../migrations/clickhouse'
@@ -10,7 +9,7 @@ import { createRedisConnection } from './redis.config'
109

1110
export default async function initProviders(app: Koa, isTest: boolean) {
1211
try {
13-
const orm = await MikroORM.init(ormConfig)
12+
const orm = await getMikroORM()
1413
app.context.em = orm.em
1514

1615
if (!isTest) {

src/config/redis.config.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import Redis from 'ioredis'
1+
import Redis, { RedisOptions } from 'ioredis'
22

33
const redisConfig = {
44
host: process.env.REDIS_HOST ?? 'redis',
@@ -8,6 +8,9 @@ const redisConfig = {
88

99
export default redisConfig
1010

11-
export function createRedisConnection(): Redis {
12-
return new Redis(redisConfig)
11+
export function createRedisConnection(opts?: RedisOptions): Redis {
12+
return new Redis({
13+
...redisConfig,
14+
...(opts ?? {})
15+
})
1316
}

src/entities/player-session.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export default class PlayerSession extends ClickHouseEntity<ClickHousePlayerSess
4848

4949
async hydrate(em: EntityManager, data: ClickHousePlayerSession): Promise<this> {
5050
const player = await em.repo(Player).findOneOrFail(data.player_id, {
51-
...getResultCacheOptions(`hydrate-player-session-${data.player_id}`, 60_000),
51+
...getResultCacheOptions(`hydrate-player-session-${data.player_id}`),
5252
populate: ['game']
5353
})
5454

src/entities/subscribers/player-group.subscriber.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,6 @@ export default class PlayerGroupSubscriber implements EventSubscriber {
6565
if (playersMap.size > 0) {
6666
await checkGroupsForPlayers(em, Array.from(playersMap.values()))
6767
}
68+
em.clear()
6869
}
6970
}

src/lib/errors/checkRateLimitExceeded.ts

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,45 @@
1-
import Redis from 'ioredis'
1+
import { Redis } from 'ioredis'
22

3-
export default async function checkRateLimitExceeded(redis: Redis, key: string, maxRequests: number): Promise<boolean> {
4-
const redisKey = `requests.${key}`
5-
const current = await redis.incr(redisKey)
3+
const cache = new Map<string, { count: number, expires: number }>()
64

7-
if (current === 1) {
8-
// this is the first request in the window, so set the key to expire
9-
await redis.expire(redisKey, 1)
5+
setInterval(() => {
6+
const now = Date.now()
7+
for (const [key, value] of cache.entries()) {
8+
if (now > value.expires) {
9+
cache.delete(key)
10+
}
11+
}
12+
}, 5000)
13+
14+
const script = `
15+
local current = redis.call('INCR', KEYS[1])
16+
if current == 1 then
17+
redis.call('EXPIRE', KEYS[1], ARGV[1])
18+
end
19+
return current
20+
`
21+
22+
export default async function checkRateLimitExceeded(
23+
redis: Redis,
24+
key: string,
25+
maxRequests: number
26+
): Promise<boolean> {
27+
// Skip cache in test environment for predictable behavior
28+
if (process.env.NODE_ENV !== 'test') {
29+
const cached = cache.get(key)
30+
if (cached && Date.now() < cached.expires) {
31+
return cached.count > maxRequests
32+
}
33+
}
34+
35+
const current = await redis.eval(script, 1, key, 1) as number
36+
37+
// Only cache in production
38+
if (process.env.NODE_ENV !== 'test') {
39+
cache.set(key, {
40+
count: current,
41+
expires: Date.now() + 500
42+
})
1043
}
1144

1245
return current > maxRequests

src/lib/perf/getResultCacheOptions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
type CacheKey = { cache: [string, number] } | undefined
22

3-
export function getResultCacheOptions(key: string, cacheExpiration = 10_000): CacheKey {
3+
export function getResultCacheOptions(key: string, cacheExpiration = 60_000): CacheKey {
44
return {
55
cache: [key, cacheExpiration]
66
}

0 commit comments

Comments
 (0)