Documentation
¶
Overview ¶
Package ipc implements the handler side of the Celerity runtime's IPC protocol: one long-lived bidirectional stream carrying configuration, events, results, credit, WebSocket sends and shutdown.
The frame loop is written against Transport rather than against gRPC types, so the protocol's behaviour can be exercised without a transport. The gRPC client is a thin adapter over it, which is the same separation the runtime draws on its own side.
Index ¶
- Constants
- type Cancel
- type CancelReason
- type Client
- type Config
- type CreditGrant
- type DialConfig
- type Drain
- type Draining
- type FromRuntime
- type HandlerConfig
- type HandlerLimit
- type Outbound
- type ProtocolVersion
- type ProtocolVersionError
- type Ready
- type ReadyAck
- type RefusedReason
- type Result
- type RuntimeConfig
- type TagMismatchError
- type ToRuntime
- type Transport
- type WsSend
- type WsSendAck
Constants ¶
const ( ProtocolMajor uint32 = 1 ProtocolMinor uint32 = 0 )
The contract version this SDK was built against, declared in the handshake.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Cancel ¶
type Cancel struct {
ID string
Reason CancelReason
}
Cancel tells the handler to stop work nobody is waiting for.
It may name an event that has already completed, which is ignored rather than treated as an error.
type CancelReason ¶
type CancelReason int
CancelReason says why an event was cancelled.
const ( CancelUnspecified CancelReason = iota CancelDeadlineExceeded // CancelCallerGone means the originating caller went away, so nothing is // waiting for the result. Sent when an HTTP client disconnects, and // deliberately not when a WebSocket connection closes: a message is closer // to a queue message than to a request, so the work is still worth // finishing. CancelCallerGone CancelShutdown )
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client serves one handler process against the runtime.
func (*Client) Draining ¶
Draining tells the runtime this process is going away, so a supervisor can roll handler processes without dropping work.
type Config ¶
type Config struct {
// Tags are the handler tags this process serves, declared in the handshake
// and checked against the blueprint by the runtime.
Tags []string
// Reconcile is given what the blueprint declares, before this process
// declares itself, and returns the tags to declare.
//
// It is how a handler whose tag is not knowable from code alone settles it:
// a WebSocket route keyed by something the API states, or a handler that
// stated only its name and takes its routing from the blueprint. Returning
// an error fails the handshake, which is the right outcome for a handler
// that could never be dispatched to. Optional.
Reconcile func(blueprint []HandlerConfig) ([]string, error)
// Resolve returns the pipeline for a tag.
Resolve func(tag string) (layer.Next, bool)
// Concurrency is the worker pool size, and the initial credit.
Concurrency int
// Limits caps individual tags within the credit window, so one slow handler
// cannot consume the whole of it.
//
// A function rather than a map because it is read after Reconcile which can
// change a tag to the one the blueprint declares, and a cap keyed by the
// tag built beforehand would name a handler the runtime does not know.
// Optional.
Limits func() map[string]int
SDKVersion string
}
Config is what a client needs in order to serve a handler process.
type CreditGrant ¶
type CreditGrant struct {
Additional uint32
}
CreditGrant returns credit to the runtime.
Credit is additive and is replenished only when the handler says so, never implicitly by a result arriving. Grants normally ride along with a result; this frame covers resizing the window and resuming after a withhold.
type DialConfig ¶
type DialConfig struct {
// Socket is the Unix socket path, which is the normal case.
Socket string
// FallbackEnabled reports that the runtime could not bind a socket and is
// serving over loopback TCP instead.
//
// The SDK uses the fallback only when the runtime says it did. Falling back
// on its own would widen who may register as a handler from the one user the
// socket's permissions allow to any process that can reach loopback.
FallbackEnabled bool
FallbackPort int
// Retry bounds how long dialling is retried. A handler process is commonly
// started alongside the runtime, so losing the race to its listener is
// ordinary rather than fatal.
RetryFor time.Duration
}
DialConfig says where the runtime is serving the handler stream.
func DialConfigFromEnv ¶
func DialConfigFromEnv() DialConfig
DialConfigFromEnv reads the dial configuration the runtime publishes.
func (DialConfig) Target ¶
func (c DialConfig) Target() (string, error)
Target returns the gRPC target to dial, preferring the Unix socket.
type Drain ¶
Drain says the runtime is going away: it stops dispatching and waits for in-flight work until the deadline.
type Draining ¶
Draining says the handler is going away, letting a supervisor roll handler processes without dropping work.
type FromRuntime ¶
type FromRuntime struct {
Config *RuntimeConfig
ReadyAck *ReadyAck
Dispatch *handler.Event
Cancel *Cancel
Drain *Drain
WsAck *WsSendAck
}
FromRuntime is a frame travelling from the runtime to the handler. Exactly one field is set.
type HandlerConfig ¶
type HandlerConfig struct {
HandlerName string
HandlerTag string
Timeout time.Duration
TracingEnabled bool
// PublishedName is the name the blueprint publishes this handler under,
// empty when it sets none.
PublishedName string
}
HandlerConfig describes one handler the blueprint declares.
type HandlerLimit ¶
HandlerLimit caps how much of the credit window one tag may occupy.
type Outbound ¶
type Outbound struct {
handler.OutboundMessage
IsBinary bool
}
Outbound is a message on its way to the runtime.
It carries the frame kind, which the public API deliberately does not as an application asks for binary through handler.BinarySender, and by the time a message reaches here the bytes are already framed and the kind is settled.
type ProtocolVersion ¶
ProtocolVersion is the version of the contract one end of the stream speaks.
Minor versions are additive, so a handler built against an earlier minor of the same major serves unchanged against a later one. A different major is not compatible in either direction and is refused at the handshake.
func (ProtocolVersion) String ¶
func (v ProtocolVersion) String() string
type ProtocolVersionError ¶
type ProtocolVersionError struct {
// Declared is what this SDK was built against.
Declared ProtocolVersion
// Served is what the runtime sent with its configuration, which is the
// zero value if it sent none.
Served ProtocolVersion
}
ProtocolVersionError reports a handshake refused because the contract this SDK speaks is not one the runtime serves.
Nothing about the application causes it, so nothing about the application fixes it, a rebuild is required against an SDK whose contract the runtime serves.
func (*ProtocolVersionError) Error ¶
func (e *ProtocolVersionError) Error() string
type Ready ¶
type Ready struct {
HandlerTags []string
// InitialCredit is the total events the runtime may have in flight to this
// process across all tags, which is the SDK's worker pool size.
InitialCredit uint32
SDKVersion string
// Limits cap individual tags so one slow tag cannot consume the whole
// credit window and starve the others.
Limits []HandlerLimit
// Protocol is the contract version this SDK was built against.
// This is required, the runtime refuses a handler that declares none,
// rather than assuming it speaks the current one.
Protocol ProtocolVersion
}
Ready declares what this handler process serves and how much work it will take. The runtime does not dispatch until it arrives.
type ReadyAck ¶
type ReadyAck struct {
Accepted bool
// UnknownTags are registered by the handler but absent from the blueprint.
UnknownTags []string
// UnhandledTags are in the blueprint but not registered by the handler.
UnhandledTags []string
// Reason says which check refused this handler. The tag lists alone cannot
// tell a version refusal from an accepted handler with nothing to report.
Reason RefusedReason
}
ReadyAck reports whether the handler's tags line up with the blueprint.
type RefusedReason ¶
type RefusedReason int
RefusedReason says which check refused a handler at the handshake.
const ( RefusedUnspecified RefusedReason = iota RefusedTagMismatch // RefusedProtocolVersion means the handler declared no contract version, // or one whose major the runtime does not serve. RefusedProtocolVersion )
type Result ¶
type Result struct {
Outcome *handler.Result
// CreditGrant is normally 1. Zero withholds, telling the runtime to stop
// dispatching to this stream until a later [CreditGrant] frame.
CreditGrant uint32
}
Result is a handler's outcome together with the credit it returns.
The two travel as one frame because they must: a result that carries no grant shrinks the credit window permanently, so the protocol gives a result nowhere to omit it.
type RuntimeConfig ¶
type RuntimeConfig struct {
TracingEnabled bool
MetricsEnabled bool
Handlers []HandlerConfig
// Protocol is the contract version the runtime serves, sent before this
// handler is asked for anything.
Protocol ProtocolVersion
}
RuntimeConfig is what the runtime sends before the handler declares itself, so the SDK can check the handlers registered in code against the ones the blueprint declares.
func (*RuntimeConfig) Tags ¶
func (c *RuntimeConfig) Tags() []string
Tags returns the handler tags the blueprint declares.
type TagMismatchError ¶
TagMismatchError reports a handshake refused because the handlers registered in code do not match the ones the blueprint declares.
It is a startup error rather than a 404 in production, which is the whole point of the check.
func (*TagMismatchError) Error ¶
func (e *TagMismatchError) Error() string
type ToRuntime ¶
type ToRuntime struct {
Ready *Ready
Result *Result
Credit *CreditGrant
WsSend *WsSend
Draining *Draining
}
ToRuntime is a frame travelling from the handler to the runtime. Exactly one field is set.
type Transport ¶
type Transport interface {
Send(*ToRuntime) error
Recv() (*FromRuntime, error)
Close() error
}
Transport carries frames in both directions.
The gRPC client stream is one implementation; an in-memory channel pair is another, and is what the protocol's behaviour is tested against.
func Dial ¶
func Dial(ctx context.Context, config DialConfig) (Transport, error)
Dial connects to the runtime's handler stream and opens the event stream.
Dialling is retried: a handler process is commonly started alongside the runtime by a supervisor, so losing the race to the runtime's listener is ordinary rather than fatal.