blog: lo vanbi
This commit is contained in:
parent
72a4bebd7f
commit
cba841a213
|
@ -0,0 +1,259 @@
|
|||
---
|
||||
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.
|
Loading…
Reference in New Issue