Skip to content
This repository has been archived by the owner on Sep 26, 2019. It is now read-only.

[NC-2100] Parallel Processing File Import Performance #683

Merged
merged 6 commits into from
Jan 30, 2019

Conversation

shemnon
Copy link
Contributor

@shemnon shemnon commented Jan 29, 2019

PR description

Improve performance of file based import by spinning off ECDSA and block validation into separate threads. The result are then joined and EVM execution and block import are performed synchronously.

Fixed Issue(s)

NC-2100

@shemnon shemnon changed the title Parallel Processing Performance [NC-2100] Parallel Processing Performance Jan 29, 2019
private final Semaphore blockBacklog = new Semaphore(2);

private final ExecutorService validationExecutor = Executors.newCachedThreadPool();
private final ExecutorService importExecutor = Executors.newSingleThreadExecutor();
Copy link
Contributor

Choose a reason for hiding this comment

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

These executors don't seem to be linked into the Pantheon life cycle so they get shutdown appropriately? That will lead to thread leaks and potentially crashes if something in those threads tries to access RocksDB after it has been closed.

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 is a command line util, called directly from BlocksSubCommand, so it won't be called except from the CLI and it will be in total ownership of the JVM. (Different story for the networked version of this patch). There is no pantheon life cycle for this class.

As for the RocksDB error, I already saw it when I didn't wait for the last block to finish. By joining on the previousBlockFuture after the loop where all the contents of the file are read in I ensure that all submitted blocks are complete and hence all the executors are emptied since the last block depends on all previous blocks and all of those depend on what was in the executors. I can add shutdown and awaitTermination to the executors but it would just be ceremony as they are already emptied at that point.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah yes, sorry I had missed that this was the util BlockImporter not the protocol spec one.

Copy link
Contributor

@ajsutton ajsutton left a comment

Choose a reason for hiding this comment

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

LGTM.

try {
blockBacklog.acquire();
} catch (final InterruptedException e) {
LOG.error(e);
Copy link
Contributor

Choose a reason for hiding this comment

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

Sadly this will lose the stack trace from the error - the first arg in log4j is Object so it just calls toString.

Suggested change
LOG.error(e);
LOG.error("", e);

Or provide a meaningful message if there is one to provide, but empty string is enough to get what you were expecting.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

try {
validationExecutor.awaitTermination(5, SECONDS);
} catch (final Exception e) {
LOG.error(e);
Copy link
Contributor

Choose a reason for hiding this comment

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

Same log4j trap as above.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

try {
importExecutor.awaitTermination(5, SECONDS);
} catch (final Exception e) {
LOG.error(e);
Copy link
Contributor

Choose a reason for hiding this comment

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

Same log4j trap as above.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

otherFuture = extractingFuture;
} else {
otherFuture =
extractingFuture.runAfterBothAsync(previousBlockFuture, () -> {}, importExecutor);
Copy link
Contributor

Choose a reason for hiding this comment

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

Couldn't this just be:

Suggested change
extractingFuture.runAfterBothAsync(previousBlockFuture, () -> {}, importExecutor);
CompletableFuture.allOf(extractingFuture, previousBlockFuture);

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'm not sure if it will interact with the particular executor it is waiting on or not. I'll benchmark it before submitting.

private final Semaphore blockBacklog = new Semaphore(2);

private final ExecutorService validationExecutor = Executors.newCachedThreadPool();
private final ExecutorService importExecutor = Executors.newSingleThreadExecutor();
Copy link
Contributor

Choose a reason for hiding this comment

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

Ah yes, sorry I had missed that this was the util BlockImporter not the protocol spec one.

try CompletableFuture.allOf
@shemnon shemnon changed the title [NC-2100] Parallel Processing Performance [NC-2100] Parallel Processing File Import Performance Jan 30, 2019
@shemnon shemnon merged commit 71f0003 into PegaSysEng:master Jan 30, 2019
@shemnon shemnon deleted the queue_perf branch February 1, 2019 14:51
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants