3D 产物上传带上退避重试,一次瞬时网络失败不再废掉整笔生成

- platform-oss:新增按前缀拼对象键的 `put_object_with_transient_retry`,与内部对象 PUT 共用同一套「可判定才重试」规则与退避(传输 / 超时 / 408 / 429 / 5xx 才重试,确定性 4xx 直接失败),body 只准备一次、每次 attempt 复用引用计数字节;重试次数或退避配置不足时按配置错误失败关闭
- api-server storage:3D 产物的模型 / 预览图 PUT 改用该入口,最多 3 次尝试、退避 250 / 500 ms(与角色动画同一口径);模型上限 512 MiB 但实际产物远小,重试成本可控
- 新增用例:重试次数为 0 与退避不足都失败关闭;对一定解析不出的域名会真的按退避重试多次
- docs/technical:写明产物 PUT 的重试口径与「每个网络动作都要能重试」的理由
This commit is contained in:
2026-09-24 20:43:39 +08:00
parent 75f757f807
commit 8af02daeea
3 changed files with 129 additions and 1 deletions
@@ -36,6 +36,11 @@ pub(crate) const MODEL3D_ASSET_KIND: &str = crate::editor_project::EDITOR_MODEL3
/// `asset_object.entity_kind`:说明这个对象属于 3D 生成产物。
const MODEL3D_OBJECT_ENTITY_KIND: &str = "model3d";
/// 产物 PUT 的重试预算:模型最大 512 MiB,一次瞬时网络失败就会废掉一笔已经计费的生成,
/// 而任务本身最多只尝试一次,所以这里按与角色动画一致的口径退避重试三次。
const MODEL3D_ARTIFACT_PUT_MAX_ATTEMPTS: usize = 3;
const MODEL3D_ARTIFACT_PUT_RETRY_DELAYS_MS: [u64; 2] = [250, 500];
const MODEL3D_OBJECT_PATH_SEGMENT: &str = "model3d";
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -131,7 +136,7 @@ pub(crate) async fn store_model3d_artifact(
.ok_or_else(|| unsupported_artifact_content_type(content_type.as_str(), slot))?;
let file_name = format!("{}.{}", slot.file_stem(), extension);
let put_result = oss_client
.put_object(
.put_object_with_transient_retry(
http_client,
OssPutObjectRequest {
prefix: LegacyAssetPrefix::CharacterDrafts,
@@ -156,6 +161,8 @@ pub(crate) async fn store_model3d_artifact(
]),
body: bytes,
},
MODEL3D_ARTIFACT_PUT_MAX_ATTEMPTS,
&MODEL3D_ARTIFACT_PUT_RETRY_DELAYS_MS,
)
.await
.map_err(|error| map_oss_error(error, "aliyun-oss"))?;