2019-02-20 20:54:46 +00:00
|
|
|
package gun
|
|
|
|
|
|
|
|
import (
|
2019-02-25 20:09:47 +00:00
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
2019-02-20 20:54:46 +00:00
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2019-02-26 21:59:44 +00:00
|
|
|
// State represents the conflict state of this value. It is usually the Unix
|
|
|
|
// time in milliseconds.
|
|
|
|
type State float64 // TODO: what if larger?
|
2019-02-25 05:14:26 +00:00
|
|
|
|
2019-02-26 21:59:44 +00:00
|
|
|
// StateNow is the current machine state (i.e. current Unix time in ms).
|
|
|
|
func StateNow() State {
|
|
|
|
// TODO: Should I use timeNowUniqueUnix or otherwise set decimal spots to disambiguate?
|
|
|
|
return State(timeNowUnixMs())
|
|
|
|
}
|
2019-02-25 05:14:26 +00:00
|
|
|
|
2019-02-26 21:59:44 +00:00
|
|
|
// StateFromTime converts a time to a State (i.e. converts to Unix ms).
|
2019-02-25 20:09:47 +00:00
|
|
|
func StateFromTime(t time.Time) State { return State(timeToUnixMs(t)) }
|
2019-02-20 20:54:46 +00:00
|
|
|
|
2019-02-26 21:59:44 +00:00
|
|
|
// ConflictResolution is how to handle two values for the same field.
|
2019-02-25 20:09:47 +00:00
|
|
|
type ConflictResolution int
|
|
|
|
|
|
|
|
const (
|
2019-02-26 21:59:44 +00:00
|
|
|
// ConflictResolutionNeverSeenUpdate occurs when there is no existing value.
|
|
|
|
// It means an update should always occur.
|
2019-02-25 20:09:47 +00:00
|
|
|
ConflictResolutionNeverSeenUpdate ConflictResolution = iota
|
2019-02-26 21:59:44 +00:00
|
|
|
// ConflictResolutionTooFutureDeferred occurs when the update is after our
|
|
|
|
// current machine state. It means the update should be deferred.
|
2019-02-25 20:09:47 +00:00
|
|
|
ConflictResolutionTooFutureDeferred
|
2019-02-26 21:59:44 +00:00
|
|
|
// ConflictResolutionOlderHistorical occurs when the update happened before
|
|
|
|
// the existing value's last update. It means it can be noted, but the
|
|
|
|
// update should be discarded.
|
2019-02-25 20:09:47 +00:00
|
|
|
ConflictResolutionOlderHistorical
|
2019-02-26 21:59:44 +00:00
|
|
|
// ConflictResolutionNewerUpdate occurs when the update happened after last
|
|
|
|
// update but is not beyond ur current machine state. It means the update
|
|
|
|
// should overwrite.
|
2019-02-25 20:09:47 +00:00
|
|
|
ConflictResolutionNewerUpdate
|
2019-02-26 21:59:44 +00:00
|
|
|
// ConflictResolutionSameKeep occurs when the update happened at the same
|
|
|
|
// time and it is lexically not the one chosen. It means the update should
|
|
|
|
// be discarded.
|
2019-02-25 20:09:47 +00:00
|
|
|
ConflictResolutionSameKeep
|
2019-02-26 21:59:44 +00:00
|
|
|
// ConflictResolutionSameUpdate occurs when the update happened at the same
|
|
|
|
// time and it is lexically the one chosen. It means the update should
|
|
|
|
// overwrite.
|
2019-02-25 20:09:47 +00:00
|
|
|
ConflictResolutionSameUpdate
|
|
|
|
)
|
2019-02-20 20:54:46 +00:00
|
|
|
|
2019-02-26 21:59:44 +00:00
|
|
|
// IsImmediateUpdate returns true for ConflictResolutionNeverSeenUpdate,
|
|
|
|
// ConflictResolutionNewerUpdate, and ConflictResolutionSameUpdate
|
2019-02-25 20:09:47 +00:00
|
|
|
func (c ConflictResolution) IsImmediateUpdate() bool {
|
|
|
|
return c == ConflictResolutionNeverSeenUpdate || c == ConflictResolutionNewerUpdate || c == ConflictResolutionSameUpdate
|
2019-02-20 20:54:46 +00:00
|
|
|
}
|
|
|
|
|
2019-02-26 21:59:44 +00:00
|
|
|
// ConflictResolve checks the existing val/state, new val/state, and the current
|
|
|
|
// machine state to choose what to do with the update. Note, the existing val
|
|
|
|
// should always exist meaning it will never return
|
|
|
|
// ConflictResolutionNeverSeenUpdate.
|
2019-02-25 20:09:47 +00:00
|
|
|
func ConflictResolve(existingVal Value, existingState State, newVal Value, newState State, sysState State) ConflictResolution {
|
|
|
|
// Existing gunjs impl serializes to JSON first to do lexical comparisons, so we will too
|
|
|
|
if sysState < newState {
|
|
|
|
return ConflictResolutionTooFutureDeferred
|
|
|
|
} else if newState < existingState {
|
|
|
|
return ConflictResolutionOlderHistorical
|
|
|
|
} else if existingState < newState {
|
|
|
|
return ConflictResolutionNewerUpdate
|
|
|
|
} else if existingVal == newVal {
|
|
|
|
return ConflictResolutionSameKeep
|
|
|
|
} else if existingJSON, err := json.Marshal(existingVal); err != nil {
|
|
|
|
panic(err)
|
|
|
|
} else if newJSON, err := json.Marshal(newVal); err != nil {
|
|
|
|
panic(err)
|
|
|
|
} else if bytes.Compare(existingJSON, newJSON) < 0 {
|
|
|
|
return ConflictResolutionSameUpdate
|
|
|
|
} else {
|
|
|
|
return ConflictResolutionSameKeep
|
2019-02-20 20:54:46 +00:00
|
|
|
}
|
|
|
|
}
|