Skip to content

Commit

Permalink
Merge pull request #1655 from AntelopeIO/GH-1639-max-trx-time-default
Browse files Browse the repository at this point in the history
Change default max-transaction-time
  • Loading branch information
heifner authored Sep 22, 2023
2 parents b4c2826 + b012cce commit f16f24b
Show file tree
Hide file tree
Showing 9 changed files with 490 additions and 235 deletions.
9 changes: 5 additions & 4 deletions docs/01_nodeos/03_plugins/producer_plugin/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ Config Options for eosio::producer_plugin:
chain is stale.
-x [ --pause-on-startup ] Start this node in a state where
production is paused
--max-transaction-time arg (=30) Limits the maximum time (in
milliseconds) that is allowed a pushed
transaction's code to execute before
being considered invalid
--max-transaction-time arg (=499) Setting this value (in milliseconds)
will restrict the allowed transaction
execution time to a value potentially
lower than the on-chain consensus
max_transaction_cpu_usage value.
--max-irreversible-block-age arg (=-1)
Limits the maximum age (in seconds) of
the DPOS Irreversible Block for a chain
Expand Down
82 changes: 37 additions & 45 deletions libraries/custom_appbase/include/eosio/chain/application.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <appbase/application_base.hpp>
#include <eosio/chain/exec_pri_queue.hpp>
#include <chrono>
#include <optional>
#include <mutex>

/*
Expand All @@ -21,73 +22,62 @@ enum class exec_window {
// the main app thread is active.
};

enum class exec_queue {
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 {
class priority_queue_executor {
public:

// Trade off on returning to appbase exec() loop as the overhead of poll/run can be measurable for small running tasks.
// This adds to the total time that the main thread can be busy when a high priority task is waiting.
static constexpr uint16_t minimum_runtime_ms = 3;

// inform how many read_threads will be calling read_only/read_exclusive queues
// Currently only used to assert if exec_queue::read_exclusive is used without any read threads
void init_read_threads(size_t num_read_threads) {
pri_queue_.init_read_threads(num_read_threads);
}

template <typename Func>
auto post( int priority, exec_queue q, 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_queue_.wrap( priority, --order_, std::forward<Func>( func)));
return boost::asio::post( io_serv_, pri_queue_.wrap( priority, q, --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 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)));
return boost::asio::post(io_serv_, pri_queue_.wrap(priority, exec_queue::read_write, --order_, std::forward<Func>(func)));
}

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

// called from main thread, highest read_only and read_write
bool execute_highest() {
// execute for at least minimum runtime
const auto end = std::chrono::high_resolution_clock::now() + std::chrono::milliseconds(minimum_runtime_ms);

bool more = false;
while (true) {
if ( exec_window_ == exec_window::write ) {
// 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();
}
more = !read_only_queue_.empty() || !read_write_queue_.empty();
// During write window only main thread is accessing anything in priority_queue_executor, no locking required
more = pri_queue_.execute_highest(exec_queue::read_write, exec_queue::read_only);
} else {
// When in read window, multiple threads including main app thread are accessing two_queue_executor, locking required
more = read_only_queue_.execute_highest_locked(false);
// When in read window, multiple threads including main app thread are accessing priority_queue_executor, locking required
more = pri_queue_.execute_highest_locked(exec_queue::read_only);
}
if (!more || std::chrono::high_resolution_clock::now() > end)
break;
}
return more;
}

bool execute_highest_read_only() {
bool execute_highest_read() {
// execute for at least minimum runtime
const auto end = std::chrono::high_resolution_clock::now() + std::chrono::milliseconds(minimum_runtime_ms);

bool more = false;
while (true) {
more = read_only_queue_.execute_highest_locked( true );
get_io_service().poll(); // schedule any queued
more = pri_queue_.execute_highest_blocking_locked(exec_queue::read_only, exec_queue::read_exclusive);
if (!more || std::chrono::high_resolution_clock::now() > end)
break;
}
Expand All @@ -97,25 +87,25 @@ class two_queue_executor {
template <typename Function>
boost::asio::executor_binder<Function, appbase::exec_pri_queue::executor>
wrap(int priority, exec_queue q, Function&& func ) {
if ( q == exec_queue::read_write )
return read_write_queue_.wrap(priority, --order_, std::forward<Function>(func));
else
return read_only_queue_.wrap( priority, --order_, std::forward<Function>( func));
return pri_queue_.wrap(priority, q, --order_, std::forward<Function>(func));
}

void stop() {
pri_queue_.stop();
}

void clear() {
read_only_queue_.clear();
read_write_queue_.clear();
pri_queue_.clear();
}

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

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

bool is_read_window() const {
Expand All @@ -126,20 +116,22 @@ class two_queue_executor {
return exec_window_ == exec_window::write;
}

auto& read_only_queue() { return read_only_queue_; }

auto& read_write_queue() { return read_write_queue_; }
size_t read_only_queue_size() { return pri_queue_.size(exec_queue::read_only); }
size_t read_write_queue_size() { return pri_queue_.size(exec_queue::read_write); }
size_t read_exclusive_queue_size() { return pri_queue_.size(exec_queue::read_exclusive); }
bool read_only_queue_empty() { return pri_queue_.empty(exec_queue::read_only); }
bool read_write_queue_empty() { return pri_queue_.empty(exec_queue::read_write); }
bool read_exclusive_queue_empty() { return pri_queue_.empty(exec_queue::read_exclusive); }

// members are ordered taking into account that the last one is destructed first
private:
boost::asio::io_service io_serv_;
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 };
appbase::exec_pri_queue pri_queue_;
std::atomic<std::size_t> order_{ std::numeric_limits<size_t>::max() }; // to maintain FIFO ordering in all queues within priority
exec_window exec_window_{ exec_window::write };
};

using application = application_t<two_queue_executor>;
using application = application_t<priority_queue_executor>;
}

#include <appbase/application_instance.hpp>
Loading

0 comments on commit f16f24b

Please sign in to comment.