Skip to content

SOLR-17705 for V2 API #3282

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open

SOLR-17705 for V2 API #3282

wants to merge 10 commits into from

Conversation

dsmiley
Copy link
Contributor

@dsmiley dsmiley commented Mar 22, 2025

https://issues.apache.org/jira/browse/SOLR-17705

Moves JSON parsing into the ResponseParser where it belongs.
Removes generated intermediate SolrResponse layer classes.

This PR is branched off of another PR, #3270, and so it includes all the changes there. Once that merges, this one will show what's new. I recommend looking at only the recent commit. And since it updates generated code, a real review requires checking out the code to generate and look at it.

dsmiley added 7 commits March 16, 2025 19:22
Is not necessarily a SolrResponse anymore.  Helps V2 API and reduces presence of NamedList.
# Conflicts:
#	solr/solrj/src/java/org/apache/solr/client/solrj/request/QueryRequest.java
We no longer enforce this; doesn't appear possible.
Moves JSON parsing into the ResponseParser where it belongs.
Removes generated intermediate SolrResponse layer classes.
@@ -322,7 +322,7 @@ public static boolean safeCheckCoreExists(String solrUrl, String coreName, Strin
Thread.sleep(clamPeriodForStatusPollMs);
}
final var coreStatusReq = new CoresApi.GetCoreStatus(coreName);
final var coreStatusRsp = coreStatusReq.process(solrClient).getParsed();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I admit the result of this work as seen in consuming/calling code right here is merely removing getParsed() since there is now no intermediate class holding the parsed value. But anyone browsing the generated code / class hierarchy will now see something simpler.

@@ -96,7 +97,7 @@ public static void postFile(SolrClient client, ByteBuffer buffer, String name, S
uploadReq.setSig(List.of(sig));
}

final var uploadRsp = uploadReq.process(client).getParsed();
final UploadToFileStoreResponse uploadRsp = uploadReq.process(client);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

IMO we've been using var too much.

Copy link
Contributor

Choose a reason for hiding this comment

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

Haha, you turned me on to it a year or two back and I've loved it since. I get that the type is less visible, but it makes the code scan better in places where the type name is a bit of a mouthful and "tidy" would otherwise split it up into multiple lines

But, to each their own.

@@ -91,17 +90,13 @@ public class {{classname}} {
{{#operation}}
{{^vendorExtensions.x-omitFromCodegen}}
{{#vendorExtensions.x-rawOutput}}
public static class {{operationIdCamelCase}}Response extends InputStreamResponse {}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

gone!

{{/vendorExtensions.x-rawOutput}}
{{^vendorExtensions.x-rawOutput}}
public static class {{operationIdCamelCase}}Response extends JacksonParsingResponse<{{modelPackage}}.{{returnType}}> {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

gone!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

seems obsolete now

Copy link
Contributor

Choose a reason for hiding this comment

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

[0] It's obsolete in its current form, but in theory it still makes sense to have some sort of test validating that the v2 deserialization plumbing actually works as intended?

We use the generated v2 code in a few places (e.g. CLIUtils, TestDistribFileStore), so I guess there's some implicit testing of this already? Maybe that's enough? idk - just thinking aloud.

@dsmiley
Copy link
Contributor Author

dsmiley commented Mar 28, 2025

WDYT @gerlowskija ?

Copy link
Contributor

@gerlowskija gerlowskija left a comment

Choose a reason for hiding this comment

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

I left a few comments inline. No huge issues, but a few ideas and questions. Curious what you think!

What's the relationship between this PR and the one that underlies it? Are you looking for consensus on both before you merge the underlying one? (Mostly asking to make sure I'm directing my review effort in the right place to keep you unblocked...)

@@ -96,7 +97,7 @@ public static void postFile(SolrClient client, ByteBuffer buffer, String name, S
uploadReq.setSig(List.of(sig));
}

final var uploadRsp = uploadReq.process(client).getParsed();
final UploadToFileStoreResponse uploadRsp = uploadReq.process(client);
Copy link
Contributor

Choose a reason for hiding this comment

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

Haha, you turned me on to it a year or two back and I've loved it since. I get that the type is less visible, but it makes the code scan better in places where the type name is a bit of a mouthful and "tidy" would otherwise split it up into multiple lines

But, to each their own.

import org.apache.solr.common.util.SimpleOrderedMap;

/** Parses JSON, deserializing to a domain type (class) via Jackson data-bind. */
public class JacksonDataBindResponseParser<T> extends ResponseParser {
Copy link
Contributor

Choose a reason for hiding this comment

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

[0] Maybe remove "DataBind" from the name, as it's implied/redundant once Jackson is mentioned?

[-0] Wdyt about marking this class "lucene.experimental" so we can rename or make changes more freely later if needed. I guess we're still held to the ResponseParser interface, so things couldn't change too drastically, but it still might be prudent since this is something that we're still trying to get right in SolrJ.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Jackson does not imply Jackson-DataBind -- the latter depends on the former, of course. Jackson is the parsing, DataBind is the, well, data binding -- mapping to POJOs via annotations etc.

Happy to add lucene.experimental wherever.

Copy link
Contributor

Choose a reason for hiding this comment

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

Jackson is the parsing, DataBind is the, well, data binding -- mapping to POJOs via annotations etc.

Oh, OK, fair enough.

I've only ever used Jackson with those annotations - I know in theory that the dependencies come separately, but it's hard for me to imagine how you'd even use Jackson if you're not converting to+from some POJO. Is it just all Map and List instances without databind? (That's rhetorical I guess - I can google it.)


@Override
public String getWriterType() {
return "json";
Copy link
Contributor

Choose a reason for hiding this comment

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

[0] I think this would need to be changed at the ResponseParser level, but it's a bummer that this method can't return a list, or indicate handling for multiple write-types. There's no reason our Jackson support should be specific to JSON, as you indicate in some comments below.

Much larger issue than this PR though...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's a separate issue for sure, and was on my mind a bit as I worked on this

Copy link
Contributor

Choose a reason for hiding this comment

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

[0] It's obsolete in its current form, but in theory it still makes sense to have some sort of test validating that the v2 deserialization plumbing actually works as intended?

We use the generated v2 code in a few places (e.g. CLIUtils, TestDistribFileStore), so I guess there's some implicit testing of this already? Maybe that's enough? idk - just thinking aloud.


/**
* A class for making a request to the config handler. Tests can use this e.g. to add custom
* components, handlers, parsers, etc. to an otherwise generic configset.
*/
@SuppressWarnings({"rawtypes"})
public class ConfigRequest extends CollectionRequiringSolrRequest {
public class ConfigRequest extends CollectionRequiringSolrRequest<SolrResponse> {
Copy link
Contributor

Choose a reason for hiding this comment

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

[0] Unrelated to your PR, but I wonder why this lives in test-framework...

parsedVal = mapper.readValue(new InputStreamReader(stream, encoding), typeParam);
}

return SimpleOrderedMap.of("response", parsedVal);
Copy link
Contributor

Choose a reason for hiding this comment

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

[0] processResponse's callers (HttpSolrClientBase, HttpSolrClient) do some error-checking on this method's retval, looking for an "error" key in the NamedList as an indicator of error. This implementation will never return a NL with that key, so it loses the benefits of that error-checking.

IMO we needn't handle that in this PR. It pre-exists your change in InputStreamResponseParser, NoOpResponseParser, etc. And there's a JIRA ticket for thinking through how we want error-handling to work in v2, so it won't get lost or fall through the cracks.

But I'm curious if you had any thoughts.

I had a plan in the back of my mind of uniting JacksonParsingResponse and InputStreamResponse, which would allow us to hook into some of the validation the ISR currently has. But it was never a great plan, and it doesn't make any sense if JPR goes away entirely. It'd be hacky, but we could have callers look for instanceof SolrJerseyResponse and inspect that POJO for errors? Or maybe the client only looks at the HTTP status code for v2 APIs?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah; good catch! Hmm. It'd be good to induce an error in a test using this API so we can verify that whatever we want to happen, actually happens. I would personally rather prefer that become a new evolutionary issue/PR rather than increasing the scope of this one. I could see us making bigger changes to accommodate that concern. I'll expand on that in the other comment...

Copy link
Contributor

Choose a reason for hiding this comment

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

I would personally rather prefer that become a new evolutionary issue/PR rather than increasing the scope of this one

Sounds good - we've already got this tracked in SOLR-17549, so we're good on that front.

// accordingly, maybe json, cbor, smile

@Override
public NamedList<Object> processResponse(InputStream stream, String encoding) throws IOException {
Copy link
Contributor

Choose a reason for hiding this comment

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

[0] I love that process can now return arbitrary types; it's a shame that can't extend to the retval of processResponse, and that we have to repackage our arbitrary type as a NL for all of the various SolrClient-internal NL-inspection code.

I think I brought this up in the related PR, but it strikes me again whether we shouldn't be slightly stricter on the allowed return types. Allow process to return types other than NL, but require retvals to implement some interface that exposes the information that most of our SolrClient impls look at internally: wasSuccessful(), getErrorMessage(), etc. That'd give our clients a uniform way to inspect responses and throw SolrServerExceptions, etc, regardless of whether we parse the response into a NamedList, or a SolrJerseyResponse, or an InputStreamResponse, or...

(This would break the error-handling logjam I've been wrestling with for awhile now too - see comment on L62 for more details.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I would honestly hate to bring back a root type merely for error handling. Java has exceptions for this; let's use that instead. We could make processResponse return whatever and for successful responses and throw something for the error case. That's a bigger change than the scope of this PR, so WDYT about merging this without additional error handling?

I propose a new JIRA issue (or your existing SOLR-17549 maybe) enhance the ResponseParser abstraction to take on the responsibility of error detection and throwing, and thus reducing some of the scope in the HTTP SolrClient side. I could imagine the processResponse method having more parameters including the HTTP status code, and using that to determine are we going to use data-bind or are we going to look for the error. We'd need to do this for XML & JavaBin; finding a way to reuse some logic where it's similar. Don't need to spec this out this second.

Copy link
Contributor

Choose a reason for hiding this comment

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

I like that overall plan - it makes sense conceptually to have ResponseParser do error detection, since that's part of the response. (And it'd be a really nice simplification for our SolrClient implementations themselves.)

And as mentioned elsewhere - it's fine with me to not put that in this PR. It's definitely a bigger change.

@dsmiley
Copy link
Contributor Author

dsmiley commented Apr 3, 2025

What's the relationship between this PR and the one that underlies it? Are you looking for consensus on both before you merge the underlying one?

Yes; these are important changes that require peer review. I don't want to be a cowboy here, doing whatever to important APIs.

@dsmiley
Copy link
Contributor Author

dsmiley commented Apr 9, 2025

Good. After merging the parent issue tonight, I'll brush up this PR, adding lucene.experimental but mostly unchanged, update CHANGES.txt. Should be in mergeable shape tomorrow.

dsmiley added 2 commits April 11, 2025 01:02
# Conflicts:
#	solr/CHANGES.txt
#	solr/core/src/test/org/apache/solr/handler/admin/ZookeeperReadAPITest.java
#	solr/solrj/src/resources/java-template/api.mustache
Copy link
Contributor Author

@dsmiley dsmiley left a comment

Choose a reason for hiding this comment

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

This PR should be ready to merge. Okay @gerlowskija ?

@dsmiley
Copy link
Contributor Author

dsmiley commented Apr 15, 2025

Well I'll merge this in another day if I don't hear back.

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

Successfully merging this pull request may close these issues.

2 participants