code.gitea.io/gitea@v1.22.3/modules/queue/queue.go (about)

     1  // Copyright 2023 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  // Package queue implements a specialized concurrent queue system for Gitea.
     5  //
     6  // Terminology:
     7  //
     8  //  1. Item:
     9  //     - An item can be a simple value, such as an integer, or a more complex structure that has multiple fields.
    10  //     Usually a item serves as a task or a message. Sets of items will be sent to a queue handler to be processed.
    11  //     - It's represented as a JSON-marshaled binary slice in the queue
    12  //     - Since the item is marshaled by JSON, and JSON doesn't have stable key-order/type support,
    13  //     so the decoded handler item may not be the same as the original "pushed" one if you use map/any types,
    14  //
    15  //  2. Batch:
    16  //     - A collection of items that are grouped together for processing. Each worker receives a batch of items.
    17  //
    18  //  3. Worker:
    19  //     - Individual unit of execution designed to process items from the queue. It's a goroutine that calls the Handler.
    20  //     - Workers will get new items through a channel (WorkerPoolQueue is responsible for the distribution).
    21  //     - Workers operate in parallel. The default value of max workers is determined by the setting system.
    22  //
    23  //  4. Handler (represented by HandlerFuncT type):
    24  //     - It's the function responsible for processing items. Each active worker will call it.
    25  //     - If an item or some items are not psuccessfully rocessed, the handler could return them as "unhandled items".
    26  //     In such scenarios, the queue system ensures these unhandled items are returned to the base queue after a brief delay.
    27  //     This mechanism is particularly beneficial in cases where the processing entity (like a document indexer) is
    28  //     temporarily unavailable. It ensures that no item is skipped or lost due to transient failures in the processing
    29  //     mechanism.
    30  //
    31  //  5. Base queue:
    32  //     - Represents the underlying storage mechanism for the queue. There are several implementations:
    33  //     - Channel: Uses Go's native channel constructs to manage the queue, suitable for in-memory queuing.
    34  //     - LevelDB: Especially useful in persistent queues for single instances.
    35  //     - Redis: Suitable for clusters, where we may have multiple nodes.
    36  //     - Dummy: This is special, it's not a real queue, it's a immediate no-op queue, which is useful for tests.
    37  //     - They all have the same abstraction, the same interface, and they are tested by the same testing code.
    38  //
    39  // 6. WorkerPoolQueue:
    40  //   - It's responsible to glue all together, using the "base queue" to provide "worker pool" functionality. It creates
    41  //     new workers if needed and can flush the queue, running all the items synchronously till it finishes.
    42  //   - Its "Push" function doesn't block forever, it will return an error if the queue is full after the timeout.
    43  //
    44  // 7. Manager:
    45  //   - The purpose of it is to serve as a centralized manager for multiple WorkerPoolQueue instances. Whenever we want
    46  //     to create a new queue, flush, or get a specific queue, we could use it.
    47  //
    48  // A queue can be "simple" or "unique". A unique queue will try to avoid duplicate items.
    49  // Unique queue's "Has" function can be used to check whether an item is already in the queue,
    50  // although it's not 100% reliable due to the lack of proper transaction support.
    51  // Simple queue's "Has" function always returns "has=false".
    52  //
    53  // A WorkerPoolQueue is a generic struct; this means it will work with any type but just for that type.
    54  // If you want another kind of items to run, you would have to call the manager to create a new WorkerPoolQueue for you
    55  // with a different handler that works with this new type of item. As an example of this:
    56  //
    57  //	 func Init() error {
    58  //		 itemQueue = queue.CreateSimpleQueue(graceful.GetManager().ShutdownContext(), "queue-name", handler)
    59  //		 ...
    60  //	 }
    61  //	 func handler(items ...*mypkg.QueueItem) []*mypkg.QueueItem { ... }
    62  package queue
    63  
    64  import "code.gitea.io/gitea/modules/util"
    65  
    66  type HandlerFuncT[T any] func(...T) (unhandled []T)
    67  
    68  var ErrAlreadyInQueue = util.NewAlreadyExistErrorf("already in queue")