floc

package module
v2.0.0-...-072ea80 Latest Latest
Warning

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

Go to latest
Published: Dec 22, 2020 License: MIT Imports: 6 Imported by: 4

README

Gopher Floc Control

go-floc

Floc: Orchestrate goroutines with ease.

GoDoc Build Status Coverage Status Go Report Card License

The goal of the project is to make the process of running goroutines in parallel and synchronizing them easy.

Announcements

Hooray! The new version v2 is released on the 1st of December, 2017!

Installation and requirements

The package requires Go v1.8 or later.

To install the package use go get gopkg.in/workanator/go-floc.v2

Documentation and examples

Please refer Godoc reference of the package for more details.

Some examples are available at the Godoc reference. Additional examples can be found in go-floc-showcase.

Features

  • Easy to use functional interface.
  • Simple parallelism and synchronization of jobs.
  • As little overhead as possible, in comparison to direct use of goroutines and sync primitives.
  • Provide better control over execution with one entry point and one exit point.

Introduction

Floc introduces some terms which are widely used through the package.

Flow

Flow is the overall process which can be controlled through floc.Flow. Flow can be canceled or completed with any arbitrary data at any point of execution. Flow has only one enter point and only one exit point.

// Design the job
flow := run.Sequence(do, something, here, ...)

// The enter point: Run the job
result, data, err := floc.Run(flow)

// The exit point: Check the result of the job.
if err != nil {
	// Handle the error
} else if result.IsCompleted() {
	// Handle the success
} else {
	// Handle other cases
}
Job

Job in Floc is a smallest piece of flow. The prototype of job function is floc.Job. Each job can read/write data with floc.Context and control the flow with floc.Control.

Cancel(), Complete(), Fail() methods of floc.Flow has permanent effect. Once finished flow cannot be canceled or completed anymore. Calling Fail and returning error from job is almost equal.

func ValidateContentLength(ctx floc.Context, ctrl floc.Control) error {
  request := ctx.Value("request").(http.Request)

  // Cancel the flow with error if request body size is too big
  if request.ContentLength > MaxContentLength {
    return errors.New("content is too big")
  }
  
  return nil
}

Example

Lets have some fun and write a simple example which calculates some statistics on text given.

const Text = `Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed
  do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
  veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
  consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
  dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident,
  sunt in culpa qui officia deserunt mollit anim id est laborum.`
  
const keyStatistics = 1

var sanitizeWordRe = regexp.MustCompile(`\W`)

type Statistics struct {
  Words      []string
  Characters int
  Occurrence map[string]int
}

// Split to words and sanitize them
SplitToWords := func(ctx floc.Context, ctrl floc.Control) error {
  statistics := ctx.Value(keyStatistics).(*Statistics)

  statistics.Words = strings.Split(Text, " ")
  for i, word := range statistics.Words {
    statistics.Words[i] = sanitizeWordRe.ReplaceAllString(word, "")
  }
  
  return nil
}

// Count and sum the number of characters in the each word
CountCharacters := func(ctx floc.Context, ctrl floc.Control) error {
  statistics := ctx.Value(keyStatistics).(*Statistics)

  for _, word := range statistics.Words {
    statistics.Characters += len(word)
  }
  
  return nil
}

// Count the number of unique words
CountUniqueWords := func(ctx floc.Context, ctrl floc.Control) error {
  statistics := ctx.Value(keyStatistics).(*Statistics)

  statistics.Occurrence = make(map[string]int)
  for _, word := range statistics.Words {
    statistics.Occurrence[word] = statistics.Occurrence[word] + 1
  }
  
  return nil
}

// Print result
PrintResult := func(ctx floc.Context, ctrl floc.Control) error {
  statistics := ctx.Value(keyStatistics).(*Statistics)

  fmt.Printf("Words Total       : %d\n", len(statistics.Words))
  fmt.Printf("Unique Word Count : %d\n", len(statistics.Occurrence))
  fmt.Printf("Character Count   : %d\n", statistics.Characters)
  
  return nil
}

// Design the flow and run it
flow := run.Sequence(
  SplitToWords,
  run.Parallel(
    CountCharacters,
    CountUniqueWords,
  ),
  PrintResult,
)

ctx := floc.NewContext()
ctx.AddValue(keyStatistics, new(Statistics))

ctrl := floc.NewControl(ctx)

_, _, err := floc.RunWith(ctx, ctrl, flow)
if err != nil {
	panic(err)
}

// Output:
// Words Total       : 64
// Unique Word Count : 60
// Character Count   : 370

Contributing

Please found information about contributing in CONTRIBUTING.md and the list of bravers who spent their priceless time and effort to make the project better in CONTRIBUTORS.md.

Documentation

Overview

Package floc allows to orchestrate goroutines with ease. The goal of the project is to make the process of running goroutines in parallel and synchronizing them easy.

Floc follows for objectives:

-- Easy to use functional interface.

-- Better control over execution with one entry point and one exit point.

-- Simple parallelism and synchronization of jobs.

-- As little overhead, in comparison to direct use of goroutines and sync primitives, as possible.

The package categorizes middleware used for architect flows in subpackages.

-- `guard` contains middleware which help protect flow from falling into panic or unpredicted behavior.

-- `pred` contains some basic predicates for AND, OR, NOT logic.

-- `run` provides middleware for designing flow, i.e. for running job sequentially, in parallel, in background and so on.

-- `errors` all error types used in the package.

Here is a quick example of what the package capable of.

// The flow computes something complex and does writing results in
// background.
flow := run.Sequence(
  run.Background(WriteToDisk),
  run.While(pred.Not(TestComputed), run.Sequence(
    run.Parallel(
      ComputeSomething,
      ComputeSomethingElse,
      guard.Panic(ComputeDangerousThing),
    ),
    run.Parallel(
      PrepareForWrite,
      UpdateComputedFlag,
    ),
  )),
  CompleteWithSuccess,
)

// The entry point: produce the result.
result, data, err := floc.Run(flow)

// The exit point: consume the result.
if err != nil {
  fmt.Println(err)
} else if result.IsCompleted() {
  fmt.Println(data)
} else {
  fmt.Printf("Finished with result %s and data %v", result.String(), data)
}

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Context

type Context interface {
	Releaser

	// Ctx returns the underlying context.
	Ctx() context.Context

	// UpdateCtx sets the new underlying context.
	UpdateCtx(ctx context.Context)

	// Done returns a channel that's closed when the flow done.
	// Successive calls to Done return the same value.
	Done() <-chan struct{}

	// Value returns the value associated with this context for key,
	// or nil if no value is associated with key.
	Value(key interface{}) (value interface{})

	// Create a new context with value and make it the current.
	// This is an equivalent to.
	//
	//    oldCtx := ctx.Ctx()
	//    newCtx := context.WithValue(oldCtx, key, value)
	//    ctx.UpdateCtx(newCtx)
	//
	AddValue(key, value interface{})
}

Context provides safe access to the underlying context. For more details on the underlying context see https://golang.org/pkg/context/#Context.

func BorrowContext

func BorrowContext(ctx context.Context) Context

BorrowContext constructs new instance from the context given. The function panics if the context given is nil.

func NewContext

func NewContext() Context

NewContext constructs new instance of TODO context.

type Control

type Control interface {
	Releaser

	// Complete finishes the flow with success status and stops
	// execution of further jobs if any.
	Complete(data interface{})

	// Cancel cancels the execution of the flow.
	Cancel(data interface{})

	// Fail cancels the execution of the flow with error.
	Fail(data interface{}, err error)

	// IsFinished tests if execution of the flow is either completed or canceled.
	IsFinished() bool

	// Result returns the result code and the result data of the flow. The call
	// to the function is effective only if the flow is finished.
	Result() (result Result, data interface{}, err error)
}

Control allows to control execution of the flow.

func NewControl

func NewControl(ctx Context) Control

NewControl constructs Control instance from context given. The function panics if the context given is nil.

type Job

type Job func(ctx Context, ctrl Control) error

Job is the small piece of flow.

type Predicate

type Predicate func(ctx Context) bool

Predicate is the function which calculates the result of some condition.

type Releaser

type Releaser interface {
	// Release should be called once when the object is not needed anymore.
	Release()
}

Releaser is responsible for releasing underlying resources.

type Result

type Result int32

Result identifies the result of execution.

const (
	None      Result = 1
	Completed Result = 2
	Canceled  Result = 4
	Failed    Result = 8
)

Possible results.

func Run

func Run(job Job) (result Result, data interface{}, err error)

Run creates a new Context and Control and runs the flow.

func RunWith

func RunWith(ctx Context, ctrl Control, job Job) (result Result, data interface{}, err error)

RunWith runs the flow with the Context and Control given.

func (Result) IsCanceled

func (result Result) IsCanceled() bool

IsCanceled tests if the result is Canceled.

func (Result) IsCompleted

func (result Result) IsCompleted() bool

IsCompleted tests if the result is Completed.

func (Result) IsFailed

func (result Result) IsFailed() bool

IsFailed tests if the result is Failed.

func (Result) IsFinished

func (result Result) IsFinished() bool

IsFinished tests if the result is either Completed or Canceled or Failed.

func (Result) IsNone

func (result Result) IsNone() bool

IsNone tests if the result is None.

func (Result) IsValid

func (result Result) IsValid() bool

IsValid tests if the result is a valid value.

func (Result) Mask

func (result Result) Mask() ResultMask

Mask constructs ResultMask with only one result masked.

func (Result) String

func (result Result) String() string

type ResultMask

type ResultMask Result

ResultMask is the mask of possible results.

func EmptyResultMask

func EmptyResultMask() ResultMask

EmptyResultMask returns empty result set.

func NewResultMask

func NewResultMask(mask Result) ResultMask

NewResultMask constructs new instance from the mask given.

func (ResultMask) IsEmpty

func (mask ResultMask) IsEmpty() bool

IsEmpty returns true if no result is masked.

func (ResultMask) IsMasked

func (mask ResultMask) IsMasked(result Result) bool

IsMasked tests if the result is masked.

func (ResultMask) String

func (mask ResultMask) String() string

Directories

Path Synopsis
Package errors defines error types used in the package.
Package errors defines error types used in the package.
Package guard contains middleware capable to protect execution of flow from crashing or from unpredicted behavior.
Package guard contains middleware capable to protect execution of flow from crashing or from unpredicted behavior.
Package pred provides predicates for basic logic operations.
Package pred provides predicates for basic logic operations.
Package run is the collection of flow architectural blocks.
Package run is the collection of flow architectural blocks.

Jump to

Keyboard shortcuts

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