Add ClickHouse-backed metered credit cap enforcement (#19586)
## Summary Implements a ClickHouse-backed polling system to enforce metered-credit caps for workflow executions, replacing reliance on Stripe billing alerts. The system re-evaluates tier caps against live pricing on every poll cycle, allowing price/tier changes to propagate immediately without recreating Stripe alert objects. ## Key Changes - **BillingUsageCapService**: New service that queries ClickHouse for current-period credit usage and evaluates whether a subscription has reached its metered-credit allowance (tier cap + credit balance) - `isClickHouseEnabled()`: Checks if ClickHouse is configured - `getCurrentPeriodCreditsUsed()`: Sums creditsUsedMicro from usageEvent table for a workspace within a billing period - `evaluateCap()`: Determines if usage has reached the allowance by reading live pricing from the subscription - **EnforceUsageCapJob**: Cron job that polls all active subscriptions and updates `hasReachedCurrentPeriodCap` on metered items - Runs every 2 minutes to keep cap enforcement in sync with live usage - Supports shadow mode (log-only) via `BILLING_USAGE_CAP_CLICKHOUSE_ENABLED` flag for safe rollout - Continues processing after per-subscription errors with detailed logging - **EnforceUsageCapCronCommand**: CLI command to register the enforcement cron job - **MeteredCreditService**: Extracted `extractMeteredPricingInfoFromSubscription()` as a pure function for callers that already hold the subscription with pricing loaded, avoiding redundant DB queries - **Configuration**: Added `BILLING_USAGE_CAP_CLICKHOUSE_ENABLED` flag to control enforcement mode (active vs. shadow) - **Constants**: Added `METERED_OPERATION_TYPES` to define which operation types count toward the metered product's credit cap ## Implementation Details - The service queries ClickHouse for the sum of `creditsUsedMicro` in the current billing period, matching Stripe meter semantics - Pricing is re-read on every evaluation, so tier changes propagate within one poll cycle without Stripe alert recreation - The cron job only updates the database when the cap state actually changes (no-op if already in the correct state) - Shadow mode allows safe validation before enabling enforcement; transitions are logged but not persisted - Comprehensive test coverage for both the service and cron job, including error handling and state transitions https://claude.ai/code/session_01VksTSrYLXJVCPVBQhQdBTe --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,7 @@ import { GenerateApiKeyCommand } from 'src/engine/core-modules/api-key/commands/
|
||||
import { MarketplaceModule } from 'src/engine/core-modules/application/application-marketplace/marketplace.module';
|
||||
import { StaleRegistrationCleanupModule } from 'src/engine/core-modules/application/application-oauth/stale-registration-cleanup/stale-registration-cleanup.module';
|
||||
import { ApplicationUpgradeModule } from 'src/engine/core-modules/application/application-upgrade/application-upgrade.module';
|
||||
import { EnforceUsageCapCronCommand } from 'src/engine/core-modules/billing/crons/commands/enforce-usage-cap.cron.command';
|
||||
import { EnterpriseKeyValidationCronCommand } from 'src/engine/core-modules/enterprise/cron/command/enterprise-key-validation.cron.command';
|
||||
import { EnterpriseModule } from 'src/engine/core-modules/enterprise/enterprise.module';
|
||||
import { EventLogCleanupModule } from 'src/engine/core-modules/event-logs/cleanup/event-log-cleanup.module';
|
||||
@@ -86,6 +87,7 @@ import { AutomatedTriggerModule } from 'src/modules/workflow/workflow-trigger/au
|
||||
ListOrphanedWorkspaceEntitiesCommand,
|
||||
EnterpriseKeyValidationCronCommand,
|
||||
GenerateApiKeyCommand,
|
||||
EnforceUsageCapCronCommand,
|
||||
],
|
||||
})
|
||||
export class DatabaseCommandModule {}
|
||||
|
||||
Reference in New Issue
Block a user