cadence

package module
v0.7.4 Latest Latest
Warning

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

Go to latest
Published: Aug 13, 2018 License: MIT Imports: 2 Imported by: 48

README

Go framework for Cadence Build Status Coverage Status

Cadence is a distributed, scalable, durable, and highly available orchestration engine we developed at Uber Engineering to execute asynchronous long-running business logic in a scalable and resilient way.

cadence-client is the framework for authoring workflows and activities.

How to use

Make sure you clone this repo into the correct location.

git clone git@github.com:uber-go/cadence-client.git $GOPATH/src/go.uber.org/cadence

or

go get go.uber.org/cadence

See samples to get started

Activity

Activity is the implementation of a particular task in the business logic.

Activities are implemented as functions. Data can be passed directly to an activity via function parameters. The parameters can be either basic types or structs, with the only requirement being that the parameters need to be serializable. Even though it is not required, we recommand that the first parameter of an activity function is of type context.Context, in order to allow the activity to interact with other framework methods. The function must return an error value, and can optionally return a result value. The result value can be either a basic type or a struct with the only requirement being that it is serializable.

The values passed to activities through invocation parameters or returned through the result value is recorded in the execution history. The entire execution history is transfered from the Cadence service to workflow workers with every event that the workflow logic needs to process. A large execution history can thus adversily impact the performance of your workflow. Therefore be mindful of the amount of data you transfer via activity invocation parameters or return values. Other than that no additional limitations exist on activity implementations.

In order to make the activity visible to the worker process hosting it, the activity needs to be registered via a call to activity.Register.

package simple

import (
	"context"

    "go.uber.org/cadence/activity"
    "go.uber.org/zap"
)

func init() {
	activity.Register(SimpleActivity)
}

// SimpleActivity is a sample Cadence activity function that takes one parameter and
// returns a string containing the parameter value.
func SimpleActivity(ctx context.Context, value string) (string, error) {
	activity.GetLogger(ctx).Info("SimpleActivity called.", zap.String("Value", value))
	return "Processed: " + value, nil
}
Workflow

Workflow is the implementation of coordination logic. Its sole purpose is to orchestrate activity executions.

Workflows are implemented as functions. Startup data can be passed to a workflow via function parameters. The parameters can be either basic types or structs, with the only requirement being that the parameters need to be serializable. The first parameter of a workflow function is of type workflow.Context. The function must return an error value, and can optional return a result value. The result value can be either a basic type or a struct with the only requirement being that the it is serializable.

Workflow functions need to execute deterministically. Therefore, here is a list of rules that workflow code should obey to be a good Cadence citizen:

  • Use workflow.Context everywhere.
  • Don’t use range over map.
  • Use workflow.SideEffect to call rand and similar nondeterministic functions like UUID generator.
  • Use workflow.Now to get current time. Use workflow.NewTimer or workflow.Sleep instead of standard Go functions.
  • Don’t use native channel and select. Use workflow.Channel and workflow.Selector.
  • Don’t use go func(...). Use workflow.Go(func(...)).
  • Don’t use non constant global variables as multiple instances of a workflow function can be executing in parallel.
  • Don’t use any blocking functions besides belonging to Channel, Selector or Future
  • Don’t use any synchronization primitives as they can cause blockage and there is no possibility of races when running under dispatcher.
  • Don't just change workflow code when there are open workflows. Always update code using workflow.GetVersion.
  • Don’t perform any IO or service calls as they are not usually deterministic. Use activities for that.
  • Don’t access configuration APIs directly from a workflow as changes in configuration will affect the workflow execution path. Either return configuration from an activity or use workflow.SideEffect to load it.

In order to make the workflow visible to the worker process hosting it, the workflow needs to be registered via a call to workflow.Register.

package simple

import (
	"time"


	"go.uber.org/cadence/workflow"
	"go.uber.org/zap"
)

func init() {
	workflow.Register(SimpleWorkflow)
}

// SimpleWorkflow is a sample Cadence workflow that accepts one parameter and
// executes an activity to which it passes the aforementioned parameter.
func SimpleWorkflow(ctx workflow.Context, value string) error {
	options := workflow.ActivityOptions{
		ScheduleToStartTimeout: time.Second * 60,
		StartToCloseTimeout:    time.Second * 60,
	}
	ctx = workflow.WithActivityOptions(ctx, options)

	var result string
	err := workflow.ExecuteActivity(ctx, activity.SimpleActivity, value).Get(ctx, &result)
	if err != nil {
		return err
	}
	workflow.GetLogger(ctx).Info(
		"SimpleActivity returned successfully!", zap.String("Result", result))

	workflow.GetLogger(ctx).Info("SimpleWorkflow completed!")
	return nil
}
Worker

A worker or “worker service” is a services hosting the workflow and activity implementations. The worker polls the “Cadence service” for tasks, performs those tasks and communicates task execution results back to the “Cadence service”. Worker services are developed, deployed and operated by Cadence customers.

You can run a Cadence worker in a new or an exiting service. Use the framework APIs to start the Cadence worker and link in all activity and workflow implementations that you require this service to execute.

package main

import (

	t "go.uber.org/cadence/.gen/go/cadence"
	"go.uber.org/cadence/.gen/go/cadence/workflowserviceclient"
	"go.uber.org/cadence/worker"

	"github.com/uber-go/tally"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"go.uber.org/yarpc"
	"go.uber.org/yarpc/api/transport"
	"go.uber.org/yarpc/transport/tchannel"
)

var HostPort = "127.0.0.1:7933"
var Domain = "SimpleDomain"
var TaskListName = "SimpleWorker"
var ClientName = "SimpleWorker"
var CadenceService = "CadenceServiceFrontend"

func main() {
	startWorker(buildLogger(), buildCadenceClient())
}

func buildLogger() *zap.Logger {
	config := zap.NewDevelopmentConfig()
	config.Level.SetLevel(zapcore.InfoLevel)

	var err error
	logger, err := config.Build()
	if err != nil {
		panic("Failed to setup logger")
	}

	return logger
}

func buildCadenceClient() workflowserviceclient.Interface {
	ch, err := tchannel.NewChannelTransport(tchannel.ServiceName(ClientName), tchannel.ListenAddr("127.0.0.1:0"))
	if err != nil {
		panic("Failed to setup tchannel")
	}
	dispatcher := yarpc.NewDispatcher(yarpc.Config{
			Name: ClientName,
			Outbounds: yarpc.Outbounds{
				CadenceService: {Unary: ch.NewSingleOutbound(HostPort)},
			},
		})
	if err := dispatcher.Start(); err != nil {
		panic("Failed to start dispatcher")
	}

	return workflowserviceclient.New(dispatcher.ClientConfig(CadenceService))
}

func startWorker(logger *zap.Logger, service workflowserviceclient.Interface) {
	// TaskListName - identifies set of client workflows, activities and workers.
	// it could be your group or client or application name.
	workerOptions := worker.Options{
		Logger:       logger,
		MetricsScope: tally.NewTestScope(TaskListName, map[string]string{}),
	}

	worker := worker.NewWorker(
		service,
		Domain,
		TaskListName,
		workerOptions)
	err := worker.Start()
	if err != nil {
		panic("Failed to start worker")
	}

	logger.Info("Started Worker.", zap.String("worker", TaskListName))
}

Contributing

We'd love your help in making Cadence-client great. Please review our instructions.

License

MIT License, please see LICENSE for details.

Documentation

Overview

Package cadence and its subdirectories contain a Cadence client side framework.

The Cadence service is a task orchestrator for your application’s tasks. Applications using Cadence can execute a logical flow of tasks, especially long-running business logic, asynchronously or synchronously. and can scale runtime on distributed systems without you, the service owner, worrying about infrastructure needs.

A quick example illustrates its use case. Consider Uber Eats where Cadence manages the entire business flow from placing an order, accepting it, handling shopping cart processes (adding, updating, and calculating cart items), entering the order in a pipeline (for preparing food and coordinating delivery), to scheduling delivery as well as handling payments.

Cadence consists of a programming framework (or client library) and a managed service (or backend). The framework enables developers to author and coordinate tasks in Go code.

The root cadence package contains common data structures. The subpackages are:

workflow - functions used to implement workflows

activity - functions used to implement activities

client - functions used to create Cadence service client used to start and monitor workflow executions.

worker - functions used to create worker instance used to host workflow and activity code.

testsuite - unit testing framework for activity and workflow testing.

Index

Constants

View Source
const LibraryVersion = internal.LibraryVersion

LibraryVersion is a semver string that represents the version of this cadence client library it will be embedded as a "version" header in every rpc call made by this client to cadence server. In addition, the version string will be used by the server to enforce compatibility checks Update to this version number is typically done by the cadence team as part of a major feature or behavior change

Variables

View Source
var ErrNoData = internal.ErrNoData

ErrNoData is returned when trying to extract strong typed data while there is no data available.

Functions

func IsCanceledError added in v0.5.1

func IsCanceledError(err error) bool

IsCanceledError return if the err is a CanceledError

func IsCustomError added in v0.5.1

func IsCustomError(err error) bool

IsCustomError return if the err is a CustomError

func IsGenericError added in v0.5.1

func IsGenericError(err error) bool

IsGenericError return if the err is a GenericError

func IsPanicError added in v0.5.1

func IsPanicError(err error) bool

IsPanicError return if the err is a PanicError

func IsTerminatedError added in v0.5.1

func IsTerminatedError(err error) bool

IsTerminatedError return if the err is a TerminatedError

func IsTimeoutError added in v0.5.1

func IsTimeoutError(err error) bool

IsTimeoutError return if the err is a TimeoutError

Types

type CanceledError

type CanceledError = internal.CanceledError

CanceledError returned when operation was canceled.

func NewCanceledError

func NewCanceledError(details ...interface{}) *CanceledError

NewCanceledError creates CanceledError instance. Return this error from activity or child workflow to indicate that it was successfully cancelled.

type CustomError

type CustomError = internal.CustomError

CustomError returned from workflow and activity implementations with reason and optional details.

func NewCustomError

func NewCustomError(reason string, details ...interface{}) *CustomError

NewCustomError create new instance of *CustomError with reason and optional details. Use CustomError for any use case specific errors that cross activity and child workflow boundaries.

Directories

Path Synopsis
Package activity contains functions and types used to implement Cadence activities.
Package activity contains functions and types used to implement Cadence activities.
Package client contains functions to create Cadence clients used to communicate to Cadence service.
Package client contains functions to create Cadence clients used to communicate to Cadence service.
Package encoded contains wrappers that are used for binary payloads deserialization.
Package encoded contains wrappers that are used for binary payloads deserialization.
cmd/dummy
This file exists to force compilation of all code that doesn't have unit tests.
This file exists to force compilation of all code that doesn't have unit tests.
Code generated by mockery v1.0.0 Code generated by mockery v1.0.0
Code generated by mockery v1.0.0 Code generated by mockery v1.0.0
Package testsuite contains unit testing framework for Cadence workflows and activities.
Package testsuite contains unit testing framework for Cadence workflows and activities.
Package worker contains functions to manage lifecycle of a Cadence client side worker.
Package worker contains functions to manage lifecycle of a Cadence client side worker.
Package workflow contains functions and types used to implement Cadence workflows.
Package workflow contains functions and types used to implement Cadence workflows.

Jump to

Keyboard shortcuts

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