go.temporal.io/server/chasm/context.go
292 LOC · 96 covered · 196 uncovered · 25 ranges · 933 concepts · 23 introducers · 387 tests
File neighbourhood
The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.
Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file
In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.
Graph controls are ready.
Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.
package chasm
import (
"context"
"errors"
"time"
commonpb "go.temporal.io/api/common/v1"
sdkpb "go.temporal.io/api/sdk/v1"
persistencespb "go.temporal.io/server/api/persistence/v1"
"go.temporal.io/server/common/log"
"go.temporal.io/server/common/metrics"
"go.temporal.io/server/common/namespace"
"google.golang.org/grpc/metadata"
)
type Context interface {
// Context is not bound to any component,
// so all methods needs to take in component as a parameter
// NOTE: component created in the current transaction won't have a ref
// this is a Ref to the component state at the start of the transition
Ref(Component) ([]byte, error)
// Now returns the current time in the context of the given component.
// In a context of a transaction, this time must be used to allow for framework support of pause and time skipping.
Now(Component) time.Time
// ExecutionKey returns the execution key for the execution the context is operating on.
ExecutionKey() ExecutionKey
// ExecutionInfo returns metadata information about the execution.
ExecutionInfo() ExecutionInfo
// Logger returns a logger tagged with execution key and other chasm framework internal information.
Logger() log.Logger
// NamespaceEntry returns the namespace entry for the execution.
NamespaceEntry() *namespace.Namespace
// EndpointByName resolves a nexus endpoint entry.
EndpointByName(endpointName string) (*persistencespb.NexusEndpointEntry, error)
// MetricsHandler returns a metrics handler with namespace tag.
MetricsHandler() metrics.Handler
// Value returns the value associated with this context for key. The behavior is the same as context.Context.Value().
// Use WithContextValues RegistrableComponentOption to set key values pair for a component upon registration.
// Registered key-value pairs will automatically be added to the Context whenever framework accesses the component.
// Alternatively, use ContextWithValue() to manually set values on Context which will take precedence over registered ones.
Value(key any) any
// RequestHeader returns the first value of the named gRPC metadata header from the inbound request context, or ""
// if absent.
//
// Only available when this Context was constructed from an inbound gRPC request, i.e. inside the start/update/read
// callbacks invoked by the chasm engine. In other contexts, such as pure tasks executed at the end of a transaction
// or background task handlers, the underlying ctx has no gRPC metadata and this method always returns "".
RequestHeader(key string) string
// Links returns the union of links attached to the given component across all requests.
// Returns nil for components that are not (yet) registered as tree nodes.
Links(Component) []*commonpb.Link
// RequestLinks returns the links attached to the given component for the specific requestID.
// Returns nil if no entry exists for that requestID. Empty requestID is rejected.
RequestLinks(Component, string) ([]*commonpb.Link, error)
// UserMetadata returns the user metadata attached to the given component, or nil if none.
UserMetadata(Component) *sdkpb.UserMetadata
// Intent() OperationIntent
// ComponentOptions(Component) []ComponentOption
// withValue should only be used by ContextWithValue() function, do NOT call it directly.
// For structs implementing this method, although the returned value has type Context,
// the concrete type MUST be the same concrete type as the receiver.
withValue(key any, value any) Context
structuredRef(Component) (ComponentRef, error)
goContext() context.Context
}
type ExecutionInfo struct {
// StateTransitionCount is the number of create/update transactions in the history of this execution.
StateTransitionCount int64
// ApproximateStateSize is the approximate size in bytes of the persisted execution state of this execution.
ApproximateStateSize int
// CloseTime is the time when the execution was closed.
// An execution is closed when its root component reaches a terminal state in its lifecycle.
// If the component is still running (not yet closed), it returns a zero time.Time value.
CloseTime time.Time
}
type EndpointRegistry interface {
GetByName(ctx context.Context, namespaceID namespace.ID, endpointName string) (*persistencespb.NexusEndpointEntry, error)
}
type MutableContext interface {
Context
// AddTask adds a task to be emitted as part of the current transaction.
// The task is associated with the given component and will be invoked via the registered handler for the given task
// referencing the component.
AddTask(Component, TaskAttributes, any)
// SetRequestLinks records the links contributed by the given request on the
// component, replacing any prior entry for the same request ID. Passing
// nil/empty links removes the entry.
SetRequestLinks(Component, string, []*commonpb.Link) error
// SetUserMetadata replaces the user metadata attached to the given component.
SetUserMetadata(Component, *sdkpb.UserMetadata) error
// Get a Ref for the component
// This ref to the component state at the end of the transition
// Same as Ref(Component) method in Context,
// this only works for components that already exists at the start of the transition
//
// If we provide this method, then the method on the engine doesn't need to
// return a Ref
// NewRef(Component) (ComponentRef, bool)
}
type immutableCtx struct {
// The context here is not really used today.
// But it will be when we support partial loading later,
// and the framework potentially needs to go to persistence to load some fields.
ctx context.Context
// now is constant for this context; child contexts inherit the same value.
now time.Time
executionKey ExecutionKey
// Not embedding the Node here to avoid exposing AddTask() method on Node,
// so that ContextImpl won't implement MutableContext interface.
root *Node
}
type mutableCtx struct {
*immutableCtx
}
// NewContext creates a new Context from an existing Context and root Node.
//
// NOTE: Library authors should not invoke this constructor directly, and instead use [ReadComponent].
func NewContext(
ctx context.Context,
node *Node,
return newContext(ctx, node)
}
// newContext creates a new immutableCtx from an existing Context and root Node.
// This is similar to NewContext, but returns *immutableCtx instead of Context interface.
func newContext(
ctx context.Context,
node *Node,
root := node.root()
workflowKey := node.backend.GetWorkflowKey()
return &immutableCtx{
ctx: ctx,
now: root.Now(nil),
root: root,
executionKey: ExecutionKey{
NamespaceID: workflowKey.NamespaceID,
BusinessID: workflowKey.WorkflowID,
RunID: workflowKey.RunID,
},
}
}
return c.root.Ref(component)
}
return c.root.componentLinks(component)
}
func (c *immutableCtx) RequestLinks(component Component, requestID string) ([]*commonpb.Link, error) {
context.go ×1
return c.root.componentRequestLinks(component, requestID)
}
return c.root.componentUserMetadata(component)
}
return c.now
}
return c.executionKey
}
executionInfo := c.root.backend.GetExecutionInfo()
var closeTime time.Time
closeTimestamp := executionInfo.GetCloseTime()
if closeTimestamp != nil {
}
StateTransitionCount: executionInfo.GetStateTransitionCount(),
ApproximateStateSize: c.root.backend.GetApproximatePersistedSize(),
CloseTime: closeTime,
}
}
return c.root.logger
}
return c.root.metricsHandler
}
if v := c.goContext().Value(key); v != nil {
}
}
return &immutableCtx{
ctx: context.WithValue(c.goContext(), key, value),
now: c.now,
root: c.root,
executionKey: c.executionKey,
}
}
func (c *immutableCtx) structuredRef(component Component) (ComponentRef, error) {
return c.root.structuredRef(component)
}
return c.root.backend.GetNamespaceEntry()
}
return c.ctx
}
func (c *immutableCtx) RequestHeader(key string) string {
if values := metadata.ValueFromIncomingContext(c.ctx, key); len(values) > 0 {
return values[0]
}
return ""
}
func (c *immutableCtx) EndpointByName(name string) (*persistencespb.NexusEndpointEntry, error) {
reg := c.root.backend.EndpointRegistry()
if reg == nil {
return nil, errors.New("endpoint registry not available")
}
return reg.GetByName(c.ctx, c.NamespaceEntry().ID(), name)
}
// NewMutableContext creates a new MutableContext from an existing Context and root Node.
//
// NOTE: Library authors should not invoke this constructor directly, and instead use the [UpdateComponent],
// [UpdateWithStartExecution], or [StartExecution] APIs.
func NewMutableContext(
ctx context.Context,
node *Node,
return &mutableCtx{
immutableCtx: newContext(ctx, node),
}
}
func (c *mutableCtx) AddTask(
component Component,
attributes TaskAttributes,
payload any,
c.root.AddTask(component, attributes, payload)
}
func (c *mutableCtx) SetRequestLinks(component Component, requestID string, links []*commonpb.Link) error {
context.go ×1
return c.root.setComponentRequestLinks(component, requestID, links)
}
func (c *mutableCtx) SetUserMetadata(component Component, md *sdkpb.UserMetadata) error {
context.go ×1
return c.root.setComponentUserMetadata(component, md)
}
return &mutableCtx{
immutableCtx: ContextWithValue(c.immutableCtx, key, value),
}
}
// ContextWithValue returns a new Context with the given key-value pair added.
// Added key-value pairs will be accessible via the Value() method on the returned Context,
// and the behavior of the key-value pair is the same as context.Context.WithValue().
//nolint:revive // unchecked-type-assertion
return any(c.withValue(key, value)).(C)
}