diff --git a/blog/vanbi-01-08-2019.markdown b/blog/vanbi-01-08-2019.markdown new file mode 100644 index 0000000..212bd34 --- /dev/null +++ b/blog/vanbi-01-08-2019.markdown @@ -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.