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

exportasync

package
v1.149.0 Latest Latest
Warning

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

Go to latest
Published: Oct 22, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Overview

Package exportasync runs a long export in the background and lets its caller read it again by its idempotency key (#2212).

graphql_export and api_export walk every page inside one tool call. A walk longer than the model client's own tool-call timeout failed on the client's side, with no asset written and no handle to reconnect to, and a client does not let the platform raise that timeout. An export asked to run in the background (async, or a page walk from a model client) is answered at once with a run id and an idempotency key; the walk runs on the replica that took the call. Calling the same export again with the key reads the run: its progress while it runs, the export's own answer once it is done, and its failure if it failed. A key whose export is still running answers with that run, so a retry never starts a second walk against the upstream.

A run is a call task (internal/platform/calltask), the record the MCP Tasks extension keeps, marked as a background export; the Tasks extension is for a client that declares it, and this is the same work for one that does not.

Index

Constants

View Source
const (
	StatusRunning   = "running"
	StatusCompleted = "completed"
	StatusFailed    = "failed"
)

The states a run is in.

Variables

View Source
var ErrNotFound = errors.New("no background export under this idempotency key")

ErrNotFound is a key no run of the caller's has.

Functions

func Begin

Begin starts work as a background export under b.Key, or joins the one already running under it, and answers the caller with the run.

func HowToReadAgain

func HowToReadAgain(tool, key string) string

HowToReadAgain is the sentence a call naming a key is told how to read its run with.

func NewKey

func NewKey() (string, error)

NewKey mints the idempotency key a background export the caller named none for is read again by.

func Report

func Report(ctx context.Context, pages, items int)

Report records how far the export running on ctx has come. A context that carries no cell, an export answered inline, records nothing.

func Result

func Result(run *Run) (*mcp.CallToolResult, error)

Result decodes a completed run's answer back into the tool result it was.

func Start

func Start(ctx context.Context, l Launch, work func(ctx context.Context) (*mcp.CallToolResult, error)) *mcp.CallToolResult

Start fills in the launch's key and timeout and begins work in the background, answering with the run.

func Wanted

func Wanted(runner Runner, async *bool, walk, modelClient bool) bool

Wanted reports whether an export runs in the background: asked for with async, or, when async is not given, a page walk from a model client, whose tool-call timeout a long walk outlasts. Nothing runs in the background without a runner.

func WithProgress

func WithProgress(ctx context.Context, cell *Cell) context.Context

WithProgress is ctx carrying cell, which Report writes to.

Types

type Answer

type Answer struct {
	RunID          string `json:"run_id"`
	Status         string `json:"status"`
	IdempotencyKey string `json:"idempotency_key"`
	PagesFetched   int    `json:"pages_fetched"`
	ItemsMerged    int    `json:"items_merged"`
	// StartedAt and UpdatedAt are when the run started and last reported.
	StartedAt time.Time `json:"started_at"`
	UpdatedAt time.Time `json:"updated_at"`
	Message   string    `json:"message"`
}

Answer is what a background export's caller is handed, at the start and on every read of a run not yet completed.

func AnswerOf

func AnswerOf(run *Run, key, how string) Answer

AnswerOf renders a run for its caller; how names the call that reads it again ("call graphql_export again with idempotency_key=...").

type Background

type Background struct {
	Runner Runner
	Tool   string
	Key    string
	// How is the sentence telling the caller how to read the run again.
	How string
	// Prior is a failed run Resume found under the key, which the answer
	// names.
	Prior *Run
}

Background is one export to start in the background.

type Cell

type Cell struct {
	// contains filtered or unexported fields
}

Cell is where a running export writes its progress for the runner to read.

func (*Cell) Load

func (c *Cell) Load() Progress

Load is the progress written last.

type Launch

type Launch struct {
	Runner Runner
	Tool   string
	Prior  *Run
	// Key is the call's idempotency_key, minted when it names none. Pointing
	// it at the argument the export writes its asset under means a call
	// naming the key after the run is gone still finds the asset.
	Key *string
	// Timeout is the call's timeout_seconds, set to MaxTimeout when it names
	// none: nobody is waiting on the call.
	Timeout    *int
	MaxTimeout time.Duration
}

Launch is one export a tool starts in the background: the fields of its own arguments the launch fills in, and how the deployment bounds it.

type Progress

type Progress struct {
	PagesFetched int `json:"pages_fetched"`
	ItemsMerged  int `json:"items_merged"`
}

Progress is how far a walk has come.

type Run

type Run struct {
	ID     string
	Status string
	// Message is the run's own sentence: what it is doing, or why it failed.
	Message  string
	Progress Progress
	// Result is the export's answer once it completed, in the wire form a
	// tool result takes.
	Result    json.RawMessage
	StartedAt time.Time
	UpdatedAt time.Time
}

Run is one background export as its caller reads it.

func Resume

func Resume(ctx context.Context, runner Runner, tool, key, how string) (res *mcp.CallToolResult, prior *Run, handled bool)

Resume answers a call that names the idempotency key of one of its caller's background exports: with the run's progress while it runs, and with the export's own answer once it completed. A run that failed is returned as prior with handled false, and the call goes on to run the export again; the new run's answer names the failure. A key no background export has, and a deployment with no runner, are not handled.

type Runner

type Runner interface {
	// Start runs work apart from the request under key, or answers with the
	// run already working under it for this caller and tool, with started
	// false.
	Start(ctx context.Context, tool, key string, work func(ctx context.Context) (*mcp.CallToolResult, error)) (run *Run, started bool, err error)
	// Find reads the latest run of tool under key for the caller, or
	// ErrNotFound.
	Find(ctx context.Context, tool, key string) (*Run, error)
}

Runner starts background exports and finds them again. The caller is read from ctx: a run is found only by the person who started it.

func RunnerOf

func RunnerOf(get func() Runner) Runner

RunnerOf is the runner a late-bound getter answers, nil when there is no getter: the export tools are wired before the task layer is installed.

Jump to

Keyboard shortcuts

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