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

260 lines
8.8 KiB
Markdown

---
title: vanbi
date: 2019-01-08
thanks: 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
```go
var Sistied = errors.New("vanbi sistied")
```
Sistied is the error returned by Vanbi.Err when the vanbi is sistied.
```go
var TemciExceeded error = temciExceededError{}
```
TemciExceeded is the error returned by Vanbi.Err when the vanbi's
temci passes.
#### type SistiFunc
```go
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
```go
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
```go
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
```go
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
```go
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
```go
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
```go
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
```go
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.