Very very confused on state-free appserver and database conn

Posted by OctavioOlguin on 09-Mar-2016 13:49

I developed several procedures that run inside a state-free appserver based on the following algorithm.. (this happens on the client)

RUN func\ConnectAppServer.p(INPUT-OUTPUT hServer, "databaseName", OUTPUT res).

run isConnected.p on hServer ("databaseName", output dbConnected).
LOG-MANAGER:WRITE-MESSAGE("databaseName connected?..  " + quoter(dbConnected) ).

EMPTY TEMP-TABLE ttViaje.      
RUN procs\viaje\GetDataTrip.p ON hServer 
            (fTrip, "" , OUTPUT DATASET dsTrip).
        
RUN  func\DisConnectTAP.p ON hServer
            ("sSuc03001.w", OUTPUT lRetOK).
hServer:DISCONNECT ().
DELETE OBJECT hServer.

But I've being seing very strange behavior, for instance, the log writing reports that dabaseName is connected indeed. but on the same session (i think that from ConnectAppServer.p stablished connection to the hServerDisconnect(). is a connection not broken by the state-free nature, I mean that database connection will remain persistent for this agent).  BUT I got a STOP due to databaseName not connected on the GetDataTrip calling...

GetDataTrip is a helper procedure to call a BE to fill the dataset with the record indentified by the fTRIP key.

The strange part of this is if retry several times the procedure... eventually will succeed.....

So  I don't know whats going....  I'll go to ckeck the OERA, and autoedge for some inspiration (and code I can steal, sorry, reuse...)

Thanks..

All Replies

Posted by Fernando Souza on 09-Mar-2016 14:03

The thing is that a state-free AppServer connection is, well, state free. There is no guarantee that you will hit the same agent every time you run a request, so no context can be expected to exist between requests. Each time you may be getting to one of the different available agents. Any context that needs to be kept must be handled by the application itself. So you would need to move the code that you are expecting to be executed into the procedure that is executing remotely, or if it's generic enough, you can put it in the 'activate' procedure.

Posted by smat-consulting on 09-Mar-2016 14:29

Your client shouldn't worry about how the server procedure gets the data. Simply ask for the data you want form the server i.e. run only getDataTrip.p on server.

The procedure that you're running on the server shouldn't have any DB references in it. It should check if DB is connected, if not connect it. Only once connected, the actual program that's looking at the DB is ran.

I am old-school, so I use persistent procedures not classes. I usually run all my programs persistent, starting them off with the activate. The program that is called from the client is a small wrapper - that ensures the DB is connected, then executes an API procedure that is in one of the persistent procedures. That API procedure orchestrates the collection of the data, the preparation (if needed) and so on...

That way, the programs don't need to be started over and over again, but are all sitting there waiting. The only program that needs to be restarted with every request is the wrapper that's called from the client...

BTW, when using temp-tables always first empty the temp-tables in the server-run procedures, to ensure there's nothing left hanging around from a previous call. And empty them also at the end of the request, so the temp-table space is not used up with stale data...

Posted by OctavioOlguin on 09-Mar-2016 16:45

So my client program should call something like:

run do_whatever_you_have_to_do_to_give_me_the_customer.p on someServer (custnum, output dataset dsClient).

The forementioned procedure it's just a wrapper that has to connect to whatever db it should to call the actual class or BE to do the actual fetching...?   sounds ok.  Other option would be to connect to database at agent start......

I was think I would be able to use this procedures to expose as rest for the mobile implementation of querys... just wondering...

On other side, would it be possible to make and example of the calling scheme you use?

TIA!

Posted by smat-consulting on 09-Mar-2016 18:18

Well, if the program you call on the server is accessing the DB you have a problem should the DB have gotten disconnected for whatever reason.

If you call a wrapper procedure that does not have a DB reference, you can simply connect the DB and do your work. If the DB is connected already, it simply hands off the request to the actual program without any further action.

I don't do app server from a guy client because OE doesn't support Apple - I use WebSpeed, as browser runs on anything: windoze, mac, tablets, mobile... However, WebSpeed is in essence using the stateless app server; the ultimate statelessness, if you will :)

I have one program that includes the cgi-defs.i, reads the passed parameters into a temp-table, and outputs text-content to the web stream. Then it calls the actual procedure that is appropriate to fulfill the request. That procedure (and all it calls) creates a JSON response, which it stores as text-blocks in a temp-table - the same tt that the wrapper procedure outputs to the web stream - one text-block after the other.

This system allows me to have only one single entry procedure. It also allows me to run the whole application form the editor for debugging and testing purposes (simply filling the parameter tt with values simulating the web-request, and outputting the text that usually would go to the web-stream  into a file).

When dealing with a GUI client/Appserver system you might not be able to have only one single wrapper program, as I do in WebSpeed. However, depending on your application, you might be able to write a handful of generic wrappers using dynamic temp-tables and datasets to pass through the request parameters and results.

Or you might be able to do some fancy preprocessor magic to use the same physical program-file for both the wrapper (in which case the  other logic is &IF-DEFed out) and the API (in which case the wrapper logic is &IF-DEFed out).

Hard to provide ideas without knowing what kind of requests you are planning on sending back and forth...

Posted by smat-consulting on 09-Mar-2016 18:23

Here's a sample wrapper/api system as mentioned above - no idea whether this is applicable in your case, just to give you an idea ( have the three files located in a temp directory within the main working directory). Try to compile the two .p with PREPROCESS option ( for example: COMPILE temp/octavio.p PREPROCESS temp/ocatavio.pp.p)...

temp/apiOcatvio.p:

/*-----------------------------------------------------------------------------

File         : api/apiOctavio.p

Purpose      : simply includes octavio.p with the API preprocessor set to TRUE

               to implement the actual API logic

 Author(s)   : initialized by SMAT-tools of SMAT-Consulting

               Thomas Hutegger - info@smat-consulting.com

 Created     :

 Notes       :

 History     :

2016-03-09 tmh created template

 ---------------------------------------------------------------------------*/

{ temp/octavio.p

 &API = "TRUE"

 }

/*---------------------------------------------------------------------------*/

temp/ocatvio.p:

/*-----------------------------------------------------------------------------

File         : api/octavio.p

Purpose      : implements wrapper and actual API

Preprocessors: &API  "" in octavio.p

                     "true" in apiOctavio.p

 Author(s)   : initialized by SMAT-tools of SMAT-Consulting

               Thomas Hutegger - info@smat-consulting.com

 Created     :

 Notes       :

 History     :

2016-03-09 tmh created template

 ---------------------------------------------------------------------------*/

DEFINE TEMP-TABLE ttSampleIn

 FIELD custNameFrom    AS CHARACTER

 FIELD custNameTo      AS CHARACTER

 /* only one record, so no index is needed */

 .

DEFINE TEMP-TABLE ttSampleOut

 FIELD custNum         AS CHARACTER

 FIELD custName        AS CHARACTER

 INDEX upi             IS UNIQUE PRIMARY

                       custName

 .

DEFINE INPUT        PARAMETER TABLE                       FOR ttSampleIn.

DEFINE       OUTPUT PARAMETER TABLE                       FOR ttSampleOut.

DEFINE       OUTPUT PARAMETER opcMessage                  AS CHARACTER NO-UNDO.

               &IF "{&API}" = ""

                 &THEN  /* wrapper */

{ temp/octavioCheckDb.i }

/* only if DB is connected the next statement is being executed */

RUN apiOctavio.p

 ( INPUT        TABLE ttSampleIn

 ,       OUTPUT TABLE ttSampleOut

 ,       OUTPUT opcMessage

 ).

                 &ELSE  /* actual api */

RUN whateverProceduresYouWantToDoTheWork.

RUN someMoreIfYoUWant.

RUN untilAllIsSaidAndDone.

                 &ENDIF

temp/ocatvioCheckDb.i:

/*-----------------------------------------------------------------------------

File         : api/checkDb.i

Purpose      : check fi the DB is connected and if not try to connect to it.

               if still not connected return error-message, otherwise let

               includer make one statement of their choice

 Author(s)   : initialized by SMAT-tools of SMAT-Consulting

               Thomas Hutegger - info@smat-consulting.com

 Created     :

 Notes       :

 History     :

2016-03-09 tmh created template

 ---------------------------------------------------------------------------*/

IF CONNECTED ( "db" )

THEN CONNECT db/sports -pf db/sports.as.pf.

IF NOT CONNECTED ( "sports")

THEN ASSIGN

 opcMessage = "ERROR - db not connected"

 .

ELSE

/*---------------------------------------------------------------------------*/

Posted by OctavioOlguin on 09-Mar-2016 19:28

Thanks!!!!!!

I'll be analizing every atom of this construct!!!!!

Greetings from Mexico

Posted by OctavioOlguin on 10-Mar-2016 09:51

For the quick fix and getting to start of the module, I connected the 3, now 2 agents (lowered the value -licensing concerns-) to the databases needed (3) and get rid off of the connect / disconnect procedures...  Ordering 5 licenses more on the server....

It's amazing and proof of the synergy of progress. I started with 10 users on workgroup server and 10 client networking and 5 appsever.... Now (2 yr latter) about to go 20 users and enterprise db....  it's been a great journey.  Thanks

This thread is closed