ipfs-cluster/state/mapstate/migrate.go
Hector Sanjuan 4549282cba Fix #277: Introduce maximum and minimum replication factor
This PR replaces ReplicationFactor with ReplicationFactorMax
and ReplicationFactor min.

This allows a CID to be pinned even though the desired
replication factor (max) is not reached, and prevents triggering
re-pinnings when the replication factor has not crossed the
lower threshold (min).

License: MIT
Signed-off-by: Hector Sanjuan <code@hector.link>
2018-01-16 16:36:06 +01:00

135 lines
2.9 KiB
Go

package mapstate
// To add a new state format
// - implement the previous format's "next" function to the new format
// - implement the new format's unmarshal function
// - add a case to the switch statement for the previous format version
// - update the code copying the from mapStateVx to mapState
import (
"bytes"
"errors"
msgpack "github.com/multiformats/go-multicodec/msgpack"
"github.com/ipfs/ipfs-cluster/api"
)
// Instances of migrateable can be read from a serialized format and migrated
// to other state formats
type migrateable interface {
next() migrateable
unmarshal([]byte) error
}
type mapStateV1 struct {
Version int
PinMap map[string]struct{}
}
// Unmarshal the serialization of a v1 state
func (st *mapStateV1) unmarshal(bs []byte) error {
buf := bytes.NewBuffer(bs)
dec := msgpack.Multicodec(msgpack.DefaultMsgpackHandle()).Decoder(buf)
return dec.Decode(st)
}
// Migrate from v1 to v2
func (st *mapStateV1) next() migrateable {
var mst2 mapStateV2
mst2.PinMap = make(map[string]pinSerialV2)
for k := range st.PinMap {
mst2.PinMap[k] = pinSerialV2{
Cid: k,
Allocations: []string{},
ReplicationFactor: -1,
}
}
return &mst2
}
type pinSerialV2 struct {
Cid string `json:"cid"`
Name string `json:"name"`
Allocations []string `json:"allocations"`
ReplicationFactor int `json:"replication_factor"`
}
type mapStateV2 struct {
PinMap map[string]pinSerialV2
Version int
}
func (st *mapStateV2) unmarshal(bs []byte) error {
buf := bytes.NewBuffer(bs)
dec := msgpack.Multicodec(msgpack.DefaultMsgpackHandle()).Decoder(buf)
return dec.Decode(st)
}
func (st *mapStateV2) next() migrateable {
var mst3 mapStateV3
mst3.PinMap = make(map[string]api.PinSerial)
for k, v := range st.PinMap {
mst3.PinMap[k] = api.PinSerial{
Cid: v.Cid,
Name: v.Name,
Allocations: v.Allocations,
ReplicationFactorMin: v.ReplicationFactor,
ReplicationFactorMax: v.ReplicationFactor,
}
}
return &mst3
}
type mapStateV3 struct {
PinMap map[string]api.PinSerial
Version int
}
func (st *mapStateV3) unmarshal(bs []byte) error {
buf := bytes.NewBuffer(bs)
dec := msgpack.Multicodec(msgpack.DefaultMsgpackHandle()).Decoder(buf)
return dec.Decode(st)
}
func (st *mapStateV3) next() migrateable {
return nil
}
func finalCopy(st *MapState, internal *mapStateV3) {
for k, v := range internal.PinMap {
st.PinMap[k] = v
}
}
func (st *MapState) migrateFrom(version int, snap []byte) error {
var m, next migrateable
switch version {
case 1:
var mst1 mapStateV1
m = &mst1
case 2:
var mst2 mapStateV2
m = &mst2
default:
return errors.New("version migration not supported")
}
err := m.unmarshal(snap)
if err != nil {
return err
}
for {
next = m.next()
if next == nil {
mst3, ok := m.(*mapStateV3)
if !ok {
return errors.New("migration ended prematurely")
}
finalCopy(st, mst3)
return nil
}
m = next
}
}