Skip to content
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

[4.0] Execute read only safe tasks on read-only thread pool #901

Merged
merged 54 commits into from
Mar 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
e2ad348
Execute read safe tasks on read-only thread pool.
heifner Mar 11, 2023
3239eb7
Update to appbase with priority queue pop()
heifner Mar 11, 2023
cfa2cc2
Read-only execution can run during write window so set read_window_de…
heifner Mar 11, 2023
c10195c
Update tests for pop() happening before execute()
heifner Mar 11, 2023
5ac1327
Cleanup variable names and comments and fix mutex lock around execute…
heifner Mar 12, 2023
aca29eb
Cleanup comments
heifner Mar 12, 2023
8d8db42
Consistently execute read-only trx via process_read_only_transaction()
heifner Mar 12, 2023
f53341b
When running without read only thread pool, execute exhausted read on…
heifner Mar 12, 2023
052faf6
max-transaction-time is required to be less than read-only-read-windo…
heifner Mar 12, 2023
1ff7e7d
Revert to appbase main branch
heifner Mar 13, 2023
d97aaf1
Add required thread safety to execution priority queue.
heifner Mar 13, 2023
f051bad
Swith db read mode when executing read-only trx on the main thread
heifner Mar 13, 2023
8ca7743
Update comments
heifner Mar 13, 2023
791c2c9
Clean up some comments
heifner Mar 13, 2023
44f7429
Move operations not safe to run in parallel onto general queue
heifner Mar 14, 2023
366a9bb
Rename read_only_trx_safe to read_only_safe
heifner Mar 14, 2023
a913cdb
Use read_only_safe queue for switching. Add comments on why.
heifner Mar 14, 2023
709c0a9
Fix merge conflicts
heifner Mar 14, 2023
d13c056
net_plugin operations do not have to be on the main thread at all.
heifner Mar 14, 2023
73da550
Rename general queue to read_write queue and read_only_safe queue to …
heifner Mar 16, 2023
5126601
Rename some variables and add some additional comments
heifner Mar 16, 2023
7893eeb
Add --num-test-runs option and fix get_table_rows to not generate an …
heifner Mar 16, 2023
d9c9150
Enabled read-only thread pool on non-producer nodes by default with 3…
heifner Mar 16, 2023
ea43426
Fix test configuration
heifner Mar 21, 2023
efa76d3
get_snapshot_requests safe to be parallel in read window
heifner Mar 21, 2023
2abdaa4
Minor pinned build script README changes
kj4ezj Mar 21, 2023
1655c66
Pull note about sudo into its own quote block
kj4ezj Mar 21, 2023
320d480
Cardinal objects should be in a numbered list
kj4ezj Mar 17, 2023
fe74adc
Restore markdown formatting erroneously deleted in commit 06c9e253277…
kj4ezj Mar 17, 2023
25c0efc
correct version after merging from 4.0.0-rc1
linh2931 Mar 21, 2023
33e1951
Shutdown all read threads when read_only queue is empty
heifner Mar 22, 2023
f47d7d2
Revert added whitespace
heifner Mar 22, 2023
ac234f6
Add multiple runs to read_only_trx_test.py. Also be explicit about 0 …
heifner Mar 22, 2023
d106d18
Fix rebase merge issue
heifner Mar 25, 2023
780fb3f
Fix includes
heifner Mar 25, 2023
376470d
Encapsulate mtx
heifner Mar 25, 2023
66aef5b
Merge remote-tracking branch 'origin/release/4.0' into read-safe-mult…
heifner Mar 27, 2023
f07011d
Minor updates
heifner Mar 27, 2023
f999202
Check test_mode
heifner Mar 27, 2023
06637d5
Add some calls to read-only actions to the test.
heifner Mar 28, 2023
e8a081c
Run everything at the same time test
heifner Mar 28, 2023
138737c
Default read-only-threads to 3 if not specified when eosio::chain_api…
heifner Mar 28, 2023
18cd288
Simplify exhausted read-only handling
heifner Mar 28, 2023
8eaa42e
Fix for slow runner hitting abi serialization deadline
heifner Mar 28, 2023
fa702aa
Integrate #904 to prevent exit from read window until all threads are…
heifner Mar 28, 2023
2dd1f67
Remove unneeded _ro_exiting_read_window and simplify read_only_execut…
heifner Mar 28, 2023
81ff398
Simplify use of exhausted read-only trx queue
heifner Mar 29, 2023
f80a95b
Add verification of posted read_only calls
heifner Mar 29, 2023
6672db3
Remove unneeded check of read-only exhausted trx queue
heifner Mar 29, 2023
6a989fd
Rename method and honor deadline.
heifner Mar 29, 2023
ca4de0e
Remove unneeded cancel and update read-only-threads help description.
heifner Mar 29, 2023
4465a8b
Cleanup window deadline calculation for read-only trxs.
heifner Mar 30, 2023
bf5746f
Remove unneeded call to cancel.
heifner Mar 30, 2023
8aab319
Resize vector to ensure it is empty
heifner Mar 30, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ git clone --recursive https://github.com/AntelopeIO/leap.git
git clone --recursive [email protected]:AntelopeIO/leap.git
```

> ℹ️ **HTTPS vs. SSH Clone** ℹ️
> ℹ️ **HTTPS vs. SSH Clone** ℹ️
Both an HTTPS or SSH git clone will yield the same result - a folder named `leap` containing our source code. It doesn't matter which type you use.

Navigate into that folder:
Expand All @@ -96,13 +96,13 @@ git submodule update --init --recursive
### Step 3 - Build
Select build instructions below for a [pinned build](#pinned-build) (preferred) or an [unpinned build](#unpinned-build).

> ℹ️ **Pinned vs. Unpinned Build** ℹ️
> ℹ️ **Pinned vs. Unpinned Build** ℹ️
We have two types of builds for Leap: "pinned" and "unpinned." The only difference is that pinned builds use specific versions for some dependencies hand-picked by the Leap engineers - they are "pinned" to those versions. In contrast, unpinned builds use the default dependency versions available on the build system at the time. We recommend performing a "pinned" build to ensure the compiler and boost versions remain the same between builds of different Leap versions. Leap requires these versions to remain the same, otherwise its state might need to be recovered from a portable snapshot or the chain needs to be replayed.

> ⚠️ **A Warning On Parallel Compilation Jobs (`-j` flag)** ⚠️
> ⚠️ **A Warning On Parallel Compilation Jobs (`-j` flag)** ⚠️
When building C/C++ software, often the build is performed in parallel via a command such as `make -j "$(nproc)"` which uses all available CPU threads. However, be aware that some compilation units (`*.cpp` files) in Leap will consume nearly 4GB of memory. Failures due to memory exhaustion will typically, but not always, manifest as compiler crashes. Using all available CPU threads may also prevent you from doing other things on your computer during compilation. For these reasons, consider reducing this value.

> 🐋 **Docker and `sudo`** 🐋
> 🐋 **Docker and `sudo`** 🐋
If you are in an Ubuntu docker container, omit `sudo` from all commands because you run as `root` by default. Most other docker containers also exclude `sudo`, especially Debian-family containers. If your shell prompt is a hash tag (`#`), omit `sudo`.

#### Pinned Build
Expand All @@ -111,12 +111,14 @@ Make sure you are in the root of the `leap` repo, then run the `install_depts.sh
sudo scripts/install_deps.sh
```

Next, run the pinned build script. You have to give it three arguments, in the following order:
- A temporary folder, for all dependencies that need to be built from source.
- A build folder, where the binaries you need to install will be built to.
- The number of jobs or CPU cores/threads to use (note the [jobs flag](#step-3---build) warning above).
Next, run the pinned build script. You have to give it three arguments in the following order:
1. A temporary folder, for all dependencies that need to be built from source.
1. A build folder, where the binaries you need to install will be built to.
1. The number of jobs or CPU cores/threads to use (note the [jobs flag](#step-3---build) warning above).

The following command runs the `pinned_build.sh` script, specifies a `deps` and `build` folder in the root of the Leap repo for the first two arguments, then builds the packages using all of your computer's CPU threads (Note: you don't need `sudo` for this command):
> 🔒 You do not need to run this script with `sudo` or as root.
For example, the following command runs the `pinned_build.sh` script, specifies a `deps` and `build` folder in the root of the Leap repo for the first two arguments, then builds the packages using all of your computer's CPU threads:
```bash
scripts/pinned_build.sh deps build "$(nproc)"
```
Expand Down
89 changes: 50 additions & 39 deletions libraries/custom_appbase/include/eosio/chain/application.hpp
Original file line number Diff line number Diff line change
@@ -1,87 +1,98 @@
#pragma once

#include <appbase/application_base.hpp>
#include <appbase/execution_priority_queue.hpp>
#include <eosio/chain/exec_pri_queue.hpp>
#include <mutex>

/*
* Custmomize appbase to support two-queue exector.
* Customize appbase to support two-queue executor.
*/
namespace appbase {

enum class exec_window {
read, // the window during which operations from read_only_trx_safe queue
// can be executed in app thread in parallel with read-only transactions
// in read-only transaction excuting threads.
write, // the window during which operations from both general and
// read_only_trx_safe queues can be executed in app thread,
// while read-only transactions are not executed in read-only
// transaction excuting threads.
read, // the window during which operations from read_only queue
// can be executed in parallel in the read-only thread pool
// as well as in the app thread.
write, // the window during which operations from both read_write and
// parallel queues can be executed in app thread,
// while read-only operations are not executed in read-only
// thread pool. The read-only thread pool is not active; only
// the main app thread is active.
};

enum class exec_queue {
read_only_trx_safe, // the queue storing operations which are safe to execute
// on app thread in parallel with read-only transactions
// in read-only transaction excuting threads.
general // the queue storing operations which can be only executed
// on the app thread while read-only transactions are
// not being executed in read-only threads
read_only, // the queue storing tasks which are safe to execute
// in parallel with other read-only tasks in the read-only
// thread pool as well as on the main app thread.
// Multi-thread safe as long as nothing is executed from the read_write queue.
read_write // the queue storing tasks which can be only executed
// on the app thread while read-only tasks are
// not being executed in read-only threads. Single threaded.
};

class two_queue_executor {
public:

template <typename Func>
auto post( int priority, exec_queue q, Func&& func ) {
if ( q == exec_queue::general )
return boost::asio::post(io_serv_, general_queue_.wrap(priority, --order_, std::forward<Func>(func)));
if ( q == exec_queue::read_write )
return boost::asio::post(io_serv_, read_write_queue_.wrap(priority, --order_, std::forward<Func>(func)));
else
return boost::asio::post(io_serv_, read_only_trx_safe_queue_.wrap(priority, --order_, std::forward<Func>(func)));
return boost::asio::post( io_serv_, read_only_queue_.wrap( priority, --order_, std::forward<Func>( func)));
}

// Legacy and deprecated. To be removed after cleaning up its uses in base appbase
template <typename Func>
auto post( int priority, Func&& func ) {
// safer to use general queue for unknown type of operation since operations
// from general queue are not executed in parallel with read-only transactions
return boost::asio::post(io_serv_, general_queue_.wrap(priority, --order_, std::forward<Func>(func)));
// safer to use read_write queue for unknown type of operation since operations
// from read_write queue are not executed in parallel with read-only operations
return boost::asio::post(io_serv_, read_write_queue_.wrap(priority, --order_, std::forward<Func>(func)));
}

boost::asio::io_service& get_io_service() { return io_serv_; }

bool execute_highest() {
if ( exec_window_ == exec_window::write ) {
if( !general_queue_.empty() && ( read_only_trx_safe_queue_.empty() || *read_only_trx_safe_queue_.top() < *general_queue_.top()) ) {
// general_queue_'s top function's priority greater than read_only_trx_safe_queue_'s top function's, or general_queue_ empty
general_queue_.execute_highest();
} else if( !read_only_trx_safe_queue_.empty() ) {
read_only_trx_safe_queue_.execute_highest();
// During write window only main thread is accessing anything in two_queue_executor, no locking required
if( !read_write_queue_.empty() && (read_only_queue_.empty() || *read_only_queue_.top() < *read_write_queue_.top()) ) {
// read_write_queue_'s top function's priority greater than read_only_queue_'s top function's, or read_only_queue_ empty
read_write_queue_.execute_highest();
} else if( !read_only_queue_.empty() ) {
read_only_queue_.execute_highest();
}
return !read_only_trx_safe_queue_.empty() || !general_queue_.empty();
return !read_only_queue_.empty() || !read_write_queue_.empty();
} else {
return read_only_trx_safe_queue_.execute_highest();
// When in read window, multiple threads including main app thread are accessing two_queue_executor, locking required
return read_only_queue_.execute_highest_locked(false);
}
}

bool execute_highest_read_only() {
return read_only_queue_.execute_highest_locked(true);
}

template <typename Function>
boost::asio::executor_binder<Function, appbase::execution_priority_queue::executor>
boost::asio::executor_binder<Function, appbase::exec_pri_queue::executor>
wrap(int priority, exec_queue q, Function&& func ) {
if ( q == exec_queue::general )
return general_queue_.wrap(priority, --order_, std::forward<Function>(func));
if ( q == exec_queue::read_write )
return read_write_queue_.wrap(priority, --order_, std::forward<Function>(func));
else
return read_only_trx_safe_queue_.wrap(priority, --order_, std::forward<Function>(func));
return read_only_queue_.wrap( priority, --order_, std::forward<Function>( func));
}

void clear() {
read_only_trx_safe_queue_.clear();
general_queue_.clear();
read_only_queue_.clear();
read_write_queue_.clear();
}

void set_to_read_window() {
void set_to_read_window(uint32_t num_threads, std::function<bool()> should_exit) {
exec_window_ = exec_window::read;
read_only_queue_.enable_locking(num_threads, std::move(should_exit));
}

void set_to_write_window() {
exec_window_ = exec_window::write;
read_only_queue_.disable_locking();
}

bool is_read_window() const {
Expand All @@ -92,15 +103,15 @@ class two_queue_executor {
return exec_window_ == exec_window::write;
}

auto& read_only_trx_safe_queue() { return read_only_trx_safe_queue_; }
auto& read_only_queue() { return read_only_queue_; }

auto& general_queue() { return general_queue_; }
auto& read_write_queue() { return read_write_queue_; }

// members are ordered taking into account that the last one is destructed first
private:
boost::asio::io_service io_serv_;
appbase::execution_priority_queue read_only_trx_safe_queue_;
appbase::execution_priority_queue general_queue_;
appbase::exec_pri_queue read_only_queue_;
appbase::exec_pri_queue read_write_queue_;
std::atomic<std::size_t> order_ { std::numeric_limits<size_t>::max() }; // to maintain FIFO ordering in both queues within priority
exec_window exec_window_ { exec_window::write };
};
Expand Down
Loading