blob: 1c15609ba60cd4b51220687b05e5ef664f666aff [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package vsync
import (
// resolveViaTimestamp takes a map of updated objects and resolves each object
// independently based on write timestamps.
// NOTE: if an object is a part of the input map but not under conflict, it is
// ignored.
func (iSt *initiationState) resolveViaTimestamp(ctx *context.T, objConfMap map[string]*objConflictState) error {
for obj, conflictState := range objConfMap {
if !conflictState.isConflict || conflictState.res != nil {
var err error
conflictState.res, err = resolveObjConflict(ctx, iSt, obj, conflictState.oldHead, conflictState.newHead, conflictState.ancestor)
if err != nil {
return err
return nil
// resolveObjConflict resolves a conflict for an object given its ID and the 3
// versions that express the conflict: the object's local version, its remote
// version (from the device contacted), and the closest common ancestor (see
// dag.go on how the ancestor is chosen). The function resolves conflicts using
// the timestamps of the conflicting mutations. It picks a mutation with the
// larger timestamp, i.e. the most recent update. If the timestamps are equal,
// mutation version numbers as a tie-breaker, picking the mutation with the
// it uses the larger version. Instead of creating a new version that resolves
// the conflict, we pick an existing version as the conflict resolution.
func resolveObjConflict(ctx *context.T, iSt *initiationState, oid, local, remote, ancestor string) (*conflictResolution, error) {
// Fetch the log records of the 3 object versions.
versions := []string{local, remote}
lrecs, err := iSt.getLogRecsBatch(ctx, oid, versions)
if err != nil {
return nil, err
// The local and remote records must exist.
locRec, remRec := lrecs[0], lrecs[1]
if locRec == nil || remRec == nil {
vlog.Fatalf("sync: resolveObjConflict: oid %s: invalid local (%s: %v) or remote recs (%s: %v)",
oid, local, locRec, remote, remRec)
var res conflictResolution
res.batchId = NoBatchId
switch {
case locRec.Metadata.UpdTime.After(remRec.Metadata.UpdTime):
res.ty = pickLocal
case locRec.Metadata.UpdTime.Before(remRec.Metadata.UpdTime):
res.ty = pickRemote
case locRec.Metadata.CurVers > remRec.Metadata.CurVers:
res.ty = pickLocal
case locRec.Metadata.CurVers < remRec.Metadata.CurVers:
res.ty = pickRemote
vlog.Fatalf("sync: resolveObjConflictByTime: local and remote update times and versions are the same, local %v remote %v", local, remote)
return &res, nil