#1 2012-11-05 16:42:40

h.hasenack
Member
From: Nijmegen, Netherlands
Registered: 2012-08-01
Posts: 173
Website

Generating records at server side

Hi Ab

I did not get time yet to study and memorize the entire manual, but here's the thing. I might be asking for something obvious.

I have an interface at server side, that is called from client side. (Nothing new there, works like a charm). This interface however produces some data that I want to store in (memory) tables so it can be adressed from client side using the obvious CreateFillAndPrepare methods etcetera.

The first question is: (How) Can I store these records on server side for availability in the correct Client session (memory tables you know). I seem to have no access to the correct REST instance on server side from within my interfaced object at server side... These objects are instantiated without any reference to the "owning" rest server...

Maybe adding an (optional) ISQLRestOwner interface for registered interfaces can fix this? The REST server would then only set the REST owner in case the object supports the SQLRestOwner interface.

(BTW except from the "root" server, my "workbase" rest servers are created dynamically for each "project database" opened).


Also: I dont like the idea of my objects being packed to JSON and unpacked from JSON just to store them, whuile the data is actually in the same process (server side).

So the second question is: How do I get around this?

Hans

Offline

#2 2012-11-05 19:15:56

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 13,187
Website

Re: Generating records at server side

About your 1st question, there is a global thread variable named ServiceContext to retrieve the running factory and its TSQLRest instance.

type
  PServiceRunningContext = ^TServiceRunningContext;
  /// will identify the currently running service on the server side
  // - is the type of the global ServiceContext threadvar
  TServiceRunningContext = record
    /// the currently running service factory
    // - it can be used within server-side implementation to retrieve the
    // associated TSQLRestServer instance
    Factory: TServiceFactoryServer;
    /// the currently runnning session identifier which launched the method
    // - make available the current session or authentication parameters
    // (including e.g. user details via Factory.RestServer.SessionGetUser)
    Session: ^TSQLRestServerSessionContext;
    /// the thread which launched the request
    // - is set by TSQLRestServer.BeginCurrentThread from multi-thread server
    // handlers - e.g. TSQLite3HttpServer or TSQLRestServerNamedPipeResponse
    RunningThread: TThread;
  end;

threadvar
  /// this thread-specific variable will be set with the currently running
  // service context (on the server side)
  // - is set by TServiceFactoryServer.ExecuteMethod() just before calling the
  // implementation method of a service, allowing to retrieve the current
  // execution context
  // - its content is reset to zero out of the scope of a method execution
  // - when used, a local copy or a PServiceRunningContext pointer should better
  // be created, since accessing a threadvar has a non negligible performance cost
  ServiceContext: TServiceRunningContext;

2. If you use in-memory optimized tables, i.e. TSQLRestServerStaticInMemory instance (see the doc), you can manage such very high-speed and low-memory storage.
You can have direct access to those, if needed, via e.g. the TSQLRestServer.GetStaticDataServer() method.
To avoid multi-threading error at writing, you can call TSQLRest.AcquireWrite() / ReleaseWrite() methods.

Offline

#3 2012-11-05 20:14:12

h.hasenack
Member
From: Nijmegen, Netherlands
Registered: 2012-08-01
Posts: 173
Website

Re: Generating records at server side

Thx - I already had an idea it was thought of.

Here's a little encapsulation tip...

In these kind of global variable constructs, I usually create a function to return (and optionally initialize) the variable. In this way I am sure "no-one" changes my global variable. (thus read-only)

I also came up with this nifty trick for (non thread) global variables because I ran into calling the initialization routine when it was actually shutting down

interface
  function MyGlobalObject:TMyObject;
  procedure DropMyGlobalObject;

implementation

VAR vMyGlobalObject:TMyObject=nil;

function MyGlobalObject:TMyObject;
begin
  if NativeInt(vMyGlobalObject)=-1 then 
    raise EFinalizetionError.Create('Cannot initialize MyGlobalObject when shutting down') //  the global object was finalized!
  else if vMyGlobalObject=nil then
    vMyGlobalObject=TMyObject.Create; // the global object needs to be initialized
  Result:=vMyGlobalObject;
end;

procedure DropMyGlobalObject;
begin
  if NativeInt(vMyGlobalObject)<>-1 then
    FreeAndNil(vMyGlobalObject); // allows re-initialization. Comes in handy for unit tests.
end;

procedure FinalizeMyGlobalObject;
begin
  DropMyGlobalObject;
  NativeInt(vMyGlobalObject):=-1; // mark as finalized.
end;

initialization

finalization
  FinalizeMyGlobalObject;
end.

Off course, the same trick goes for interfaces too. And as objects are always (at least!) on 4byte boundaries, the object pointer is impossible to be -1 for any real object instance, so... it;'s pretty safe,

One drawback is that there is no exception handling available during shutdown, so you may consider using a different solution than raising an exception. In the debugger, however, it works like a charm to find initialization/finalization culprits.

It would be nice to have this also for thread variables, but unfortunately there is no safe/watertight way I know of to "catch" terminating threads.

Last edited by h.hasenack (2012-11-05 20:31:23)

Offline

#4 2012-11-05 21:29:39

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 13,187
Website

Re: Generating records at server side

In fact, a threadvar kind of variable is implemented using an internal (inlined) function, and uses the OS abilities to have a per-thread storage.
After execution of the service method, it is initialized back to nil. So it is safe to be used.

Using explicitely a thread-driven approach allows several servers to be run at once in the same process, which may be the case in some uses.

Offline

#5 2012-11-06 07:35:51

h.hasenack
Member
From: Nijmegen, Netherlands
Registered: 2012-08-01
Posts: 173
Website

Re: Generating records at server side

Really?
I am pretty sure that an objkect/interface assigned to a threadvar will NOT be released automatically.
It it is set to nil, it's probably because you programmed it that way, as the compiler does not do this for you.
The encapsulation tip was mainly about making your global vars readonly.
The finalization tip was about the termination of the application to catch faulty initialization calls.

BTW I know about threadvars, they are simply thread-local but application global variables.

Offline

#6 2012-11-06 08:52:59

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 13,187
Website

Re: Generating records at server side

I wanted to say that the framework set it to nil after method execution.
There is a try..finally section to protect the threadvar content.

We may make the global vars readonly, but it may also use a pointer or a wrong value, in some cases.

Thanks for the input.

Offline

#7 2012-11-06 09:12:18

h.hasenack
Member
From: Nijmegen, Netherlands
Registered: 2012-08-01
Posts: 173
Website

Re: Generating records at server side

Thx

Offline

Board footer

Powered by FluxBB