daw
daw

Reputation: 2049

How to sync conflicting changes with Google Drive API

I have a google drive app which will auto-save changes. If you have two active sessions then they will overwrite each other. The app supports merging changes but I can't see how to safely integrate this with the drive API. Some options I have considered:

  1. Version safe commits

    • Use google drive to "only update if current revision in drive == X otherwise fail"
    • If failed then fetch latest version, merge and retry

    • problem: I don't think drive supports this. Previous API versions used etags but I see no mention of this in the current documenation.

  2. Pre-commit check

    • check current saved version, if still current, save
    • otherwise download, merge and update

    • problem: obvious race condition between clients

  3. Post-commit check

    • save new version
    • if new version is as expected: done
    • if new version higher than expected: download previous version, merge and update

    • problem: I don't have much faith this is safe. I can see multiple clients getting in edit loops.

  4. Google real-time api - field binding

    • replace file format with a google rt datamodel

    • problem: It would require redesigning just for google-rt

  5. Google real-time api - document support

    • use google rt api external document support

    • problem: I don't think this solves the problem

I would really like a way to achieve #1 but any suggestions would be helpful. I would be happy enough with a basic locking / handover scheme between clients but I don't think Drive supports that either.

Upvotes: 2

Views: 1232

Answers (2)

devconsole
devconsole

Reputation: 7915

To follow up on user1828559's answer, the following Java code seems to work well:

private File updateDriveFile(Drive drive, File file, byte[] data) throws IOException {
    try {
        ByteArrayContent mediaContent = new ByteArrayContent(MIME_TYPE, data);
        Drive.Files.Update update = drive.files().update(file.getId(), file, mediaContent);

        update.getRequestHeaders().setIfMatch(file.getEtag());

        return update.execute();
    }
    catch (GoogleJsonResponseException e) {
        if (isConflictError(e.getDetails())) {
            logger.warn("ETag precondition failed, concurrent modification detected!");
            return null;
        }

        throw e;
    }
}

private boolean isConflictError(GoogleJsonError error) {
    if (error.getCode() == 412) {
        final List<GoogleJsonError.ErrorInfo> errors = error.getErrors();
        if (errors != null && errors.size() == 1) {
            final GoogleJsonError.ErrorInfo errorInfo = errors.get(0);
            if ("header".equals(errorInfo.getLocationType()) &&
                    "If-Match".equals(errorInfo.getLocation()) &&
                    "conditionNotMet".equals(errorInfo.getReason()))
                return true;
        }
    }

    return false;
}

Upvotes: 0

user1828559
user1828559

Reputation: 241

According to here, "If-Match" using etags still works. Not sure if it applies to data, but at least it applies to metadata.

Upvotes: 1

Related Questions