修复资产计费边界风险
资产生成预扣费改为 fail-closed,避免钱包异常时继续调用外部生成 新增钱包退款 outbox,退款失败时本地落盘并后台重放 拼图首图后台任务改用 SpacetimeDB claim 表实现跨实例互斥 计费 ledger id 统一绑定 request_id,并让前端重试复用 x-request-id 同步 SpacetimeDB bindings、后端架构文档和 Hermes 决策记录
This commit is contained in:
@@ -32,6 +32,11 @@ pub struct AppConfig {
|
||||
pub tracking_outbox_batch_size: usize,
|
||||
pub tracking_outbox_flush_interval: Duration,
|
||||
pub tracking_outbox_max_bytes: u64,
|
||||
pub wallet_refund_outbox_enabled: bool,
|
||||
pub wallet_refund_outbox_dir: PathBuf,
|
||||
pub wallet_refund_outbox_batch_size: usize,
|
||||
pub wallet_refund_outbox_flush_interval: Duration,
|
||||
pub wallet_refund_outbox_max_bytes: u64,
|
||||
pub log_filter: String,
|
||||
pub otel_enabled: bool,
|
||||
pub admin_username: Option<String>,
|
||||
@@ -183,6 +188,11 @@ impl Default for AppConfig {
|
||||
tracking_outbox_batch_size: 500,
|
||||
tracking_outbox_flush_interval: Duration::from_millis(1_000),
|
||||
tracking_outbox_max_bytes: 256 * 1024 * 1024,
|
||||
wallet_refund_outbox_enabled: true,
|
||||
wallet_refund_outbox_dir: PathBuf::from("server-rs/.data/wallet-refund-outbox"),
|
||||
wallet_refund_outbox_batch_size: 100,
|
||||
wallet_refund_outbox_flush_interval: Duration::from_millis(1_000),
|
||||
wallet_refund_outbox_max_bytes: 64 * 1024 * 1024,
|
||||
log_filter: "info,tower_http=info".to_string(),
|
||||
otel_enabled: false,
|
||||
admin_username: None,
|
||||
@@ -409,6 +419,27 @@ impl AppConfig {
|
||||
{
|
||||
config.tracking_outbox_max_bytes = max_bytes;
|
||||
}
|
||||
if let Some(enabled) = read_first_bool_env(&["GENARRATIVE_WALLET_REFUND_OUTBOX_ENABLED"]) {
|
||||
config.wallet_refund_outbox_enabled = enabled;
|
||||
}
|
||||
if let Some(dir) = read_first_non_empty_env(&["GENARRATIVE_WALLET_REFUND_OUTBOX_DIR"]) {
|
||||
config.wallet_refund_outbox_dir = PathBuf::from(dir);
|
||||
}
|
||||
if let Some(batch_size) =
|
||||
read_first_usize_env(&["GENARRATIVE_WALLET_REFUND_OUTBOX_BATCH_SIZE"])
|
||||
{
|
||||
config.wallet_refund_outbox_batch_size = batch_size;
|
||||
}
|
||||
if let Some(flush_interval_ms) =
|
||||
read_first_positive_u64_env(&["GENARRATIVE_WALLET_REFUND_OUTBOX_FLUSH_INTERVAL_MS"])
|
||||
{
|
||||
config.wallet_refund_outbox_flush_interval = Duration::from_millis(flush_interval_ms);
|
||||
}
|
||||
if let Some(max_bytes) =
|
||||
read_first_positive_u64_env(&["GENARRATIVE_WALLET_REFUND_OUTBOX_MAX_BYTES"])
|
||||
{
|
||||
config.wallet_refund_outbox_max_bytes = max_bytes;
|
||||
}
|
||||
if let Some(otel_enabled) = read_first_bool_env(&["GENARRATIVE_OTEL_ENABLED"]) {
|
||||
config.otel_enabled = otel_enabled;
|
||||
}
|
||||
@@ -1380,6 +1411,11 @@ mod tests {
|
||||
std::env::remove_var("GENARRATIVE_TRACKING_OUTBOX_BATCH_SIZE");
|
||||
std::env::remove_var("GENARRATIVE_TRACKING_OUTBOX_FLUSH_INTERVAL_MS");
|
||||
std::env::remove_var("GENARRATIVE_TRACKING_OUTBOX_MAX_BYTES");
|
||||
std::env::remove_var("GENARRATIVE_WALLET_REFUND_OUTBOX_ENABLED");
|
||||
std::env::remove_var("GENARRATIVE_WALLET_REFUND_OUTBOX_DIR");
|
||||
std::env::remove_var("GENARRATIVE_WALLET_REFUND_OUTBOX_BATCH_SIZE");
|
||||
std::env::remove_var("GENARRATIVE_WALLET_REFUND_OUTBOX_FLUSH_INTERVAL_MS");
|
||||
std::env::remove_var("GENARRATIVE_WALLET_REFUND_OUTBOX_MAX_BYTES");
|
||||
std::env::remove_var("GENARRATIVE_OTEL_ENABLED");
|
||||
std::env::set_var("GENARRATIVE_API_LISTEN_BACKLOG", "2048");
|
||||
std::env::set_var("GENARRATIVE_API_WORKER_THREADS", "6");
|
||||
@@ -1396,6 +1432,14 @@ mod tests {
|
||||
std::env::set_var("GENARRATIVE_TRACKING_OUTBOX_BATCH_SIZE", "250");
|
||||
std::env::set_var("GENARRATIVE_TRACKING_OUTBOX_FLUSH_INTERVAL_MS", "2000");
|
||||
std::env::set_var("GENARRATIVE_TRACKING_OUTBOX_MAX_BYTES", "1048576");
|
||||
std::env::set_var("GENARRATIVE_WALLET_REFUND_OUTBOX_ENABLED", "false");
|
||||
std::env::set_var(
|
||||
"GENARRATIVE_WALLET_REFUND_OUTBOX_DIR",
|
||||
"/tmp/genarrative-wallet-refund-outbox",
|
||||
);
|
||||
std::env::set_var("GENARRATIVE_WALLET_REFUND_OUTBOX_BATCH_SIZE", "50");
|
||||
std::env::set_var("GENARRATIVE_WALLET_REFUND_OUTBOX_FLUSH_INTERVAL_MS", "3000");
|
||||
std::env::set_var("GENARRATIVE_WALLET_REFUND_OUTBOX_MAX_BYTES", "524288");
|
||||
std::env::set_var("GENARRATIVE_OTEL_ENABLED", "true");
|
||||
}
|
||||
|
||||
@@ -1421,6 +1465,17 @@ mod tests {
|
||||
std::time::Duration::from_millis(2_000)
|
||||
);
|
||||
assert_eq!(config.tracking_outbox_max_bytes, 1_048_576);
|
||||
assert!(!config.wallet_refund_outbox_enabled);
|
||||
assert_eq!(
|
||||
config.wallet_refund_outbox_dir,
|
||||
std::path::PathBuf::from("/tmp/genarrative-wallet-refund-outbox")
|
||||
);
|
||||
assert_eq!(config.wallet_refund_outbox_batch_size, 50);
|
||||
assert_eq!(
|
||||
config.wallet_refund_outbox_flush_interval,
|
||||
std::time::Duration::from_millis(3_000)
|
||||
);
|
||||
assert_eq!(config.wallet_refund_outbox_max_bytes, 524_288);
|
||||
assert!(config.otel_enabled);
|
||||
|
||||
unsafe {
|
||||
@@ -1436,6 +1491,11 @@ mod tests {
|
||||
std::env::remove_var("GENARRATIVE_TRACKING_OUTBOX_BATCH_SIZE");
|
||||
std::env::remove_var("GENARRATIVE_TRACKING_OUTBOX_FLUSH_INTERVAL_MS");
|
||||
std::env::remove_var("GENARRATIVE_TRACKING_OUTBOX_MAX_BYTES");
|
||||
std::env::remove_var("GENARRATIVE_WALLET_REFUND_OUTBOX_ENABLED");
|
||||
std::env::remove_var("GENARRATIVE_WALLET_REFUND_OUTBOX_DIR");
|
||||
std::env::remove_var("GENARRATIVE_WALLET_REFUND_OUTBOX_BATCH_SIZE");
|
||||
std::env::remove_var("GENARRATIVE_WALLET_REFUND_OUTBOX_FLUSH_INTERVAL_MS");
|
||||
std::env::remove_var("GENARRATIVE_WALLET_REFUND_OUTBOX_MAX_BYTES");
|
||||
std::env::remove_var("GENARRATIVE_OTEL_ENABLED");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user