Documentation
¶
Overview ¶
Package callcatchup puts the connection a tool call names into service when this process does not serve it.
A connection exists because the connection store holds a row for it. Several replicas run over one database, and a connection saved through one of them is a row before it is anything in the others' memory: it reaches them as an announcement on the reload bus some time after the save returns. A replica that answers only from what the bus has delivered refuses a call against a connection that is saved, with a message an operator cannot tell from a genuine misconfiguration.
Two kinds have had that window closed one at a time, by reading the store on a name their own connection map does not hold (#1714 for graphql, #1746 for api). The same window is open on every other kind, and their tool handlers are not all in this repository to reach into — trino's and s3's are the upstream toolkits'. So it is closed here instead, in the one place every tool call already passes through with the kind and connection name resolved: a call naming a connection this process does not serve takes it on from the store before the handler runs, whatever kind it is (#1757).
Nothing about the resolution is new. The store read, the per-name lock, the shared read and the withdrawal of a connection deleted while it was being taken on are internal/conncatchup's, and putting the connection in service is pkg/connreconcile's — the same call the reload bus makes when the announcement does arrive.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrNotFound = errors.New("the connection store holds no such connection")
ErrNotFound is how a store reports a connection it does not hold. It is what separates "no such connection" from "the store could not answer": the first leaves the process serving what it serves, the second is logged.
Functions ¶
This section is empty.
Types ¶
type RecordStore ¶
type RecordStore[R any] interface { Get(ctx context.Context, kind, name string) (R, error) Persistent() bool }
RecordStore is the platform's connection store, as far as this package reads it: one saved connection of a kind, as the record type the store keeps, and whether the store outlives the process.
type Resolver ¶
type Resolver struct {
// contains filtered or unexported fields
}
Resolver takes on the connection a call names. One belongs to a process.
func New ¶
func New(source ToolkitSource, store Store) *Resolver
New returns a Resolver, or nil where there is nothing to catch up to: no registry to put a connection in service on, or no store holding what another replica saved.
func (*Resolver) TakeOn ¶
TakeOn puts the named connection of kind into service when this process does not serve it, so the call that named it is answered as the saving replica answers it. A connection this process already serves costs one map lookup.
It reports nothing. A connection the store does not hold is a call against a connection that does not exist, and the handler's own refusal names it far better than this layer could; a store that cannot answer leaves the handler to refuse as it did before.
type Store ¶
type Store interface {
// ConnectionConfig returns one saved connection's configuration, reporting
// a connection it does not hold with an error satisfying ErrNotFound.
ConnectionConfig(ctx context.Context, kind, name string) (map[string]any, error)
}
Store reads the connections an operator saved, of any kind. A deployment that keeps its connections in its configuration file alone wires none.
func Reader ¶
Reader adapts the platform's connection store to the Store a catch-up reads, translating the store's absent-record error into ErrNotFound and handing back the configuration map a toolkit installs.
A nil store, or one that does not outlive the process and so holds nothing another replica wrote, adapts to nil: there is nothing to catch up to where every replica knows only what it was handed.
type ToolkitSource ¶
ToolkitSource is the live toolkit registry.