Billing - optimize usageEvent CH table (#20019)

- Update usageEvent clickhouse table, partitioning, indexing and
projection (auto materialized view) to optimize credit usage queries
- Add caching for available credits and billing subscription


To do in next PR: deprecate enforceCapUsage cron. Bonus : real-time on
billingSubscription
This commit is contained in:
Etienne
2026-04-28 16:06:49 +02:00
committed by GitHub
parent 2a3b8adcfb
commit fbaea0639a
33 changed files with 601 additions and 379 deletions
@@ -28,6 +28,7 @@ import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.ent
import { WorkspaceModule } from 'src/engine/core-modules/workspace/workspace.module';
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
import { RowLevelPermissionModule } from 'src/engine/metadata-modules/row-level-permission-predicate/row-level-permission.module';
import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module';
@Module({
imports: [
@@ -37,6 +38,7 @@ import { RowLevelPermissionModule } from 'src/engine/metadata-modules/row-level-
PermissionsModule,
WorkspaceModule,
BillingModule,
WorkspaceCacheModule,
TypeOrmModule.forFeature([
BillingSubscriptionEntity,
BillingSubscriptionItemEntity,
@@ -24,6 +24,7 @@ import { BillingSubscriptionEntity } from 'src/engine/core-modules/billing/entit
import { SubscriptionStatus } from 'src/engine/core-modules/billing/enums/billing-subscription-status.enum';
import { BillingWebhookEvent } from 'src/engine/core-modules/billing/enums/billing-webhook-events.enum';
import { BillingSubscriptionService } from 'src/engine/core-modules/billing/services/billing-subscription.service';
import { BillingUsageService } from 'src/engine/core-modules/billing/services/billing-usage.service';
import { StripeBillingAlertService } from 'src/engine/core-modules/billing/stripe/services/stripe-billing-alert.service';
import { StripeCustomerService } from 'src/engine/core-modules/billing/stripe/services/stripe-customer.service';
import { StripeSubscriptionScheduleService } from 'src/engine/core-modules/billing/stripe/services/stripe-subscription-schedule.service';
@@ -32,6 +33,7 @@ import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queu
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
import { WorkspaceService } from 'src/engine/core-modules/workspace/services/workspace.service';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
import {
CleanWorkspaceDeletionWarningUserVarsJob,
type CleanWorkspaceDeletionWarningUserVarsJobData,
@@ -59,6 +61,8 @@ export class BillingWebhookSubscriptionService {
private readonly workspaceService: WorkspaceService,
private readonly stripeSubscriptionScheduleService: StripeSubscriptionScheduleService,
private readonly stripeBillingAlertService: StripeBillingAlertService,
private readonly billingUsageService: BillingUsageService,
private readonly workspaceCacheService: WorkspaceCacheService,
) {}
async processStripeEvent(
@@ -140,6 +144,11 @@ export class BillingWebhookSubscriptionService {
workspaceId,
);
await this.billingUsageService.flushAvailableCreditsFromCache(workspace.id);
await this.workspaceCacheService.invalidateAndRecompute(workspace.id, [
'billingSubscription',
]);
const shouldSuspend = this.shouldSuspendWorkspace(data);
if (shouldSuspend) {