-
Notifications
You must be signed in to change notification settings - Fork 17
fix lost connection and reconnect automatically #83
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
Conversation
|
|
||
| void SetFaultedState(); | ||
|
|
||
| bool IsFaultedState { get; } |
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.
Can we make IsFaultedState writeable instead of SetFaultedState method?
|
|
||
| public bool IsConnected() | ||
| { | ||
| return !(_responseReader?.IsFaultedState ?? true) && (_physicalConnection?.IsConnected() ?? 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.
Quite hard to read. Maybe better to split in several expressions\extract some variable?
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 be redesigned.
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.
Initialization of ResponseReader and PhysicalConnection should be moved to constructor. Then all fields could be made readonly and there will be no reason for testing them for null.
| _clientOptions.LogWriter?.WriteLine($"End reading from connection, read bytes count: {readBytesCount}"); | ||
|
|
||
| if (ProcessReadBytes(readBytesCount)) | ||
| if (readWork.IsCompleted) |
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 if readWork.IsCompleted == false? Can it be if reading is in progress?
| { | ||
| Task Connect(); | ||
|
|
||
| bool IsConnected(); |
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 should be state, not only boolean flag.
|
|
||
| public bool IsConnected() | ||
| { | ||
| return !(_responseReader?.IsFaultedState ?? true) && (_physicalConnection?.IsConnected() ?? 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.
Should be redesigned.
| namespace ProGaudi.Tarantool.Client | ||
| { | ||
| public interface ILogicalConnection | ||
| using System; |
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.
Please, respect our coding guidelines: usings should be outside of namespace.
|
|
||
| public bool IsConnected() | ||
| { | ||
| return !(_responseReader?.IsFaultedState ?? true) && (_physicalConnection?.IsConnected() ?? 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.
Initialization of ResponseReader and PhysicalConnection should be moved to constructor. Then all fields could be made readonly and there will be no reason for testing them for null.
| using ProGaudi.Tarantool.Client.Model; | ||
| using ProGaudi.Tarantool.Client.Utils; | ||
|
|
||
| public class LogicalConnectionWrapper : ILogicalConnection |
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 this class? Maybe we should move all the code to Box?
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.
prefix all calls in Box to check connection and reconnect, or implement something like ILogicalConnection (with ability to drop logical connection and reconnect) inside box and make calls to it
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.
If it manages logical connections then this is a manager, not a wrapper.
| { | ||
| _disposed = true; | ||
| _stream?.Dispose(); | ||
| Interlocked.Exchange(ref _stream, null)?.Dispose(); |
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.
Don't replace stream with null. This is bad design decision.
| { | ||
| lock (this) | ||
| { | ||
| if (_pendingRequests == null) |
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 did we introduced lock there?
| { | ||
| if (_disposed) | ||
| { | ||
| 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.
Or we should throw exception?
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.
then we need catch exceptions here instead of check: https://github.com/progaudi/tarantool-csharp/pull/83/files/4be0d2ca63c07b90d6c7d9449e3da7ebb0624b65#diff-8b31d61323fab83274f6898980abbf6bR59
| _clientOptions.LogWriter?.WriteLine($"End reading from connection, read bytes count: {readBytesCount}"); | ||
|
|
||
| if (ProcessReadBytes(readBytesCount)) | ||
| if (readWork.Status == TaskStatus.RanToCompletion) |
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 if Status == Ready?
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.
aensidhe
left a 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.
A lot of locking introduced without clear reason. Necessary lock was removed.
| return false; | ||
| } | ||
|
|
||
| if (!_responseReader.IsConnected() || !_physicalConnection.IsConnected()) |
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.
IsConnected should not change state.
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.
fixed
| using ProGaudi.Tarantool.Client.Model; | ||
| using ProGaudi.Tarantool.Client.Utils; | ||
|
|
||
| public class LogicalConnectionWrapper : ILogicalConnection |
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.
If it manages logical connections then this is a manager, not a wrapper.
| long headerLength; | ||
| var headerBuffer = CreateAndSerializeBuffer(request, requestId, bodyBuffer, out headerLength); | ||
|
|
||
| lock (_physicalConnection) |
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 lock is necessary. Otherwise you'll end up with two threads mixing requests and broken pipeline
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.
fixed
| public void Dispose() | ||
| { | ||
| _disposed = true; | ||
| _stream?.Dispose(); |
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 this lock?
| _disposed = true; | ||
|
|
||
| _stream?.Dispose(); | ||
| _socket?.Dispose(); |
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.
Socket is already disposed. Look to constructor.
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.
how it may be already disposed when we call dispose manually when disconnect detected?
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.
Stream owns socket and will dispose it, because we pass True to stream parameter.
|
|
||
| public Task<MemoryStream> GetResponseTask(RequestId requestId) | ||
| { | ||
| lock (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.
Why do we need this lock?
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.
avoid concurrent access to _pendingRequests, while it perhaps may be destroying and should not accept requests any more
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.
Better make _pendingRequests not readonly and set it to null in Dispose via InterlockedExchange.
Then you can copy it here via Interlocked.Exchange to local variable and check for null-ness.
Then we can rid of _disposed flag or made it calculatable.
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.
still minor posibility of race condition without explicit lock
d64e990 to
1e10433
Compare
aensidhe
left a 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.
Good work, we discussed changes.
| _physicalConnectionLock.ExitWriteLock(); | ||
| } | ||
| } | ||
| catch (Exception ex) |
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.
Merge this into one try-catch-finally, please.
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.
fixed
|
|
||
| _clientOptions.LogWriter?.WriteLine($"{nameof(LogicalConnectionManager)}: Connected..."); | ||
|
|
||
| if (_clientOptions.ConnectionOptions.PingCheckInterval > 0) |
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.
Can we treat 0 as disabled ping?
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.
done
|
|
||
| public async Task Connect() | ||
| { | ||
| _clientOptions.LogWriter?.WriteLine($"{nameof(LogicalConnectionManager)}: Connecting..."); |
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 discussed it in person.
- rewrite it to ReadWriteLockSlim.
What we miss in our discussion:
- cleanup of old connection and old timer.
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.
cleanup of _droppableLogicalConnection is in Connect or in LogicalConnectionManager.Dispose
cleanup of timer is in LogicalConnectionManager.Dispose
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.
using ReadWriteLock, not Slim - the only wait point is Connect and it is long, no reason for spin wait
| TaskCompletionSource<MemoryStream> request; | ||
| if (_pendingRequests.TryGetValue(requestId, out request)) | ||
| { | ||
| _pendingRequests.Remove(requestId); |
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.
Remove does not throw exceptions if key is missing. We can remove check for TryGetValue.
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.
with the check we avoid redundant call when value is missing. minor performance gain.
| _clientOptions.LogWriter?.WriteLine($"Connection read failed: {readWork.Exception}"); | ||
| Dispose(); | ||
| } | ||
| else |
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.
Please, reduce nesting.
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.
fixed
|
Reviewed 13 of 13 files at r1. src/tarantool.client/LogicalConnectionManager.cs, line 42 at r1 (raw file):
If zero is bad value for PingCheckInterval - incorporate validation logic into setter of it. src/tarantool.client/LogicalConnectionManager.cs, line 80 at r1 (raw file):
Why do we need this flag? It is not resetted to false ever. src/tarantool.client/LogicalConnectionManager.cs, line 114 at r1 (raw file):
Why do we need to wait on it at all? Comments from Reviewable |
|
Review status: all files reviewed at latest revision, 4 unresolved discussions, some commit checks failed. src/tarantool.client/LogicalConnection.cs, line 157 at r1 (raw file):
How about send headerBuffer and bodyBuffer as 1 buffer (concat arrays befor sent?) and remove lock? src/tarantool.client/LogicalConnectionManager.cs, line 42 at r1 (raw file): Previously, aensidhe (Anatoly Popov) wrote…
As i understand - 0 is good value for PingCheckInterval. If it is zero, ping packets will not be sent. Comments from Reviewable |
done:
q:
This change is