feat: enforce durable queue resource limits and backpressure

This commit is contained in:
2026-09-17 17:25:56 +08:00
parent 3ce7d2c104
commit 5c8825e4bc
38 changed files with 1231 additions and 30 deletions

View File

@@ -10,6 +10,7 @@ import (
"net/http"
"net/url"
"strconv"
"time"
)
// APIError 提供可分支处理的错误码和请求追踪标识,不包含内部数据库错误。
@@ -18,6 +19,8 @@ type APIError struct {
Code string
Message string
RequestID string
// RetryAfter 服务端建议的最小退避时间;缺失、无效或超过一小时返回零,SDK 不自动重试。
RetryAfter time.Duration
}
func (e *APIError) Error() string {
@@ -56,7 +59,7 @@ func (c *Client) ListTasks(ctx context.Context, in ListTasksRequest) (TaskPage,
return out, err
}
// CancelTask 当前只取消 WAITING 状态且不归属流程的任务;重复请求使用原键与原版本。
// CancelTask 保存取消意图;运行中等待结果确认,重复请求保留原键与原版本。
func (c *Client) CancelTask(ctx context.Context, id, key string, in CancelTaskRequest) (CancelTaskResult, error) {
var out CancelTaskResult
if !validID(id) {
@@ -150,7 +153,11 @@ func (c *Client) call(ctx context.Context, method, path, key string, in, out any
decoded := json.Unmarshal(raw, &envelope)
if response.StatusCode < 200 || response.StatusCode >= 300 {
if decoded == nil && envelope.Error != nil && len(envelope.Error.Code) <= 64 && len(envelope.Error.Message) <= 1024 && validID(envelope.RequestID) {
return &APIError{response.StatusCode, envelope.Error.Code, envelope.Error.Message, envelope.RequestID}
var retryAfter time.Duration
if seconds, err := strconv.ParseUint(response.Header.Get("Retry-After"), 10, 32); err == nil && seconds > 0 && seconds <= 3600 {
retryAfter = time.Duration(seconds) * time.Second
}
return &APIError{StatusCode: response.StatusCode, Code: envelope.Error.Code, Message: envelope.Error.Message, RequestID: envelope.RequestID, RetryAfter: retryAfter}
}
return &HTTPError{StatusCode: response.StatusCode}
}
@@ -159,3 +166,13 @@ func (c *Client) call(ctx context.Context, method, path, key string, in, out any
}
return json.Unmarshal(envelope.Data, out)
}
// ConfigureQueue 完整替换队列控制参数;重复请求保留原版本及幂等键。
func (c *Client) ConfigureQueue(ctx context.Context, id, key string, in ConfigureQueueRequest) (CreateQueueResult, error) {
var out CreateQueueResult
if !validID(id) {
return out, errors.New("invalid queue id")
}
err := c.call(ctx, "POST", "/v1/queues/"+id+":configure", key, in, &out)
return out, err
}