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