Skip to main content

Codec Server

A simple example inspired by temporalio/samples-go/codecserver

main.go
package main

import (
"context"
"errors"
"log"
"log/slog"
"net/http"
"os"
"os/signal"
"syscall"

"github.com/cludden/protoc-gen-go-temporal/examples/example"
examplev1 "github.com/cludden/protoc-gen-go-temporal/gen/example/v1"
"github.com/cludden/protoc-gen-go-temporal/pkg/codec"
"github.com/cludden/protoc-gen-go-temporal/pkg/scheme"
"github.com/urfave/cli/v2"
"go.temporal.io/sdk/client"
"go.temporal.io/sdk/converter"
tlog "go.temporal.io/sdk/log"
"go.temporal.io/sdk/worker"
)

func main() {
app, err := examplev1.NewExampleCli(
examplev1.NewExampleCliOptions().
WithClient(func(cmd *cli.Context) (client.Client, error) {
return client.Dial(client.Options{
DataConverter: converter.NewCompositeDataConverter(
converter.NewNilPayloadConverter(),
converter.NewByteSlicePayloadConverter(),
converter.NewProtoPayloadConverter(),
),
Logger: tlog.NewStructuredLogger(slog.Default()),
})
}).
WithWorker(func(cmd *cli.Context, c client.Client) (worker.Worker, error) {
w := worker.New(c, examplev1.ExampleTaskQueue, worker.Options{})
examplev1.RegisterExampleActivities(w, &example.Activities{})
examplev1.RegisterExampleWorkflows(w, &example.Workflows{})
return w, nil
}),
)
if err != nil {
log.Fatalf("error initializing example cli: %v", err)
}

app.Commands = append(app.Commands, &cli.Command{
Name: "codec",
Usage: "run remote codec server",
Action: func(cmd *cli.Context) error {
handler := converter.NewPayloadCodecHTTPHandler(
codec.NewProtoJSONCodec(
scheme.New(
examplev1.WithExampleSchemeTypes(),
),
),
)

srv := &http.Server{
Addr: "0.0.0.0:8080",
Handler: handler,
}

go func() {
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
<-sigChan

if err := srv.Shutdown(context.Background()); err != nil {
log.Fatalf("error shutting down server: %v", err)
}
}()

if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
log.Fatalf("server error: %v", err)
}
return nil
},
})

// run cli
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}

Run this example

  1. Clone the examples
    git clone https://github.com/cludden/protoc-gen-go-temporal && cd protoc-gen-go-temporal
  2. Start the codec server
    go run examples/codecserver/main.go codec
  3. In a different terminal, start temporal using the codec server
    temporal server start-dev \
    --dynamic-config-value "frontend.enableUpdateWorkflowExecution=true" \
    --dynamic-config-value "frontend.enableUpdateWorkflowExecutionAsyncAccepted=true" \
    --ui-codec-endpoint http://localhost:8080
  4. In a different terminal, run the worker
    go run examples/codecserver/main.go worker
  5. In a different terminal, execute a workflow, signal, query, and update
    # execute a workflow in the background
    go run examples/codecserver/main.go create-foo --name test -d

    # signal the workflow
    go run examples/codecserver/main.go set-foo-progress -w create-foo/test --progress 5.7

    # query the workflow
    go run examples/codecserver/main.go get-foo-progress -w create-foo/test

    # update the workflow
    go run examples/codecserver/main.go update-foo-progress -w create-foo/test --progress 100
  6. In the UI, switch to the JSON tab and disable the Decode Event History toggle and verify that all payloads have metadata with "encoding": "YmluYXJ5L3Byb3RvYnVm", which is binary/protobuf base64-encoded