mirror of https://github.com/ceph/go-ceph
rados: add generic operation type for basis of data handling in op types
Signed-off-by: John Mulligan <jmulligan@redhat.com>
This commit is contained in:
parent
29ebb61850
commit
53d2d83bc4
|
@ -0,0 +1,127 @@
|
|||
package rados
|
||||
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// The file operation.go exists to support both read op and write op types that
|
||||
// have some pretty common behaviors between them. In C/C++ its assumed that
|
||||
// the buffer types and other pointers will not be freed between passing them
|
||||
// to the action setup calls (things like rados_write_op_write or
|
||||
// rados_read_op_omap_get_vals2) and the call to Operate(...). Since there's
|
||||
// nothing stopping one from sleeping for hours between these calls, or passing
|
||||
// the op to other functions and calling Operate there, we want a mechanism
|
||||
// that's (fairly) simple to understand and won't run afoul of Go's garbage
|
||||
// collection. That's one reason the operation type tracks the steps (the
|
||||
// parts that track complex inputs and outputs) so that as long as the op
|
||||
// exists it will have a reference to the step, which will have references
|
||||
// to the C language types.
|
||||
|
||||
type opKind string
|
||||
|
||||
const (
|
||||
readOp opKind = "read"
|
||||
writeOp opKind = "write"
|
||||
)
|
||||
|
||||
// OperationError is an error type that may be returned by an Operate call.
|
||||
// It captures the error from the operate call itself and any errors from
|
||||
// steps that can return an error.
|
||||
type OperationError struct {
|
||||
kind opKind
|
||||
OpError error
|
||||
StepErrors map[int]error
|
||||
}
|
||||
|
||||
func (e OperationError) Error() string {
|
||||
subErrors := []string{}
|
||||
if e.OpError != nil {
|
||||
subErrors = append(subErrors,
|
||||
fmt.Sprintf("op=%s", e.OpError))
|
||||
}
|
||||
for idx, es := range e.StepErrors {
|
||||
subErrors = append(subErrors,
|
||||
fmt.Sprintf("Step#%d=%s", idx, es))
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
"%s operation error: %s",
|
||||
e.kind,
|
||||
strings.Join(subErrors, ", "))
|
||||
}
|
||||
|
||||
// opStep provides an interface for types that are tied to the management of
|
||||
// data being input or output from write ops and read ops. The steps are
|
||||
// meant to simplify the internals of the ops themselves and be exportable when
|
||||
// appropriate. If a step is not being exported it should not be returned
|
||||
// from an ops action function. If the step is exported it should be
|
||||
// returned from an ops action function.
|
||||
//
|
||||
// Not all types implementing opStep are expected to need all the functions
|
||||
// in the interface. However, for the sake of simplicity on the op side, we use
|
||||
// the same interface for all cases and expect those implementing opStep
|
||||
// just embed the without* types that provide no-op implementation of
|
||||
// functions that make up this interface.
|
||||
type opStep interface {
|
||||
// update the state of the step after the call to Operate.
|
||||
// It can be used to convert values from C and cache them and/or
|
||||
// communicate a failure of the action associated with the step. The
|
||||
// update call will only be made once. Implementations are not required to
|
||||
// handle this call being made more than once.
|
||||
update() error
|
||||
// free will be called to free any resources, especially C memory, that
|
||||
// the step is managing. The behavior of free should be idempotent and
|
||||
// handle being called more than once.
|
||||
free()
|
||||
}
|
||||
|
||||
// operation represents some of the shared underlying mechanisms for
|
||||
// both read and write op types.
|
||||
type operation struct {
|
||||
steps []opStep
|
||||
}
|
||||
|
||||
// free will call the free method of all the steps this operation
|
||||
// contains.
|
||||
func (o *operation) free() {
|
||||
for i := range o.steps {
|
||||
o.steps[i].free()
|
||||
}
|
||||
}
|
||||
|
||||
// update the operation and the steps it contains. The top-level result
|
||||
// of the rados call is passed in as ret and used to construct errors.
|
||||
// The update call of each step is used to update the contents of each
|
||||
// step and gather any errors from those steps.
|
||||
func (o *operation) update(kind opKind, ret C.int) error {
|
||||
stepErrors := map[int]error{}
|
||||
for i := range o.steps {
|
||||
if err := o.steps[i].update(); err != nil {
|
||||
stepErrors[i] = err
|
||||
}
|
||||
}
|
||||
if ret == 0 && len(stepErrors) == 0 {
|
||||
return nil
|
||||
}
|
||||
return OperationError{
|
||||
kind: kind,
|
||||
OpError: getError(ret),
|
||||
StepErrors: stepErrors,
|
||||
}
|
||||
}
|
||||
|
||||
// withoutUpdate can be embedded in a struct to help indicate
|
||||
// the type implements the opStep interface but has a no-op
|
||||
// update function.
|
||||
type withoutUpdate struct{}
|
||||
|
||||
func (*withoutUpdate) update() error { return nil }
|
||||
|
||||
// withoutFree can be embedded in a struct to help indicate
|
||||
// the type implements the opStep interface but has a no-op
|
||||
// free function.
|
||||
type withoutFree struct{}
|
||||
|
||||
func (*withoutFree) free() {}
|
Loading…
Reference in New Issue