Skip to content

Commit ea87894

Browse files
committed
feature: add query stats for admins
1 parent 27e3918 commit ea87894

4 files changed

Lines changed: 90 additions & 6 deletions

File tree

hasura/metadata/actions.graphql

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ type Mutation {
4444
): SuccessOutput
4545
}
4646

47+
type Query {
48+
dbStats: [DbStats]
49+
}
50+
4751
type Mutation {
4852
deleteMatch(
4953
match_id: String!
@@ -376,3 +380,18 @@ type FileContentResponse {
376380
size: bigint!
377381
}
378382

383+
type DbStats {
384+
queryid: String!
385+
query: String!
386+
calls: Int!
387+
total_exec_time: Float!
388+
mean_exec_time: Float!
389+
max_exec_time: Float!
390+
min_exec_time: Float!
391+
total_rows: Int!
392+
shared_blks_hit: Int!
393+
shared_blks_read: Int!
394+
local_blks_hit: Int!
395+
local_blks_read: Int!
396+
}
397+

hasura/metadata/actions.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,13 @@ actions:
5353
permissions:
5454
- role: administrator
5555
comment: Create directory on game server
56+
- name: dbStats
57+
definition:
58+
kind: ""
59+
handler: '{{HASURA_GRAPHQL_ACTIONS_HOOK}}'
60+
forward_client_headers: true
61+
permissions:
62+
- role: administrator
5663
- name: deleteMatch
5764
definition:
5865
kind: synchronous
@@ -334,4 +341,5 @@ custom_types:
334341
- name: FileItem
335342
- name: FileListResponse
336343
- name: FileContentResponse
344+
- name: DbStats
337345
scalars: []

src/matchmaking/matchmaking-lobby.service.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -162,22 +162,19 @@ export class MatchmakingLobbyService {
162162
}>;
163163
},
164164
) {
165-
166165
const { players } = await this.hasura.query({
167166
players: {
168167
__args: {
169168
where: {
170-
steam_id: { _in: lobby.players.map(p => p.steam_id) }
171-
}
169+
steam_id: { _in: lobby.players.map((p) => p.steam_id) },
170+
},
172171
},
173172
steam_id: true,
174173
elo: true,
175174
},
176175
});
177176

178-
const eloMap = new Map(
179-
players.map(p => [p.steam_id, p.elo])
180-
);
177+
const eloMap = new Map(players.map((p) => [p.steam_id, p.elo]));
181178

182179
const _players = lobby.players.map(({ steam_id }) => {
183180
const playerElo = eloMap.get(steam_id);

src/system/system.controller.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { GameServerNodeService } from "src/game-server-node/game-server-node.ser
1313
import { LoggingService } from "src/k8s/logging/logging.service";
1414
import { isRoleAbove } from "src/utilities/isRoleAbove";
1515
import { PassThrough } from "stream";
16+
import { PostgresService } from "src/postgres/postgres.service";
1617

1718
@Controller("system")
1819
export class SystemController {
@@ -22,13 +23,72 @@ export class SystemController {
2223
private readonly notifications: NotificationsService,
2324
private readonly gameServerNodeService: GameServerNodeService,
2425
private readonly loggingService: LoggingService,
26+
private readonly postgres: PostgresService,
2527
) {}
2628

2729
@Get("healthz")
2830
public async status() {
2931
return;
3032
}
3133

34+
@HasuraAction()
35+
public async dbStats() {
36+
// Define a type for the result rows
37+
type DbStatRow = {
38+
queryid: string | number;
39+
query: string;
40+
calls: number;
41+
total_exec_time: number;
42+
mean_exec_time: number;
43+
max_exec_time: number;
44+
min_exec_time: number;
45+
total_rows: number;
46+
shared_blks_hit: number;
47+
shared_blks_read: number;
48+
local_blks_hit: number;
49+
local_blks_read: number;
50+
};
51+
52+
const result = await this.postgres.query<DbStatRow>(`
53+
SELECT
54+
queryid,
55+
query,
56+
SUM(calls) AS calls,
57+
SUM(total_exec_time) AS total_exec_time,
58+
AVG(mean_exec_time) AS mean_exec_time,
59+
MAX(max_exec_time) AS max_exec_time,
60+
MIN(min_exec_time) AS min_exec_time,
61+
SUM(rows) AS total_rows,
62+
SUM(shared_blks_hit) AS shared_blks_hit,
63+
SUM(shared_blks_read) AS shared_blks_read,
64+
SUM(local_blks_hit) AS local_blks_hit,
65+
SUM(local_blks_read) AS local_blks_read
66+
FROM pg_stat_statements
67+
WHERE query NOT LIKE '/* pgbouncer */%'
68+
GROUP BY queryid, query
69+
HAVING SUM(calls) > 5
70+
ORDER BY mean_exec_time DESC
71+
LIMIT 50;
72+
`);
73+
74+
return (result as unknown as any[]).map(
75+
(row): DbStatRow => ({
76+
queryid: row.queryid,
77+
query: row.query,
78+
calls: Number(row.calls),
79+
total_exec_time: Number(row.total_exec_time),
80+
mean_exec_time: Number(row.mean_exec_time),
81+
max_exec_time: Number(row.max_exec_time),
82+
min_exec_time: Number(row.min_exec_time),
83+
total_rows: Number(row.total_rows),
84+
shared_blks_hit: Number(row.shared_blks_hit),
85+
shared_blks_read: Number(row.shared_blks_read),
86+
local_blks_hit: Number(row.local_blks_hit),
87+
local_blks_read: Number(row.local_blks_read),
88+
}),
89+
);
90+
}
91+
3292
@Post("logs/download")
3393
public async logs(
3494
@Req() request: Request,

0 commit comments

Comments
 (0)