Conflict resolution in Android


#1

We are using an Android App which uses following version of couchbase.

compile 'com.couchbase.lite:couchbase-lite-android:1.3.1’
compile ‘com.couchbase.lite:couchbase-lite-android-forestdb:1.3.1’

We are using database.addChangeListener(mListener); to detect the changes being performed in database documents.

mListener = new Database.ChangeListener() {
@Override
public void changed(Database.ChangeEvent event) {
List changes = event.getChanges();
if (changes != null && changes.size() > 0) {
for (int i = 0; i < changes.size(); i++) {
DocumentChange documentChange = changes.get(i);

            if (documentChange.isConflict()) {
              
                try {
                    Document document = mDatabase.getExistingDocument(documentChange.getDocumentId());
                    fixConflicts(document);
                } catch (CouchbaseLiteException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

};

in fixConflicts() method we are doing something following:
get Document which is Changed.
get changed Document’s ConflictedRevisions
getAll the properties from ChangedDocument and store it temporary
Now loop the all conflicted revisions and compare the each property(map keys) with our temporary stored document’s properties(map keys)
Insert the property from conflicted document to temporary stored document if it is missing in temporary stored document
Now we follow this logic for each conflicted documents.
And after getting final properties(missing keys) we create a new version of Document and remove all conflicted versions.

ISSUE: When I post from web and mobile then on refresh after sometime initially only mobile post get displayed then on refresh after sometime only web post get displayed and mobile post get removed.
And some times both post get displayed.
When we compare properties of current version’s and conflicted version’s document, both get same properties.
So we are unable to add missing property on Android side.


#2

HI @abhay_naik,

I don’t know where the problems are. If you could share the fixConflicts() method, we might be able to identify it.

Thanks,
Hideki


#3

@hideki Thanks for your interest. Here is my fix Confliction code.

Map<String, Object> main;
Map<String, BinResTLH> timelineHomeList;

private void fixConflicts(final Document documentTL_Home) throws CouchbaseLiteException
    {

        mDatabase.runInTransaction(new TransactionalTask()
        {
            @Override
            public boolean run() {
                try {
                    List<SavedRevision> conflicted_RevisionArrayList_home = documentTL_Home.getConflictingRevisions();
                    Map<String, Object> mergedProps = resolveConflictForPostDocument(documentTL_Home, conflicted_RevisionArrayList_home);
                    SavedRevision current = documentTL_Home.getCurrentRevision();
                    for (SavedRevision rev : conflicted_RevisionArrayList_home)
                    {
                        UnsavedRevision newRev = rev.createRevision();
                        if (rev.getId().equals(current.getId()))
                        {
                            newRev.setProperties(mergedProps);
                        }
                        else
                        {
                            newRev.setIsDeletion(true);
                        }
                        newRev.save(true);
                    }
                } catch (CouchbaseLiteException e) {
                    return false;
                }
                return true;
            }
        });
    }

public Map<String, Object> resolveConflictForPostDocument(Document documentTL, List<SavedRevision> conflicted_RevisionArrayList_home_profile)
    {
        if (documentTL != null)
        {
            main = documentTL.getProperties();

            Map<String, Object> temp = new HashMap<>(main);

            // Original TL document
            timelineHomeList = documentOperationMainTimeline.getUserHomeTimelinePostMap(documentTL.getId());
            for (SavedRevision conflictedRevision : conflicted_RevisionArrayList_home_profile)
            {
                HashMap<String, BinResTLH> mMapHome = (HashMap<String, BinResTLH>) documentOperationMainTimeline.getUserHomeTimelinePostMapS(conflictedRevision.getDocument());

                ArrayList<String> keysUUID = new ArrayList<>(mMapHome.keySet());
                for (String key : keysUUID) {
                    Log.e("log_conflict", "key => " + key);
                    if (!timelineHomeList.containsKey(key))
                    {
                        Log.e("log_conflict", "key not exist in our doc");
                        // add this missing(conflicted) pojo to original TL_ doc's map
                        timelineHomeList.put(key, mMapHome.get(key));
                    }
                    else
                    {
                        Log.e("log_conflict", "key exists in our doc, skip");
                    }
                }
            }


            temp.put("timeline", timelineHomeList);

            return temp;
        }
        return null;
    }

#4

Hi @abhay_naik,
Your conflict resolution code looks fine. From code review, I can not find the issue.