ipfs-cluster/rpcutil/rpcutil.go
Hector Sanjuan 6447ea51d2 Remove *Serial types. Use pointers for all types.
This takes advantange of the latest features in go-cid, peer.ID and
go-multiaddr and makes the Go types serializable by default.

This means we no longer need to copy between Pin <-> PinSerial, or ID <->
IDSerial etc. We can now efficiently binary-encode these types using short
field keys and without parsing/stringifying (in many cases it just a cast).

We still get the same json output as before (with minor modifications for
Cids).

This should greatly improve Cluster performance and memory usage when dealing
with large collections of items.

License: MIT
Signed-off-by: Hector Sanjuan <hector@protocol.ai>
2019-02-27 17:04:35 +00:00

153 lines
4.2 KiB
Go

// Package rpcutil provides utility methods to perform go-libp2p-gorpc calls,
// particularly gorpc.MultiCall().
package rpcutil
import (
"context"
"errors"
"fmt"
"time"
"github.com/ipfs/ipfs-cluster/api"
peer "github.com/libp2p/go-libp2p-peer"
)
// CtxsWithTimeout returns n contexts, derived from the given parent
// using the given timeout.
func CtxsWithTimeout(
parent context.Context,
n int,
timeout time.Duration,
) ([]context.Context, []context.CancelFunc) {
ctxs := make([]context.Context, n, n)
cancels := make([]context.CancelFunc, n, n)
for i := 0; i < n; i++ {
ctx, cancel := context.WithTimeout(parent, timeout)
ctxs[i] = ctx
cancels[i] = cancel
}
return ctxs, cancels
}
// CtxsWithCancel returns n cancellable contexts, derived from the given parent.
func CtxsWithCancel(
parent context.Context,
n int,
) ([]context.Context, []context.CancelFunc) {
ctxs := make([]context.Context, n, n)
cancels := make([]context.CancelFunc, n, n)
for i := 0; i < n; i++ {
ctx, cancel := context.WithCancel(parent)
ctxs[i] = ctx
cancels[i] = cancel
}
return ctxs, cancels
}
// MultiCancel calls all the provided CancelFuncs. It
// is useful with "defer Multicancel()"
func MultiCancel(cancels []context.CancelFunc) {
for _, cancel := range cancels {
cancel()
}
}
// The copy functions below are used in calls to Cluste.multiRPC()
// CopyPIDsToIfaces converts a peer.ID slice to an empty interface
// slice using pointers to each elements of the original slice.
// Useful to handle gorpc.MultiCall() replies.
func CopyPIDsToIfaces(in []peer.ID) []interface{} {
ifaces := make([]interface{}, len(in), len(in))
for i := range in {
ifaces[i] = &in[i]
}
return ifaces
}
// CopyIDsToIfaces converts an api.ID slice to an empty interface
// slice using pointers to each elements of the original slice.
// Useful to handle gorpc.MultiCall() replies.
func CopyIDsToIfaces(in []*api.ID) []interface{} {
ifaces := make([]interface{}, len(in), len(in))
for i := range in {
in[i] = &api.ID{}
ifaces[i] = in[i]
}
return ifaces
}
// CopyIDSliceToIfaces converts an api.ID slice of slices
// to an empty interface slice using pointers to each elements of the
// original slice. Useful to handle gorpc.MultiCall() replies.
func CopyIDSliceToIfaces(in [][]*api.ID) []interface{} {
ifaces := make([]interface{}, len(in), len(in))
for i := range in {
ifaces[i] = &in[i]
}
return ifaces
}
// CopyPinInfoToIfaces converts an api.PinInfo slice to
// an empty interface slice using pointers to each elements of
// the original slice. Useful to handle gorpc.MultiCall() replies.
func CopyPinInfoToIfaces(in []*api.PinInfo) []interface{} {
ifaces := make([]interface{}, len(in), len(in))
for i := range in {
in[i] = &api.PinInfo{}
ifaces[i] = in[i]
}
return ifaces
}
// CopyPinInfoSliceToIfaces converts an api.PinInfo slice of slices
// to an empty interface slice using pointers to each elements of the original
// slice. Useful to handle gorpc.MultiCall() replies.
func CopyPinInfoSliceToIfaces(in [][]*api.PinInfo) []interface{} {
ifaces := make([]interface{}, len(in), len(in))
for i := range in {
ifaces[i] = &in[i]
}
return ifaces
}
// CopyEmptyStructToIfaces converts an empty struct slice to an empty interface
// slice using pointers to each elements of the original slice.
// Useful to handle gorpc.MultiCall() replies.
func CopyEmptyStructToIfaces(in []struct{}) []interface{} {
ifaces := make([]interface{}, len(in), len(in))
for i := range in {
ifaces[i] = &in[i]
}
return ifaces
}
// RPCDiscardReplies returns a []interface{} slice made from a []struct{}
// slice of then given length. Useful for RPC methods which have no response
// types (so they use empty structs).
func RPCDiscardReplies(n int) []interface{} {
replies := make([]struct{}, n, n)
return CopyEmptyStructToIfaces(replies)
}
// CheckErrs returns nil if all the errors in a slice are nil, otherwise
// it returns a single error formed by joining the error messages existing
// in the slice with a line-break.
func CheckErrs(errs []error) error {
errMsg := ""
for _, e := range errs {
if e != nil {
errMsg += fmt.Sprintf("%s\n", e.Error())
}
}
if len(errMsg) > 0 {
return errors.New(errMsg)
}
return nil
}