Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "game-services",
"version": "0.80.5",
"version": "0.80.6",
"description": "",
"main": "src/index.ts",
"scripts": {
Expand Down
7 changes: 6 additions & 1 deletion src/entities/subscribers/player-group.subscriber.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Player from '../player'
import LeaderboardEntry from '../leaderboard-entry'
import PlayerGameStat from '../player-game-stat'
import { createRedisConnection } from '../../config/redis.config'
import { captureException } from '@sentry/node'

const enableLogging = process.env.NODE_ENV !== 'test'

Expand Down Expand Up @@ -57,7 +58,8 @@ export default class PlayerGroupSubscriber implements EventSubscriber {
console.info(`Group memberships lock created for ${player.id}`)
}

if (await checkGroupMemberships(em, player)) {
const shouldRefresh = await checkGroupMemberships(em, player)
if (shouldRefresh) {
const label = `Refreshing memberships for ${player.id}`

/* v8 ignore next 3 */
Expand All @@ -73,6 +75,9 @@ export default class PlayerGroupSubscriber implements EventSubscriber {
}
}
}
} catch (err) {
console.error(`Failed checking memberships: ${(err as Error).message}`)
captureException(err)
} finally {
if (lockCreated) {
/* v8 ignore next 3 */
Expand Down
8 changes: 4 additions & 4 deletions src/lib/groups/checkGroupMemberships.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class PlayerGroupMember {
export default async function checkGroupMemberships(em: EntityManager, player: Player): Promise<boolean> {
const groups = await em.repo(PlayerGroup).find({
game: player.game
}, getResultCacheOptions(`groups-for-memberships-${player.game}`))
}, getResultCacheOptions(`groups-for-memberships-${player.game}`, 1000))

if (groups.length === 0) {
return false
Expand All @@ -38,7 +38,7 @@ export default async function checkGroupMemberships(em: EntityManager, player: P
console.time(label)
}

let shouldFlush = false
let shouldRefresh = false

for (const group of groups) {
await group.members.init({ ref: true })
Expand All @@ -49,7 +49,7 @@ export default async function checkGroupMemberships(em: EntityManager, player: P
const notEligibleButInGroup = !playerIsEligible && playerCurrentlyInGroup

if (eligibleButNotInGroup || notEligibleButInGroup) {
shouldFlush = true
shouldRefresh = true
}

const groupMember = new PlayerGroupMember(player, group)
Expand All @@ -74,5 +74,5 @@ export default async function checkGroupMemberships(em: EntityManager, player: P
console.timeEnd(label)
}

return shouldFlush
return shouldRefresh
}
23 changes: 22 additions & 1 deletion src/tasks/cleanupOnlinePlayers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type CleanupStats = {
newSessionDurationMinutes: number[]
newSessionDurationLessThanOneMinute: number
presenceUpdated: number
presenceDeleted: number
}

let cleanupStats: Record<number, CleanupStats>
Expand All @@ -31,7 +32,8 @@ function getOrCreateGameCleanupStats(gameId: number): CleanupStats {
hadSessionSinceOriginal: 0,
newSessionDurationMinutes: [],
newSessionDurationLessThanOneMinute: 0,
presenceUpdated: 0
presenceUpdated: 0,
presenceDeleted: 0
}
}
return cleanupStats[gameId]
Expand Down Expand Up @@ -134,6 +136,12 @@ async function cleanupPresence(em: EntityManager, clickhouse: ClickHouseClient,
}
}

async function removeDisconnectedPresence(em: EntityManager, presence: PlayerPresence) {
const gameStats = getOrCreateGameCleanupStats(presence.playerAlias.player.game.id)
gameStats.presenceDeleted++
await em.removeAndFlush(presence)
}

export default async function cleanupOnlinePlayers() {
const orm = await MikroORM.init(ormConfig)
const em = orm.em.fork()
Expand All @@ -157,6 +165,17 @@ export default async function cleanupOnlinePlayers() {
await cleanupSession(em, clickhouse, session)
}

// todo, find out how this is happening
const disconnectedPresence = await em.repo(PlayerPresence).find({
player: null
})

console.info(`Found ${disconnectedPresence.length} disconnected presence`)

for (const presence of disconnectedPresence) {
await removeDisconnectedPresence(em, presence)
}

const onlinePresence = await em.repo(PlayerPresence).find({
online: true,
updatedAt: {
Expand All @@ -167,6 +186,8 @@ export default async function cleanupOnlinePlayers() {
populate: ['player']
})

console.info(`Found ${onlinePresence.length} online presence`)

for (const presence of onlinePresence) {
await cleanupPresence(em, clickhouse, presence)
}
Expand Down
29 changes: 29 additions & 0 deletions tests/tasks/cleanupOnlinePlayers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { formatDateForClickHouse } from '../../src/lib/clickhouse/formatDateTime
import PlayerPresenceFactory from '../fixtures/PlayerPresenceFactory'
import assert from 'node:assert'
import PlayerPresence from '../../src/entities/player-presence'
import Player from '../../src/entities/player'

describe('cleanupOnlinePlayers', () => {
beforeEach(() => {
Expand Down Expand Up @@ -387,4 +388,32 @@ describe('cleanupOnlinePlayers', () => {
expect(updatedPresence.online).toBe(true)
expect(updatedPresence.updatedAt).toEqual(originalUpdatedAt)
})

it('should delete presence that no longer has a player', async () => {
vi.useRealTimers()

const [, game] = await createOrganisationAndGame()
const player = await new PlayerFactory([game])
.state(async (player) => ({
presence: await new PlayerPresenceFactory(player.game)
.online()
.state(() => ({ updatedAt: subDays(new Date(), 2) }))
.one()
}))
.one()
await em.persistAndFlush(player)

assert(player.presence)
const originalPresenceId = player.presence.id

await em.nativeDelete(Player, player.id)

const originalPresence = await em.repo(PlayerPresence).findOne(originalPresenceId)
assert(originalPresence)

await cleanupOnlinePlayers()

const updatedPresence = await em.refresh(originalPresence)
expect(updatedPresence).toBeNull()
})
})
Loading