Skip to content

Commit

Permalink
More timers & optimizations (#58)
Browse files Browse the repository at this point in the history
* add search timer

* improve teammates query

* format
  • Loading branch information
owens1127 authored Jun 23, 2024
1 parent 0ba9f03 commit 90a54c8
Show file tree
Hide file tree
Showing 11 changed files with 70 additions and 42 deletions.
3 changes: 2 additions & 1 deletion open-api/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -2451,7 +2451,8 @@
{
"schema": {
"type": "string",
"minLength": 1
"minLength": 1,
"maxLength": 40
},
"required": true,
"name": "query",
Expand Down
4 changes: 3 additions & 1 deletion src/data-access-layer/__test__/teammates.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ cleanupPostgresAfterAll()

describe("getPlayer", () => {
it("returns the correct shape", async () => {
const data = await getTeammates("4611686018443649478").catch(console.error)
const data = await getTeammates("4611686018443649478", {
count: 10
}).catch(console.error)

const parsed = z.array(zTeammate).safeParse(data)
if (!parsed.success) {
Expand Down
4 changes: 2 additions & 2 deletions src/data-access-layer/history.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { InstanceForPlayer } from "../schema/components/InstanceForPlayer"
import { postgres } from "../services/postgres"
import { activityHistoryQueryTimer } from "../services/prometheus/metrics"
import { withTimer } from "../services/prometheus/util"
import { withHistogramTimer } from "../services/prometheus/util"

export const getActivities = async (
membershipId: bigint | string,
Expand All @@ -15,7 +15,7 @@ export const getActivities = async (
cursor?: Date
}
) => {
return await withTimer(
return await withHistogramTimer(
activityHistoryQueryTimer,
{
count: count,
Expand Down
52 changes: 30 additions & 22 deletions src/data-access-layer/player-search.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { PlayerInfo } from "../schema/components/PlayerInfo"
import { DestinyMembershipType } from "../schema/enums/DestinyMembershipType"
import { postgres } from "../services/postgres"
import { playerSearchQueryTimer } from "../services/prometheus/metrics"
import { withHistogramTimer } from "../services/prometheus/util"

/**
* Case insensitive search
Expand All @@ -18,29 +20,35 @@ export async function searchForPlayer(
}> {
const searchTerm = query.trim().toLowerCase()

const results = await postgres.queryRows<PlayerInfo>(
`SELECT
membership_id::text AS "membershipId",
membership_type AS "membershipType",
icon_path AS "iconPath",
display_name AS "displayName",
bungie_global_display_name AS "bungieGlobalDisplayName",
bungie_global_display_name_code AS "bungieGlobalDisplayNameCode",
last_seen AS "lastSeen",
is_private AS "isPrivate"
FROM player
WHERE lower(${opts.global ? "bungie_name" : "display_name"}) LIKE $1
${opts.membershipType ? "AND membership_type = $3" : ""}
AND last_seen > TIMESTAMP 'epoch'
ORDER BY _search_score DESC
LIMIT $2;`,
{
params: opts.membershipType
? [searchTerm + "%", opts.count, opts.membershipType]
: [searchTerm + "%", opts.count],
fetchCount: opts.count
}
const results = await withHistogramTimer(
playerSearchQueryTimer,
{ prefixLength: searchTerm.split("#")[0]?.length ?? 0 },
() =>
postgres.queryRows<PlayerInfo>(
`SELECT
membership_id::text AS "membershipId",
membership_type AS "membershipType",
icon_path AS "iconPath",
display_name AS "displayName",
bungie_global_display_name AS "bungieGlobalDisplayName",
bungie_global_display_name_code AS "bungieGlobalDisplayNameCode",
last_seen AS "lastSeen",
is_private AS "isPrivate"
FROM player
WHERE lower(${opts.global ? "bungie_name" : "display_name"}) LIKE $1
${opts.membershipType ? "AND membership_type = $3" : ""}
AND last_seen > TIMESTAMP 'epoch'
ORDER BY _search_score DESC
LIMIT $2;`,
{
params: opts.membershipType
? [searchTerm + "%", opts.count, opts.membershipType]
: [searchTerm + "%", opts.count],
fetchCount: opts.count
}
)
)

return {
searchTerm,
results
Expand Down
8 changes: 4 additions & 4 deletions src/data-access-layer/player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
} from "../schema/components/PlayerProfile"
import { postgres } from "../services/postgres"
import { playerProfileQueryTimer } from "../services/prometheus/metrics"
import { withTimer } from "../services/prometheus/util"
import { withHistogramTimer } from "../services/prometheus/util"

export const getPlayer = async (membershipId: bigint | string) => {
return await postgres.queryRow<PlayerInfo>(
Expand All @@ -27,7 +27,7 @@ export const getPlayer = async (membershipId: bigint | string) => {
)
}
export const getPlayerActivityStats = async (membershipId: bigint | string) => {
return await withTimer(
return await withHistogramTimer(
playerProfileQueryTimer,
{
method: "getPlayerActivityStats"
Expand Down Expand Up @@ -78,7 +78,7 @@ export const getPlayerActivityStats = async (membershipId: bigint | string) => {
}

export const getPlayerGlobalStats = async (membershipId: bigint | string) => {
return await withTimer(
return await withHistogramTimer(
playerProfileQueryTimer,
{
method: "getPlayerGlobalStats"
Expand Down Expand Up @@ -112,7 +112,7 @@ export const getPlayerGlobalStats = async (membershipId: bigint | string) => {
}

export const getWorldFirstEntries = async (membershipId: bigint | string) => {
return await withTimer(
return await withHistogramTimer(
playerProfileQueryTimer,
{
method: "getWorldFirstEntries"
Expand Down
12 changes: 6 additions & 6 deletions src/data-access-layer/teammates.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Teammate } from "../schema/components/Teammate"
import { postgres } from "../services/postgres"

export const getTeammates = async (membershipId: bigint | string) => {
export const getTeammates = async (membershipId: bigint | string, { count }: { count: number }) => {
return await postgres.queryRows<Teammate>(
`WITH self AS (
SELECT
Expand All @@ -18,6 +18,8 @@ export const getTeammates = async (membershipId: bigint | string) => {
JOIN activity_player AS teammate USING (instance_id)
WHERE membership_id <> $1::bigint
GROUP BY (membership_id)
ORDER BY clears DESC, time_played DESC
LIMIT $2
)
SELECT
agg_data.time_played as "estimatedTimePlayedSeconds",
Expand All @@ -34,12 +36,10 @@ export const getTeammates = async (membershipId: bigint | string) => {
'isPrivate', "is_private"
) AS "playerInfo"
FROM agg_data
JOIN player USING (membership_id)
ORDER BY clears DESC, time_played DESC
LIMIT 100;`,
JOIN player USING (membership_id);`,
{
params: [membershipId],
fetchCount: 100
params: [membershipId, count],
fetchCount: count
}
)
}
4 changes: 3 additions & 1 deletion src/routes/player/membershipId/teamates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ export const playerTeammatesRoute = new RaidHubRoute({
return RaidHubRoute.fail(ErrorCode.PlayerPrivateProfileError, { membershipId })
}

const teamates = await getTeammates(membershipId)
const teamates = await getTeammates(membershipId, {
count: 100
})

return RaidHubRoute.ok(teamates)
}
Expand Down
2 changes: 1 addition & 1 deletion src/routes/player/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Players who have not attempted a raid may not appear in the search results.
Results are ordered by a combination of the number of raid completions and last played date.`,
query: z.object({
count: z.coerce.number().int().min(1).max(50).default(20),
query: z.string().min(1),
query: z.string().min(1).max(40),
membershipType: zDestinyMembershipType.default(-1).openapi({
description:
"Filter by Destiny membership type. Defaults to -1 (all). Note that the membership type of an account is determined by the platform the was first created on"
Expand Down
13 changes: 11 additions & 2 deletions src/services/prometheus/metrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,25 @@ export const httpRequestTimer = new Histogram({
buckets: [0.1, 1, 5, 15, 50, 100, 200, 300, 400, 500, 1000, 2000, 5000, 10000]
})

const QueryBuckets = [0.1, 0.5, 1, 5, 10, 50, 100, 250, 500, 1000, 5000, 10000]

export const activityHistoryQueryTimer = new Histogram({
name: "activity_history_query_duration_ms",
help: "Duration of activity history queries in ms",
labelNames: ["count", "cutoff", "cursor"],
buckets: [0.1, 0.5, 1, 5, 10, 50, 100, 250, 500, 1000, 5000, 10000]
buckets: QueryBuckets
})

export const playerProfileQueryTimer = new Histogram({
name: "player_profile_query_duration_ms",
help: "Duration of player profile queries in ms",
labelNames: ["method"],
buckets: [0.1, 0.5, 1, 5, 10, 50, 100, 250, 500, 1000, 5000, 10000]
buckets: QueryBuckets
})

export const playerSearchQueryTimer = new Histogram({
name: "search_player_query_duration_ms",
help: "Duration of player search queries in ms",
labelNames: ["prefixLength"],
buckets: QueryBuckets
})
8 changes: 7 additions & 1 deletion src/services/prometheus/registry.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { Registry } from "prom-client"
import { activityHistoryQueryTimer, httpRequestTimer, playerProfileQueryTimer } from "./metrics"
import {
activityHistoryQueryTimer,
httpRequestTimer,
playerProfileQueryTimer,
playerSearchQueryTimer
} from "./metrics"

export const prometheusRegistry = new Registry()

prometheusRegistry.registerMetric(httpRequestTimer)
prometheusRegistry.registerMetric(activityHistoryQueryTimer)
prometheusRegistry.registerMetric(playerProfileQueryTimer)
prometheusRegistry.registerMetric(playerSearchQueryTimer)
2 changes: 1 addition & 1 deletion src/services/prometheus/util.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Histogram } from "prom-client"

export const withTimer = async <K extends string, T>(
export const withHistogramTimer = async <K extends string, T>(
metric: Histogram<K>,
labels: Partial<Record<K, string | number>>,
fn: () => Promise<T>
Expand Down

0 comments on commit 90a54c8

Please sign in to comment.