Files
v-hono/retry.v
T
qinqx e7760ea80d 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)
2026-01-25 14:07:34 +08:00

558 lines
16 KiB
V
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)
}