workflow

package
v0.7.2 Latest Latest
Warning

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

Go to latest
Published: Jul 19, 2026 License: MIT Imports: 9 Imported by: 0

README

Workflow Format

Define a Mailer pipeline in YAML or JSON instead of Go. A workflow document describes a pipeline's shape and configuration; it is parsed, validated, compiled into the same SDK objects the fluent API produces, and run by the same engine.

workflow.yaml → Parse → Validate → Resolve Secrets → Compile → Execute

The workflow compiler and runner are implemented for the declarative built-ins described below. Ref-based map/flatMap/process remain reserved for a future function registry.

No user code

The operators are declarative built-ins — filtering, field projection/rename/set, key-by-field, and count/sum aggregation are all expressed as configuration over the JSON record model, so a workflow needs no Go functions to compile and run. The Postgres row mapping is likewise declarative (fixed table + field→column map). Transforms that genuinely need arbitrary code (map/flatMap/process with a ref) are reserved for a future function registry and aren't compilable yet.

Top-level structure

name: my-pipeline        # optional, for logs/metrics
version: "1"             # optional format version
env: { ... }             # optional; SDK defaults when omitted
source: { ... }          # required
pipeline: [ ... ]        # optional ordered operators
sink: { ... }            # required

Durations

Any duration field is a Go duration string: "30s", "5m", "500ms", "1h30m". Omitting a duration means the SDK default.

env

Field Type Default SDK method
bufferSize int 1024 WithBufferSize
shutdownTimeout duration 30s WithShutdownTimeout
checkpointing.interval duration WithCheckpointing
checkpointing.dir string checkpoint.NewFileStorage
state.backend memory | pebble memory WithStateBackend
state.dir string Pebble root dir (required for pebble)

source

type is one of kafka, slice, generator.

source:
  type: kafka
  kafka:
    brokers: [localhost:9092]
    topic: orders           # or topics: [a, b] for consumer-group
    groupID: my-group
    startFrom: earliest     # earliest | latest
    exactlyOnce: true       # pair with a txnKafka sink
    parallel: false         # per-partition readers (no group)
    deserialize: json       # "json" or a registered deserializer ref
    commitBatch: 0          # commit every N messages (0 = per message)
    fetchMinBytes: 1
    fetchMaxBytes: 10485760
    watermark:
      maxOutOfOrderness: 1s
      interval: 500ms
    sasl: { mechanism: plain, username: u, password: p }
    tls:  { caFile: ca.pem, insecureSkipVerify: false }

slice/generator sources carry inline test data:

source:
  type: generator
  records:
    - { key: sentence, value: "hello world" }

pipeline

An ordered list of operators. Each operator is a discriminated union: a required unique id, a type, and a matching typed config block (named after the type). Each block decodes into its own struct, so a field that belongs to another kind, or is misspelled, is rejected.

The declarative operators need no user code — the compiler builds them from config over the JSON record model:

type Config
filter { field, operator, value } — 9 comparison operators
selectFields { fields: [...] } — keep only these fields
renameFields { renames: [{from, to}] }
setFields { sets: [{field, value}] }
keyBy { field, partitions } — set the keyed-state key from a record field
reduce { function: count|sum, field } — built-in aggregations
window { type, size, slide, gap, offset, idleTimeout }
pipeline:
  - id: completed-only
    type: filter
    filter: { field: status, operator: equals, value: completed }
  - id: project
    type: selectFields
    selectFields: { fields: [customer_id, amount] }
  - id: by-customer
    type: keyBy
    keyBy: { field: customer_id, partitions: 8 }
  - id: window
    type: window
    window: { type: tumbling, size: 5m }   # tumbling | sliding | session
  - id: totals
    type: reduce
    reduce: { function: sum, field: amount }

map, flatMap, and process exist in the schema as ref-based operators ({ ref, label, parallelism }) for a future function registry, but the declarative compiler cannot build them yet.

Compiling and running

c := &compiler.Compiler{BaseDataDir: "./data"}   // Secrets defaults to environment lookup
env, err := c.Compile(wf)                         // validate → resolve → source → env → operators → sink
// ... then env.Execute(ctx) to run

Compile produces a complete *mailer.StreamExecutionEnv without starting it and without connecting (except a Postgres sink's pool). CompileWorkflow additionally returns the pipeline graph and the derived delivery guarantee (at-most-once / at-least-once / exactly-once).

For files, use the runner package or CLI:

result, err := runner.RunFile(ctx, "workflow.yaml", runner.Options{BaseDataDir: "./data"})
go run ./cmd/mailer-workflow --file examples/workflows/order-totals.yaml
go run ./cmd/mailer-workflow --file examples/workflows/order-totals.yaml --dry-run --describe

sink

type is one of kafka, txnKafka, postgres, stdout, blackhole.

sink:
  type: txnKafka          # exactly-once (transactional)
  txnKafka:
    brokers: [localhost:9092]
    topic: order-totals
    transactionalID: order-totals-pipeline   # stable, unique per instance
    markerTopic: ""       # default "<topic>.checkpoints"
    serialize: json
sink:
  type: kafka             # at-least-once
  kafka:
    brokers: [localhost:9092]
    topic: out
    batchSize: 100
    batchTimeout: 1s
    acks: leader          # none | leader | all
    async: false
    serialize: json
    maxRetries: 3
    onError: drop         # drop | dlq | fail
sink:
  type: postgres
  postgres:
    dsn: ${POSTGRES_DSN}     # ${VAR} resolved by the configured secret resolver
    table: customer_totals   # single fixed table (no per-record tables)
    mapping:                 # jsonField → column
      customer_id: customer_id
      payment.total: total_amount
    mode: upsert             # insert | upsert
    conflictColumns: [customer_id]
    updateColumns: [total_amount]  # optional; default = non-conflict mapped columns
    batchSize: 100
    flushInterval: 5s
    maxRetries: 3
    onError: drop

The Postgres row mapping is fully declarative: a single fixed table and a mapping of JSON field paths to column names. A workflow cannot generate arbitrary table names per record, and table/column names are validated as safe SQL identifiers. Mapped numbers keep exact precision (json.Number → int64/float64); a missing field becomes SQL NULL; a nested object/array is stored as JSON. Constructing a Postgres sink opens its connection pool (unlike Kafka/test sinks, which are lazy). mode: upsert compiles to INSERT ... ON CONFLICT ... DO UPDATE; conflict/update columns must be safe identifiers and present in the declarative mapping.

Parsing

wf, err := workflow.Load("pipeline.yaml")   // dispatches on .yaml/.yml/.json
// or workflow.ParseYAML(bytes) / workflow.ParseJSON(bytes)

Parsing is strict: unknown keys are rejected (typo protection) and durations are validated. It does not check that the document is a runnable pipeline — that is validation.

Validation

if err := workflow.Validate(wf); err != nil {
    log.Fatal(err) // every problem, reported together
}

Validate runs entirely offline — it opens no Kafka, Postgres, or Pebble connection (its only side effect is creating the configured state/checkpoint directories to confirm they can be created). An invalid workflow therefore never reaches the runtime. It accumulates all problems into one error:

Workflow validation failed:
  - pipeline[2] "totals": reduce requires a keyBy before it
  - env.checkpointing.interval: checkpoint interval must be greater than zero
  - sink.txnKafka.transactionalID: a transactional id is required for transactional Kafka

Checks:

  • Structural — supported version; valid, present workflow name; source and sink present and of supported type; operator ids present and unique; every operator's config block matches its type.
  • Configuration — Kafka brokers non-empty and topic present (source and sink); window durations valid per kind (size/slide/gap); parallelism and keyBy partitions not negative; checkpoint interval positive; state/checkpoint directories creatable; a txnKafka sink has a transactional id.
  • Pipeline orderingkeyBy before reduce/window; window before the aggregation that consumes it. (Only stateless operators can set parallelism — that's guaranteed by the schema, since the field exists only on stateless config blocks.)
  • Delivery guarantee — if either the source declares exactlyOnce: true or the sink is txnKafka, the full set is required: exactly-once Kafka source + txnKafka sink + checkpointing enabled + a stable transactional id. A half-configured exactly-once pipeline is rejected before it can run.

See testdata/ for complete example documents.

Documentation

Overview

Package workflow defines a declarative format for Mailer pipelines.

A workflow document (YAML or JSON) describes a pipeline's source, ordered operators, sink, and runtime settings. Supported built-in operators are fully declarative over the JSON record model: filters, field projection/rename/set, key-by-field, count/sum reduce, and windows compile into the same SDK objects as hand-written Go pipelines.

Arbitrary Go transforms (map/flatMap/process refs) remain in the schema for a future registry, but the declarative compiler rejects them today.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Validate

func Validate(wf *Workflow) error

Validate checks a parsed workflow for structural, configuration, pipeline-ordering, and delivery-guarantee problems, returning every problem found as a ValidationErrors (or nil if the workflow is valid). It performs no network I/O and opens no Kafka, Postgres, or Pebble connection — the only side effect is creating configured state/checkpoint directories to verify they can be created. An invalid workflow therefore never reaches the runtime.

Types

type CheckpointSpec

type CheckpointSpec struct {
	// Interval between injected checkpoint barriers. Required when
	// checkpointing is present.
	Interval Duration `yaml:"interval" json:"interval"`

	// Dir is the checkpoint file-storage directory
	// (checkpoint.NewFileStorage). Required.
	Dir string `yaml:"dir" json:"dir"`
}

CheckpointSpec configures barrier checkpointing with file storage.

type Duration

type Duration time.Duration

Duration is a time.Duration that (de)serializes as a Go duration string ("30s", "5m", "500ms") in YAML and JSON. The zero value is omitted by omitempty (it is an integer kind underneath), so optional duration fields default cleanly.

func (Duration) MarshalJSON

func (d Duration) MarshalJSON() ([]byte, error)

MarshalJSON renders the duration as its string form.

func (Duration) MarshalYAML

func (d Duration) MarshalYAML() (any, error)

MarshalYAML renders the duration as its string form.

func (Duration) Std

func (d Duration) Std() time.Duration

Std returns the underlying time.Duration.

func (Duration) String

func (d Duration) String() string

func (*Duration) UnmarshalJSON

func (d *Duration) UnmarshalJSON(b []byte) error

func (*Duration) UnmarshalYAML

func (d *Duration) UnmarshalYAML(value *yaml.Node) error

type EnvSpec

type EnvSpec struct {
	// BufferSize is the bounded edge capacity between stages
	// (WithBufferSize). 0 = SDK default (1024).
	BufferSize int `yaml:"bufferSize,omitempty" json:"bufferSize,omitempty"`

	// ShutdownTimeout is the drain wait before forced shutdown
	// (WithShutdownTimeout). 0 = SDK default (30s).
	ShutdownTimeout Duration `yaml:"shutdownTimeout,omitempty" json:"shutdownTimeout,omitempty"`

	// Checkpointing enables barrier checkpointing (WithCheckpointing).
	// Omitted = disabled.
	Checkpointing *CheckpointSpec `yaml:"checkpointing,omitempty" json:"checkpointing,omitempty"`

	// State selects the state backend (WithStateBackend). Omitted =
	// in-memory.
	State *StateSpec `yaml:"state,omitempty" json:"state,omitempty"`
}

EnvSpec mirrors the StreamExecutionEnv Configuration methods.

type FilterConfig

type FilterConfig struct {
	Field    string `yaml:"field" json:"field"`
	Operator string `yaml:"operator" json:"operator"` // equals, greater_than, contains, exists, …
	Value    any    `yaml:"value,omitempty" json:"value,omitempty"`
}

FilterConfig keeps records where the value at Field satisfies Operator against Value (declarative — no code).

filter: { field: status, operator: equals, value: completed }

type KafkaSinkSpec

type KafkaSinkSpec struct {
	Brokers []string `yaml:"brokers" json:"brokers"`
	Topic   string   `yaml:"topic" json:"topic"`

	BatchSize    int      `yaml:"batchSize,omitempty" json:"batchSize,omitempty"`
	BatchTimeout Duration `yaml:"batchTimeout,omitempty" json:"batchTimeout,omitempty"`

	// Acks is "none", "leader", or "all". Empty = leader.
	Acks  string `yaml:"acks,omitempty" json:"acks,omitempty"`
	Async bool   `yaml:"async,omitempty" json:"async,omitempty"`

	// Serialize names a registered serializer, or "json". Empty = raw.
	Serialize string `yaml:"serialize,omitempty" json:"serialize,omitempty"`

	MaxRetries int    `yaml:"maxRetries,omitempty" json:"maxRetries,omitempty"`
	OnError    string `yaml:"onError,omitempty" json:"onError,omitempty"` // drop | dlq | fail

	SASL *SASLSpec `yaml:"sasl,omitempty" json:"sasl,omitempty"`
	TLS  *TLSSpec  `yaml:"tls,omitempty" json:"tls,omitempty"`
}

KafkaSinkSpec mirrors the KafkaSink functional options (at-least-once).

type KafkaSourceSpec

type KafkaSourceSpec struct {
	Brokers []string `yaml:"brokers" json:"brokers"`

	// Topic or Topics (consumer-group). Exactly one is used.
	Topic  string   `yaml:"topic,omitempty" json:"topic,omitempty"`
	Topics []string `yaml:"topics,omitempty" json:"topics,omitempty"`

	GroupID string `yaml:"groupID,omitempty" json:"groupID,omitempty"`

	// StartFrom is "earliest" or "latest". Empty = earliest.
	StartFrom string `yaml:"startFrom,omitempty" json:"startFrom,omitempty"`

	// ExactlyOnce disables eager commits + enables read_committed
	// (pair with a transactional sink).
	ExactlyOnce bool `yaml:"exactlyOnce,omitempty" json:"exactlyOnce,omitempty"`

	// Parallel uses one reader per partition (no consumer group;
	// mutually exclusive with GroupID).
	Parallel bool `yaml:"parallel,omitempty" json:"parallel,omitempty"`

	// Watermark enables event-time watermark generation.
	Watermark *WatermarkSpec `yaml:"watermark,omitempty" json:"watermark,omitempty"`

	// Deserialize names a registered deserializer, or "json" for the
	// built-in JSON deserializer. Empty = raw bytes.
	Deserialize string `yaml:"deserialize,omitempty" json:"deserialize,omitempty"`

	// CommitBatch commits offsets every N messages (0 = per message).
	CommitBatch int `yaml:"commitBatch,omitempty" json:"commitBatch,omitempty"`

	// FetchMinBytes / FetchMaxBytes tune the fetch request.
	FetchMinBytes int `yaml:"fetchMinBytes,omitempty" json:"fetchMinBytes,omitempty"`
	FetchMaxBytes int `yaml:"fetchMaxBytes,omitempty" json:"fetchMaxBytes,omitempty"`

	SASL *SASLSpec `yaml:"sasl,omitempty" json:"sasl,omitempty"`
	TLS  *TLSSpec  `yaml:"tls,omitempty" json:"tls,omitempty"`
}

KafkaSourceSpec mirrors the KafkaSource functional options.

type KeyByConfig

type KeyByConfig struct {
	// Field is the dotted path whose value is the partition key. Required.
	Field string `yaml:"field" json:"field"`
	// Partitions is the keyed-worker count. 0 = SDK default (16).
	Partitions int    `yaml:"partitions,omitempty" json:"partitions,omitempty"`
	Label      string `yaml:"label,omitempty" json:"label,omitempty"`
}

KeyByConfig partitions the stream by a record field (declarative).

type Operator

type Operator struct {
	// ID names this operator (required and unique — used for graph
	// nodes, error paths, and cross-references).
	ID string `yaml:"id,omitempty" json:"id,omitempty"`

	// Type is one of the declarative operator kinds:
	//   filter, selectFields, renameFields, setFields, keyBy, reduce,
	//   window (fully declarative — no user code), or
	//   map, flatMap, process (ref-based — require a function registry,
	//   not compilable by the declarative compiler).
	// It must match the single config block that is set.
	Type string `yaml:"type" json:"type"`

	// Declarative built-ins (compilable without user code).
	Filter       *FilterConfig `yaml:"filter,omitempty" json:"filter,omitempty"`
	SelectFields *SelectConfig `yaml:"selectFields,omitempty" json:"selectFields,omitempty"`
	RenameFields *RenameConfig `yaml:"renameFields,omitempty" json:"renameFields,omitempty"`
	SetFields    *SetConfig    `yaml:"setFields,omitempty" json:"setFields,omitempty"`
	KeyBy        *KeyByConfig  `yaml:"keyBy,omitempty" json:"keyBy,omitempty"`
	Reduce       *ReduceConfig `yaml:"reduce,omitempty" json:"reduce,omitempty"`
	Window       *WindowConfig `yaml:"window,omitempty" json:"window,omitempty"`

	// Ref-based (need a registered Go function; not declaratively
	// compilable yet).
	Map     *RefConfig `yaml:"map,omitempty" json:"map,omitempty"`
	FlatMap *RefConfig `yaml:"flatMap,omitempty" json:"flatMap,omitempty"`
	Process *RefConfig `yaml:"process,omitempty" json:"process,omitempty"`
}

Operator is one operator in the pipeline: a discriminated union. Type names the operator kind and exactly one matching typed config block carries its configuration. There is no shared bag of fields — each kind decodes into its own typed struct, so a field that belongs to another kind (or is misspelled) is rejected by the strict decoder rather than silently ignored.

pipeline:
  - type: map
    map: { ref: parseOrder }
  - type: keyBy
    keyBy: { ref: byCustomer, partitions: 8 }
  - type: window
    window: { type: tumbling, size: 5m }

type PostgresSinkSpec

type PostgresSinkSpec struct {
	// DSN is the connection string. ${VAR} / $VAR are expanded from
	// the environment at compile time.
	DSN string `yaml:"dsn" json:"dsn"`

	// Table is the single destination table (optionally schema.table).
	// Fixed for the whole workflow — a workflow cannot generate
	// per-record table names. Required.
	Table string `yaml:"table" json:"table"`

	// Mapping maps JSON field paths to column names
	// (jsonField → column). Required and non-empty.
	Mapping map[string]string `yaml:"mapping" json:"mapping"`

	// Mode is "insert" or "upsert". Empty = insert.
	Mode string `yaml:"mode,omitempty" json:"mode,omitempty"`

	// ConflictColumns is the ON CONFLICT target for upsert mode.
	ConflictColumns []string `yaml:"conflictColumns,omitempty" json:"conflictColumns,omitempty"`

	// UpdateColumns are set to EXCLUDED values on conflict. Empty in
	// upsert mode updates every non-conflict inserted column.
	UpdateColumns []string `yaml:"updateColumns,omitempty" json:"updateColumns,omitempty"`

	BatchSize     int      `yaml:"batchSize,omitempty" json:"batchSize,omitempty"`
	FlushInterval Duration `yaml:"flushInterval,omitempty" json:"flushInterval,omitempty"`
	MaxRetries    int      `yaml:"maxRetries,omitempty" json:"maxRetries,omitempty"`
	OnError       string   `yaml:"onError,omitempty" json:"onError,omitempty"` // drop | dlq | fail
}

PostgresSinkSpec mirrors the PostgresSink options. The record→row mapping is logic, so Mapper is a registered ref.

type RecordSpec

type RecordSpec struct {
	Key   string `yaml:"key,omitempty" json:"key,omitempty"`
	Value string `yaml:"value" json:"value"`
}

RecordSpec is an inline record for slice/generator sources.

type ReduceConfig

type ReduceConfig struct {
	// Function is the built-in aggregation: "count" or "sum".
	Function string `yaml:"function" json:"function"`
	// Field is the numeric field to aggregate (required for "sum").
	Field string `yaml:"field,omitempty" json:"field,omitempty"`
	Label string `yaml:"label,omitempty" json:"label,omitempty"`
}

ReduceConfig is a built-in keyed aggregation (declarative).

type RefConfig

type RefConfig struct {
	Ref         string `yaml:"ref" json:"ref"`
	Label       string `yaml:"label,omitempty" json:"label,omitempty"`
	Parallelism int    `yaml:"parallelism,omitempty" json:"parallelism,omitempty"`
}

RefConfig is a map/flatMap/process operator that references a registered Go function by name. Compiling it requires a function registry (not part of the declarative compiler).

type RenameConfig

type RenameConfig struct {
	Renames []RenameSpec `yaml:"renames" json:"renames"`
}

RenameConfig renames fields in order.

type RenameSpec

type RenameSpec struct {
	From string `yaml:"from" json:"from"`
	To   string `yaml:"to" json:"to"`
}

RenameSpec moves a field from one dotted path to another.

type SASLSpec

type SASLSpec struct {
	Mechanism string `yaml:"mechanism" json:"mechanism"` // e.g. "plain", "scram-sha-256"
	Username  string `yaml:"username" json:"username"`
	Password  string `yaml:"password" json:"password"`
}

SASLSpec mirrors auth.SASLConfig (shared by Kafka source and sink).

type SelectConfig

type SelectConfig struct {
	Fields []string `yaml:"fields" json:"fields"`
}

SelectConfig keeps only the listed fields (projection).

type SetConfig

type SetConfig struct {
	Sets []SetSpec `yaml:"sets" json:"sets"`
}

SetConfig assigns constant values to fields.

type SetSpec

type SetSpec struct {
	Field string `yaml:"field" json:"field"`
	Value any    `yaml:"value" json:"value"`
}

SetSpec assigns a constant value to a dotted field path.

type SinkSpec

type SinkSpec struct {
	// Type is "kafka", "txnKafka", "postgres", "stdout", or "blackhole".
	Type string `yaml:"type" json:"type"`

	Kafka    *KafkaSinkSpec    `yaml:"kafka,omitempty" json:"kafka,omitempty"`
	TxnKafka *TxnKafkaSinkSpec `yaml:"txnKafka,omitempty" json:"txnKafka,omitempty"`
	Postgres *PostgresSinkSpec `yaml:"postgres,omitempty" json:"postgres,omitempty"`
}

SinkSpec is a tagged union over sink types. Exactly one per-type sub-spec is set, matching Type.

type SourceSpec

type SourceSpec struct {
	// Type is "kafka", "slice", or "generator".
	Type string `yaml:"type" json:"type"`

	Kafka *KafkaSourceSpec `yaml:"kafka,omitempty" json:"kafka,omitempty"`

	// Slice / Generator carry test data inline (mainly for examples
	// and tests). Records are raw string key/value pairs.
	Records []RecordSpec `yaml:"records,omitempty" json:"records,omitempty"`
}

SourceSpec is a tagged union over source types. Exactly one of the per-type sub-specs is set, matching Type.

type StateSpec

type StateSpec struct {
	// Backend is "memory" or "pebble".
	Backend string `yaml:"backend" json:"backend"`

	// Dir is the Pebble root directory (one DB per operator owner
	// underneath). Required when Backend is "pebble".
	Dir string `yaml:"dir,omitempty" json:"dir,omitempty"`
}

StateSpec selects and configures a state backend.

type TLSSpec

type TLSSpec struct {
	CAFile             string `yaml:"caFile,omitempty" json:"caFile,omitempty"`
	CertFile           string `yaml:"certFile,omitempty" json:"certFile,omitempty"`
	KeyFile            string `yaml:"keyFile,omitempty" json:"keyFile,omitempty"`
	InsecureSkipVerify bool   `yaml:"insecureSkipVerify,omitempty" json:"insecureSkipVerify,omitempty"`
}

TLSSpec mirrors auth.TLSConfig.

type TxnKafkaSinkSpec

type TxnKafkaSinkSpec struct {
	Brokers []string `yaml:"brokers" json:"brokers"`
	Topic   string   `yaml:"topic" json:"topic"`

	// TransactionalID must be stable across restarts and unique per
	// pipeline instance. Required.
	TransactionalID string `yaml:"transactionalID" json:"transactionalID"`

	// MarkerTopic overrides the recovery-marker topic
	// (default "<topic>.checkpoints").
	MarkerTopic string `yaml:"markerTopic,omitempty" json:"markerTopic,omitempty"`

	Serialize string `yaml:"serialize,omitempty" json:"serialize,omitempty"`

	// SASL/TLS authenticate against hosted or secured clusters. Both
	// the transactional producer and the recovery marker probe use
	// the same credentials.
	SASL *SASLSpec `yaml:"sasl,omitempty" json:"sasl,omitempty"`
	TLS  *TLSSpec  `yaml:"tls,omitempty" json:"tls,omitempty"`
}

TxnKafkaSinkSpec mirrors the TxnKafkaSink options (exactly-once).

type ValidationError

type ValidationError struct {
	Path string
	Msg  string
}

ValidationError is a single problem found in a workflow, located by a dotted document path (e.g. "env.checkpointing.interval" or `pipeline[2] "totals"`).

func (*ValidationError) Error

func (e *ValidationError) Error() string

type ValidationErrors

type ValidationErrors []*ValidationError

ValidationErrors is the aggregate result of Validate: every problem found, reported together.

func (ValidationErrors) Error

func (v ValidationErrors) Error() string

type WatermarkSpec

type WatermarkSpec struct {
	// MaxOutOfOrderness is the allowed lateness (watermark =
	// max-seen-timestamp − this). Required.
	MaxOutOfOrderness Duration `yaml:"maxOutOfOrderness" json:"maxOutOfOrderness"`

	// Interval between emitted watermarks. 0 = SDK default (500ms).
	Interval Duration `yaml:"interval,omitempty" json:"interval,omitempty"`
}

WatermarkSpec configures bounded-out-of-orderness watermarks.

type WindowConfig

type WindowConfig struct {
	// Type is "tumbling", "sliding", or "session".
	Type string `yaml:"type" json:"type"`

	// Size is the window length (tumbling, sliding). Required for those.
	Size Duration `yaml:"size,omitempty" json:"size,omitempty"`

	// Slide is the slide interval (sliding only; must be <= Size).
	Slide Duration `yaml:"slide,omitempty" json:"slide,omitempty"`

	// Gap is the inactivity gap (session only).
	Gap Duration `yaml:"gap,omitempty" json:"gap,omitempty"`

	// Offset shifts window boundaries off the epoch (tumbling, sliding).
	Offset Duration `yaml:"offset,omitempty" json:"offset,omitempty"`

	// IdleTimeout fires remaining windows after this much inactivity
	// (WindowWithIdleTimeout). 0 = disabled.
	IdleTimeout Duration `yaml:"idleTimeout,omitempty" json:"idleTimeout,omitempty"`

	Label string `yaml:"label,omitempty" json:"label,omitempty"`
}

WindowConfig configures a window operator (after keyBy).

type Workflow

type Workflow struct {
	// Name identifies the pipeline (used in logs, metrics labels, and
	// as a default transactional-id prefix). Optional.
	Name string `yaml:"name,omitempty" json:"name,omitempty"`

	// Version is the workflow-format version. "1" (or empty) today.
	Version string `yaml:"version,omitempty" json:"version,omitempty"`

	// Env configures the execution environment (buffer size, shutdown
	// timeout, checkpointing, state backend). Optional; SDK defaults
	// apply when omitted.
	Env *EnvSpec `yaml:"env,omitempty" json:"env,omitempty"`

	// Source is where records enter the pipeline. Required.
	Source SourceSpec `yaml:"source" json:"source"`

	// Pipeline is the ordered list of operators between source and
	// sink. May be empty (source → sink passthrough).
	Pipeline []Operator `yaml:"pipeline,omitempty" json:"pipeline,omitempty"`

	// Sink is where results leave the pipeline. Required.
	Sink SinkSpec `yaml:"sink" json:"sink"`
}

Workflow is a complete declarative pipeline definition: the root of a workflow YAML/JSON document.

func Load

func Load(path string) (*Workflow, error)

Load reads and parses a workflow document from a file, choosing the decoder by extension: .yaml/.yml → YAML, .json → JSON.

func ParseJSON

func ParseJSON(data []byte) (*Workflow, error)

ParseJSON decodes a workflow document from JSON. Decoding is strict: unknown fields are rejected. Structural parsing only.

func ParseYAML

func ParseYAML(data []byte) (*Workflow, error)

ParseYAML decodes a workflow document from YAML. Decoding is strict: unknown fields are rejected, so typos in keys are caught immediately. This is structural parsing only — it does not validate that the document describes a runnable pipeline (that is a later phase).

type WorkflowSpec

type WorkflowSpec = Workflow

WorkflowSpec is an alias for Workflow — the name the compiler API uses for a parsed workflow document.

Directories

Path Synopsis
Package compiler turns a parsed, validated workflow spec into the existing Mailer SDK objects (sources, operators, sinks, environment).
Package compiler turns a parsed, validated workflow spec into the existing Mailer SDK objects (sources, operators, sinks, environment).
Package operators provides built-in stateless operators for declarative workflows.
Package operators provides built-in stateless operators for declarative workflows.
Package record provides a structured JSON view of a types.Record so declarative, field-level operations (map/filter/project by field name) can read and modify record contents safely.
Package record provides a structured JSON view of a types.Record so declarative, field-level operations (map/filter/project by field name) can read and modify record contents safely.

Jump to

Keyboard shortcuts

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