MLE-29883 (GH #1938) Include document version in bulk reads - #1968
MLE-29883 (GH #1938) Include document version in bulk reads#1968rjdew-progress wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates the Java MarkLogic client to include a usable document version number (for optimistic locking) in DocumentDescriptor objects returned from bulk multi-document reads/searches, aligning DocumentManager.read(String...) behavior with DocumentManager.exists(String).
Changes:
- Extracts document version from multipart
Content-Disposition(viaversionId) for bulk reads and propagates it intoDocumentDescriptor. - Updates the conditional documents test to assert that bulk read/search descriptor versions are present and match
exists().
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| marklogic-client-api/src/main/java/com/marklogic/client/impl/OkHttpServices.java | Parse versionId from multipart headers and apply it to DocumentDescriptor for bulk document records. |
| marklogic-client-api/src/test/java/com/marklogic/client/test/ConditionalDocumentTest.java | Strengthen assertions to verify bulk read/search descriptors carry the same version as exists(). |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| format = getHeaderFormat(part); | ||
| mimetype = getHeaderMimetype(OkHttpServices.getHeader(part, HEADER_CONTENT_TYPE)); | ||
| length = getHeaderLength(OkHttpServices.getHeader(part, HEADER_CONTENT_LENGTH)); | ||
| uri = getHeaderUri(part); | ||
| version = getHeaderVersion(part); | ||
| extractedHeaders = true; |
1fc0e7e to
42840b2
Compare
| } | ||
|
|
||
| // Bulk multi-document reads carry the version as a "versionId" param on Content-Disposition, not as an ETag header. | ||
| static private long getHeaderVersion(BodyPart part) { |
There was a problem hiding this comment.
One thought on where to put this - I tried to avoid adding anything to OkHttpServices because it's so large already. The trick is where to put it instead, as there's not a good pattern for that.
Nonetheless, I would still lean towards a separate class in the impl.okhttp subpackage. It could be as simple as ContentDispositionUtil with a public static String getVersion. One small bonus would be that it provides a nice home for declaring the version as a private static, e.g. private static final VERSION_PATTERN = "...".
Could also add the ETag fallback, which seems useful since that was the existing behavior - I can't say for sure, but maybe an older version of MarkLogic returned it as an ETag?
There was a problem hiding this comment.
Moved to OkHttpUtil and now fallback to the ETAG header.
| updateMimetype(descriptor, getMimetype()); | ||
| updateLength(descriptor, getLength()); | ||
| updateVersion(descriptor, content.getHeader(HEADER_ETAG)); | ||
| updateVersion(descriptor, content.getVersion()); |
There was a problem hiding this comment.
Yeah, seeing this existing code here makes me think that Copilot's suggestion is a good one about using the ETag as a fallback.
42840b2 to
7b2d266
Compare
7b2d266 to
421b3d6
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.
Suppressed comments (1)
marklogic-client-api/src/main/java/com/marklogic/client/impl/okhttp/OkHttpUtil.java:277
- getHeaderVersion() only matches a Content-Disposition parameter spelled exactly as " versionId=" (note the required leading space and no quotes). Per header grammar, optional whitespace is allowed and servers may emit
;versionId=...orversionId="...", which would cause version parsing to fail and fall back to UNKNOWN_VERSION (or ETag), reintroducing the original issue.
public static long getHeaderVersion(BodyPart part) {
String contentDisposition = getHeader(part, HEADER_CONTENT_DISPOSITION);
String versionRegex = ".* versionId=([0-9]+).*";
if (contentDisposition != null && contentDisposition.matches(versionRegex)) {
String version = contentDisposition.replaceFirst("^.*" + versionRegex + ".*$", "$1");
Impact
Users who rely on MarkLogic optimistic locking cannot use DocumentManager.read(String uris...) to get a usable document version number. This can prevent correct version checks before writes and affect concurrent update handling.
Expected behavior
DocumentManager.read(String uris...) should return DocumentRecords with DocumentDescriptors that include the correct document version number, like DocumentManager.exists(String uri) does.
Actual behavior
DocumentManager.read(String uris...) returns a DocumentPage with DocumentRecords whose DocumentDescriptors always have the version number set to -1.
Steps to reproduce
Use DocumentManager.exists(String uri).
Observe that it returns a DocumentDescriptor with the proper version number set.
Use DocumentManager.read(String uris...).
Observe that it returns a DocumentPage with DocumentRecords whose DocumentDescriptors have the version number set to -1.
GitHub issue #1938