Skip to content

Duplicate-check 409 assumes the colliding check is not archived #488

Description

@prestoncabe

Summary

The DocumentAlreadyExistsException branch of createCustomCheck always reports the collision as a non-archived check, even though it can be reached when the existing check is archived. The user is then told they already have a check that does not appear anywhere in their list.

Details

createCustomCheck first looks the check up and passes the real flag (EligibilityCheckResource.java:91):

return duplicateCheckResponse(request, existingCheck.get().getIsArchived());

but the write-collision branch hardcodes it (EligibilityCheckResource.java:100):

} catch (DocumentAlreadyExistsException e) {
    Log.info("Check " + checkId + " was created concurrently");
    return duplicateCheckResponse(request, false);
}

For a genuine two-writer race false is right — a just-created check is not archived. The problem is that the branch is also reachable without a race, because the pre-check silently swallows read failures. getWorkingCustomCheckgetCustomCheck (EligibilityCheckRepositoryImpl.java:141) calls FirestoreUtils.getFirestoreDocById, which catches every exception and returns Optional.empty() (FirestoreUtils.java:158):

}catch(Exception e){
    Log.error("Error fetching document from firestore: ", e);
    return Optional.empty();
}

So any transient read error — deadline exceeded, a blip, an auth hiccup — is indistinguishable from "no such check". The create then fails ALREADY_EXISTS against an existing archived document and the user gets:

You already have a check named "X" in module "Y".

while that check is filtered out of their list (EligibilityCheckRepositoryImpl.java:34) and so cannot be found.

Possible directions

  • re-read the document inside the catch and use its actual isArchived value
  • or have the pre-check distinguish "not found" from "read failed", the way getFirestoreDocsByFields already does, and return a 503 instead of guessing
  • or make the collision message status-neutral so it never asserts a state that was not checked

Related to #484 and to the restore gap it exposes.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    • Status
      No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions