Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the preferred way of server code notifying multiple clients of data changes within a single Delphi Application?

I have a large Delphi Application which has core 'server' code containing my data. Within the same app, 'client' the user is able to open and close multiple non-modal 'client' forms to inspect this data. Data changes fall into two types - major (e.g structural changes like data has been added or deleted) and minor such as a change to a data value. Existing open client forms must update to show changed data within a short-ish time. This is not a database, my 'server' using my own data structures so my solutions may have missed possibly standard techniques that are available within a formal database structure. That said, I have repeated my solutions so many times now that I thought I would ask if there are formal techniques and possibly Delphi components that would improve or simplify my code. I am about to move to multithreaded code which make the question even more relevant to me.

I use two methods:

  1. Timestamp. The 'server' code maintains an Int64 value taken from QueryPerformanceCounter. Client forms examine this value on a 300ms ticking timer and update themselves if their copy of the timestamp differs from the server's. I guess this is my 'pull' solution.

  2. Interface notification. The 'server' code maintains a class descended from TInterfaceList with AddClient and RemoveClient methods which register a simple common client notifcation interface. Each of the clients registers itself with this list when created and unregisters on destroy. Data changes at the server trigger an iteration through this list calling each client to advise it of change. I guess this is my 'push' solution.

I love interfaces and solution 2 seems nice since it avoides ticking timers and is easily debugged (although the unregister calls can be problematic with order of destruction). There are potential performance implications tooh because it is quite likely that there may be thousands of data changes per second and I have to be careful to use a BeginUpdate / EndUpdate mechanism to convert my many server data changes into one actual notification call. Ultimately I end up needing a timer of some kind to aggregate the calls into one gentle update of a displayed form.

Both solutions work nicely though and I'm torn between the two. For a mulithreaded solution I'm sure there are other pitfalls I know nothing about. Any comments would be appreciated. I'm using XE2.

like image 418
Brian Frost Avatar asked Feb 27 '12 09:02

Brian Frost


2 Answers

You need to take into consideration what you want to happen when the number of clients grows, then decide between the two evils:

  • is it OK if my performance degrades while being sure all data is current, always and everywhere in the application (then you need the observer pattern)
  • is it OK if the data in some places lags behind in order to improve performance (then you could use polling and make the interval longer when the poll iterations cause too much slowdown)

I'm not a fan of polling, as it usually leads to very convoluted solutions (well, at least the things I tried, maybe I did it the wrong way back then).

I'd implement the Observer Pattern in Delphi using interfaces, you could use this or this as a start.

like image 200
Jeroen Wiert Pluimers Avatar answered Oct 12 '22 08:10

Jeroen Wiert Pluimers


A used the Windows API to solve a problem similar to this one. But I believe my approach is simpler. In my applicaion I event didn't know the number of "clients" and which forms where actually clients.

What I did is:

  1. Broadcast a Windows message to all forms opened by my application (screen.forms[X]). The message includes in the WParam a pointer to a record which contains information about an event. Also, different actions broadcast distinc messages. WM_RECORDUPDATE for DB updates for example.

  2. Windows (clients) listen for message in the way of:

    procedure RecordUpdateMessage(var msg: TMessage); message WM_RECORDUPDATE;

Procedure RecordUpdateMessage read the record pointed by msg.WParam and based on the data in the record desides if to react to the update, insert or delete of a record in the DB.

like image 23
Christopher Ramírez Avatar answered Oct 12 '22 08:10

Christopher Ramírez