Documentation
¶
Overview ¶
Package faultbackend wraps a backend.Backend so a test can make it misbehave: fail chosen operations, and suspend one operation until another reaches an agreed point.
The second capability is the reason the package exists. A distributed-storage bug is usually an interleaving, not an error path, and reproducing one with sleeps yields a flaky test that proves nothing on a loaded CI machine. A Gate blocks the matching operation inside the backend until the test releases it, so the interleaving is stated in the test rather than raced for.
Index ¶
- type Backend
- func (b *Backend) Add(r Rule) *Backend
- func (b *Backend) CompareAndSwap(ctx context.Context, key string, expected backend.Version, data []byte) (backend.Version, bool, error)
- func (b *Backend) Count(match func(Op) bool) int
- func (b *Backend) Delete(ctx context.Context, key string) error
- func (b *Backend) List(ctx context.Context, prefix string) ([]string, error)
- func (b *Backend) Ops() []Op
- func (b *Backend) PutIfAbsent(ctx context.Context, key string, data []byte) (bool, error)
- func (b *Backend) Read(ctx context.Context, key string) ([]byte, error)
- func (b *Backend) ReadVersioned(ctx context.Context, key string) ([]byte, backend.Version, error)
- func (b *Backend) Reset()
- func (b *Backend) Write(ctx context.Context, key string, data []byte) error
- type Gate
- type Kind
- type Op
- type Rule
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Backend ¶
Backend is a backend.Backend that applies [Rule]s to the operations passing through it, and records them. The zero value is not usable; call Wrap.
It deliberately forwards none of the optional backend capabilities (backend.Viewer, backend.Sizer, ReaderAt, ObjectCreator): every one of them has a mandatory fallback, so a wrapped backend exercises the same code as an unwrapped one, only slower.
func (*Backend) Add ¶
Add installs a rule. Rules are consulted in the order they were added, and the first one to match an operation decides it.
func (*Backend) CompareAndSwap ¶
func (b *Backend) CompareAndSwap( ctx context.Context, key string, expected backend.Version, data []byte, ) (backend.Version, bool, error)
CompareAndSwap implements backend.Backend. Gating it is how a test states the interleaving the commit protocol turns on: one writer suspended inside its conditional write while another commits over it.
func (*Backend) Delete ¶
Delete implements backend.Backend.
func (*Backend) List ¶
List implements backend.Backend.
func (*Backend) PutIfAbsent ¶
PutIfAbsent implements backend.Backend.
func (*Backend) Read ¶
Read implements backend.Backend.
func (*Backend) ReadVersioned ¶
ReadVersioned implements backend.Backend.
type Gate ¶
type Gate struct {
// contains filtered or unexported fields
}
Gate suspends the backend operations matching it until the test releases them, so a test can state an interleaving instead of racing for it: arrange for the operation to arrive, drive the other goroutine to the point of interest, then release.
A gated operation blocks the goroutine that issued it inside the backend, so the code under test needs no hooks of its own.
func (*Gate) Await ¶
Await blocks until an operation is suspended at the gate and returns it, failing the test if none arrives.
type Kind ¶
type Kind int
Kind is the backend operation a Rule matches.
The backend operations a rule can match.
type Rule ¶
type Rule struct {
Kind Kind
Match func(Op) bool
// Err, when non-nil, is returned instead of performing the operation.
Err error
// Before, when non-nil, runs before the operation. It may block, which is what suspends the
// calling goroutine inside the backend (see [Gate]).
Before func(Op)
// Replace, when non-nil, rewrites the bytes a read returns. It models the failure a returned
// error cannot: a store that hands back data which is not what was written, and says nothing.
// It applies to [Read] alone; the wrapper implements no [backend.Viewer], so every read of an
// object's bytes — including one made through [backend.ReadView] — passes through it.
Replace func(Op, []byte) []byte
// Times limits how many operations the rule applies to. Zero ⇒ unlimited.
Times int
// contains filtered or unexported fields
}
Rule decides what happens to the operations it matches. A rule with no Match matches every operation of its Kind.