如何通过 Cloud Billing Catalog API 确定最小计费周期?
结论
仅凭 Cloud Billing Catalog API,无法确定资源的最小计量单位或最小计费周期。
baseUnit: "s" 并不表示 Cloud DNS 托管区域按秒取整计费。它只是定价表达式中的基础换算单位。Managed Zone 的用量如何采集、取整和汇总,仍需以对应产品的计费文档为准,也就是不足一小时按一小时计算。
API 字段应如何理解
这些字段分别描述报价单位、内部基础单位,以及两者的换算关系:
usageUnit: "mo":unitPrice对应的报价单位是月。baseUnit: "s":计价系统用秒来表达或传递基础用量。baseUnitConversionFactor: 2505600:一个报价单位所包含的基础单位数量,即:
1 mo = 2505600 s
换算后:
2505600 / 3600 = 696 h
696 / 24 = 29 d
这里的 mo 是计费系统定义的标准化价格单位,不能直接理解为当前自然月的实际秒数。因此,不能由此推断某个月一定按 29 天结算,也不能推断用量精确到秒,只有不足一秒时才会进位。
首档价格为:
0.20 USD / mo
将它换算成内部基础单位价格:
0.20 / 2505600 USD / s
如果计费系统接收到的基础用量是 (B),理论上的价格换算形式为:
cost = (B / baseUnitConversionFactor) × unitPrice
问题在于,Catalog API 并未说明资源的原始活动如何转换成 (B)。如果产品规则先将托管区域的存续时间向上取整到小时,再转换成秒,那么存续时间不足一小时的区域仍会产生 3600 s 的计费用量,而不是按实际存续秒数计费。
例如,某个托管区域只存在 10 分钟,产品规则可能先进行以下处理:
10 min → ceil(10 / 60) h → 1 h → 3600 s
然后再根据 Catalog 中的月价和换算因子计算:
cost = (3600 / 2505600) × 0.20
所以,按小时取整与使用秒作为内部基础单位并不矛盾。前者规定如何量化用量,后者用于表达价格和换算单位。
aggregationInfo 也不是最小计费周期
响应中的:
{
"aggregationLevel": "ACCOUNT",
"aggregationInterval": "MONTHLY",
"aggregationCount": 1
}
表示该 SKU 的阶梯用量按账号每月聚合一次,主要用于确定不同 tieredRates 门槛的应用方式。
例如:
{
"startUsageAmount": 0,
"unitPrice": {
"nanos": 200000000
}
},
{
"startUsageAmount": 25,
"unitPrice": {
"nanos": 100000000
}
}
这表示账号的月度聚合用量超过 25 个报价单位后,后续用量进入下一价格档位。MONTHLY 不表示资源至少需要购买一个月,也不表示每月只计算一次,更不表示不足一个月时不能按比例计费。
这里需要区分四个概念:
| 概念 | Managed Zone 示例 | Catalog API 是否完整提供 |
|---|---|---|
| 报价单位 | mo | 是 |
| 内部基础单位 | s | 是 |
| 阶梯价格聚合周期 | MONTHLY | 是 |
| 原始用量采集、取整规则 | 不足一小时按一小时 | 否 |
正确的判断步骤
判断一个 SKU 的实际计费粒度时,可以按以下顺序处理:
- 使用 Catalog API 获取
usageUnit、baseUnit、换算因子、价格档位和聚合方式。 - 查阅对应产品的定价说明,确认用量如何采集、取整和按比例折算。
- 检查产品是否设有最低计费时长、启动费、按请求进位或其他特殊规则。
- 使用 Cloud Billing 导出的实际用量验证边界场景,例如资源只存在几分钟或跨越整点。
- 如果产品文档未说明取整规则,就不能根据
baseUnit推断,应向 Google Cloud 支持确认该 SKU 的计量逻辑。
解析 Catalog 响应的示例
下面的代码只能解释价格如何换算,无法判断产品是否按小时取整:
from decimal import Decimal
pricing_expression = {
"usageUnit": "mo",
"baseUnit": "s",
"baseUnitConversionFactor": 2505600,
}
unit_price = Decimal("0.20")
factor = Decimal(str(pricing_expression["baseUnitConversionFactor"]))
price_per_base_unit = unit_price / factor
print(f"Price: {unit_price} USD/{pricing_expression['usageUnit']}")
print(
f"Conversion: 1 {pricing_expression['usageUnit']} "
f"= {factor} {pricing_expression['baseUnit']}"
)
print(
f"Internal equivalent: {price_per_base_unit} "
f"USD/{pricing_expression['baseUnit']}"
)
如果产品文档明确规定按小时向上取整,可以在估算程序中单独实现这项规则:
from decimal import Decimal, ROUND_CEILING
SECONDS_PER_HOUR = Decimal("3600")
BASE_UNIT_CONVERSION_FACTOR = Decimal("2505600")
MONTHLY_UNIT_PRICE = Decimal("0.20")
def estimate_managed_zone_cost(actual_seconds: int) -> Decimal:
if actual_seconds <= 0:
return Decimal("0")
billed_hours = (
Decimal(actual_seconds) / SECONDS_PER_HOUR
).to_integral_value(rounding=ROUND_CEILING)
billed_base_units = billed_hours * SECONDS_PER_HOUR
return (
billed_base_units
/ BASE_UNIT_CONVERSION_FACTOR
* MONTHLY_UNIT_PRICE
)
这段代码表达的是先按照产品规则量化用量,再根据 Catalog 价格进行换算。它并不是对 Google Cloud 账单系统实现细节的复刻。
注意事项
- 不要将
baseUnit理解为用量采样频率、取整粒度或最低购买时长。 - 不要将
aggregationInterval: "MONTHLY"理解为最低计费周期。 - 不要用自然月的秒数替换
baseUnitConversionFactor。估算时应使用对应价格版本返回的换算因子。 pricingInfo可能包含不同生效时间的价格。计算时应选择与实际用量时间相匹配的记录。- 计算阶梯价时,需要同时考虑
aggregationLevel和aggregationInterval,不能对每个资源单独套用。 - Catalog API 主要说明单位用量的价格及换算方式,产品文档则说明哪些行为会产生多少计费用量。
因此,这个响应与 Cloud DNS 按小时取整的规则并不冲突。s 是表达价格时使用的基础单位,小时才是产品文档规定的用量折算粒度。
备注:内容仅供参考。
