feat: integrate multi-cloud storage (S3, OSS, COS, Local)

Add built-in storage support from v-hono-storage module:
- StorageProvider interface with 4 backends
- FileService with HTTP handlers
- Multipart upload support
- 86 property-based tests
- Retry mechanism with exponential backoff

Breaking: None (backward compatible)
This commit is contained in:
qinqx
2026-01-25 14:07:34 +08:00
parent fa26a136e6
commit e7760ea80d
33 changed files with 17951 additions and 159 deletions
+557
View File
@@ -0,0 +1,557 @@
module hono
import time
import rand
import math
// ============================================================================
// 重试配置
// ============================================================================
// 重试配置结构
pub struct RetryConfig {
pub:
max_retries int = 3 // 最大重试次数
initial_delay int = 1000 // 初始延迟(毫秒)
max_delay int = 30000 // 最大延迟(毫秒)
multiplier f64 = 2.0 // 延迟倍增因子
jitter bool = true // 是否添加随机抖动
jitter_factor f64 = 0.25 // 抖动因子 (0.0 - 1.0)
}
// 默认重试配置
pub fn default_retry_config() RetryConfig {
return RetryConfig{
max_retries: 3
initial_delay: 1000
max_delay: 30000
multiplier: 2.0
jitter: true
jitter_factor: 0.25
}
}
// 激进重试配置(更多重试,更短延迟)
pub fn aggressive_retry_config() RetryConfig {
return RetryConfig{
max_retries: 5
initial_delay: 500
max_delay: 10000
multiplier: 1.5
jitter: true
jitter_factor: 0.2
}
}
// 保守重试配置(更少重试,更长延迟)
pub fn conservative_retry_config() RetryConfig {
return RetryConfig{
max_retries: 2
initial_delay: 2000
max_delay: 60000
multiplier: 3.0
jitter: true
jitter_factor: 0.3
}
}
// ============================================================================
// 重试结果
// ============================================================================
// 重试执行结果
pub struct RetryResult[T] {
pub:
success bool
value T
total_retries int
total_delay int // 总延迟时间(毫秒)
final_error StorageError
history []RetryAttempt
}
// 创建成功的重试结果
fn new_retry_success[T](value T, total_retries int, total_delay int, history []RetryAttempt) RetryResult[T] {
return RetryResult[T]{
success: true
value: value
total_retries: total_retries
total_delay: total_delay
final_error: StorageError{}
history: history
}
}
// 创建失败的重试结果
fn new_retry_failure[T](final_error StorageError, total_retries int, total_delay int, history []RetryAttempt) RetryResult[T] {
return RetryResult[T]{
success: false
value: T{}
total_retries: total_retries
total_delay: total_delay
final_error: final_error
history: history
}
}
// ============================================================================
// 重试执行器
// ============================================================================
// 重试执行器
pub struct RetryExecutor {
config RetryConfig
}
// 创建重试执行器
pub fn new_retry_executor(config RetryConfig) RetryExecutor {
return RetryExecutor{
config: config
}
}
// 使用默认配置创建重试执行器
pub fn new_default_retry_executor() RetryExecutor {
return RetryExecutor{
config: default_retry_config()
}
}
// 计算下一次重试的延迟时间
pub fn (r RetryExecutor) calculate_delay(attempt int) int {
// 指数退避: delay = initial_delay * (multiplier ^ attempt)
base_delay := f64(r.config.initial_delay) * math.pow(r.config.multiplier, f64(attempt))
// 限制最大延迟
mut delay := int(math.min(base_delay, f64(r.config.max_delay)))
// 添加随机抖动
if r.config.jitter && delay > 0 {
jitter_range := int(f64(delay) * r.config.jitter_factor)
if jitter_range > 0 {
jitter := rand.int_in_range(0, jitter_range * 2) or { 0 }
delay = delay - jitter_range + jitter
}
}
// 确保延迟不为负
if delay < 0 {
delay = 0
}
return delay
}
// 执行带重试的操作(返回 []u8)
pub fn (r RetryExecutor) execute_bytes(operation fn () ![]u8, provider string, op_name string) RetryResult[[]u8] {
mut history := []RetryAttempt{}
mut total_delay := 0
mut last_error := StorageError{}
for attempt in 0 .. r.config.max_retries + 1 {
// 执行操作
result := operation() or {
// 解析错误
error_msg := err.msg()
storage_err := parse_error_message(error_msg, provider, op_name)
// 检查是否可重试
if !storage_err.is_retryable() {
// 不可重试错误,立即返回
return new_retry_failure[[]u8](storage_err, attempt, total_delay, history)
}
last_error = storage_err
// 如果还有重试机会
if attempt < r.config.max_retries {
delay := r.calculate_delay(attempt)
// 记录重试尝试
history << RetryAttempt{
attempt_number: attempt + 1
timestamp: time.now().unix()
error_message: error_msg
delay_ms: delay
}
total_delay += delay
// 等待后重试
if delay > 0 {
time.sleep(delay * time.millisecond)
}
}
continue
}
// 操作成功
return new_retry_success[[]u8](result, attempt, total_delay, history)
}
// 所有重试都失败
mut final_error := last_error
final_error.retry_history = history
return new_retry_failure[[]u8](final_error, r.config.max_retries, total_delay, history)
}
// 执行带重试的操作(返回 StorageResult)
pub fn (r RetryExecutor) execute_storage_result(operation fn () !StorageResult, provider string, op_name string) RetryResult[StorageResult] {
mut history := []RetryAttempt{}
mut total_delay := 0
mut last_error := StorageError{}
for attempt in 0 .. r.config.max_retries + 1 {
// 执行操作
result := operation() or {
// 解析错误
error_msg := err.msg()
storage_err := parse_error_message(error_msg, provider, op_name)
// 检查是否可重试
if !storage_err.is_retryable() {
// 不可重试错误,立即返回
return new_retry_failure[StorageResult](storage_err, attempt, total_delay, history)
}
last_error = storage_err
// 如果还有重试机会
if attempt < r.config.max_retries {
delay := r.calculate_delay(attempt)
// 记录重试尝试
history << RetryAttempt{
attempt_number: attempt + 1
timestamp: time.now().unix()
error_message: error_msg
delay_ms: delay
}
total_delay += delay
// 等待后重试
if delay > 0 {
time.sleep(delay * time.millisecond)
}
}
continue
}
// 操作成功
return new_retry_success[StorageResult](result, attempt, total_delay, history)
}
// 所有重试都失败
mut final_error := last_error
final_error.retry_history = history
return new_retry_failure[StorageResult](final_error, r.config.max_retries, total_delay, history)
}
// 执行带重试的操作(返回 bool)
pub fn (r RetryExecutor) execute_bool(operation fn () !bool, provider string, op_name string) RetryResult[bool] {
mut history := []RetryAttempt{}
mut total_delay := 0
mut last_error := StorageError{}
for attempt in 0 .. r.config.max_retries + 1 {
// 执行操作
result := operation() or {
// 解析错误
error_msg := err.msg()
storage_err := parse_error_message(error_msg, provider, op_name)
// 检查是否可重试
if !storage_err.is_retryable() {
// 不可重试错误,立即返回
return new_retry_failure[bool](storage_err, attempt, total_delay, history)
}
last_error = storage_err
// 如果还有重试机会
if attempt < r.config.max_retries {
delay := r.calculate_delay(attempt)
// 记录重试尝试
history << RetryAttempt{
attempt_number: attempt + 1
timestamp: time.now().unix()
error_message: error_msg
delay_ms: delay
}
total_delay += delay
// 等待后重试
if delay > 0 {
time.sleep(delay * time.millisecond)
}
}
continue
}
// 操作成功
return new_retry_success[bool](result, attempt, total_delay, history)
}
// 所有重试都失败
mut final_error := last_error
final_error.retry_history = history
return new_retry_failure[bool](final_error, r.config.max_retries, total_delay, history)
}
// 执行带重试的操作(无返回值)
pub fn (r RetryExecutor) execute_void(operation fn () !, provider string, op_name string) RetryResult[bool] {
mut history := []RetryAttempt{}
mut total_delay := 0
mut last_error := StorageError{}
for attempt in 0 .. r.config.max_retries + 1 {
// 执行操作
operation() or {
// 解析错误
error_msg := err.msg()
storage_err := parse_error_message(error_msg, provider, op_name)
// 检查是否可重试
if !storage_err.is_retryable() {
// 不可重试错误,立即返回
return new_retry_failure[bool](storage_err, attempt, total_delay, history)
}
last_error = storage_err
// 如果还有重试机会
if attempt < r.config.max_retries {
delay := r.calculate_delay(attempt)
// 记录重试尝试
history << RetryAttempt{
attempt_number: attempt + 1
timestamp: time.now().unix()
error_message: error_msg
delay_ms: delay
}
total_delay += delay
// 等待后重试
if delay > 0 {
time.sleep(delay * time.millisecond)
}
}
continue
}
// 操作成功
return new_retry_success[bool](true, attempt, total_delay, history)
}
// 所有重试都失败
mut final_error := last_error
final_error.retry_history = history
return new_retry_failure[bool](final_error, r.config.max_retries, total_delay, history)
}
// 执行带重试的操作(返回 string)
pub fn (r RetryExecutor) execute_string(operation fn () !string, provider string, op_name string) RetryResult[string] {
mut history := []RetryAttempt{}
mut total_delay := 0
mut last_error := StorageError{}
for attempt in 0 .. r.config.max_retries + 1 {
// 执行操作
result := operation() or {
// 解析错误
error_msg := err.msg()
storage_err := parse_error_message(error_msg, provider, op_name)
// 检查是否可重试
if !storage_err.is_retryable() {
// 不可重试错误,立即返回
return new_retry_failure[string](storage_err, attempt, total_delay, history)
}
last_error = storage_err
// 如果还有重试机会
if attempt < r.config.max_retries {
delay := r.calculate_delay(attempt)
// 记录重试尝试
history << RetryAttempt{
attempt_number: attempt + 1
timestamp: time.now().unix()
error_message: error_msg
delay_ms: delay
}
total_delay += delay
// 等待后重试
if delay > 0 {
time.sleep(delay * time.millisecond)
}
}
continue
}
// 操作成功
return new_retry_success[string](result, attempt, total_delay, history)
}
// 所有重试都失败
mut final_error := last_error
final_error.retry_history = history
return new_retry_failure[string](final_error, r.config.max_retries, total_delay, history)
}
// ============================================================================
// 辅助函数
// ============================================================================
// 从错误消息解析 StorageError
fn parse_error_message(error_msg string, provider string, operation string) StorageError {
// 尝试从错误消息中提取信息
// 格式: "provider/operation: message (kind: xxx, http_status: yyy)"
// 检查是否包含已知的错误类型关键字
kind := detect_error_kind_from_message(error_msg)
// 尝试提取 HTTP 状态码
http_status := extract_http_status_from_message(error_msg)
return StorageError{
kind: kind
message: error_msg
provider: provider
operation: operation
http_status: http_status
retry_count: 0
details: map[string]string{}
retry_history: []RetryAttempt{}
}
}
// 从错误消息检测错误类型
fn detect_error_kind_from_message(msg string) StorageErrorKind {
lower_msg := msg.to_lower()
// 可重试错误
if lower_msg.contains('timeout') || lower_msg.contains('timed out') {
return .network_timeout
}
if lower_msg.contains('service unavailable') || lower_msg.contains('503') {
return .service_unavailable
}
if lower_msg.contains('rate limit') || lower_msg.contains('too many requests') || lower_msg.contains('429') {
return .rate_limited
}
if lower_msg.contains('connection reset') || lower_msg.contains('connection refused') {
return .connection_reset
}
if lower_msg.contains('temporary') || lower_msg.contains('retry') {
return .temporary_failure
}
// 不可重试错误
if lower_msg.contains('invalid credentials') || lower_msg.contains('401') || lower_msg.contains('unauthorized') {
return .invalid_credentials
}
if lower_msg.contains('access denied') || lower_msg.contains('403') || lower_msg.contains('forbidden') {
return .access_denied
}
if lower_msg.contains('bucket not found') || lower_msg.contains('nosuchbucket') {
return .bucket_not_found
}
if lower_msg.contains('not found') || lower_msg.contains('404') || lower_msg.contains('nosuchkey') {
return .object_not_found
}
if lower_msg.contains('invalid') && (lower_msg.contains('key') || lower_msg.contains('name')) {
return .invalid_object_key
}
if lower_msg.contains('quota') || lower_msg.contains('limit exceeded') {
return .quota_exceeded
}
if lower_msg.contains('config') {
return .invalid_config
}
return .unknown
}
// 从错误消息提取 HTTP 状态码
fn extract_http_status_from_message(msg string) int {
// 尝试查找 "http_status: xxx" 模式
if status_idx := msg.index('http_status:') {
start := status_idx + 12
mut end := start
for end < msg.len && (msg[end].is_digit() || msg[end] == ` `) {
end++
}
status_str := msg[start..end].trim_space()
return status_str.int()
}
// 尝试查找常见的 HTTP 状态码
status_codes := [400, 401, 403, 404, 409, 413, 429, 500, 502, 503, 504]
for code in status_codes {
if msg.contains(code.str()) {
return code
}
}
return 0
}
// ============================================================================
// 简化的重试函数
// ============================================================================
// 使用默认配置执行带重试的操作(返回 []u8)
pub fn retry_bytes(operation fn () ![]u8, provider string, op_name string) RetryResult[[]u8] {
executor := new_default_retry_executor()
return executor.execute_bytes(operation, provider, op_name)
}
// 使用默认配置执行带重试的操作(返回 StorageResult)
pub fn retry_storage_result(operation fn () !StorageResult, provider string, op_name string) RetryResult[StorageResult] {
executor := new_default_retry_executor()
return executor.execute_storage_result(operation, provider, op_name)
}
// 使用默认配置执行带重试的操作(返回 bool)
pub fn retry_bool(operation fn () !bool, provider string, op_name string) RetryResult[bool] {
executor := new_default_retry_executor()
return executor.execute_bool(operation, provider, op_name)
}
// 使用默认配置执行带重试的操作(无返回值)
pub fn retry_void(operation fn () !, provider string, op_name string) RetryResult[bool] {
executor := new_default_retry_executor()
return executor.execute_void(operation, provider, op_name)
}
// 使用默认配置执行带重试的操作(返回 string)
pub fn retry_string(operation fn () !string, provider string, op_name string) RetryResult[string] {
executor := new_default_retry_executor()
return executor.execute_string(operation, provider, op_name)
}
// 使用自定义配置执行带重试的操作(返回 []u8)
pub fn retry_bytes_with_config(operation fn () ![]u8, provider string, op_name string, config RetryConfig) RetryResult[[]u8] {
executor := new_retry_executor(config)
return executor.execute_bytes(operation, provider, op_name)
}
// 使用自定义配置执行带重试的操作(返回 StorageResult)
pub fn retry_storage_result_with_config(operation fn () !StorageResult, provider string, op_name string, config RetryConfig) RetryResult[StorageResult] {
executor := new_retry_executor(config)
return executor.execute_storage_result(operation, provider, op_name)
}