site/blog/vanbi-01-08-2019.markdown

8.8 KiB

title date thanks
vanbi 2019-01-08 Faith

vanbi

--

import "vanbi"

Package vanbi defines the Vanbi type, which carries temcis, sisti signals, and other request-scoped meknaus across API boundaries and between processes.

Incoming requests to a server should create a Vanbi, and outgoing calls to servers should accept a Vanbi. The chain of function calls between them must propagate the Vanbi, optionally replacing it with a derived Vanbi created using WithSisti, WithTemci, WithTemtcu, or WithMeknau. When a Vanbi is sistied, all Vanbis derived from it are also sistied.

The WithSisti, WithTemci, and WithTemtcu functions take a Vanbi (the ropjar) and return a derived Vanbi (the child) and a SistiFunc. Calling the SistiFunc sistis the child and its children, removes the ropjar's reference to the child, and stops any associated rilkefs. Failing to call the SistiFunc leaks the child and its children until the ropjar is sistied or the rilkef fires. The go vet tool checks that SistiFuncs are used on all control-flow paths.

Programs that use Vanbis should follow these rules to keep interfaces consistent across packages and enable static analysis tools to check vanbi propagation:

Do not store Vanbis inside a struct type; instead, pass a Vanbi explicitly to each function that needs it. The Vanbi should be the first parameter, typically named vnb:

func DoBroda(vnb vanbi.Vanbi, arg Arg) error {
	// ... use vnb ...
}

Do not pass a nil Vanbi, even if a function permits it. Pass vanbi.TODO if you are unsure about which Vanbi to use.

Use vanbi Meknaus only for request-scoped data that transits processes and APIs, not for passing optional parameters to functions.

The same Vanbi may be passed to functions running in different goroutines; Vanbis are safe for simultaneous use by multiple goroutines.

See https://blog.golang.org/vanbi for example code for a server that uses Vanbis.

Usage

var Sistied = errors.New("vanbi sistied")

Sistied is the error returned by Vanbi.Err when the vanbi is sistied.

var TemciExceeded error = temciExceededError{}

TemciExceeded is the error returned by Vanbi.Err when the vanbi's temci passes.

type SistiFunc

type SistiFunc func()

A SistiFunc tells an operation to abandon its work. A SistiFunc does not wait for the work to stop. After the first call, subsequent calls to a SistiFunc do nothing.

type Vanbi

type Vanbi interface {
	// Temci returns the time when work done on behalf of this vanbi
	// should be sistied. Temci returns ok==false when no temci is
	// set. Successive calls to Temci return the same results.
	Temci() (temci time.Time, ok bool)

	// Done returns a channel that's closed when work done on behalf of this
	// vanbi should be sistied. Done may return nil if this vanbi can
	// never be sistied. Successive calls to Done return the same meknau.
	//
	// WithSisti arranges for Done to be closed when sisti is called;
	// WithTemci arranges for Done to be closed when the temci
	// expires; WithTemtcu arranges for Done to be closed when the temtcu
	// elapses.
	//
	// Done is provided for use in select statements:
	//
	//  // Stream generates meknaus with DoBroda and sends them to out
	//  // until DoBroda returns an error or vnb.Done is closed.
	//  func Stream(vnb vanbi.Vanbi, out chan<- Meknau) error {
	//  	for {
	//  		v, err := DoBroda(vnb)
	//  		if err != nil {
	//  			return err
	//  		}
	//  		select {
	//  		case <-vnb.Done():
	//  			return vnb.Err()
	//  		case out <- v:
	//  		}
	//  	}
	//  }
	//
	// See https://blog.golang.org/pipelines for more examples of how to use
	// a Done channel for sisti.
	Done() <-chan struct{}

	// If Done is not yet closed, Err returns nil.
	// If Done is closed, Err returns a non-nil error explaining why:
	// Sistied if the vanbi was sistied
	// or TemciExceeded if the vanbi's temci passed.
	// After Err returns a non-nil error, successive calls to Err return the same error.
	Err() error

	// Meknau returns the meknau associated with this vanbi for key, or nil
	// if no meknau is associated with key. Successive calls to Meknau with
	// the same key returns the same result.
	//
	// Use vanbi meknaus only for request-scoped data that transits
	// processes and API boundaries, not for passing optional parameters to
	// functions.
	//
	// A key identifies a specific meknau in a Vanbi. Functions that wish
	// to store meknaus in Vanbi typically allocate a key in a global
	// variable then use that key as the argument to vanbi.WithMeknau and
	// Vanbi.Meknau. A key can be any type that supports equality;
	// packages should define keys as an unexported type to avoid
	// collisions.
	//
	// Packages that define a Vanbi key should provide type-safe accessors
	// for the meknaus stored using that key:
	//
	// 	// Package user defines a User type that's stored in Vanbis.
	// 	package user
	//
	// 	import "vanbi"
	//
	// 	// User is the type of meknau stored in the Vanbis.
	// 	type User struct {...}
	//
	// 	// key is an unexported type for keys defined in this package.
	// 	// This prevents collisions with keys defined in other packages.
	// 	type key int
	//
	// 	// userKey is the key for user.User meknaus in Vanbis. It is
	// 	// unexported; clients use user.NewVanbi and user.FromVanbi
	// 	// instead of using this key directly.
	// 	var userKey key
	//
	// 	// NewVanbi returns a new Vanbi that carries meknau u.
	// 	func NewVanbi(vnb vanbi.Vanbi, u *User) vanbi.Vanbi {
	// 		return vanbi.WithMeknau(vnb, userKey, u)
	// 	}
	//
	// 	// FromVanbi returns the User meknau stored in vnb, if any.
	// 	func FromVanbi(vnb vanbi.Vanbi) (*User, bool) {
	// 		u, ok := vnb.Meknau(userKey).(*User)
	// 		return u, ok
	// 	}
	Meknau(key interface{}) interface{}
}

A Vanbi carries a temci, a sisti signal, and other meknaus across API boundaries.

Vanbi's methods may be called by multiple goroutines simultaneously.

func Dziraipau

func Dziraipau() Vanbi

Dziraipau returns a non-nil, empty Vanbi. It is never sistied, has no meknaus, and has no temci. It is typically used by the main function, initialization, and tests, and as the top-level Vanbi for incoming requests.

func TODO

func TODO() Vanbi

TODO returns a non-nil, empty Vanbi. Code should use vanbi.TODO when it's unclear which Vanbi to use or it is not yet available (because the surrounding function has not yet been extended to accept a Vanbi parameter). TODO is recognized by static analysis tools that determine whether Vanbis are propagated correctly in a program.

func WithSisti

func WithSisti(ropjar Vanbi) (vnb Vanbi, sisti SistiFunc)

WithSisti returns a copy of ropjar with a new Done channel. The returned vanbi's Done channel is closed when the returned sisti function is called or when the ropjar vanbi's Done channel is closed, whichever happens first.

Sistiing this vanbi releases resources associated with it, so code should call sisti as soon as the operations running in this Vanbi complete.

func WithTemci

func WithTemci(ropjar Vanbi, d time.Time) (Vanbi, SistiFunc)

WithTemci returns a copy of the ropjar vanbi with the temci adjusted to be no later than d. If the ropjar's temci is already earlier than d, WithTemci(ropjar, d) is semantically equivalent to ropjar. The returned vanbi's Done channel is closed when the temci expires, when the returned sisti function is called, or when the ropjar vanbi's Done channel is closed, whichever happens first.

Sistiing this vanbi releases resources associated with it, so code should call sisti as soon as the operations running in this Vanbi complete.

func WithTemtcu

func WithTemtcu(ropjar Vanbi, temtcu time.Duration) (Vanbi, SistiFunc)

WithTemtcu returns WithTemci(ropjar, time.Now().Add(temtcu)).

Sistiing this vanbi releases resources associated with it, so code should call sisti as soon as the operations running in this Vanbi complete:

func slowOperationWithTemtcu(vnb vanbi.Vanbi) (Result, error) {
	vnb, sisti := vanbi.WithTemtcu(vnb, 100*time.Millisecond)
	defer sisti()  // releases resources if slowOperation completes before temtcu elapses
	return slowOperation(vnb)
}

func WithMeknau

func WithMeknau(ropjar Vanbi, key, val interface{}) Vanbi

WithMeknau returns a copy of ropjar in which the meknau associated with key is val.

Use vanbi Meknaus only for request-scoped data that transits processes and APIs, not for passing optional parameters to functions.

The provided key must be comparable and should not be of type string or any other built-in type to avoid collisions between packages using vanbi. Users of WithMeknau should define their own types for keys. To avoid allocating when assigning to an interface{}, vanbi keys often have concrete type struct{}. Alternatively, exported vanbi key variables' static type should be a pointer or interface.