Remove CloseQuietly and migrate its usages to other methods.#10247
Remove CloseQuietly and migrate its usages to other methods.#10247clintropolis merged 22 commits intoapache:masterfrom
Conversation
These other methods include: 1) New method CloseableUtils.closeAndWrapExceptions, which wraps IOExceptions in RuntimeExceptions for callers that just want to avoid dealing with checked exceptions. Most usages were migrated to this method, because it looks like they were mainly attempts to avoid declaring a throws clause, and perhaps were unintentionally suppressing IOExceptions. 2) New method CloseableUtils.closeInCatch, designed to properly close something in a catch block without losing exceptions. Some usages from catch blocks were migrated here, when it seemed that they were intended to avoid checked exception handling, and did not really intend to also suppress IOExceptions. 3) New method CloseableUtils.closeAndSuppressExceptions, which sends all exceptions to a "chomper" that consumes them. Nothing is thrown or returned. The behavior is slightly different: with this method, _all_ exceptions are suppressed, not just IOExceptions. Calls that seemed like they had good reason to suppress exceptions were migrated here. 4) Some calls were migrated to try-with-resources, in cases where it appeared that CloseQuietly was being used to avoid throwing an exception in a finally block. 🎵 You don't have to go home, but you can't stay here... 🎵
clintropolis
left a comment
There was a problem hiding this comment.
Still working on review of this, a bit more than half done. I need to think harder about the places which were previously eating an IOException that will now have to deal with a RuntimeException, specifically the http client and json parser stuff, and also the low level segment mapping stuffs.
| Closer closer = Closer.create(); | ||
| closer.registerAll(cursors); | ||
| CloseQuietly.close(closer); | ||
| CloseableUtils.closeAndWrapExceptions(closer); |
There was a problem hiding this comment.
Looking at the usage of this method, it appears that it is expected to eat the exceptions:
catch (Throwable t) {
closeAllCursors(sequenceCursors);
cancellationGizmo.cancel(t);
out.offer(ResultBatch.TERMINAL);
}
so, I think this method should be using closeAndSuppressExceptions. I guess callers could also be re-arranged to do the things that absolutely need done before closing the cursors, however, nothing would directly catch an exception thrown here, so it is sort of like screaming into the void, so I think we should just suppress them.
There was a problem hiding this comment.
I agree that it would be better to suppress IOException. We shouldn't throw IOException immediately especially at Line 650 since the terminal resultBatch must be added to the outputQueue after closing all cursors.
There was a problem hiding this comment.
I'll change it to CloseableUtils.closeAndSuppressExceptions
| } | ||
| } | ||
| CloseQuietly.close(columnarMultiInts); | ||
| CloseableUtils.closeAndWrapExceptions(columnarMultiInts); |
There was a problem hiding this comment.
Hmm, I know this is a test and probably doesn't really matter, but it seems like mapper.close should get called probably. Should this be using closeAndSuppressExceptions to ensure that it gets closed, or make them both part of a closer that gets passed into this method?
There was a problem hiding this comment.
I put them both in a single CloseableUtils.closeAll
| } | ||
| } | ||
| CloseQuietly.close(columnarMultiInts); | ||
| CloseableUtils.closeAndWrapExceptions(columnarMultiInts); |
There was a problem hiding this comment.
same comment about closing mapper
| } | ||
| catch (Exception ex) { | ||
| CloseQuietly.close(closer); | ||
| return Optional.empty(); |
There was a problem hiding this comment.
This method was using CloseQuietly to eat exceptions, although probably not well enough since it was only eating IOExceptions. I think it should probably be using closeAndSuppressExceptions.
Per the javadocs of acquireReferences:
...
* IMPORTANT NOTE: to fulfill the contract of this method, implementors must return a closeable to indicate that the
* reference can be acquired, even if there is nothing to close. Implementors should avoid allowing this method or the
* {@link Closeable} it creates to throw exceptions.
*
* For callers: if this method returns non-empty, IT MUST BE CLOSED, else reference counts can potentially leak.
...
There was a problem hiding this comment.
Good point, changed this to suppress.
| Closer closer = Closer.create(); | ||
| closer.registerAll(cursors); | ||
| CloseQuietly.close(closer); | ||
| CloseableUtils.closeAndWrapExceptions(closer); |
There was a problem hiding this comment.
I agree that it would be better to suppress IOException. We shouldn't throw IOException immediately especially at Line 650 since the terminal resultBatch must be added to the outputQueue after closing all cursors.
|
|
||
| // Second exception | ||
| Assert.assertEquals(1, e.getSuppressed().length); | ||
| Assert.assertThat(e.getSuppressed()[0], CoreMatchers.instanceOf(RuntimeException.class)); |
There was a problem hiding this comment.
Hmm, why not IllegalArgumentException?
There was a problem hiding this comment.
Ah, yeah, that's a better test. I changed it.
|
|
||
| // Second exception | ||
| Assert.assertEquals(1, e.getSuppressed().length); | ||
| Assert.assertThat(e.getSuppressed()[0], CoreMatchers.instanceOf(RuntimeException.class)); |
| catch (Exception ex) { | ||
| CloseQuietly.close(resultSupplier); | ||
| throw ex; | ||
| catch (Throwable e) { |
There was a problem hiding this comment.
Why catching Throwables vs catching only Exceptions? It doesn't seem useful to do anything when you see any Error here.
There was a problem hiding this comment.
Hmm, I can't think of a reason. Changed it back to Exception.
There was a problem hiding this comment.
Now that I think about it more, I guess the reason is that we always want to close stuff, even if some weird Throwable got thrown. It's making it behave like a finally or like a try-with-resources. I changed it back to Throwable. Let me know if you disagree.
| public void close() | ||
| { | ||
| CloseQuietly.close(closer); | ||
| CloseableUtils.closeAndWrapExceptions(closer); |
There was a problem hiding this comment.
nit: you can modify the signature of this method to throw IOExceptoin, but this should also be fine.
There was a problem hiding this comment.
The superclass won't allow it. (It overrides close and declares it to throw nothing.) I guess I could change the superclass, but I didn't want to go that far.
| protected void loadBuffer(int bufferNum) | ||
| { | ||
| CloseQuietly.close(holder); | ||
| CloseableUtils.closeAndWrapExceptions(holder); |
There was a problem hiding this comment.
This seems fine because ResourceHolder.close() doesn't throw IOException, but this code seems confusing to me since, until looking at the code closely, I wasn't sure if it's fine to not execute the below lines when this line throws an exception. How about adding a comment here, such as "this line should never throw any checked exceptions"?
There was a problem hiding this comment.
Same comment for other places where close ResourceHolders.
There was a problem hiding this comment.
I didn't realize it didn't throw IOException. So actually, I could just remove the CloseableUtils call completely here, and replace it with holder.close(). I'll do that.
There was a problem hiding this comment.
Did that, along with the other block layout column suppliers.
|
|
||
| if (prevVal instanceof Closeable) { | ||
| CloseQuietly.close((Closeable) prevVal); | ||
| CloseableUtils.closeAndWrapExceptions((Closeable) prevVal); |
There was a problem hiding this comment.
This changes the behavior, but any callers of fromIterableVersionOne() doesn't seem creating GenericIndexed from Closeable types. How about removing this?
There was a problem hiding this comment.
If it's ok with you, I'd prefer to leave it in for this patch, because I didn't want to go through and make sure it's always ok to skip closing.
|
This pull request has been marked as stale due to 60 days of inactivity. It will be closed in 4 weeks if no further activity occurs. If you think that's incorrect or this pull request should instead be reviewed, please simply write any comment. Even if closed, you can still revive the PR at any time or discuss it on the dev@druid.apache.org list. Thank you for your contributions. |
|
This pull request/issue has been closed due to lack of activity. If you think that is incorrect, or the pull request requires review, you can revive the PR at any time. |
|
nice try, stalebot |
|
This issue is no longer marked as stale. |
|
This pull request/issue is no longer marked as stale. |
|
Fixed up some merge conflicts. |
|
Fixed up some more merge conflicts. |
| * Like {@link Closeable#close()} but sends any exceptions to the provided Consumer, and then throws them away. | ||
| * | ||
| * If the Consumer throws an exception, that exception is thrown by this method. So if your intent is to chomp | ||
| * exceptions, you should avoid writing a Consumer that might thrown an exception. |
There was a problem hiding this comment.
| * exceptions, you should avoid writing a Consumer that might thrown an exception. | |
| * exceptions, you should avoid writing a Consumer that might throw an exception. |
|
Fixed up some more merge conflicts. |
CloseQuietly.closeis a method that closes something. It suppresses and logs anyIOExceptions it gets, but re-throws any non-IOExceptions. It is used extensively and it
seems likely to me that most usages did not intend to have these specific behaviors.
So this patch gets rid of the method and migrates call sites to various more sanely
behaving methods.
These other methods include:
in RuntimeExceptions for callers that just want to avoid dealing with
checked exceptions. Most usages were migrated to this method, because it
looks like they were mainly attempts to avoid declaring a throws clause,
and perhaps were unintentionally suppressing IOExceptions.
in a catch block without losing exceptions. Some usages from catch blocks
were migrated here, when it seemed that they were intended to avoid checked
exception handling, and did not really intend to also suppress IOExceptions.
exceptions to a "chomper" that consumes them. Nothing is thrown or returned.
The behavior is slightly different: with this method, all exceptions are
suppressed, not just IOExceptions. Calls that seemed like they had good
reason to suppress exceptions were migrated here.
that CloseQuietly was being used to avoid throwing an exception in a finally
block.
🎵 You don't have to go home, but you can't stay here... 🎵