Skip to content

Fix external plugin trigger callbacks#593

Merged
intel352 merged 4 commits into
mainfrom
fix-external-trigger-provider
May 10, 2026
Merged

Fix external plugin trigger callbacks#593
intel352 merged 4 commits into
mainfrom
fix-external-trigger-provider

Conversation

@intel352

Copy link
Copy Markdown
Contributor

Summary

  • add external plugin CreateTrigger and ConfigureCallback RPCs
  • configure callback broker wiring for server-loaded external plugins
  • create remote triggers during Configure so YAML trigger config reaches plugins
  • preserve module/step compatibility by disabling trigger factories when callback setup is unavailable

Verification

  • adversarial/security review PASS after fixes
  • go test ./cmd/server ./plugin/external/... ./plugin/...
  • go test ./...

Context

Needed by workflow-compute before trigger.compute_completed can become a native Workflow external plugin trigger instead of a polling/callback workaround.

External trigger providers need a dedicated CreateTrigger path and host callback broker setup so Workflow plugins can provide native trigger types without overloading module creation.
Copilot AI review requested due to automatic review settings May 10, 2026 02:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends the external plugin gRPC surface to properly support external trigger instances and host callback wiring, so external plugins can create triggers during Configure() (after YAML config is known) and reliably call back into the host engine.

Changes:

  • Added ConfigureCallback and CreateTrigger RPCs to the external plugin protocol and implemented them in the plugin-side gRPC server.
  • Updated host-side external plugin loading to provision a callback broker/server and to disable trigger factories when callback setup is unavailable (to preserve module/step compatibility).
  • Refactored RemoteTrigger so trigger handles are created during Configure() (ensuring YAML trigger config reaches the plugin) and added focused tests for the new lifecycle.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
plugin/external/sdk/grpc_server.go Implements ConfigureCallback and CreateTrigger on the plugin server; adds callback connection tracking.
plugin/external/sdk/grpc_server_test.go Adds trigger-provider lifecycle and routing tests for the new RPCs.
plugin/external/remote_trigger.go Defers remote trigger creation to Configure(); adds handle guards for lifecycle methods.
plugin/external/remote_trigger_test.go New tests validating trigger creation/config forwarding and error propagation.
plugin/external/remote_step_test.go Extends stub client to satisfy new RPCs for tests.
plugin/external/proto/plugin.proto Adds ConfigureCallback + CreateTrigger RPCs and request messages.
plugin/external/proto/plugin.pb.go Regenerated protobuf types for new messages/RPCs.
plugin/external/proto/plugin_grpc.pb.go Regenerated gRPC stubs for new methods.
plugin/external/manager.go Allows injecting a host CallbackServer into external plugin clients.
plugin/external/manager_test.go New test verifying callback server is retained by the manager.
plugin/external/adapter.go Configures callback broker during adapter creation and disables trigger factories when callback setup fails/unavailable; defers trigger handle creation.
plugin/external/adapter_test.go Adds coverage for callback setup and trigger factory deferral behavior.
cmd/server/main.go Wires a callback server into the external plugin manager for server-loaded plugins.

Comment thread plugin/external/sdk/grpc_server.go
Comment thread plugin/external/remote_trigger.go Outdated
@github-actions

github-actions Bot commented May 10, 2026

Copy link
Copy Markdown

⏱ Benchmark Results

No significant performance regressions detected.

benchstat comparison (baseline → PR)
## benchstat: baseline → PR
baseline-bench.txt:262: parsing iteration count: invalid syntax
baseline-bench.txt:293582: parsing iteration count: invalid syntax
baseline-bench.txt:584301: parsing iteration count: invalid syntax
baseline-bench.txt:932419: parsing iteration count: invalid syntax
baseline-bench.txt:1272632: parsing iteration count: invalid syntax
baseline-bench.txt:1603341: parsing iteration count: invalid syntax
benchmark-results.txt:262: parsing iteration count: invalid syntax
benchmark-results.txt:313796: parsing iteration count: invalid syntax
benchmark-results.txt:617484: parsing iteration count: invalid syntax
benchmark-results.txt:896896: parsing iteration count: invalid syntax
benchmark-results.txt:1221149: parsing iteration count: invalid syntax
benchmark-results.txt:1695114: parsing iteration count: invalid syntax
goos: linux
goarch: amd64
pkg: github.com/GoCodeAlone/workflow/dynamic
cpu: AMD EPYC 7763 64-Core Processor                
                            │ baseline-bench.txt │        benchmark-results.txt        │
                            │       sec/op       │    sec/op      vs base              │
InterpreterCreation-4              3.121m ± 215%   3.131m ± 193%       ~ (p=0.818 n=6)
ComponentLoad-4                    3.607m ±   9%   3.635m ±   6%       ~ (p=0.485 n=6)
ComponentExecute-4                 1.939µ ±   1%   1.961µ ±   3%  +1.16% (p=0.009 n=6)
PoolContention/workers-1-4         1.083µ ±   0%   1.091µ ±   2%  +0.74% (p=0.039 n=6)
PoolContention/workers-2-4         1.083µ ±   2%   1.092µ ±   2%       ~ (p=0.071 n=6)
PoolContention/workers-4-4         1.088µ ±   1%   1.097µ ±   1%  +0.87% (p=0.037 n=6)
PoolContention/workers-8-4         1.086µ ±   1%   1.089µ ±   2%       ~ (p=0.411 n=6)
PoolContention/workers-16-4        1.093µ ±   2%   1.096µ ±   1%       ~ (p=0.494 n=6)
ComponentLifecycle-4               3.576m ±   0%   3.614m ±   1%  +1.05% (p=0.002 n=6)
SourceValidation-4                 2.291µ ±   0%   2.313µ ±   1%  +0.96% (p=0.002 n=6)
RegistryConcurrent-4               812.7n ±   5%   795.7n ±   3%       ~ (p=0.394 n=6)
LoaderLoadFromString-4             3.585m ±   0%   3.626m ±   1%  +1.14% (p=0.002 n=6)
geomean                            17.43µ          17.52µ         +0.53%

                            │ baseline-bench.txt │        benchmark-results.txt         │
                            │        B/op        │     B/op      vs base                │
InterpreterCreation-4               2.027Mi ± 0%   2.027Mi ± 0%       ~ (p=0.669 n=6)
ComponentLoad-4                     2.180Mi ± 0%   2.180Mi ± 0%       ~ (p=0.223 n=6)
ComponentExecute-4                  1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-1-4          1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-2-4          1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-4-4          1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-8-4          1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-16-4         1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
ComponentLifecycle-4                2.183Mi ± 0%   2.183Mi ± 0%       ~ (p=0.310 n=6)
SourceValidation-4                  1.984Ki ± 0%   1.984Ki ± 0%       ~ (p=1.000 n=6) ¹
RegistryConcurrent-4                1.133Ki ± 0%   1.133Ki ± 0%       ~ (p=1.000 n=6) ¹
LoaderLoadFromString-4              2.182Mi ± 0%   2.182Mi ± 0%       ~ (p=0.418 n=6)
geomean                             15.25Ki        15.25Ki       -0.00%
¹ all samples are equal

                            │ baseline-bench.txt │        benchmark-results.txt        │
                            │     allocs/op      │  allocs/op   vs base                │
InterpreterCreation-4                15.68k ± 0%   15.68k ± 0%       ~ (p=1.000 n=6)
ComponentLoad-4                      18.02k ± 0%   18.02k ± 0%       ~ (p=1.000 n=6)
ComponentExecute-4                    25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-1-4            25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-2-4            25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-4-4            25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-8-4            25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-16-4           25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
ComponentLifecycle-4                 18.07k ± 0%   18.07k ± 0%       ~ (p=1.000 n=6) ¹
SourceValidation-4                    32.00 ± 0%    32.00 ± 0%       ~ (p=1.000 n=6) ¹
RegistryConcurrent-4                  2.000 ± 0%    2.000 ± 0%       ~ (p=1.000 n=6) ¹
LoaderLoadFromString-4               18.06k ± 0%   18.06k ± 0%       ~ (p=1.000 n=6) ¹
geomean                               183.3         183.3       +0.00%
¹ all samples are equal

pkg: github.com/GoCodeAlone/workflow/middleware
                                  │ baseline-bench.txt │       benchmark-results.txt       │
                                  │       sec/op       │   sec/op     vs base              │
CircuitBreakerDetection-4                  285.2n ± 6%   284.9n ± 4%       ~ (p=0.677 n=6)
CircuitBreakerExecution_Success-4          21.48n ± 0%   21.34n ± 0%  -0.68% (p=0.002 n=6)
CircuitBreakerExecution_Failure-4          66.24n ± 1%   66.18n ± 0%       ~ (p=0.197 n=6)
geomean                                    74.04n        73.82n       -0.30%

                                  │ baseline-bench.txt │       benchmark-results.txt        │
                                  │        B/op        │    B/op     vs base                │
CircuitBreakerDetection-4                 144.0 ± 0%     144.0 ± 0%       ~ (p=1.000 n=6) ¹
CircuitBreakerExecution_Success-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
CircuitBreakerExecution_Failure-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                              ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                  │ baseline-bench.txt │       benchmark-results.txt        │
                                  │     allocs/op      │ allocs/op   vs base                │
CircuitBreakerDetection-4                 1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
CircuitBreakerExecution_Success-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
CircuitBreakerExecution_Failure-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                              ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/GoCodeAlone/workflow/module
                                 │ baseline-bench.txt │       benchmark-results.txt        │
                                 │       sec/op       │    sec/op     vs base              │
JQTransform_Simple-4                     877.6n ± 25%   921.1n ± 23%       ~ (p=0.240 n=6)
JQTransform_ObjectConstruction-4         1.450µ ±  1%   1.469µ ±  1%  +1.31% (p=0.006 n=6)
JQTransform_ArraySelect-4                3.305µ ±  1%   3.304µ ±  1%       ~ (p=0.699 n=6)
JQTransform_Complex-4                    38.16µ ±  1%   38.40µ ±  1%       ~ (p=0.093 n=6)
JQTransform_Throughput-4                 1.774µ ±  3%   1.778µ ±  1%       ~ (p=0.219 n=6)
SSEPublishDelivery-4                     63.34n ±  0%   64.54n ±  0%  +1.88% (p=0.002 n=6)
geomean                                  1.619µ         1.643µ        +1.48%

                                 │ baseline-bench.txt │        benchmark-results.txt         │
                                 │        B/op        │     B/op      vs base                │
JQTransform_Simple-4                   1.273Ki ± 0%     1.273Ki ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_ObjectConstruction-4       1.773Ki ± 0%     1.773Ki ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_ArraySelect-4              2.625Ki ± 0%     2.625Ki ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_Complex-4                  16.22Ki ± 0%     16.22Ki ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_Throughput-4               1.984Ki ± 0%     1.984Ki ± 0%       ~ (p=1.000 n=6) ¹
SSEPublishDelivery-4                     0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                             ²                 +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                 │ baseline-bench.txt │       benchmark-results.txt        │
                                 │     allocs/op      │ allocs/op   vs base                │
JQTransform_Simple-4                     10.00 ± 0%     10.00 ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_ObjectConstruction-4         15.00 ± 0%     15.00 ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_ArraySelect-4                30.00 ± 0%     30.00 ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_Complex-4                    324.0 ± 0%     324.0 ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_Throughput-4                 17.00 ± 0%     17.00 ± 0%       ~ (p=1.000 n=6) ¹
SSEPublishDelivery-4                     0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                             ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/GoCodeAlone/workflow/schema
                                    │ baseline-bench.txt │       benchmark-results.txt       │
                                    │       sec/op       │   sec/op     vs base              │
SchemaValidation_Simple-4                   1.105µ ±  4%   1.095µ ± 9%       ~ (p=0.394 n=6)
SchemaValidation_AllFields-4                1.707µ ± 28%   1.651µ ± 5%  -3.31% (p=0.026 n=6)
SchemaValidation_FormatValidation-4         1.606µ ±  2%   1.587µ ± 2%       ~ (p=0.195 n=6)
SchemaValidation_ManySchemas-4              1.822µ ±  4%   1.822µ ± 4%       ~ (p=0.565 n=6)
geomean                                     1.533µ         1.512µ       -1.36%

                                    │ baseline-bench.txt │       benchmark-results.txt        │
                                    │        B/op        │    B/op     vs base                │
SchemaValidation_Simple-4                   0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_AllFields-4                0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_FormatValidation-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_ManySchemas-4              0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                    │ baseline-bench.txt │       benchmark-results.txt        │
                                    │     allocs/op      │ allocs/op   vs base                │
SchemaValidation_Simple-4                   0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_AllFields-4                0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_FormatValidation-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_ManySchemas-4              0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/GoCodeAlone/workflow/store
                                   │ baseline-bench.txt │        benchmark-results.txt        │
                                   │       sec/op       │    sec/op     vs base               │
EventStoreAppend_InMemory-4                1.192µ ± 11%   1.204µ ± 15%        ~ (p=0.818 n=6)
EventStoreAppend_SQLite-4                  1.381m ±  2%   1.254m ±  4%   -9.24% (p=0.002 n=6)
GetTimeline_InMemory/events-10-4           13.62µ ±  7%   13.61µ ±  3%        ~ (p=0.589 n=6)
GetTimeline_InMemory/events-50-4           77.15µ ±  2%   76.47µ ±  2%        ~ (p=0.240 n=6)
GetTimeline_InMemory/events-100-4          126.0µ ± 23%   154.0µ ±  3%  +22.20% (p=0.041 n=6)
GetTimeline_InMemory/events-500-4          645.9µ ±  1%   625.9µ ±  1%   -3.10% (p=0.002 n=6)
GetTimeline_InMemory/events-1000-4         1.332m ±  1%   1.276m ±  1%   -4.19% (p=0.002 n=6)
GetTimeline_SQLite/events-10-4             107.7µ ±  1%   105.6µ ±  1%   -2.01% (p=0.002 n=6)
GetTimeline_SQLite/events-50-4             252.6µ ±  1%   246.8µ ±  1%   -2.32% (p=0.002 n=6)
GetTimeline_SQLite/events-100-4            430.3µ ±  1%   417.1µ ±  0%   -3.07% (p=0.002 n=6)
GetTimeline_SQLite/events-500-4            1.848m ±  0%   1.782m ±  1%   -3.59% (p=0.002 n=6)
GetTimeline_SQLite/events-1000-4           3.589m ±  1%   3.466m ±  1%   -3.42% (p=0.002 n=6)
geomean                                    222.3µ         220.2µ         -0.97%

                                   │ baseline-bench.txt │        benchmark-results.txt         │
                                   │        B/op        │     B/op      vs base                │
EventStoreAppend_InMemory-4                  790.5 ± 7%     753.0 ± 9%       ~ (p=0.310 n=6)
EventStoreAppend_SQLite-4                  1.986Ki ± 1%   1.985Ki ± 2%       ~ (p=0.732 n=6)
GetTimeline_InMemory/events-10-4           7.953Ki ± 0%   7.953Ki ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-50-4           46.62Ki ± 0%   46.62Ki ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-100-4          94.48Ki ± 0%   94.48Ki ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-500-4          472.8Ki ± 0%   472.8Ki ± 0%       ~ (p=0.567 n=6)
GetTimeline_InMemory/events-1000-4         944.3Ki ± 0%   944.3Ki ± 0%       ~ (p=0.121 n=6)
GetTimeline_SQLite/events-10-4             16.74Ki ± 0%   16.74Ki ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-50-4             87.14Ki ± 0%   87.14Ki ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-100-4            175.4Ki ± 0%   175.4Ki ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-500-4            846.1Ki ± 0%   846.1Ki ± 0%  -0.00% (p=0.011 n=6)
GetTimeline_SQLite/events-1000-4           1.639Mi ± 0%   1.639Mi ± 0%       ~ (p=0.210 n=6)
geomean                                    67.35Ki        67.08Ki       -0.41%
¹ all samples are equal

                                   │ baseline-bench.txt │        benchmark-results.txt        │
                                   │     allocs/op      │  allocs/op   vs base                │
EventStoreAppend_InMemory-4                  7.000 ± 0%    7.000 ± 0%       ~ (p=1.000 n=6) ¹
EventStoreAppend_SQLite-4                    53.00 ± 0%    53.00 ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-10-4             125.0 ± 0%    125.0 ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-50-4             653.0 ± 0%    653.0 ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-100-4           1.306k ± 0%   1.306k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-500-4           6.514k ± 0%   6.514k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-1000-4          13.02k ± 0%   13.02k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-10-4               382.0 ± 0%    382.0 ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-50-4              1.852k ± 0%   1.852k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-100-4             3.681k ± 0%   3.681k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-500-4             18.54k ± 0%   18.54k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-1000-4            37.29k ± 0%   37.29k ± 0%       ~ (p=1.000 n=6) ¹
geomean                                     1.162k        1.162k       +0.00%
¹ all samples are equal

Benchmarks run with go test -bench=. -benchmem -count=6.
Regressions ≥ 20% are flagged. Results compared via benchstat.

Copilot AI review requested due to automatic review settings May 10, 2026 03:08

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.

Comment thread plugin/external/sdk/grpc_server.go Outdated
Comment thread plugin/external/remote_trigger.go Outdated
Comment thread plugin/external/remote_trigger.go
Comment thread plugin/external/sdk/grpc_server_test.go Outdated

Copy link
Copy Markdown
Contributor Author

Addressed the latest review comments in 72235c92.

  • CreateTrigger now binds callbacks to the configured workflow identifier via CreateTriggerRequest.Name, with fallback to trigger type for backward compatibility.
  • RemoteTrigger.Configure supports multiple pipelines/workflowTypes, keeps one remote handle per workflow, and dedupes idempotent retries using the deterministic protobuf config fingerprint.
  • Trigger lifecycle fans out across handles, rolls back partial start failures, rejects stale configure errors, handles nil lifecycle responses, and clears destroyed handles so repeated Destroy no-ops.
  • Tests cover workflow callback routing, fallback routing, multi-pipeline configure, partial retry dedupe, duplicate config conflicts, invalid workflow type shape, configure-error retry clearing, lifecycle fanout, rollback, and destroy clearing.

Local verification before push:

go test ./plugin/external/... ./cmd/server
golangci-lint run --timeout=10m ./plugin/external/... ./cmd/server
git diff --check

@intel352 intel352 merged commit 182240d into main May 10, 2026
18 checks passed
@intel352 intel352 deleted the fix-external-trigger-provider branch May 10, 2026 03:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants