Files
gitea-MIRROR/modules/queue/base_levelqueue_unique.go
William Desportes 87a703819b fix(queue): bump levelqueue to v0.5.0 and remove workarounds (#40219)
levelqueue v0.5.1 fixes this upstream
(https://gitea.com/lunny/levelqueue/pulls/8), so this now only bumps the
dependency.

A push wrote "high" before the payload, so an interrupted push left an
id counted with no key. `LPop` then returned `ErrNotFound` without
advancing "low" and the wrapper retried forever: nothing popped, no
worker started, later items unreachable. All were only fixable by
clearing the queue, losing the backlog:
- https://github.com/go-gitea/gitea/issues/32006 — mail, 0 workers
- https://github.com/go-gitea/gitea/issues/28919 — mirror, "already in
queue"
- https://github.com/go-gitea/gitea/issues/33862 — archives hanging
- https://github.com/go-gitea/gitea/issues/33882 — self-healing request

v0.5.1 writes counter and payload in one batch, moves the counters past
missing keys, and repairs leftover unique-queue set entries, so affected
instances recover on their next pop.

No test: the behaviour lives in the dependency, which covers it in its
own `recover_test.go`.

---------

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-11-07 22:26:53 +00:00

72 lines
1.7 KiB
Go

// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package queue
import (
"context"
"sync"
"gitea.dev/modules/nosql"
"gitea.com/lunny/levelqueue"
"github.com/syndtr/goleveldb/leveldb"
)
type baseLevelQueueUnique struct {
*baseLevelQueueCommonImpl
internal *levelqueue.UniqueQueue
conn string
cfg *BaseConfig
db *leveldb.DB
muBase sync.Mutex // protects multi-step queue+set operations, e.g. the length check followed by a push
}
var (
_ baseQueue = (*baseLevelQueueUnique)(nil)
_ baseQueueNotifiableInterface = (*baseLevelQueueUnique)(nil)
)
func newBaseLevelQueueUnique(cfg *BaseConfig) (baseQueue, error) {
conn, db, err := prepareLevelDB(cfg)
if err != nil {
return nil, err
}
q := &baseLevelQueueUnique{conn: conn, cfg: cfg, db: db}
q.internal, err = levelqueue.NewUniqueQueue(db, []byte(cfg.QueueFullName), []byte(cfg.SetFullName), false)
if err != nil {
return nil, err
}
q.baseLevelQueueCommonImpl = baseLevelQueueCommon(q.cfg, &q.muBase, q.internal)
return q, nil
}
func (q *baseLevelQueueUnique) HasItem(ctx context.Context, data []byte) (bool, error) {
q.muBase.Lock()
defer q.muBase.Unlock()
return q.internal.Has(data)
}
func (q *baseLevelQueueUnique) Len(ctx context.Context) (int, error) {
q.muBase.Lock()
defer q.muBase.Unlock()
return int(q.internal.Len()), nil
}
func (q *baseLevelQueueUnique) Close() error {
q.muBase.Lock()
defer q.muBase.Unlock()
err := q.internal.Close()
q.db = nil // the db is not managed by us, it's managed by the nosql manager
_ = nosql.GetManager().CloseLevelDB(q.conn)
return err
}
func (q *baseLevelQueueUnique) RemoveAll(ctx context.Context) error {
q.muBase.Lock()
defer q.muBase.Unlock()
return q.internal.RemoveAll()
}