API Support Forum
OEC API > API Support > Getting the "ConnectionError" when connecting to the Gain Futures server
Author Topic: Getting the "ConnectionError" when connecting to the Gain Futures server
(10 messages, Page 1 of 1)
Moderators: VPfau
RAnde
Posts: 20
Joined: Sep 05, 2017


Posted: Jun 25, 2020 @ 05:54 AM             Msg. 1 of 10
Hi team,

I'm getting the "ConnectionError" in OnDisconnected event after connecting to the GF server. This issue only occurs when I call the GF request inside a complex process.
The code has worked fine and this issue does not happen if I call the request separatedly.

I made a search and see that there is an issue about connection in the old version of GF API. But I'm using the latest one 4.0.3.33 from NuGet.

Is this a known issue from GF? Or there is something wrong that I did cause the problem?

I followed the example on GF documentation and the code has been working.
Any suggestion to address this issue is appreciated.

Thanks.
SRuscak
Posts: 50
Joined: Aug 24, 2017


Posted: Jun 25, 2020 @ 02:52 PM             Msg. 2 of 10
Hello.

No, connection is working fine on our end.

>call the GF request inside a complex process
Do you mean the login request?

>inside a complex process
The problem may be thread timing. If you're not on the GFAPI thread, you should call Connect inside GF.Threading.Invoke. On the other hand, if the GFAPI thread is being hogged by a complex process, then Advance won't be called frequently enough to successfully log in.
RAnde
Posts: 20
Joined: Sep 05, 2017


Posted: Jun 26, 2020 @ 05:42 AM             Msg. 3 of 10
Hi Ruscak,

1. Do you mean the login request?
I implement a class that encapsulates things which deal with GF. When I make a request from my appliction to GF, the work-flow in that class is as below
- Create GF Client -> create GFCLientRunner and start -> Subscribe events -> Connect to GF server -> process the request (submit order, get position information...) -> dispose GF Client.

Here are the relevant methods

private void CreateClientRunnerAndConnect(RequestData requestData)
{
// Create GFClient and its runner
_clientRunner = _clientRunner.WithClient(GFApi.CreateClient());
_clientRunner.Start();

// Subscribe general events of GF Client
SubscribeEvents(_clientRunner);

ConnectToServer(_clientRunner, requestData);

CreateSignal(requestData, GainCapitalFutureOperationType.ConnectServer);
}

private void ConnectToServer(IClientRunner clientRunner, RequestData requestData)
{
// ReSharper disable once InconsistentNaming
var applicationUUID = GetApplicationUUID(requestData);

var connectionContextBuilder = new ConnectionContextBuilder()
.WithUserName(requestData.UserName)
.WithPassword(requestData.Password)
.WithHost(requestData.Url)
.WithPort(GainCapitalFuturesConfiguration.Default.OrderServerPort)
.WithUUID(applicationUUID)
.WithForceLogin(true)
.Build();

// connect to GF server
clientRunner.Client.Connection.Aggregate.Disconnect();
clientRunner.Client.Connection.Aggregate.Connect(connectionContextBuilder);
}



Methods on ClientRunner

public ClientRunner WithClient(IGFClient client)
{
_runner = new GFClientRunner(client);
Client = client;
return this;
}

public void Start()
{
if(_runner == null)
throw new InvalidOperationException("GFClientRunner is not initialized");

_runner.Start();
}

Is there anything wrong with those codes?

2. The problem may be thread timing. If you're not on the GFAPI thread, you should call Connect inside GF.Threading.Invoke
I see that the GFClientRunner will create a new thread to call Advance(). It may not be on the same thread with my connection process. So I put the connection process into GFClient.Thread.Invoke(), but it does not work. The same issue happened.

3. On the other hand, if the GFAPI thread is being hogged by a complex process, then Advance won't be called frequently enough to successfully log in.
In my process, there are a lot of Tasks which are executed in newly Thread (by Task.Run()) before the GF request is called. Also, I'm using the async/await approach to call between services/functions.

Does this somehow cause a side-effect when working with GF API?
I have tried to call the GF request in place that the process does not call Task.Run() much. And it has worked.

Note that, when the issue happened, I have only called the GF request first time. So it means the problem is not from the sharing existing GFClient (as you can see the code above, I encapsulate the whole GF request process inside a class. Another request in my process will create a new instance of that class and work with another GFClient).

I'm getting stuck and have no any clue. Do you have any other suggestion?
And please check your server log and give me any information which relates to the issue.

Thanks a lot.
SRuscak
Posts: 50
Joined: Aug 24, 2017


Posted: Jun 26, 2020 @ 08:45 AM             Msg. 4 of 10
Creating a client per-request is not a use case we've designed for. Typically we expect a single instance that runs for the life of the application, which may connect and disconnect many times, even for different users. Your use case might work, but I can't be sure.

It looks to me that, in your code, after calling Aggregate.Connect the code does not wait for LoginComplete to fire before calling CreateSignal. Aggregate.Connect is not synchronous.

As for a side effect involving async/await... it may be possible. We will investigate and release a patch if something turns up.
RAnde
Posts: 20
Joined: Sep 05, 2017


Posted: Jun 29, 2020 @ 05:12 AM             Msg. 5 of 10
Hi Ruscak,

#1 - It looks to me that, in your code, after calling Aggregate.Connect the code does not wait for LoginComplete to fire before calling CreateSignal. Aggregate.Connect is not synchronous.

I know that GF API works as an asynchronous approach. Actually, my CreateSignal method will make the current thread to wait until there is a corresponding signal raised in LoginComplete event indicating that the connection process is completed. I only continue to execute my request (call SendOrder, Lookup symbol...) after that process.

Because my application works as synchronous. Every request (get account information, place order...) needs a result returned. So that's how I deal with your asynchronous API.
The point is, in some places I call my request, my code does not connect to GF successfully. The Disconnected and LoginFailed events are raised instead of LoginComplete.
Because my approach has worked, so I think the problem is not from here. If you think it's from my process which I deal with the asynchronous GF API, would you please specify what is wrong here?

#2 - Creating a client per-request is not a use case we've designed for. Typically we expect a single instance that runs for the life of the application, which may connect and disconnect many times, even for different users. Your use case might work, but I can't be sure.

2.1 My application allows multiple users, who have a specific GF Account, work on it. For now, a GF Client will be created and connected to GF server with user's credential every time the user makes a request with their account. And that GF Client instance will be disconnected and disposed within the request.
There may are a lot of users who make requests at the same time. And according to your API documentation, GF API does not support multi-threading. If I use only one single instance of GF Client for the whole application, how can I deal with this requirement?
User cannot wait because the single GF Client instance has been working for another.

2.2 By the way, I have tried to use one single instance of GF Client as your suggestion. It has worked, but it is not stable. In some places during my process, the issue still happens.
When the issue happens, I have tried to re-connect again, sometimes it's connected successfully, sometimes not. The log as below

// The log when the issue happens
GF client disconnected. Message: ConnectionError. Reason: LoginFailure
Login Failed. Reason: ConnectionError

// The log when re-connecting
Login Failed. Reason: ConnectionError

As you can see, the Disconnected event is not even raised when I re-connect. So I don't know exactly the rule of the issue.

#3. One clue I have about this issue. In the place where the issue happens, it takes more time to create an instance of GF Client than where the issue does not occur. Usually, it takes around 10 seconds to create GF Client. But in my process where the issue happens, it takes more than 1 minute (even 2 mins).

I'm getting stuck with this issue several days. I really want to find out the root cause. I'm ready to provide any detail that you need to address this issue.

Thanks a lot.
Edited by RAnde on Jun 29, 2020 05:16 AM
SRuscak
Posts: 50
Joined: Aug 24, 2017


Posted: Jun 29, 2020 @ 10:16 AM             Msg. 6 of 10
Internally, we use a client-per-application model, but I believe we do have clients that use a client-per-user model. As for a client-per-request model, one downside is that there is a significant chunk of initialization data that gets downloaded on every login. But more importantly, only one simultaneous login per user is allowed, so you would have to make requests sequential per-user.

Looking at your server logs, I see that there are some 'user already connected' errors. This could be because of the above issue, or because you aren't disconnecting after the request, or because you aren't using 'force login' in the connection context.

Connection can't be perfectly reliable, so maybe you should use a reconnection attempt strategy. I posted one here (https://apisupport.gainfutures.com/Topic/Index/1336), if you want to adapt it.
RAnde
Posts: 20
Joined: Sep 05, 2017


Posted: Jun 30, 2020 @ 05:24 AM             Msg. 7 of 10
As I mentioned in previous posts, I already disconnect and dispose the GFClient instance at the end of request. I also use the "force login" option when building the connection context.

I'm disconnecting one time before connecting to GF server (as recommended in GF examples), and one more time at the end of request. Does this cause any thing wrong?
I make this question because the issue frequency decreases explicitly after I remove the disconnecting at the end of request (just disconnect before calling GFClient.Connection.Aggrerate.Connect())

You can see details in the code below

private void ConnectToServer(IClientRunner clientRunner, RequestData requestData)
{
// ReSharper disable once InconsistentNaming
var applicationUUID = GetApplicationUUID(requestData);

var connectionContextBuilder = new ConnectionContextBuilder()
.WithUserName(requestData.UserName)
.WithPassword(requestData.Password)
.WithHost(requestData.Url)
.WithPort(GainCapitalFuturesConfiguration.Default.OrderServerPort)
.WithUUID(applicationUUID)
.WithForceLogin(true)
.Build();

// connect to GF server
clientRunner.Client.Connection.Aggregate.Disconnect();
clientRunner.Client.Connection.Aggregate.Connect(connectionContextBuilder);
}

public void Dispose()
{
if (_runner != null)
{
_runner.Dispose();
_runner = null;
}

if (Client == null)
return;

Disconnect();
Client = null;
}

private void Disconnect()
{
if (Client.Connection.Aggregate.IsConnected || Client.Connection.Aggregate.IsConnecting)
Client.Connection.Aggregate.Disconnect();
}


I gonna try the "one Client-per-user" approach combined with the "re-connect" strategy.
One question, should I start and stop the client runner (stop calling advance()) within a request for the "one Client-per-user" OR just start the client runner one time when it is initiated (I still connect and disconnect client inside a request, but keep the client runner running)?

Thanks Ruscak
Edited by RAnde on Jun 30, 2020 05:35 AM
SRuscak
Posts: 50
Joined: Aug 24, 2017


Posted: Jun 30, 2020 @ 01:20 PM             Msg. 8 of 10
The connection operates as a state machine. If it's already disconnected, there should be no need to disconnect again.

As for the client runner, I don't think there's any benefit in running it while the client is disconnected.
RAnde
Posts: 20
Joined: Sep 05, 2017


Posted: Jul 08, 2020 @ 04:51 AM             Msg. 9 of 10
Hi Ruscak,

I want to change my application design in which I will use the same login session for multiple operations of various requests. It's kind of connection pooling. When a request is made, the GFClient instance (unique per user) will be put in the pool, and its state is set to "Used". If there is another request made simultaneously by same user, it will pick up the corresponding GFClient in the pool which is already logged in for the first request.
Basically, that means that multiple requests from the same user can be processed in the same login session (for that user). This will avoid the bottleneck when I have to process requests sequentially per user as you mentioned above.

But I have a concern. GF API works asynchronously, so I have to subscribe to callback events to handle the request's result. And for now, the callback event only processes one request at a time. I'm not sure that GF API works properly in case there are multiple requests made in the same login session and the subscribed callback events must handle all of them.
Is the GF API designed to work like that? Is there any unexpected exception If I apply that new design?

Thanks in advance
SRuscak
Posts: 50
Joined: Aug 24, 2017


Posted: Jul 08, 2020 @ 07:41 AM             Msg. 10 of 10
Reusing a connection for multiple requests from the same user sounds like a good idea. You won't have to log in and initialize every time.

>This will avoid the bottleneck when I have to process requests sequentially per user as you mentioned above
The requests must be made on the GFAPI thread, therefore they will be sequential. But there is no need to wait for the response before sending another request.

>the callback event only processes one request at a time
Well, it processes one *response* at a time. When Advance is called by the GFAPI thread, it processes all requests and responses, including raising events if need be. If you make two requests, the server will return two responses and GFAPI will raise an event for each, when it arrives. The callback event args will contain enough information to identify the request that initiated it.

>I'm not sure that GF API works properly in case there are multiple requests made in the same login session and the subscribed callback events must handle all of them.
This is normal. Consider an application like Gain Trader https://gainfutures.com/gain-trader/. It uses a single GFAPI login session and executes many many requests.