Events not working on some machines

I have code that has been working for years, and indeed still works fine on my computer. But on the customer’s computer, the events are not getting through. Does anyone know of a reason why this might be? Logging shows everything being sensible.

I’m using the “The Winter 2010 release of RemObjects SDK”, in Delphi 2007.

The code is below. I left in the old loop just in case, but the GUID value is correct, obtained from Session.GUID. Also note that this is actually the server, but the “IClientToServerEvents” is the wrong way around because of the terminology in the application. So it is more “server to client” really in RO terms.

/Matthew Jones/

procedure TCProcessThread.SendDataStored(nResult : Integer);
var
xEvents : IClientToServerEvents_Writer;
xSessionList : TStringList;
begin
Report('Sending DataStored status ’ + IntToStr(nResult));
xSessionList := TStringList.Create;
try
MyServiceModule.ROInMemorySessionManager.Critical.Enter;
try
BS2Client.ROInMemorySessionManager.GetAllSessions(xSessionList);
finally
MyServiceModule.ROInMemorySessionManager.Critical.Leave;
end;

	xEvents := (MyServiceModule.ROEventRepository as IClientToServerEvents_Writer);
	xEvents.ExcludeSessionList := False;
	xEvents.SessionList.Clear;
	xEvents.SessionList.Add(m_szSessionGUID);
	xEvents.DataStored(EmptyGUID {session.SessionID}, nResult);
	Report('Sent DataStored to ' + m_szSessionGUID, true);

// for nLoop := 0 to xSessionList.Count - 1 do
// begin
// xEvents := (MyServiceModule.ROEventRepository as IClientToServerEvents_Writer);
// xEvents.ExcludeSessionList := False;
// xEvents.SessionList.Clear;
// xEvents.SessionList.Add(xSessionList[nLoop]);
// xEvents.DataStored(EmptyGUID {session.SessionID}, nResult);
// Report('Sent DataStored to ’ + xSessionList[nLoop]);
// end;
finally
FreeAndNil(xSessionList);
end;
end;

I should say that my code to create the event receiver does set legacy events.

procedure TClientConnection.PrepareEventReceiver(xHandlerObject : TObject);
begin
m_xHandlerObject := xHandlerObject as TEventProxy;

m_xROEventReceiver := TROEventReceiver.Create(nil);

//{$IFDEF RO_6}
m_xROEventReceiver.LegacyEvents := True;
//{$ENDIF}
m_xROEventReceiver.Message := m_xMessage;
m_xROEventReceiver.SynchronizeInvoke := False;
m_xROEventReceiver.Interval := 4000;
m_xROEventReceiver.Channel := m_xChannel;
m_xROEventReceiver.ServiceName := ‘BS2DataLink’;
m_xROEventReceiver.Activate;
m_xROEventReceiver.RegisterEventHandlers([EID_ClientToServerEvents], [m_xHandlerObject]);
end;

Hmm, not sure if I just “reported as issue” my own post, and thus marked it as spam or something! I figured the little bug icon meant that I was raising it as a formal issue…

Anyway, digging further, what I need is a way to know whether this is failing on the client or the server. And how far through the system it is getting, and why it is failing. I now have it failing in a VM, so repeating it is easy. I just cannot work out how to debug it further.

Okay, further info: Adding debug logging in the Envelope processing seems to imply that the server is not sending the message to the client. On the machine that it is working on, I can see two envelopes being prepared. On the one it is failing on, only one is prepared. The event is triggered by receipt of a packet of data to process, and indicates completion. I presume that the first outgoing packet is the ack for the incoming packet, and the second (missing on some computers) is the notification event.

How can I tell what is happening?

I added a dump of the xSessionList contents, and get:

0: Processing: Sending DataStored to
{16ECA176-6E4D-490D-89DF-33320AEFA363}
0: Processing: Session list
{16ECA176-6E4D-490D-89DF-33320AEFA363}

Thus the GUID is correct.

In TROInMemoryEventRepository.DoStoreEventData the call to free the event is not happening in the failing version. I presume that this means that sessionref.ActiveEventReference.DispatchEvent which calls lItem.Server.DispatchEvent is failing to run.

finally
  if newevent.RemoveRef < 1 then
		newevent.Free;
end;

Okay, digging deeper still, the item is queued, and eventually TSendEvent.Callback is called to do the actual sending. This then fails in the

  except
    on E: EROQueueFull do begin
      if Thread = nil then
        lRetry := True
      else
        Caller.QueueItem(Self);
    end;

which then just requeues it. It sits waiting forever at this point, being called and called and called, but always the queue is full.

Anyone know why the queue got full, and didn’t empty? client, or server responsible?

The error comes from TROSCServerWorker.WaitForAck, which times out. This is not surprising, since the server doesn’t seem to get it, so it won’t acknowledge it. Hence it times out (very quickly TBH) and goes around again. I think therefore I’m at the boundary of whether it got sent or not - I don’t think I can find out. help!

Okay, I appear to be quite behind in RO SDK versions, so my next step is to install the latest. Will check after doing that…

Okay, done that. Even more interesting! The new version works fine on my PC, as it did before, but now when the server sends an event the client disconnects. I’ll work out why - maybe that’s a clue.

Oh dear, it seems that this is not in my code at all. Probably a clue as to the problem, but some way to find out what would be nice… Will pick up on this tomorrow.

Hello Matthew,

This case is going to be hard to resolve. At the moment I have two alternatives of what to do with this:

  • try switching to another implementation of SuperTCP channel, server side or both. As far as I understand you are using Indy based components, try using Synapse based ones i.e. TROSynapseSuperTCPChannel and TROSynapseSuperTCPServer.
  • I’m not sure we can resolve this remotely so we need a test case to investigate. Try to narrow things down to a simple test case and send it to support@. If we are ‘lucky’ we could see it failing.

Best regards - Sergey

Okay, this one is so horrible to admit to. Took me over a week, adding logging and digging deep. Turns out that I have requested a small change to the TRoThreadPool object so that if you are smart enough to set the MaxThreads to zero, you are told it won’t work. My code sets up a pool as a fraction of the work to be done, and when the work is small, that division ends up as zero. No threads to do work means timeouts. By checking this on setting, and raising an exception, it would save anyone else this hardship. Like I say, it works fine on my PC, because there is lots of work for it…

Hello Matthew,

Glad to hear that you could resolve this case. It is a valuable experience for you and for us, customizing the thread pool is not the most frequently used feature. We will consider adding a MaxThreads check to the code, thanks for the idea.
And as you can see it was impossible for me to find the actual issue without seeing your code.

Best regards - Sergey

“And as you can see it was impossible for me to find the actual issue without seeing your code” - indeed, and of course a test case is hard to set up, as without the real code it would work. Due to the nature of comms work, a test setup would have been a VM with the development system, and another one to be the remote. Such is life.