feat(deploy): configurable CPU/RAM/disk for app databases
Let users size the database workload from the deploy wizard the same way Redis/RabbitMQ are configured: a resource card (CPU request/limit, memory request/limit, storage) on the Resources step, stored in optionalServiceResources.database. - entity/DTO: add `database` to optionalServiceResources - k8s: resolveDatabaseResources() applies user-selected resources on both the Helm and K8s-API deploy paths (was hardcoded 100m/256Mi→500m/512Mi) - billing: bill database CPU/RAM as a separate line on top of the app's resources; merge it through the upgrade path too - wizard: db resource card on the Resources step, disk moved into the card, cost preview + review summary include the database resources Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -637,6 +637,16 @@ export class BillingService {
|
||||
replicas: newResources.replicas ?? base.replicas,
|
||||
...(newResources.dbStorageSize && { dbStorageSize: newResources.dbStorageSize }),
|
||||
...(newResources.appStorageSize && { appStorageSize: newResources.appStorageSize }),
|
||||
...(newResources.databaseResources && {
|
||||
databaseResources: {
|
||||
...base.databaseResources,
|
||||
...newResources.databaseResources,
|
||||
storageGi:
|
||||
newResources.databaseResources.storageGi ??
|
||||
base.databaseResources?.storageGi ??
|
||||
1,
|
||||
},
|
||||
}),
|
||||
...(newResources.redisResources && {
|
||||
redisResources: {
|
||||
...base.redisResources,
|
||||
@@ -734,7 +744,9 @@ export class BillingService {
|
||||
? (app as Application).optionalServiceResources
|
||||
: undefined;
|
||||
const dtoExtras =
|
||||
'redisResources' in app ? (app as CalculateCostDto) : undefined;
|
||||
'redisResources' in app || 'databaseResources' in app
|
||||
? (app as CalculateCostDto)
|
||||
: undefined;
|
||||
const productType =
|
||||
'productType' in app
|
||||
? ((app as Application).productType ?? ProductType.APPLICATION)
|
||||
@@ -753,6 +765,7 @@ export class BillingService {
|
||||
enableRabbitmq: !!app.enableRabbitmq,
|
||||
enableElasticsearch: managed ? false : !!app.enableElasticsearch,
|
||||
enableCustomDomain: managed ? false : enableCustomDomain,
|
||||
databaseResources: optionalRes?.database ?? dtoExtras?.databaseResources,
|
||||
redisResources: optionalRes?.redis ?? dtoExtras?.redisResources,
|
||||
rabbitmqResources: optionalRes?.rabbitmq ?? dtoExtras?.rabbitmqResources,
|
||||
};
|
||||
@@ -919,6 +932,7 @@ export class BillingService {
|
||||
enableRabbitmq: config.enableRabbitmq,
|
||||
enableElasticsearch: config.enableElasticsearch,
|
||||
enableCustomDomain: config.enableCustomDomain,
|
||||
databaseResources: config.databaseResources,
|
||||
redisResources: config.redisResources,
|
||||
rabbitmqResources: config.rabbitmqResources,
|
||||
};
|
||||
|
||||
@@ -144,6 +144,12 @@ export class CalculateCostDto {
|
||||
@IsBoolean()
|
||||
enableElasticsearch?: boolean;
|
||||
|
||||
@ApiPropertyOptional({ type: OptionalServiceResourcesDto, description: 'Per-database CPU/RAM/storage limits' })
|
||||
@IsOptional()
|
||||
@ValidateNested()
|
||||
@Type(() => OptionalServiceResourcesDto)
|
||||
databaseResources?: OptionalServiceResourcesDto;
|
||||
|
||||
@ApiPropertyOptional({ type: OptionalServiceResourcesDto })
|
||||
@IsOptional()
|
||||
@ValidateNested()
|
||||
@@ -213,6 +219,12 @@ export class UpgradeResourcesDto {
|
||||
@IsString()
|
||||
appStorageSize?: string;
|
||||
|
||||
@ApiPropertyOptional({ type: OptionalServiceResourcesDto, description: 'Database resource limits' })
|
||||
@IsOptional()
|
||||
@ValidateNested()
|
||||
@Type(() => OptionalServiceResourcesDto)
|
||||
databaseResources?: OptionalServiceResourcesDto;
|
||||
|
||||
@ApiPropertyOptional({ type: OptionalServiceResourcesDto, description: 'Managed Redis resource limits' })
|
||||
@IsOptional()
|
||||
@ValidateNested()
|
||||
|
||||
@@ -654,8 +654,14 @@ export class PricingCatalogService implements OnModuleInit {
|
||||
}
|
||||
const replicas = dto.replicas ?? 1;
|
||||
const hasDatabase = dto.databaseType !== DatabaseType.NONE && dto.databaseType !== 'none';
|
||||
const cpuQty = this.parseCpuToCores(dto.cpuLimit) * replicas;
|
||||
const memoryQty = this.parseMemoryToGb(dto.memoryLimit) * replicas;
|
||||
let cpuQty = this.parseCpuToCores(dto.cpuLimit) * replicas;
|
||||
let memoryQty = this.parseMemoryToGb(dto.memoryLimit) * replicas;
|
||||
// The database runs as its own single-replica workload; bill its CPU/RAM on
|
||||
// top of the app's, using the user-selected per-database resources.
|
||||
if (hasDatabase && dto.databaseResources) {
|
||||
cpuQty += this.parseCpuToCores(dto.databaseResources.cpuLimit);
|
||||
memoryQty += this.parseMemoryToGb(dto.databaseResources.memoryLimit);
|
||||
}
|
||||
const storageQty =
|
||||
(dto.dbStorageSize
|
||||
? parseFloat(String(dto.dbStorageSize).replace(/Gi$/i, '')) || 0
|
||||
|
||||
Reference in New Issue
Block a user