Skip to content

Commit 901cbb0

Browse files
committed
[server] Fix usage of AccountStatementProvider by making it cache per userId
1 parent 2136d72 commit 901cbb0

File tree

4 files changed

+16
-12
lines changed

4 files changed

+16
-12
lines changed

components/gitpod-protocol/src/util/garbage-collected-cache.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ export class GarbageCollectedCache<T> {
3737
if (!entry) {
3838
return undefined;
3939
}
40+
// Still valid?
41+
if (entry.expiryDate < Date.now()) {
42+
this.store.delete(entry.key);
43+
return undefined;
44+
}
4045
return entry.value;
4146
}
4247

components/server/ee/src/billing/entitlement-service-chargebee.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export class EntitlementServiceChargebee implements EntitlementService {
7979
runningInstances: Promise<WorkspaceInstance[]>,
8080
): Promise<boolean> {
8181
// As retrieving a full AccountStatement is expensive we want to cache it as much as possible.
82-
const cachedAccountStatement = this.accountStatementProvider.getCachedStatement();
82+
const cachedAccountStatement = this.accountStatementProvider.getCachedStatement(userId);
8383
const lowerBound = this.getRemainingUsageHoursLowerBound(cachedAccountStatement, date.toISOString());
8484
if (lowerBound && (lowerBound === "unlimited" || lowerBound > Accounting.MINIMUM_CREDIT_FOR_OPEN_IN_HOURS)) {
8585
return true;
@@ -108,7 +108,7 @@ export class EntitlementServiceChargebee implements EntitlementService {
108108
return "unlimited";
109109
}
110110

111-
const diffInMillis = new Date(cachedStatement.endDate).getTime() - new Date(date).getTime();
111+
const diffInMillis = Math.max(0, new Date(cachedStatement.endDate).getTime() - new Date(date).getTime());
112112
const maxPossibleUsage = millisecondsToHours(diffInMillis) * MAX_PARALLEL_WORKSPACES;
113113
return cachedStatement.remainingHours - maxPossibleUsage;
114114
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,13 +94,13 @@ export const productionEEContainerModule = new ContainerModule((bind, unbind, is
9494
// GitpodServerImpl (stateful per user)
9595
rebind(GitpodServerImpl).to(GitpodServerEEImpl).inRequestScope();
9696
bind(EligibilityService).toSelf().inRequestScope();
97-
bind(AccountStatementProvider).toSelf().inRequestScope();
9897

9998
// various
10099
rebind(HostContainerMapping).to(HostContainerMappingEE).inSingletonScope();
101100
bind(EMailDomainService).to(EMailDomainServiceImpl).inSingletonScope();
102101
rebind(BlockedUserFilter).toService(EMailDomainService);
103102
bind(SnapshotService).toSelf().inSingletonScope();
103+
bind(AccountStatementProvider).toSelf().inSingletonScope();
104104

105105
bind(UserDeletionServiceEE).toSelf().inSingletonScope();
106106
rebind(UserDeletionService).to(UserDeletionServiceEE).inSingletonScope();

components/server/ee/src/user/account-statement-provider.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { injectable, inject } from "inversify";
88
import { WorkspaceInstance } from "@gitpod/gitpod-protocol";
99
import { AccountStatement } from "@gitpod/gitpod-protocol/lib/accounting-protocol";
1010
import { AccountService } from "@gitpod/gitpod-payment-endpoint/lib/accounting";
11+
import { GarbageCollectedCache } from "@gitpod/gitpod-protocol/lib/util/garbage-collected-cache";
1112

1213
export type CachedAccountStatement = Pick<AccountStatement, "remainingHours" | "endDate">;
1314

@@ -19,20 +20,18 @@ export type CachedAccountStatement = Pick<AccountStatement, "remainingHours" | "
1920
export class AccountStatementProvider {
2021
@inject(AccountService) protected readonly accountService: AccountService;
2122

22-
protected cachedStatement: CachedAccountStatement | undefined;
23+
/**
24+
* AccountStatements, cached by userId
25+
*/
26+
protected readonly cachedStatements = new GarbageCollectedCache<CachedAccountStatement>(5 * 60, 10 * 60);
2327

24-
setCachedStatement(cachedStatement: CachedAccountStatement) {
25-
this.cachedStatement = cachedStatement;
26-
}
27-
28-
getCachedStatement(): CachedAccountStatement | undefined {
29-
return this.cachedStatement;
28+
getCachedStatement(userId: string): CachedAccountStatement | undefined {
29+
return this.cachedStatements.get(userId);
3030
}
3131

3232
async getAccountStatement(userId: string, date: string): Promise<AccountStatement> {
3333
const statement = await this.accountService.getAccountStatement(userId, date);
34-
// Fill cache
35-
this.setCachedStatement({
34+
this.cachedStatements.set(userId, {
3635
remainingHours: statement.remainingHours,
3736
endDate: statement.endDate,
3837
});

0 commit comments

Comments
 (0)