|
| 1 | +// Copyright © 2026 Meroxa, Inc. |
| 2 | +// |
| 3 | +// Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +// you may not use this file except in compliance with the License. |
| 5 | +// You may obtain a copy of the License at |
| 6 | +// |
| 7 | +// http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +// |
| 9 | +// Unless required by applicable law or agreed to in writing, software |
| 10 | +// distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +// See the License for the specific language governing permissions and |
| 13 | +// limitations under the License. |
| 14 | + |
| 15 | +// Package sdk is the Go SDK for building Conduit processors. A processor |
| 16 | +// receives records flowing through a pipeline, transforms them, and returns the |
| 17 | +// result. Authors implement the [Processor] interface (or adapt a function with |
| 18 | +// [NewProcessorFunc]) and the SDK handles the rest: configuration parsing, |
| 19 | +// schema encode/decode middleware, and the plumbing that connects the processor |
| 20 | +// to the Conduit engine. |
| 21 | +// |
| 22 | +// # Standalone vs. built-in processors |
| 23 | +// |
| 24 | +// The same [Processor] implementation can run in two ways: |
| 25 | +// |
| 26 | +// - Standalone: the processor is compiled to a WebAssembly module |
| 27 | +// (GOOS=wasip1 GOARCH=wasm) and executed by Conduit in a wazero runtime. |
| 28 | +// The module's main function calls [Run], which becomes the entry point. |
| 29 | +// This is the default and recommended mode — it isolates the processor from |
| 30 | +// the engine and lets it be distributed as a single portable binary. |
| 31 | +// - Built-in: the processor is compiled natively into a custom Conduit |
| 32 | +// build. This avoids the WebAssembly boundary (and its per-call |
| 33 | +// serialization cost) at the price of coupling the processor to the engine |
| 34 | +// binary. Built-in processors do not call [Run]; the engine invokes the |
| 35 | +// [Processor] methods directly. |
| 36 | +// |
| 37 | +// Author code is identical in both modes. Only the entry point and build |
| 38 | +// constraints differ, so write to the [Processor] contract and let the build |
| 39 | +// target decide how the processor is hosted. |
| 40 | +// |
| 41 | +// # Implementing a Processor |
| 42 | +// |
| 43 | +// Embed [UnimplementedProcessor] in your type. It provides no-op implementations |
| 44 | +// of the optional methods and satisfies the unexported marker method that keeps |
| 45 | +// the interface closed, so adding a method to [Processor] in a later release is |
| 46 | +// not a breaking change for existing processors: |
| 47 | +// |
| 48 | +// type myProcessor struct { |
| 49 | +// sdk.UnimplementedProcessor |
| 50 | +// cfg myConfig |
| 51 | +// } |
| 52 | +// |
| 53 | +// func (p *myProcessor) Specification() (sdk.Specification, error) { ... } |
| 54 | +// func (p *myProcessor) Configure(ctx context.Context, cfg config.Config) error { ... } |
| 55 | +// func (p *myProcessor) Process(ctx context.Context, recs []opencdc.Record) []sdk.ProcessedRecord { ... } |
| 56 | +// |
| 57 | +// # Lifecycle |
| 58 | +// |
| 59 | +// The runtime calls a processor's methods in a fixed order, and (for a single |
| 60 | +// processor instance) never concurrently — the standalone command loop in [Run] |
| 61 | +// processes one command at a time. A processor therefore does not need to guard |
| 62 | +// its own fields against concurrent access by the SDK, but it must not assume |
| 63 | +// any parallelism either. |
| 64 | +// |
| 65 | +// 1. Specification — called to discover the processor's name, version, and |
| 66 | +// configuration parameters. Must be side-effect free; it may be called |
| 67 | +// before Configure and without any configuration. |
| 68 | +// 2. Configure — called once with the user's configuration. Validate and store |
| 69 | +// it here. Do not open connections or start background work; that is Open's |
| 70 | +// job. See [ParseConfig] for turning the raw config map into a typed struct. |
| 71 | +// 3. Open — called once after Configure. Acquire resources and start any |
| 72 | +// background work here. |
| 73 | +// 4. Process — called repeatedly, once per incoming batch, until shutdown. See |
| 74 | +// the record-handling contract below. |
| 75 | +// 5. Teardown — called once when the pipeline is shutting down. No other method |
| 76 | +// is called after Teardown returns; the processor is then discarded. Release |
| 77 | +// everything Open acquired. |
| 78 | +// |
| 79 | +// Process may be called more than once with the same records (for example after |
| 80 | +// a restart when records were not flushed downstream), so processing must be |
| 81 | +// idempotent. |
| 82 | +// |
| 83 | +// # Record handling and error propagation |
| 84 | +// |
| 85 | +// Process receives a batch of [opencdc.Record] values and returns a |
| 86 | +// [ProcessedRecord] for each. The returned slice is positional: the result at |
| 87 | +// index i is the outcome of the input record at index i. Each input record may |
| 88 | +// carry raw or structured data in its key and payload; a processor that reads |
| 89 | +// structured fields should enable the schema-decode middleware (see below) or |
| 90 | +// handle both shapes. |
| 91 | +// |
| 92 | +// The concrete [ProcessedRecord] type an author returns decides how the record |
| 93 | +// continues through the pipeline: |
| 94 | +// |
| 95 | +// - [SingleRecord] — the transformed record continues downstream. This is the |
| 96 | +// common case. |
| 97 | +// - [MultiRecord] — the record is split into zero or more records. Returning |
| 98 | +// an empty MultiRecord is equivalent to [FilterRecord]; returning one record |
| 99 | +// is equivalent to [SingleRecord]. |
| 100 | +// - [FilterRecord] — the record is acknowledged and dropped from the pipeline. |
| 101 | +// Use this to intentionally discard records; it is not an error. |
| 102 | +// - [ErrorRecord] — processing failed. The record is nacked and handled |
| 103 | +// according to the pipeline's error policy (for example routed to a dead- |
| 104 | +// letter queue or halting the pipeline). Returning an ErrorRecord is the |
| 105 | +// only way to signal a per-record failure — a processor must not drop a |
| 106 | +// record it could not process, or at-least-once delivery is violated. |
| 107 | +// |
| 108 | +// Because filtering, splitting, and failing are all expressed through the |
| 109 | +// return value rather than through the process's exit or a returned error, |
| 110 | +// Process itself does not return an error: a batch always produces a result for |
| 111 | +// every record it accounts for. |
| 112 | +// |
| 113 | +// # Middleware |
| 114 | +// |
| 115 | +// [DefaultProcessorMiddleware] wraps every processor run through [Run] with |
| 116 | +// schema decode and encode middleware. Decode middleware fetches the schema |
| 117 | +// referenced in a record's metadata and turns raw key/payload bytes into |
| 118 | +// [opencdc.StructuredData] before Process sees them; encode middleware reverses |
| 119 | +// that afterwards, so a processor can operate on structured data without dealing |
| 120 | +// with schema resolution. A processor tunes this via [Processor.MiddlewareOptions] |
| 121 | +// (see [ProcessorWithSchemaDecodeConfig] and [ProcessorWithSchemaEncodeConfig]). |
| 122 | +// |
| 123 | +// # Neighboring packages |
| 124 | +// |
| 125 | +// - github.com/conduitio/conduit-commons/opencdc defines the Record type that |
| 126 | +// flows through Process. |
| 127 | +// - github.com/conduitio/conduit-commons/config defines the configuration and |
| 128 | +// parameter types used in Specification and Configure. |
| 129 | +// - The schema subpackage is the author-facing API for creating and fetching |
| 130 | +// schemas from within a processor. |
| 131 | +// - The wasm and pprocutils subpackages are engine plumbing and are not meant |
| 132 | +// to be imported by processor authors. |
| 133 | +package sdk |
0 commit comments