-
Notifications
You must be signed in to change notification settings - Fork 664
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
fix(client): setup separate SyncArbiter for ViewClientActor with 4 threads #2970
Conversation
c527f90
to
e08a5ee
Compare
@@ -32,6 +34,8 @@ impl ShutdownableThread { | |||
impl Drop for ShutdownableThread { | |||
fn drop(&mut self) { | |||
self.shutdown(); | |||
// Leaving some time for all threads to stop after system is stopped. | |||
thread::sleep(Duration::from_millis(100)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that it is not a solution, but a symptomatic patch. Is there an issue about this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, we should be explicitly waiting for all threads to stop.
…reads Change from 2752 + allow storage failures in code reachable from view client. Test plan --------- Run existing tests
Co-authored-by: Vlad Frolov <[email protected]>
This reverts commit 2c2ad3f.
) -> Addr<ViewClientActor> { | ||
let request_manager = Arc::new(RwLock::new(ViewClientRequestManager::new())); | ||
SyncArbiter::start(config.view_client_threads, move || { | ||
// ViewClientActor::start_in_arbiter(&Arbiter::current(), move |_ctx| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this line be removed?
@@ -171,5 +166,5 @@ pub fn start_with_config( | |||
|
|||
trace!(target: "diagnostic", key="log", "Starting NEAR node with diagnostic activated"); | |||
|
|||
(client_actor, view_client) | |||
(client_actor, view_client, vec![client_arbiter, arbiter]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we need to keep the arbiters?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we join them at the end, it seems like System::run blocks until stop signal and we want to wait for all threads to end, but not 100% sure it's needed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it also fix #2948 ?
does not fix, only makes it affect chain store |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SyncArbiter seems to be the right thing to use here, but does it mean that every other actor in our node runs on system arbiter, including the network and the RPC server? This would mean we are using a single OS thread for everything, but view clients. CC @pmnoxx
I'll delegate reviewing concurrency in this code to @pmnoxx and @frol
Also, @damons mentioned that our storage might allow dirty reads (we are using batched operations, instead of transactions API in rocksdb) so this PR might expose this issue now. If he is right, prepare for nodes returning garbage through RPC. CC @ailisp .
Network spawns many threads since #2772 |
Our JSONRpc server (not to be confused with ViewClienActor) still uses system arbiter, is it correct? If it is so then it means we have one thread for all RPC and regular ClientActor. CC @frol
This is quite dangerous. Can we make sure our ViewClientActor does not serve torn data? If it is the case then lots of tools built on top of it will start failing, e.g. the bridge. |
Our database is generally set up in a way that we do not modify stuff, we only insert and delete (except for very few exceptions). Correspondingly, the ViewClient will either serve your request successfully (all the data it expected was present throughout), or will fail. Mostly failures will be due to concurrent GC, in which case the same request sent in ~1 second will also fail anyway. The alternatives to this fix are:
Let's observe how ViewClient actually behaves with this change. In expectation, we will not observe any transient errors. |
Since we have multiple columns in the database can it be possible that while block production or block import is happening and we have populated only some of the columns the ViewClient will swoop in and read fresh data from some columns and old data from other columns?
I would prefer if we were certain before merging in. I am launching a persistent bridge today, and some of our partners will be using it. It would not look nice on us if it breaks because one of our RPC returns garbage data occasionally. |
@mikhailOK can correct me if I'm wrong, but I believe that all the affected end points in the ViewClient have the following pattern:
This "read B" part can fail for two reasons: a) We GCed B after A was read. Note that GC would not GC B before it GCed A, but naturally the timing could have been: VC reads A -> GC deletes A -> GC deleted B -> VC reads B. In this case the ViewClient will fail with this change (and would not fail before this change because VC and GC are were in the same thread), but it is OK, since A is GCed, such a request is expected to start failing. b) We inserted A before ViewClient read it, but haven't had a chance to insert B. Indeed, if the block production was not writing atomically, it would be possible: BP writes A -> VC reads A -> VC attempts to read B -> BP writes B I see you above mention that we do not use Transaction API, and have a concern that it might imply a possibility for dirty reads. Fortunately, in RocksDB batched writes are atomic, so dirty reads are impossible (second paragraph here). Note that even with atomic writes, it is still possible to have: VC reads A -> (atomically BP writes A, BP writes B) But it's OK, because VC will fail on reading A, and will not attempt to read B, so this data race is also acceptable. |
Http server starts it's own thread for accepting connections and workers. There is a bug in Actix library. It looks like Actix doesn't guarantee fairness. While there are messages in mailbox, Actix will prioritize processing messages until mailbox is empty. Execution of any other actors or tasks scheduled with run_later will be delayed. |
@mikhailOK is there something that stops us from merging this PR? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
Actix unfairness sounds like a WAI. AFAIR run_later is designed to not give guarantees on when it is executed, it only guarantees that it is executed after the given time interval, this is from the documentation:
Execute closure after specified period of time within same Actor and Context
Maksym Zavershynskyi
NEAR Protocol
https://near.org/
… On Jul 11, 2020, at 4:57 PM, Alexander Skidanov ***@***.***> wrote:
Merged #2970 <#2970> into master.
—
You are receiving this because your review was requested.
Reply to this email directly, view it on GitHub <#2970 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/AILKVB7G52VRCXP37PEOYBLR3D35BANCNFSM4OWJGHJQ>.
|
We currently call `genesis_state` instead `Chain::new`, which means every time we initialize client or view client we will compute genesis state again. Since #2970 we start 4 view client actors and one client actor, so it means that we could be calling `gensis_state` five times at the same time, which consumes too much memory and doesn't make any sense. This PR fixes it by computing genesis state only once on initialization. Test plan --------- Manually verify that with this fix, we can start a testnet node without needing an absurd amount of memory.
Change from #2752 + allow storage failures in code reachable from view client.
Test plan
Run existing tests