Skip to content

MLE-29883 (GH #1938) Include document version in bulk reads - #1968

Open
rjdew-progress wants to merge 1 commit into
developfrom
MLE-29883
Open

MLE-29883 (GH #1938) Include document version in bulk reads#1968
rjdew-progress wants to merge 1 commit into
developfrom
MLE-29883

Conversation

@rjdew-progress

Copy link
Copy Markdown

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

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 (via versionId) for bulk reads and propagates it into DocumentDescriptor.
  • 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.

Comment on lines 4527 to 4532
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;
jonmille
jonmille previously approved these changes Aug 17, 2026
}

// 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) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, seeing this existing code here makes me think that Copilot's suggestion is a good one about using the ETag as a fallback.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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=... or versionId="...", 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");

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants