-
Notifications
You must be signed in to change notification settings - Fork 248
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
Small NodeManager refactoring #253
Conversation
cmd/statusd/utils.go
Outdated
//time to sync | ||
time.Sleep(10 * time.Second) | ||
|
||
loginResponse := common.APIResponse{} |
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.
var loginResponse common.APIResponse
is an equivalent and more reader friendly style. ;)
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.
Arguable. I prefer :=
style, for example.
geth/node/manager.go
Outdated
@@ -140,7 +141,11 @@ func (m *NodeManager) StopNode() (<-chan struct{}, error) { | |||
|
|||
// stopNode stop Status node. Stopped node cannot be resumed. | |||
func (m *NodeManager) stopNode() (<-chan struct{}, error) { | |||
if m.node == nil || m.nodeStarted == nil || m.nodeStopped == nil { | |||
if m.node == nil { |
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 have direct access to the NodeManager.node
unguarded by mutex. We might need to create a new issue to refactor this properly, but we can't handle it here.
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.
Created issue for this #258
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.
PR looks good, we can atleast for now ensure any changes we make should follow the proper guard of the mutex, we should create a seperate issue for the overal refactoring of the package to ensure proper guarding of NodeManager.node
.
Thanks.
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.
What worries me the most is that in NodeManager
methods, we sometimes use a mutex to guard NodeManager.node
access and sometimes not.
Also, there is a lot of repetition with checking if node exists, is started or stopped. I am not sure it's possible to extract these logic to a separate method though...
cmd/statusd/utils.go
Outdated
@@ -1414,6 +1418,29 @@ func testJailFunctionCall(t *testing.T) bool { | |||
return true | |||
} | |||
|
|||
func testNodeOffline(t *testing.T) bool { | |||
|
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.
Style: an unnecessary blank line. The same at the end of the function's body.
geth/node/manager.go
Outdated
// make sure that node is fully started | ||
if m.node == nil || m.nodeStarted == nil { | ||
if m.nodeStarted == nil { |
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.
Above you use this condition m.nodeStarted == nil || m.nodeStopped == nil
to return ErrNoRunningNode
. Is there a reason why it's different here?
Also, if this pattern:
m.RLock()
defer m.RUnlock()
if m.node == nil {
return nil, ErrNodeOffline
}
if m.nodeStarted == nil || m.nodeStopped == nil {
return nil, ErrNoRunningNode
}
repeats so frequently, maybe it would be possible to extract it to a new private method?
@adambabik I exported it to a private func so we dont repeat that check. |
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.
Requesting last small changes.
geth/node/manager.go
Outdated
@@ -351,8 +358,13 @@ func (m *NodeManager) RestartNode() (<-chan struct{}, error) { | |||
|
|||
// restartNode restart running Status node, fails if node is not running | |||
func (m *NodeManager) restartNode() (<-chan struct{}, error) { | |||
// check if we have a node running | |||
if m.node == nil { |
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.
Could it be written like
if err := m.isNodeAvailable(); err != nil {
return nil, err
}
here as well?
geth/node/manager.go
Outdated
if m.node == nil || m.nodeStarted == nil { | ||
return nil, ErrNoRunningNode | ||
// check if we have a node running | ||
if m.node == nil { |
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.
This if is unnecessary as m.isNodeAvailable()
is just below.
geth/node/manager.go
Outdated
@@ -563,3 +575,16 @@ func (m *NodeManager) RPCServer() (*rpc.Server, error) { | |||
|
|||
return m.rpcServer, nil | |||
} | |||
|
|||
// Check if we have a node running and make sure is fully started |
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.
The comment should have a format isNodeAvailable checks if we have a running node and...
.
@adambabik fixed small changes. |
geth/node/manager.go
Outdated
|
||
// isNodeAvailable check if we have a node running and make sure is fully started | ||
func (m *NodeManager) isNodeAvailable() error { | ||
if m.node == nil { |
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.
Since the node
field is supposed to be secured through the mutex, this should call:
defer m.Unlock()
m.Lock()
Before it's contents.
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, it would be the safest to add it here. But NodeManager
's methods need a closer look anyway as some of them don't use the mutex without a reason.
@influx6 could you review again? I made sure that locks are only in the main methods, not in helper methods. |
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.
There're a number of fixes I've requested but there's a question that worries me the most.
Why is this at all happening? In the test we're checking Login
after we stopped the node but that bug isn't actually about that. It's about what happens when we've started the node without an internet connection and tried to Login
.
Could you try to reproduce it this way, please, if you haven't done it yet?
geth/node/manager.go
Outdated
|
||
if m.node == nil { | ||
return ErrNodeOffline | ||
} |
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.
Probably, the check for starting node should be before checking its nodeStarted
value. However, I'm not a proponent for such kind of defensive programming.
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.
Actually, m.nodeStarted
is set before m.node
.
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.
You're right. Was too sleepy and thought that check was m == nil
somehow %)
geth/node/manager.go
Outdated
if m.node == nil || m.nodeStarted == nil || m.nodeStopped == nil { | ||
return nil, ErrNoRunningNode | ||
if err := m.isNodeAvailable(); err != nil { | ||
return nil, err | ||
} |
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.
- These 2 conditions are not equivalent.
- Also, I think this check should rather be in
m.StopNode
, right?
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.
- Not trivial, but it looks it is. If
m.nodeStopped
isnil
, thenm.node
must benil
as well. It's becausem.nodeStopped
isnil
only before the node is started and is never set tonil
afterwards. It gets its value in a lock right afterm.node
is assigned. Generally, this whole logic to start/stop a node is overcomplicated with locks and channels floating around. We can refactor this file and save 50% LOC. - Correct.
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.
Well, my concern is about this:
status-go/geth/node/manager.go
Line 101 in f445b11
m.nodeStopped = make(chan struct{}, 1) |
First,
node
is assigned and only then nodeStopped
. So if somebody calls stopNode
in parallel with startNode
, there may be a race condition that node
is already assigned while nodeStopped
is not yet.The initial if condition would return true here while yours will return false.
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.
node
and nodeStopped
assignments are guarded with a Lock()
. StopNode()
also makes a Lock()
so it's not possible. But I think we can just add this one condition, it won't hurt anything, it just will be an additional check.
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.
Well, yeah, now they are. But when you move isNodeAvailable
to StopNode
they won't be, right?
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.
They will, because isNodeAvailable()
call will be after acquiring a lock. Anyway, I will add that condition.
geth/node/manager.go
Outdated
} | ||
|
||
if m.node == nil { | ||
return ErrNodeOffline |
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.
Hm. I may be wrong but I believe both these cases mean ErrNoRunningNode
.
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.
Yeah, I was wondering about this one too. I am not sure we can tell if a node is offline from here. Even if there is no Internet access m.node
probably is not nil
. I will check that as I guess it was not verified.
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.
Follow up: #242 (comment)
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.
After testing against the current develop using iOS simulator: status-im/status-mobile#1295 (comment)
geth/node/manager.go
Outdated
@@ -563,3 +565,16 @@ func (m *NodeManager) RPCServer() (*rpc.Server, error) { | |||
|
|||
return m.rpcServer, nil | |||
} | |||
|
|||
// isNodeAvailable check if we have a node running and make sure is fully started | |||
func (m *NodeManager) isNodeAvailable() error { |
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'm thinking about renaming this into isNodeStarted
as it may be more expressive about what it actually does. What do you think?
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's also not entirely true because we have that <-m.nodeStarted
floating around which only guarantees that node is started... We had a problem with naming this actually :D
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.
So think I that it's not entirely true. Hm. Added a refactoring note.
1c4ee41
to
3ef9a88
Compare
@adambabik as #242 has been closed, what do you think about applying this issue as a small refactoring and reverting changes related to the bug about an offline node? I like that checks got more structured. |
@tiabc it sounds good. However, I will leave only checks and remove the rest. |
Added check to see if there is an node
3ef9a88
to
65b7fcb
Compare
@tiabc I removed all code related to the offline bug. |
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.
As this part will be refactored, I don't insist on making m.isNodeAvailable()
return bool.
Changes:
NodeManager.isNodeAvailable()
,