Skip to content
This repository has been archived by the owner on Nov 11, 2021. It is now read-only.

Commit

Permalink
Migrated backend to read historical mmr data from timescaledb
Browse files Browse the repository at this point in the history
Fixes #311
  • Loading branch information
ThomasK33 committed Mar 2, 2021
1 parent a5f8e52 commit 4631295
Show file tree
Hide file tree
Showing 7 changed files with 27 additions and 107 deletions.
2 changes: 1 addition & 1 deletion devops/kubernetes/src/fortify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ export class Fortify extends Chart {

resources: {
limits: {
cpu: "0.2",
cpu: "1",
memory: "512Mi",
},
requests: {
Expand Down
15 changes: 2 additions & 13 deletions services/backend/package-lock.json

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

3 changes: 1 addition & 2 deletions services/backend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "backend",
"version": "1.8.3",
"version": "1.8.4",
"private": true,
"main": "build/index.js",
"types": "build/index.d.ts",
Expand Down Expand Up @@ -72,7 +72,6 @@
"typescript": "^4.2.2"
},
"dependencies": {
"@influxdata/influxdb-client": "^1.10.0",
"@sentry/node": "^6.2.1",
"apollo-metrics": "^1.0.1",
"apollo-server-express": "^2.21.0",
Expand Down
9 changes: 8 additions & 1 deletion services/backend/src/graphql/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,26 @@ import { MetricsService } from "@shared/services/metrics";
import createMetricsPlugin from "apollo-metrics";

import { ApolloServerPlugin } from "apollo-server-plugin-base";
import { Logger } from "@shared/logger";

const { IGNORE_ERROR_CODES } = process.env;

const ignorableErrorCodes = IGNORE_ERROR_CODES?.split(";");

@injectable()
export class GraphQL {
constructor(@inject(MetricsService) private metrics: MetricsService) {}
constructor(
@inject(MetricsService) private metrics: MetricsService,
@inject(Logger) private logger: Logger,
) {}

server() {
const apolloMetricsPlugin = createMetricsPlugin(
this.metrics.register,
) as ApolloServerPlugin<Record<string, string>>;

const { logger } = this;

const server = new ApolloServer({
tracing: true,
playground: true,
Expand Down Expand Up @@ -161,6 +167,7 @@ export class GraphQL {
}

Sentry.captureException(err);
logger.debug(err);
});
}
},
Expand Down
3 changes: 2 additions & 1 deletion services/backend/src/graphql/modules/match.ts
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,7 @@ const convertDbMatchToGqlMatch = (dbMatch: DbMatch) => {
})),
// id: dbMatch.id,
averageMMR: dbMatch.averageMMR,
mode: FortifyGameMode[dbMatch.gameMode] as GameMode,
mode: FortifyGameMode[dbMatch.gameMode].toUpperCase() as GameMode,
pool: [],
players: dbMatch.slots.map<MatchPlayerSnapshot>((slot, index) => {
return {
Expand Down Expand Up @@ -563,6 +563,7 @@ const convertDbMatchToGqlMatch = (dbMatch: DbMatch) => {
win_streak: 0,
wins: 0,
xp: 0,
persona_name: slot.user?.name,
},
// TODO: Once match data will be stored, load private player state
};
Expand Down
98 changes: 13 additions & 85 deletions services/backend/src/graphql/modules/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,10 @@ import { GQLModule } from "../../definitions/module";
import { gql, ApolloError } from "apollo-server-express";
import { Resolvers, MmrHistory } from "../../definitions/graphql/types";
import { PostgresConnector } from "@shared/connectors/postgres";
import { InfluxDBConnector } from "@shared/connectors/influxdb";
import { PermissionScope } from "@shared/definitions/context";

import {
fluxDuration,
flux,
fluxDateTime,
FluxParameterLike,
} from "@influxdata/influxdb-client";
import { EventService } from "@shared/services/eventService";
import { TwitchUnlinkedEvent } from "@shared/events/systemEvents";
import { Logger } from "@shared/logger";

export interface InfluxMMRQueryRow {
result: string;
Expand All @@ -34,8 +27,8 @@ export interface InfluxMMRQueryRow {
export class UserModule implements GQLModule {
constructor(
@inject(PostgresConnector) private postgres: PostgresConnector,
@inject(InfluxDBConnector) private influx: InfluxDBConnector,
@inject(EventService) private eventService: EventService,
@inject(Logger) private logger: Logger,
) {}

typeDef = gql`
Expand Down Expand Up @@ -101,7 +94,7 @@ export class UserModule implements GQLModule {
`;

resolver(): Resolvers {
const { postgres, influx, eventService } = this;
const { postgres, eventService } = this;

return {
Query: {
Expand Down Expand Up @@ -216,83 +209,18 @@ export class UserModule implements GQLModule {
);
}

// Write influxdb queries to fetch data points
const queryApi = await influx.queryApi();

let start: FluxParameterLike | undefined = undefined;
let stop: FluxParameterLike | undefined = undefined;

if (args.duration) {
start = fluxDuration(
`-${args.duration <= 60 ? args.duration : 30}d`,
);
stop = fluxDateTime(new Date().toISOString());
}

if (args.startDate && args.endDate) {
const startDate = args.startDate
? new Date(args.startDate)
: (() => {
const date = new Date();
date.setHours(date.getHours() - 24);
return date;
})();

const endDate = args.endDate
? new Date(args.endDate)
: new Date();

start = fluxDateTime(startDate.toISOString());
stop = fluxDateTime(endDate.toISOString());
}
const mmrStatsRepo = postgres.getMmrStatsRepo();

// stub these in as default values
if (!start || !stop) {
start = fluxDuration("-30d");
stop = fluxDateTime(new Date().toISOString());
}

let mode = "standard";

if (args.mode?.toLowerCase() !== "normal") {
mode = args.mode?.toLowerCase() ?? "standard";
}

const fluxQuery = flux`
from(bucket: "mmr")
|> range(start: ${start}, stop: ${stop})
|> filter(fn: (r) => r["_measurement"] == "mmr")
|> filter(fn: (r) => r["_field"] == "mmr" or r["_field"] == "rank")
|> filter(fn: (r) => r["steamid"] == ${steamid})
|> filter(fn: (r) => r["type"] == ${mode})
|> yield(name: "history")
`;

const rawRows = (await queryApi.collectRows(
fluxQuery,
)) as InfluxMMRQueryRow[];

const mmrs = rawRows.filter((row) => row._field === "mmr");
const ranks = rawRows.filter(
(row) => row._field === "rank",
);

const history: MmrHistory[] = [];

// Merge both data points based on _time
// this is not optimized at all and will probably be very expensive to run
for (const mmr of mmrs) {
history.push({
date: mmr._time,
mmr: parseInt(mmr._value),
rank: parseInt(
ranks.find((rank) => rank._time === mmr._time)
?._value ?? "0",
),
});
}
// TODO: Incorporate duration and time range from args

return history;
return mmrStatsRepo
.createQueryBuilder()
.select(["mmr", "rank", "time AS date"])
.where(
"time BETWEEN NOW() - interval '30 days' AND NOW()",
)
.andWhere('"userSteamid" = :steamid', { steamid })
.getRawMany<MmrHistory>();
},
},
};
Expand Down
4 changes: 0 additions & 4 deletions services/backend/src/inversify.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import { LeaderboardModule } from "./graphql/modules/leaderboard";

import { PostgresConnector } from "@shared/connectors/postgres";
import { RedisConnector } from "@shared/connectors/redis";
import { InfluxDBConnector } from "@shared/connectors/influxdb";
import { VaultConnector } from "@shared/connectors/vault";
import { KafkaConnector } from "@shared/connectors/kafka";

Expand Down Expand Up @@ -45,17 +44,14 @@ container.bind<GQLModule>("module").to(LeaderboardModule);

container.bind<GQLDirective>("directive").to(AuthDirective);

container.bind(InfluxDBConnector).toSelf().inSingletonScope();
container.bind(PostgresConnector).toSelf().inSingletonScope();
container.bind(RedisConnector).toSelf().inSingletonScope();
container.bind(KafkaConnector).toSelf().inSingletonScope();

container.bind<Connector>("connector").toService(InfluxDBConnector);
container.bind<Connector>("connector").toService(PostgresConnector);
container.bind<Connector>("connector").toService(RedisConnector);
container.bind<Connector>("connector").toService(KafkaConnector);

container.bind<HealthCheckable>("healthCheck").toService(InfluxDBConnector);
container.bind<HealthCheckable>("healthCheck").toService(PostgresConnector);
container.bind<HealthCheckable>("healthCheck").toService(RedisConnector);
container.bind<HealthCheckable>("healthCheck").toService(VaultConnector);
Expand Down

0 comments on commit 4631295

Please sign in to comment.