Skip to content

GEODE-6164: CacheClientProxy's closeSocket should be called atomically#2972

Merged
gesterzhou merged 1 commit intodevelopfrom
feature/GEODE-6164
Dec 8, 2018
Merged

GEODE-6164: CacheClientProxy's closeSocket should be called atomically#2972
gesterzhou merged 1 commit intodevelopfrom
feature/GEODE-6164

Conversation

@gesterzhou
Copy link
Contributor

Thank you for submitting a contribution to Apache Geode.

@jhuynh1 @Bill

In order to streamline the review of the contribution we ask you
to ensure the following steps have been taken:

For all changes:

  • Is there a JIRA ticket associated with this PR? Is it referenced in the commit message?

  • Has your PR been rebased against the latest commit within the target branch (typically develop)?

  • Is your initial contribution a single, squashed commit?

  • Does gradlew build run cleanly?

  • Have you written or updated unit tests to verify your changes?

  • If adding new dependencies to the code, are these dependencies licensed in a way that is compatible for inclusion under ASF 2.0?

Note:

Please ensure that once the PR is submitted, you check travis-ci for build issues and
submit an update to your PR as soon as possible. If you need help, please send an
email to dev@geode.apache.org.

@gesterzhou gesterzhou force-pushed the feature/GEODE-6164 branch 2 times, most recently from 85dde7d to 4751056 Compare December 7, 2018 22:04
if (!closeSocket()) {
// other thread who closed the socket will be responsible to
// releaseResourcesForAddress and clearClientInterestList
return;
Copy link
Contributor

Choose a reason for hiding this comment

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

The callers of this method will possibly return without doing the rest of the work. As long as the callers are ok/expecting this to occur, then this should be ok...


CacheServerStats stats = mock(CacheServerStats.class);
doNothing().when(stats).incCurrentQueueConnections();
// InternalCache cache = cacheRule.getCache();
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove this comment?

((CompletableFuture<Void>) result1).join();
((CompletableFuture<Void>) result2).join();
((CompletableFuture<Void>) result3).join();
assertThatCode(() -> result1.get(2, SECONDS)).doesNotThrowAnyException();
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be bumped up to a higher number? Maybe just have it have a max wait of 60 seconds, that way it won't fail if for some reason the close happens to take longer or maybe the vm we are running on got cpu starved for a bit?

if (this._socketClosed.compareAndSet(false, true)) {
private boolean closeSocket() {
String remoteHostAddress = this._remoteHostAddress;
if (this._socketClosed.compareAndSet(false, true) && remoteHostAddress != null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it make sense to break the && condition into another if call instead?

if (remoteHostAddress == null) {
return false?
}

Copy link
Contributor

Choose a reason for hiding this comment

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

I think it's worth comments above each of closeSocket() and closeTransientFields() explaining that _socketClosed is used to provide one-shot protection around the close logic in both methods.


private void closeTransientFields() {
closeSocket();
if (!closeSocket()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

What happens if remoteHostAddress is null, we end up returning false from closeSocket and not doing the rest of this method. Is that acceptable?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

remoteHostAddress is null only when closeTransientFields() is called. So it's ok not to do anything else.

Copy link
Contributor

@Bill Bill left a comment

Choose a reason for hiding this comment

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

Good stuff @gesterzhou. I made a couple suggestions that might add a little bit to readability/maintainability. Functionally this looks good to me.

CacheClientProxy proxy = new CacheClientProxy(ccn, socket, proxyID, true,
Handshake.CONFLATION_DEFAULT, Version.CURRENT, 1L, true,
null, null);
Future<Void> result1 = executorServiceRule.runAsync(() -> proxy.close());
Copy link
Contributor

Choose a reason for hiding this comment

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

runAsync() returns a CompletableFuture<>. Since you'll need a CompletableFuture later (e.g. line 77), why not capture a CompletableFuture<Void> ref here to avoid the cast later?

((CompletableFuture<Void>) result3).join();
assertThatCode(() -> result1.get(60, SECONDS)).doesNotThrowAnyException();
assertThatCode(() -> result2.get(60, SECONDS)).doesNotThrowAnyException();
assertThatCode(() -> result3.get(60, SECONDS)).doesNotThrowAnyException();
Copy link
Contributor

Choose a reason for hiding this comment

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

FYI if you capture CompletableFuture<> refs above, then these three joins can become one statement:

CompletableFuture.allOf(result1,result2,result3).join();

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sounds good. I enhanced the code.

Copy link
Contributor

Choose a reason for hiding this comment

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

nice

InetAddress address = mock(InetAddress.class);
when(socket.getInetAddress()).thenReturn(address);
when(address.getHostAddress()).thenReturn("localhost");
doNothing().when(sc).asyncClose(any(), eq("localhost"), eq(null));
Copy link
Contributor

Choose a reason for hiding this comment

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

This would read a lot easier for me if:

  • variables that could be declared final (all of them!) were declared final
  • vertical space was used to separate each mocked object (its construction and configuration via when(...))

Copy link
Contributor

Choose a reason for hiding this comment

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

Better!

if (this._socketClosed.compareAndSet(false, true)) {
private boolean closeSocket() {
String remoteHostAddress = this._remoteHostAddress;
if (this._socketClosed.compareAndSet(false, true) && remoteHostAddress != null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it's worth comments above each of closeSocket() and closeTransientFields() explaining that _socketClosed is used to provide one-shot protection around the close logic in both methods.

@Bill
Copy link
Contributor

Bill commented Dec 8, 2018

Looks good to me now.

@gesterzhou gesterzhou merged commit 4fde138 into develop Dec 8, 2018
@gesterzhou gesterzhou deleted the feature/GEODE-6164 branch December 8, 2018 01:50
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.

3 participants