RO super TCP channel events throws exceptions (after upgrade to RO 10.0.0.1537)

Our application crashes with the following (illogical) error message
“Error reading parameter aUserInfo: Unexpected class found in stream; class “TUserInfo” does not descend from “TUserInfo”.”

It looks like the issue is server related but I have to investigate this further.

I already posted this now because it is a blocking issue and maybe you already have an idea on what causes this.

FYI: I did not notice this at development time when testing with a single client on the same machine as the server. Possibly the issue surfaces easier with multiple clients

I will try to make a RO 10.0.0.1495 build of the server and see if the issue is fixed.

Any tips on what causes this or what resolves this are appreciated

FYI some extra info:
Apparantly I have had similar issues in the past which were fixed by making sure the Login function was called.
I think the issue is caused when the connection disappears (also in the past).
But now the issue occurs .

            if True then // FVC 2018-08-07: Always enabled, we get EROException, Unexpected class found in stream; class "TUserInfo" does not descend from "TUserInfo". errors otherwise when opening 'verkoop' or 'bestellingen' - apparantly events received when not logged in screw up RO     ClientParameters.AutoAanmeldenRobot then
            begin
              TRobotLogOnHelper.RobotLogin();
class procedure TRobotLogOnHelper.RobotLogIn();
{$ifndef OFFROBOT}
var
 cguid : TGUID;
{$endif}
begin
{$ifndef OFFROBOT}
  try
    { Calls the remote login method }
    if assigned(dmGlobalRobot) then
    begin
      //dmGlobalRobot.Sum(1,2); // FVC TEMP

      cguid := StringToGUID(dmGlobalRobot.Login(dmGlobalRobot.Identification));
      dmGlobalRobot.SetClientId(cguid);
      //dmGlobalRobot.BinMessage.ClientID := cguid;
     // dmGlobalRobot.BinMessage_ER.ClientID := cguid;
    end;
  finally // wrap up
  end;    // try/finally
{$endif}
end;

Attached you can find the RODL and itf files
rodlanditf.zip (23.9 KB)

A preliminary test with RO 10.0.1521 + a slightly older version of XE11 does not have the issue.

However looking at our crash history for all our customers (~1000 computers) we see from 5 to 20 crashes each month since 2018 (=beginning of our crash history)
So the issue was always there but occured in rare circumstances.

Hi,

What exactly super tcp channel you are using?

we have these versions of it:

  • Socket
  • Synapse
  • Indy

Try to change used channel with other one: Indy → Socket or Synapse, Socket ->Indy or Synapse, etc.
change server and client.
it is possible that tcp package is broken by some reasons during transmission so it cannot be read properly …

We are using delphi synapse (TROSynapseSuperTCPChannel/TROSynapseSuperTCPChannel) for both client and server
Our implementation has not changed in the recent years.
Our implementation works with RO 10.0.0.1521.
Are there external component changes between 1521 and 1537?
Are there internal changes that might explain this?

How can we make the code robuster? Looking at the callstack there is not much I can do on our side.
Any suggestions?

Is there any progress on this issue?
We now have to make releases using an 2 development machines (an old one which still has RO 1521 for the super tcp server).

Hi,

I think, checking stream with “broken” event we can detect what was a failure - broken stream itself or not.

You can save incoming stream via correspondent event of channel or message.

What are you suggesting?
Should I add some code to my application?
For which purpose?
to solve the issue or to send you some data which you can investigate?

Hi,

I suggest to save incoming events to file so later we can inspect content of this file for any failure.

yep, you should

you said earlier

so I have suspicious that incoming stream with event is broken in some way and want to confirm this.

can you confirm, that those crashes were happened with the same clients or it were crashed randomly?

Are you suggesting I change my client side by hooking the ReceiveStream event?
If the code below OK?
I assume a lot of files will be generated.
How can we identify the stream which will result in the uncaught exception?

procedure TdmGlobalRobot2.ROSuperTCPChannelReceiveStream(aStream: TStream);
var
  path,fname:string;
  fs:TFileStream;
begin
  if ls().FileLoggingEnabled then
  begin
    path:=IncludeTrailingPathDelimiter(OfficinallMainDir())+'log\robotclient';
    if CreateDirectory(path,true) then
    begin
      fname := IncludeTrailingPathDelimiter(path)+'robotclientstream'+CreateFileNameDateTime(True,True)+'.bin';
      fs:=TFileStream.Create(fname,fmCreate);
      try
        fs.CopyFrom(aStream);
      finally
        FreeAndNil(fs);
      end;
    end;
  end;
  inherited;
end;

Hi,

Your code is ok.

You can use similar event of message:

procedure TClientForm.EventReceiverMessageReadFromStream(aStream: TStream);

it will allow to save only events. of course, EventReceiver should use personal message component.

also I though to save all streams under the same message.
it will allow to keep the latest stream only.

FYI: My newly built client does not seem to crash on my local tests
I’ve been stresstesting it for the past few hours.

However I am not really confident since the problem in the past also occured more easily for our customers than in our development environment.

I am able to crash the old client executable however.
I also figured out that the apparant random crashes are not random but are caused each time another client registers.

So with the old code a single client works
but as of the second client login the previous clients throw an error

Our code:

function TRobotLoginService.Login(const UserId,machineName,extra: ROAnsiString): ROAnsiString;
var
  newuser          : TUserInfo;
  RobotEvntswriter : IRobotEvents2_Writer;
  msg_c_a          : TMsgLogClientAdded;
  msg2s            : TMsgLogToSession;
begin
  DmdStockRobot.LogMsg('TRobotLoginService.Login called for user '+UserId);

  { Checks if the user is already logged in }
  if (fUsers.Search('UserID', UserId) <> NIL) then
    raise Exception.CreateFmt('Client %s is already logged in'#13'Select an other ClientID', [UserId]);

  { Adds the user to the internal list of logged users }
  newuser := fUsers.Add;
  newuser.UserID := UserId;
  newuser.SessionID := GUIDToString(Session.SessionID);

  { Stores the UserID of the user in the session.
    This will be used in the OnLogout and OnSendMessage methods }
  Session.Values['UserID'] := UserId;
  result := newuser.SessionID;

  RegisterEventClient(GuidToString(Session.SessionID), EID_RobotServerEvents2);
  RegisterEventClient(GuidToString(Session.SessionID), EID_RobotEvents2);

  // -- krg -- 31/08/2005 13:46:59 ------------------------------------------
  // De code heeft eigenlijk geen nut (maar verwijderen we nie).  De reden is
  // simpel.  Je client zal dit bericht nooit ontvangen omdat de SessionID nog
  // niet toegekend is aan de client-kant.
  try
    if supports (EventRepository, IRobotEvents2_Writer, RobotEvntswriter) then
    begin
      RobotEvntswriter.SessionList.CommaText := Result;
      { Only broadcasts to the session listed in SessionList }
      RobotEvntswriter.ExcludeSessionList := FALSE;
      { Generates the OnLogin event }
      RobotEvntswriter.OnLogin(EmptyGUID, newuser);
    end;
  finally
    RobotEvntswriter := nil;
  end;

Meanwhile I think the cause of the issue is probably a name collision for 2 types with the name TUserInfo.

What happens for events when there is a new and an old intf file which contains
RegisterROClass(TUserInfo, DefaultNamespace);
in its initialization section?

Hi,

If you have two Intf that contain the same declarations, these Intf have to use different DefaultNamespace like

const DefaultNamespace: string = 'NewLibrary_old';

and

const DefaultNamespace: string = 'NewLibrary_new';

You can specify Message.DefaultNamespaces for using types from specified _intf like

Message.DefaultNamespaces := new_intf.DefaultNamespaces;

They should be different (see attached files)
It is also unclear to me why the problem occurs now (with newer XE version and newer RO version but identical code: unit order in dpr is the same)
RobotLibrary_Intf.pas (125.1 KB)
RobotLibrary2_Intf.pas (226.3 KB)

Hi,

you can set

EventReciever.Message.DefaultNamespaces := RobotLibrary2_Intf.DefaultNamespaces;

for using RobotLibrary2_Intf.TUserInfo type in events

or

EventReciever.Message.DefaultNamespaces := RobotLibrary_Intf.DefaultNamespaces;

for using RobotLibrary_Intf.TUserInfo