feat: enforce shared application resource quotas across namespaces
This commit is contained in:
@@ -13,8 +13,15 @@ import (
|
||||
"git.yuntuo.hk/k2safe/orbit/pulse/worker"
|
||||
)
|
||||
|
||||
// 以下额度操作要求调用者已取得队列排他锁,且在同一事务中写入任务/尝试。
|
||||
// 以下额度操作要求调用者先取得应用额度锁,再取得队列排他锁,且在同一事务中写入任务/尝试。
|
||||
func reserveQueueTask(ctx context.Context, tx *sql.Tx, ns, queue core.ID) error {
|
||||
v, err := readApplicationResources(ctx, tx, ns)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if v.UnfinishedTasks >= uint64(v.MaxUnfinishedTasks) {
|
||||
return fault.ApplicationFull
|
||||
}
|
||||
r, err := tx.ExecContext(ctx, `UPDATE pulse_queues SET unfinished_tasks=unfinished_tasks+1 WHERE namespace_id=? AND id=? AND unfinished_tasks<max_unfinished_tasks`, ns, queue)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -26,11 +33,16 @@ func reserveQueueTask(ctx context.Context, tx *sql.Tx, ns, queue core.ID) error
|
||||
if n != 1 {
|
||||
return fault.QueueFull
|
||||
}
|
||||
return nil
|
||||
r, err = tx.ExecContext(ctx, `UPDATE orbit_application_resources SET unfinished_tasks=unfinished_tasks+1 WHERE application_id=? AND unfinished_tasks<max_unfinished_tasks`, v.ApplicationID)
|
||||
return exactlyOne(r, err)
|
||||
}
|
||||
|
||||
func releaseQueueTask(ctx context.Context, tx *sql.Tx, ns, queue core.ID) error {
|
||||
r, err := tx.ExecContext(ctx, `UPDATE pulse_queues SET unfinished_tasks=unfinished_tasks-1 WHERE namespace_id=? AND id=? AND unfinished_tasks>0`, ns, queue)
|
||||
r, err := tx.ExecContext(ctx, `UPDATE orbit_application_resources SET unfinished_tasks=unfinished_tasks-1 WHERE application_id=`+applicationForNamespace+` AND unfinished_tasks>0`, ns)
|
||||
if err := exactlyOne(r, err); err != nil {
|
||||
return err
|
||||
}
|
||||
r, err = tx.ExecContext(ctx, `UPDATE pulse_queues SET unfinished_tasks=unfinished_tasks-1 WHERE namespace_id=? AND id=? AND unfinished_tasks>0`, ns, queue)
|
||||
return exactlyOne(r, err)
|
||||
}
|
||||
|
||||
@@ -51,6 +63,9 @@ func queueStartBudget(ctx context.Context, tx *sql.Tx, ns, queue core.ID, now ti
|
||||
|
||||
// queueCanClaim 把尚未 Start 的预占也算入当前窗口可用空间,避免限流时空耗尝试预算。
|
||||
func queueCanClaim(ctx context.Context, tx *sql.Tx, ns, queue core.ID, now time.Time) (bool, error) {
|
||||
if ok, err := applicationCanClaim(ctx, tx, ns, now); err != nil || !ok {
|
||||
return false, err
|
||||
}
|
||||
rate, used, _, err := queueStartBudget(ctx, tx, ns, queue, now)
|
||||
if err != nil || used >= rate {
|
||||
return false, err
|
||||
@@ -72,6 +87,9 @@ func consumeQueueStart(ctx context.Context, tx *sql.Tx, ns, queue core.ID, now t
|
||||
end := now.Add(time.Second)
|
||||
until = &end
|
||||
}
|
||||
if err := consumeApplicationStart(ctx, tx, ns, now); err != nil {
|
||||
return err
|
||||
}
|
||||
r, err := tx.ExecContext(ctx, `UPDATE pulse_queues SET start_window_until=?,starts_in_window=? WHERE namespace_id=? AND id=?`, until, used+1, ns, queue)
|
||||
return exactlyOne(r, err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user