2017-03-10 16:24:25 +00:00
|
|
|
package raft
|
2017-02-13 15:46:53 +00:00
|
|
|
|
|
|
|
import (
|
2018-06-27 04:03:15 +00:00
|
|
|
"context"
|
2017-02-13 15:46:53 +00:00
|
|
|
"errors"
|
|
|
|
|
2018-06-27 04:03:15 +00:00
|
|
|
"go.opencensus.io/tag"
|
|
|
|
"go.opencensus.io/trace"
|
|
|
|
|
2017-02-13 15:46:53 +00:00
|
|
|
"github.com/ipfs/ipfs-cluster/api"
|
2017-03-10 16:24:25 +00:00
|
|
|
"github.com/ipfs/ipfs-cluster/state"
|
2017-02-13 15:46:53 +00:00
|
|
|
|
|
|
|
consensus "github.com/libp2p/go-libp2p-consensus"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Type of consensus operation
|
|
|
|
const (
|
|
|
|
LogOpPin = iota + 1
|
|
|
|
LogOpUnpin
|
|
|
|
)
|
|
|
|
|
|
|
|
// LogOpType expresses the type of a consensus Operation
|
|
|
|
type LogOpType int
|
|
|
|
|
|
|
|
// LogOp represents an operation for the OpLogConsensus system.
|
|
|
|
// It implements the consensus.Op interface and it is used by the
|
|
|
|
// Consensus component.
|
|
|
|
type LogOp struct {
|
2019-02-27 18:43:29 +00:00
|
|
|
SpanCtx trace.SpanContext `codec:"s,omitempty"`
|
|
|
|
TagCtx []byte `codec:"t,omitempty"`
|
|
|
|
Cid *api.Pin `codec:"c,omitempty"`
|
|
|
|
Type LogOpType `codec:"p,omitempty"`
|
2019-02-27 18:50:46 +00:00
|
|
|
consensus *Consensus `codec:"-"`
|
|
|
|
tracing bool `codec:"-"`
|
2017-02-13 15:46:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ApplyTo applies the operation to the State
|
|
|
|
func (op *LogOp) ApplyTo(cstate consensus.State) (consensus.State, error) {
|
|
|
|
var err error
|
2018-06-27 04:03:15 +00:00
|
|
|
ctx := context.Background()
|
|
|
|
if op.tracing {
|
|
|
|
tagmap, err := tag.Decode(op.TagCtx)
|
|
|
|
if err != nil {
|
|
|
|
logger.Error(err)
|
|
|
|
}
|
|
|
|
ctx = tag.NewContext(ctx, tagmap)
|
|
|
|
var span *trace.Span
|
|
|
|
ctx, span = trace.StartSpanWithRemoteParent(ctx, "consensus/raft/logop/ApplyTo", op.SpanCtx)
|
|
|
|
defer span.End()
|
|
|
|
}
|
|
|
|
|
|
|
|
state, ok := cstate.(state.State)
|
2017-02-13 15:46:53 +00:00
|
|
|
if !ok {
|
|
|
|
// Should never be here
|
|
|
|
panic("received unexpected state type")
|
|
|
|
}
|
|
|
|
|
2019-02-27 17:04:35 +00:00
|
|
|
pin := op.Cid
|
|
|
|
// We are about to pass "pin" it to go-routines that will make things
|
|
|
|
// with it (read its fields). However, as soon as ApplyTo is done, the
|
|
|
|
// next operation will be deserealized on top of "op". We nullify it
|
|
|
|
// to make sure no data races occur.
|
|
|
|
op.Cid = nil
|
2018-10-26 22:40:06 +00:00
|
|
|
|
2017-02-13 15:46:53 +00:00
|
|
|
switch op.Type {
|
|
|
|
case LogOpPin:
|
2019-02-27 17:04:35 +00:00
|
|
|
err = state.Add(ctx, pin)
|
2017-02-13 15:46:53 +00:00
|
|
|
if err != nil {
|
2019-02-19 17:59:46 +00:00
|
|
|
logger.Error(err)
|
2017-02-13 15:46:53 +00:00
|
|
|
goto ROLLBACK
|
|
|
|
}
|
|
|
|
// Async, we let the PinTracker take care of any problems
|
2018-06-27 04:03:15 +00:00
|
|
|
op.consensus.rpcClient.GoContext(
|
|
|
|
ctx,
|
2018-10-30 02:02:58 +00:00
|
|
|
"",
|
2017-02-13 15:46:53 +00:00
|
|
|
"Cluster",
|
|
|
|
"Track",
|
2019-02-27 17:04:35 +00:00
|
|
|
pin,
|
2017-02-13 15:46:53 +00:00
|
|
|
&struct{}{},
|
2018-10-30 02:02:58 +00:00
|
|
|
nil,
|
|
|
|
)
|
2017-02-13 15:46:53 +00:00
|
|
|
case LogOpUnpin:
|
2019-02-27 17:04:35 +00:00
|
|
|
err = state.Rm(ctx, pin.Cid)
|
2017-02-13 15:46:53 +00:00
|
|
|
if err != nil {
|
2019-02-19 17:59:46 +00:00
|
|
|
logger.Error(err)
|
2017-02-13 15:46:53 +00:00
|
|
|
goto ROLLBACK
|
|
|
|
}
|
|
|
|
// Async, we let the PinTracker take care of any problems
|
2018-06-27 04:03:15 +00:00
|
|
|
op.consensus.rpcClient.GoContext(
|
|
|
|
ctx,
|
2018-10-30 02:02:58 +00:00
|
|
|
"",
|
2017-02-13 15:46:53 +00:00
|
|
|
"Cluster",
|
|
|
|
"Untrack",
|
2019-02-27 18:43:29 +00:00
|
|
|
pin,
|
2017-02-13 15:46:53 +00:00
|
|
|
&struct{}{},
|
2018-10-30 02:02:58 +00:00
|
|
|
nil,
|
|
|
|
)
|
2017-02-13 15:46:53 +00:00
|
|
|
default:
|
|
|
|
logger.Error("unknown LogOp type. Ignoring")
|
|
|
|
}
|
|
|
|
return state, nil
|
|
|
|
|
|
|
|
ROLLBACK:
|
|
|
|
// We failed to apply the operation to the state
|
|
|
|
// and therefore we need to request a rollback to the
|
|
|
|
// cluster to the previous state. This operation can only be performed
|
|
|
|
// by the cluster leader.
|
|
|
|
logger.Error("Rollbacks are not implemented")
|
|
|
|
return nil, errors.New("a rollback may be necessary. Reason: " + err.Error())
|
|
|
|
}
|