Skip to content

Commit 435d47d

Browse files
author
Simon Emms
committed
[server]: cache the getUserCount on the requireEELicense calls
1 parent bcf6c99 commit 435d47d

File tree

4 files changed

+57
-2
lines changed

4 files changed

+57
-2
lines changed

components/server/ee/src/container-module.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ import { GitLabAppSupport } from "./gitlab/gitlab-app-support";
4949
import { Config } from "../../src/config";
5050
import { SnapshotService } from "./workspace/snapshot-service";
5151
import { BitbucketAppSupport } from "./bitbucket/bitbucket-app-support";
52+
import { UserCounter } from "./user/user-counter";
5253

5354
export const productionEEContainerModule = new ContainerModule((bind, unbind, isBound, rebind) => {
5455
rebind(Server).to(ServerEE).inSingletonScope();
@@ -69,6 +70,8 @@ export const productionEEContainerModule = new ContainerModule((bind, unbind, is
6970
bind(BitbucketAppSupport).toSelf().inSingletonScope();
7071
bind(GitHubEnterpriseApp).toSelf().inSingletonScope();
7172

73+
bind(UserCounter).toSelf().inSingletonScope();
74+
7275
bind(LicenseEvaluator).toSelf().inSingletonScope();
7376
bind(LicenseKeySource).to(DBLicenseKeySource).inSingletonScope();
7477

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* Copyright (c) 2022 Gitpod GmbH. All rights reserved.
3+
* Licensed under the GNU Affero General Public License (AGPL).
4+
* See License-AGPL.txt in the project root for license information.
5+
*/
6+
7+
import { injectable } from 'inversify';
8+
9+
@injectable()
10+
export class UserCounter {
11+
public expires: Date | null = null;
12+
13+
protected data: number | null = null;
14+
15+
protected readonly timeout: number = 60 * 1000; // Cache data for 1 minute
16+
17+
get count(): number | null {
18+
if (this.expires !== null && Date.now() >= this.expires.getTime()) {
19+
// The timestamp is in range - return the data
20+
return this.data;
21+
}
22+
// Not in range - return null
23+
return null;
24+
}
25+
26+
set count(userCount: number | null) {
27+
this.expires = new Date(Date.now() + this.timeout);
28+
29+
this.data = userCount;
30+
}
31+
}

components/server/ee/src/workspace/gitpod-server-impl.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import { SnapshotService, WaitForSnapshotOptions } from "./snapshot-service";
4242
import { ClientMetadata, traceClientMetadata } from "../../../src/websocket/websocket-connection-manager";
4343
import { BitbucketAppSupport } from "../bitbucket/bitbucket-app-support";
4444
import { URL } from 'url';
45+
import { UserCounter } from "../user/user-counter";
4546

4647
@injectable()
4748
export class GitpodServerEEImpl extends GitpodServerImpl {
@@ -75,6 +76,8 @@ export class GitpodServerEEImpl extends GitpodServerImpl {
7576

7677
@inject(SnapshotService) protected readonly snapshotService: SnapshotService;
7778

79+
@inject(UserCounter) protected readonly userCounter: UserCounter;
80+
7881
initialize(client: GitpodClient | undefined, user: User | undefined, accessGuard: ResourceAccessGuard, clientMetadata: ClientMetadata, connectionCtx: TraceContext | undefined, clientHeaderFields: ClientHeaderFields): void {
7982
super.initialize(client, user, accessGuard, clientMetadata, connectionCtx, clientHeaderFields);
8083

@@ -153,7 +156,15 @@ export class GitpodServerEEImpl extends GitpodServerImpl {
153156
}
154157

155158
protected async requireEELicense(feature: Feature) {
156-
const userCount = await this.userDB.getUserCount(true);
159+
const cachedUserCount = this.userCounter.count;
160+
161+
let userCount: number;
162+
if (cachedUserCount === null) {
163+
userCount = await this.userDB.getUserCount(true);
164+
this.userCounter.count = userCount;
165+
} else {
166+
userCount = cachedUserCount;
167+
}
157168

158169
if (!this.licenseEvaluator.isEnabled(feature, userCount)) {
159170
throw new ResponseError(ErrorCodes.EE_LICENSE_REQUIRED, "enterprise license required");

components/server/ee/src/workspace/workspace-factory.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,27 @@ import { ResponseError } from 'vscode-jsonrpc';
1616
import { ErrorCodes } from '@gitpod/gitpod-protocol/lib/messaging/error';
1717
import { HostContextProvider } from '../../../src/auth/host-context-provider';
1818
import { UserDB } from '@gitpod/gitpod-db/lib';
19+
import { UserCounter } from '../user/user-counter';
1920

2021
@injectable()
2122
export class WorkspaceFactoryEE extends WorkspaceFactory {
2223

2324
@inject(LicenseEvaluator) protected readonly licenseEvaluator: LicenseEvaluator;
2425
@inject(HostContextProvider) protected readonly hostContextProvider: HostContextProvider;
26+
@inject(UserCounter) protected readonly userCounter: UserCounter;
2527

2628
@inject(UserDB) protected readonly userDB: UserDB;
2729

2830
protected async requireEELicense(feature: Feature) {
29-
const userCount = await this.userDB.getUserCount(true);
31+
const cachedUserCount = this.userCounter.count;
32+
33+
let userCount: number;
34+
if (cachedUserCount === null) {
35+
userCount = await this.userDB.getUserCount(true);
36+
this.userCounter.count = userCount;
37+
} else {
38+
userCount = cachedUserCount;
39+
}
3040

3141
if (!this.licenseEvaluator.isEnabled(feature, userCount)) {
3242
throw new ResponseError(ErrorCodes.EE_LICENSE_REQUIRED, "enterprise license required");

0 commit comments

Comments
 (0)