Skip to content

Conversation

@belugabehr
Copy link
Contributor

@belugabehr belugabehr commented Mar 28, 2019

  1. Use ArrayDeque instead of LinkedList
  2. Use ThreadLocalRandom instead of Random
  3. Remove the 'running' flag - use the Thread#join facility to detect if the thread has stopped running. Using a flag can cause race condition issues and is superfluous.
  4. Make static final variable names in all caps
  5. General cleanup

This class is likely to be faster than Stack when used as a stack, and faster than LinkedList when used as a queue.

https://docs.oracle.com/javase/7/docs/api/java/util/ArrayDeque.html

if (si == requestOfDeath) {
break;
}
if (si != null) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This looks like a lot of code change, but I simply removed this check 'is null' check and subsequently had to fix the indentation of the large block of code beneath it.

@jhuan31
Copy link

jhuan31 commented Mar 28, 2019

I'm wondering why findbugs didn't complain about the unused code... I filed PR #853 because findbugs complained in PR #850, which seems pretty innocent--just add a few metric lines. I guess either this PR or #850 changed the original logic in a subtle way.

Copy link
Contributor

@anmolnar anmolnar left a comment

Choose a reason for hiding this comment

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

+1
Great refactoring.

this.join();
this.flush();
} catch (InterruptedException e) {
LOG.warn("Interrupted while wating for " + this + " to finish");
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: LOG.warn("Interrupted while wating for {} to finish", this);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@anmolnar Well, if you can believe it, lol, I am trying to touch less where I can in order to make a review easier. Also, using SLF4J parameter-ized logging is the fastest way to do logging when logging is disabled. It would be very unlikely that a WARN message is disabled so it's actually faster to just keep it as is. If I was writing this code from scratch, I would use a parameter logging message here for consistency, but there's no strong argument to change it now.

Copy link
Contributor

Choose a reason for hiding this comment

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

Got you. You can leave it as is.
In my understanding parameterised logging is not slower than constructing message beforehand, but I agree that it only has performance gain when not logging the message.

@anmolnar
Copy link
Contributor

anmolnar commented Apr 2, 2019

@belugabehr Please update the comment and description of this PR. Thanks!

@belugabehr belugabehr changed the title Zookeeper 3020 2 ZOOKEEPER-3020: Review of SyncRequestProcessor Apr 2, 2019
private final RequestProcessor nextProcessor;

private Thread snapInProcess = null;
private final Semaphore snapThreadMutex = new Semaphore(1);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

So, just FYI. I wanted to make this change because of two reasons:

  1. It removes the special-case 'null' check (line 131) when the first iteration occurs.
  2. The last 'snapInProcess' variable is never cleared out so there is always a dangling pointer to the last instance that ran... i.e., it never gets GC'ed until the next time a snap process runs and kicks the previous reference out.

@anmolnar
Copy link
Contributor

anmolnar commented Apr 3, 2019

@belugabehr Jira doesn't indicate that, but I suppose this is going to be a master-only commit

@belugabehr
Copy link
Contributor Author

@anmolnar Yes. All of my work is for master, unless anyone has strong feelings that it should be committed elsewhere. That decision is above my pay grade here :)

Copy link

@enixon enixon left a comment

Choose a reason for hiding this comment

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

Thanks @belugabehr , for breaking this in to pieces! Much easier to review. :)

Overall looks very sane to me, just a couple of points.

continue;
}
toFlush.add(si);
if (toFlush.size() == FLUSH_SIZE) {
Copy link

Choose a reason for hiding this comment

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

any danger from changing > to == here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The only things that adds an item to this collection is the thread itself, and the 'add' exists immediately before the check against FLUSH_SIZE. There is no risk of somehow jumping over the threshold. A 'greater than' check is confusing to the reader because it implies that there might be some way to jump the threshold.

while (true) {
Request si = null;
if (toFlush.isEmpty()) {
Request si = queuedRequests.poll();
Copy link

Choose a reason for hiding this comment

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

Is behavior changing here? Your logic of tolling from a poll directly to a take instead of forcing a loop via continue looks right to me - is there a way that take can respond with a null value or otherwise break something in the new arrangement?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Retrieves and removes the head of this queue, waiting if necessary until an element becomes available.

https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/BlockingQueue.html#take()

The only way a 'null' could become available here is if a 'null' value is inserted into the collection. However, that is prevented on line 212.

I believe this is a good improvement because previously it was always checking 'isEmpty' to make a decision about how to interact with the 'queuedRequests' queue. I have removed that condition and it will just go ahead and try to pull something from the queue.

@enixon
Copy link

enixon commented Apr 9, 2019

lgtm +1 (non-committer)

@belugabehr
Copy link
Contributor Author

@anmolnar Any additional ideas for improvements?

@asfgit asfgit closed this in 876aaf4 Apr 30, 2019
@anmolnar
Copy link
Contributor

Committed to master branch. Thanks @belugabehr !

RokLenarcic pushed a commit to RokLenarcic/zookeeper that referenced this pull request Sep 3, 2022
1. Use ArrayDeque instead of LinkedList
2. Use ThreadLocalRandom instead of Random
3. Remove the 'running' flag - use the Thread#join facility to detect if the thread has stopped running. Using a flag can cause race condition issues and is superfluous.
4. Make static final variable names in all caps
5. General cleanup

> This class is likely to be faster than Stack when used as a stack, and faster than LinkedList when used as a queue.

https://docs.oracle.com/javase/7/docs/api/java/util/ArrayDeque.html

Author: Beluga Behr <dam6923@gmail.com>

Reviewers: andor@apache.org

Closes apache#876 from BELUGABEHR/ZOOKEEPER-3020-2 and squashes the following commits:

8f1df37 [Beluga Behr] General cleanup
83cab73 [Beluga Behr] Simplify and streamline access pattern of queuedRequests
5e0b801 [Beluga Behr] Use a semaphore to track status of snapshot thread
d119ef7 [Beluga Behr] No need to keep a flag which states if the thread is alive
e2059cf [Beluga Behr] Replace LinkedList with ArrayDeque
57a0449 [Beluga Behr] Replace Random with ThreadLocalRandom
17bd1fa [Beluga Behr] Require that the request not be 'null'
b28ff94 [Beluga Behr] Refactor flush method
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