Help us shape the future of Go! Take the 2026 Go Developer Survey

waitpoll

package
v3.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 20, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Overview

Package waitpoll provides shared polling loops for wait-style tools.

Index

Constants

View Source
const (
	// TransferBound is how long a transfer handler waits for the move, 45
	// seconds.
	TransferBound time.Duration = 45e9
	// TransferInterval is the time between two reads of the moved object, 2
	// seconds.
	TransferInterval time.Duration = 2e9
)

The timing project.transfer and group.transfer wait with. GitLab 19.4 made both transfers asynchronous: the request answers with the object where it still is, and a background worker moves it seconds later. Both handlers read the object back until it sits where the transfer put it, and they share one timing so the two descriptions that state it stay true together.

The bound keeps the whole call, the transfer request included, below the 60 seconds a common MCP client waits for a tool call before giving up on it (the TypeScript SDK's default request timeout). A wait that ran into that timeout would hand the model a client-side failure instead of the answer that says the move is still queued, which is the one it can act on.

Both are written as nanoseconds rather than as products of time.Second: a product at package level is an arithmetic mutant no coverage profile reaches, so gremlins reports it as never covered although every test that reads the bound reads it.

Variables

This section is empty.

Functions

func AnsweredAt added in v3.1.0

func AnsweredAt(resp *gl.Response) time.Time

AnsweredAt is when GitLab answered a transfer, read from the Date header of its response, so it is GitLab's clock and not this server's that a failure is dated against. It is zero when the response carries no date it can read.

func TransferFailureReason added in v3.3.0

func TransferFailureReason(ctx context.Context, client *gitlabclient.Client, todoID int64) string

TransferFailureReason returns the reason GitLab gives for the failed transfer the to-do item todoID records, cleaned to one line of at most 500 characters, or "" when there is none to give.

GitLab records the reason from 19.5, on the namespace the transfer moved, and serves it only over GraphQL. An instance known to run an older release is not asked, since it refuses the document. One whose release is not known is asked anyway: its version endpoint did not answer, it refused the token Metadata: Read (a fine-grained token without it), or it named something that is not a version, and a refusal from it costs nothing but the reason.

The read is the last word on a transfer the caller is already being told failed, so every way it can fail gives "" rather than an error that would take the failure's place: GitLab failing the request, a release that does not know the field, a fine-grained token refused the item, and an item GitLab gives no reason for.

The worker creates the item and stores the reason a moment later, in two writes (Projects::TransferWorker and Namespaces::Groups::TransferWorker), so a read between them finds the item without the reason. An item answered with no reason is therefore read once more after [transferReasonRetry], and no more: a reason still missing then is one GitLab did not store, or one it cleared because the transfer was scheduled again, which a retry of the worker does. A caller that goes away during the pause gets no reason and no second read.

func Until added in v3.1.0

func Until[T any](ctx context.Context, opts UntilOptions[T]) (state T, landed bool, err error)

Until reads an object every Interval until Landed accepts what Read returned, and answers that state and true. It answers the zero T and false when Bound runs out first, and the caller's context error when that context ends first: a caller that went away wants no answer, whether the change landed or not.

A read that fails is not an answer and does not end the wait. The change being waited for was already accepted, so one read GitLab fails to serve (a 502 from a proxy, a timeout) says nothing about it and the next may succeed. A failure that persists runs the wait to Bound, which the caller reports as a change not yet applied, the same answer a slow worker produces.

Each read runs under the bound as well as the caller's context, so a read still in flight when the bound runs out is abandoned rather than awaited.

Types

type DurationFunc

type DurationFunc func(seconds int) time.Duration

DurationFunc converts user-facing seconds into the duration used by timers.

type Options

type Options[T any] struct {
	Request         *mcp.CallToolRequest
	IntervalSeconds int
	TimeoutSeconds  int
	FailOnError     *bool
	PollDuration    DurationFunc
	ProgressMessage func(attempt int) string
	Poll            func(context.Context) (T, error)
	Status          func(T) string
	FailureError    func(T) error
}

Options configures a polling loop for one wait-style tool.

type Result

type Result[T any] struct {
	Item        T
	WaitedFor   string
	PollCount   int
	FinalStatus string
	TimedOut    bool
}

Result reports the final item and polling metadata.

func Poll

func Poll[T any](ctx context.Context, opts Options[T]) (Result[T], error)

Poll polls until the item reaches a terminal status, the context is canceled, or the configured timeout expires.

type TransferFailure added in v3.3.0

type TransferFailure struct {
	// TodoID is the item's id, which [TransferFailureReason] reads the reason
	// of.
	TodoID int64
	// Destination is the path the item names, where the transfer was to put
	// the object.
	Destination string
}

TransferFailure is the to-do item GitLab left the caller when a transfer failed in the background.

func TransferFailed added in v3.1.0

func TransferFailed(ctx context.Context, client *gitlabclient.Client, target TransferTarget, answeredAt time.Time) (failure TransferFailure, failed bool, err error)

TransferFailed reports whether GitLab has recorded that a transfer of target failed in the background, answering the to-do item that records it. Since GitLab 19.4 that item is the only report of such a failure: the transfer's own answer came before the worker ran, and the object is left where it was. From 19.5 the item also carries the reason, which the REST to-do entity leaves out and TransferFailureReason reads over GraphQL.

An item counts only when it was created no earlier than two seconds before answeredAt, because GitLab keeps one pending item per object and a user, and an item left by an earlier failure says nothing about this transfer. The two seconds are the Date header's whole-second precision and a worker that can fail before the web node writes that header. It follows that a failure is not seen while an earlier failure's item is still pending, since GitLab adds no second one, and that nothing is seen at all when answeredAt is zero: the item could not be told apart from an older one, and no request is made. Only the first page of 100 pending items is read.

type TransferTarget added in v3.1.0

type TransferTarget struct {
	// ID is the project's or the group's numeric id.
	ID int64
	// Group says the object is a group rather than a project.
	Group bool
}

TransferTarget names the object a transfer moves, as GitLab's to-do list is filtered by it: a project by its project_id with target type Project, and a group by its group_id with target type Namespace, the class GitLab records a group target under.

type UntilOptions added in v3.1.0

type UntilOptions[T any] struct {
	// Request carries the caller's progress token. Nil sends no progress.
	Request *mcp.CallToolRequest
	// Interval is the time before each read, the first included: the caller
	// has just been told the change was accepted, so a read at once would only
	// find it pending.
	Interval time.Duration
	// Bound is how long the whole wait may take.
	Bound time.Duration
	// Message is the progress text sent before each read.
	Message string
	// Read fetches the object's current state.
	Read func(context.Context) (T, error)
	// Landed reports whether a state Read returned is the one waited for.
	Landed func(T) bool
}

UntilOptions configures Until: how to read an object GitLab changes in the background, and how to tell that the change has landed.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL