Best practices for method services

Hi All,

Rookie here with RO (Remoting SDK) and internet server programming in general. We have been working through RO and have made some progress but would like to double check to make sure we are moving in the right direction. We are building a Statefull server using Delphi hosting with an Azure windows virtual machine. The client will be a JavaScript web client.

A few questions and an example of one of our service method follows:

  1. When passing data back and forth between the client and the server we decided to use JSON. We ended up converting our data, which are predominately multi-dimensional arrays of doubles into JSON strings before transfer with our RO service methods. Is this a reasonable approach when considering our clients will be using JavaScript?
  2. If the client in a particular session makes multiple calls to method services without waiting for a return, I am concerned about possible memory/thread conflicts in the server For example ask the server to calculate and before that finishes starting changing data used by the calculation. Do you have to build a task queue of some sort on the server side or is this something RO handles or is the burden placed on the client?
  3. When developing an API for third party use, how do you best handle changes to your service methods without breaking clients?

[ROServiceMethod]
function Calculate: UnicodeString;

function TMyService.Calculate : UnicodeString;
var
DM : TDMData;
jo : TJSONObject;
begin
DM := (Session as TDMSession).GetDM; //Get reference to session
DoCalculations(DM); //Run internal calculations
jo := TJsonObject.Create;
DM.ToJSONObject(jo); //Converts results in JSON string
result := jo.ToString;
jo.Free;
end;

Thanks -Bob

for platform supported by RO (Delphi, .NET, Cocoa, JS, Java), better to use BinMessage - it will produce better performance

as I understand, you mean asynchronous calls. server will finish calculation and will send response to client in any way so this burden is placed on the client - they should decide what response is more actual.
with recent AsyncEx interface (delphi-side), this can be handled easily.

you shouldn’t change number of parameters and their types for existing methods.
for example, in Delphi sources, internal API was handled like

  IOTAProcess60 = interface(IUnknown)
  end;
  IOTAProcess70 = interface(IOTAProcess60)
  end;
..
  IOTAProcess150 = interface(IOTAProcess90)
  end;
  IOTAProcess = interface(IOTAProcess150)
  end;

so you can also use this behavior for major updates

thanks for the reply,

Yes asynchronous calls from the same client. Since the RO server can handle asynchronous calls, does that mean each service method will be in it’s own thread? Thus, we should be careful when accessing any of our data stored in the session object?

If we use BinMessage for RO messaging we would still have to convert our internal data to a JSON string so JS will be able to work with it? My understanding is BinMessage is the outside wrapper that RO wraps my data (a string in this case) and passes it on to the client?

Thanks

-Bob

yep - each call has own thread by default, but it is depending on used class factory.

it depends on used session manager. we have DBSessionManagers too

as I understand, you have a lot of multi-dimensional arrays of doubles so binmessage content will be

  • count (int32)
  • arr[0]… arr[N](double)

in case of Json, all doubles should be converted to string and later again converted to double, as a result, you will lose performance.
I suggest to you made a test : pass your array via BinMessage and via JsonMessage and compare performance.

1 Like

But what if you have a two dimensional array. Can something like this be passes directly without conversion using BinMessage? If yes, how would JS see the definition of the array on the client side?

Type
TMyArray = array[1…5, 1…10] of double;

Var
a : TMyArray;

Thanks Bob

looks like I misread.
this can be passed as

mytype = struct (a, b: double);
TMyArray = array of mytype;

Is there an example you can point be to where Struct is used with JS? Thanks Bob

if you declare this array in ServiceBuilder, it will produce this JS code:

// Struct: MyItem
__namespace.MyItem = function MyItem() {
    this.a = {dataType : "Double", value : null};
    this.b = {dataType : "Double", value : null};
};
__namespace.MyItem.prototype = new RemObjects.SDK.ROStructType();
__namespace.MyItem.prototype.constructor = __namespace.MyItem;
RemObjects.SDK.RTTI["MyItem"] = __namespace.MyItem;

// Struct: MyArray
__namespace.MyArray = function MyArray() {
    this.NewField = {dataType : "MyItem", value : null};
};
__namespace.MyArray.prototype = new RemObjects.SDK.ROStructType();
__namespace.MyArray.prototype.constructor = __namespace.MyArray;
RemObjects.SDK.RTTI["MyArray"] = __namespace.MyArray;

// Service: NewService
__namespace.NewService = function NewService(__channel, __message, __service_name) {
  RemObjects.SDK.ROService.call(this, __channel, __message, __service_name);
  this.fServiceName = this.fServiceName || __service_name || "NewService";
};

//    function echo(const NewParam: MyArray): MyArray;
__namespace.NewService.prototype.echo = function(
	NewParam,
	__success, __error) {
    try {
        var msg = this.fMessage.clone();
        msg.initialize(this.fServiceName, "echo");
        msg.write("NewParam", "MyArray", NewParam);
        msg.finalize();
        this.fChannel.dispatch(msg, function (__message) {
		var __result = __message.read("Result", "MyArray");
	        __success(
		__result
		);
        }, __error);

    } catch (e) {
        __error(msg, e);
    };
};

Is that the general approach to let the client mange its request to the server or should the server queue them somehow? You mentioned AsyncEX, is there something similar for JS? In our case the Web App is statefull so the server does have to be careful with processing order. Thanks for any guidance here. Bob

usual (i.e. non-super) channels can handle only one request per channel.
super channels can send requests without waiting for responses.
as I understand, you are using plain channels only and don’t use background threads for sending additional responces.
in this case, only one request from specific client will be handled by server.

JS has no asynchronous calls by default, but you can create functions that were called asynchronous via setTimeout or something similar