#1 2016-04-01 07:56:13

StxLog
Member
From: France
Registered: 2015-09-14
Posts: 58

Memory leak when connected with 2 client

Hi,



I have a TSQLRestServerDB declared as such:

  TSQLTESTRestServerDB = class(TSQLRestServerDB)
    public
      onMount: TSQLRestServerCallBack;
      onUnmount: TSQLRestServerCallBack;
    published
      procedure Mount(Ctxt: TSQLRestServerURIContext);
      procedure Unmount(Ctxt: TSQLRestServerURIContext);
  end;

And my main server class as such:

  TMainServer = class(TObject)
  protected
    procedure SetLog;
    procedure Connect;
    function OnConnect(Sender: TSQLRestServer; Session: TAuthSession; Ctxt: TSQLRestServerURIContext): boolean;
    function OnDisconnect(Sender: TSQLRestServer; Session: TAuthSession; Ctxt: TSQLRestServerURIContext): boolean;
    function OnUpdate(Sender: TSQLRestServer; Event: TSQLEvent; aTable: TSQLRecordClass; const aID: TID; const aSentData: RawUTF8): boolean;
  public
    fLog: TSynLog;
    fMODELBDD: TSQLModel;
    fBDD: TSQLDataBase;
    fREST: TSQLTESTRestServerDB;
    fRestTab: TObjectDictionary<RawUTF8, TSQLRestServerDB>;
    fHTTPServer: TSQLHttpServer;

    constructor Create;
    destructor Destroy; override;
    procedure MountREST(Ctxt: TSQLRestServerURIContext);
    procedure UnmountREST(Ctxt: TSQLRestServerURIContext);
  end;

So I set up my connection like this:

  fMODELBDD := TSQLModel.Create([]);
  try
    fBDD := TSQLDataBase.Create(ExeVersion.ProgramFilePath+'MainTest.s3db', '', SQLITE_OPEN_CREATE or SQLITE_OPEN_READWRITE);
    try
      fREST := TSQLTESTRestServerDB.Create(fMODELBDD, fBDD, True);
      try
        fREST.CreateMissingTables();

        fREST.OnSessionCreate := OnConnect;
        fREST.OnSessionClosed := OnDisconnect;
        fREST.OnUpdateEvent := OnUpdate;

        //My events
        fREST.onMount := MountREST;
        fREST.onUnmount := UnmountREST;
        // /

          fHTTPServer := TSQLHttpServer.Create(AnsiString('8080'), [fREST], '+', useHttpApiRegisteringURI);
          try
            fRestTab := TObjectDictionary<RawUTF8, TSQLRestServerDB>.Create([doOwnsValues]);

            fLog.Add.Log(sllInfo, ' - Init ok');
            fLog.Add.Log(sllInfo, ' - Server set on port ::8080::');
          except
            FreeAndNil(fHTTPServer);
          end;
      except
        FreeAndNil(fREST);
      end;
    except
      FreeAndNil(fBDD);
    end;
  except
    FreeAndNil(fMODELBDD);
  end;

MountREST:

procedure TMainServer.MountREST(Ctxt: TSQLRestServerURIContext);
var ref: RawUTF8;
begin
  if UrlDecodeNeedParameters(Ctxt.Parameters, 'ref') then begin
    ref := Ctxt['ref'];
    fLog.Enter();
    fLog.Add.Log(sllInfo, 'Ref: '+ref);

    if fRestTab.ContainsKey(ref) then begin
      //If already mounted
      Ctxt.Results([ref]);
    end else begin
      //if not
      fRestTab.Add(ref,
        TSQLRestServerDB.Create(
          TSQLModel.Create([], ref),
          TSQLDataBase.Create(ExeVersion.ProgramFilePath+ref+'.s3db', '', SQLITE_OPEN_CREATE or SQLITE_OPEN_READWRITE), True)
        );
      try
        //fRestTab.Items[refUniqueServeur].Model.Owner := fRestTab.Items[refUniqueServeur];    //Is this usefull?
        fRestTab.Items[ref].CreateMissingTables;

        if fHTTPServer.AddServer(fRestTab.Items[ref]) then begin
          Ctxt.Results([ref]);
          fLog.Add.Log(sllInfo, ' ++ '''+ref+''' mounted');
        end else begin
          fLog.Add.Log(sllError, ' xx '''+ref+''' couldn''t be mounted');
        end;
      except
        fRestTab.Items[ref].DB.DBClose;
        fRestTab.Items[ref].DB.Free;
        fRestTab.Items[ref].Model.Free;
        fRestTab.Remove(ref);
        fLog.Add.Log(sllError, 'failure while mounting: '+ref);
      end;
    end;

  end else begin
    fLog.Add.Log(sllError, 'Mount called without parameter');
    Ctxt.Results(['ERREUR: ref missing']);
  end;
end;

and very quickly how i clean my REST item when calling UnmountREST:

    if fHTTPServer.RemoveServer(fRestTab.Items[ref]) then begin
      fRestTab.Items[ref].DB.DBClose;
      fRestTab.Items[ref].DB.Free;
      fRestTab.Items[ref].Model.Free;
      fRestTab.Remove(ref);  //autoFree because of [doOwnsValues]
      fLog.Add.Log(sllInfo, ' -- '''+ref+''' unmounted');
    end else begin
      fLog.Add.Log(sllError, ' xx '''+ref+''' couldn''t be unmounted');
    end;

And now for my memory leak:
If I do with my client something like this:

//Connection to the first REST
  fClient1 := TSQLHttpClient.Create('localhost', '8080', TSQLModel.Create([]), false);
  if not fClient1.ServerTimeStampSynchronize then
    raise Exception.Create('Fail while timestamp synchro');
  if not fClient1.SetUser('Admin', 'synopse') then
    raise Exception.Create('Fail while SetUser');
//Call MountREST without connecting to it
  fClient1.CallBackGetResult('Mount', ['ref', 'TEST002']);
//Then unmount and disconnect
  fClient1.CallBackGetResult('Unmount', ['ref', 'TEST002']);
  FreeAndNil(fClient1);

I have absolutely no problem when exiting the server app, no memory leak.
But if I init a connection with the second mounted REST like this

//Connection to the first REST
  fClient1 := TSQLHttpClient.Create('localhost', '8080', TSQLModel.Create([]), false);
  if not fClient1.ServerTimeStampSynchronize then
    raise Exception.Create('Fail while timestamp synchro');
  if not fClient1.SetUser('Admin', 'synopse') then
    raise Exception.Create('Fail while SetUser');
//Call MountREST and connect to it
  fClient1.CallBackGetResult('Mount', ['ref', 'TEST002']);
  fClient2 := TSQLHttpClient.Create('localhost', '8080', TSQLModel.Create([], 'TEST002'), false);
  if not fClient2.ServerTimeStampSynchronize then
    raise Exception.Create('CLIENT2: Fail while timestamp synchro');
  if not fClient2.SetUser('Admin', 'synopse') then
    raise Exception.Create('CLIENT2: Fail while SetUser');
//Then disconnect, unmount and disconnect again
  FreeAndNil(fClient2);
  fClient1.CallBackGetResult('Unmount', ['ref', 'TEST002']);
  FreeAndNil(fClient1);

I have memory leak of type unknow.

Why is it only when i'm connecting client to it? Am I doing something wrong?

If you need sample app or the memory leak file just tell me.


Thanks all for your help

Offline

#2 2016-04-01 09:57:44

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,706
Website

Re: Memory leak when connected with 2 client

Where is the memory leak? On the client or the server side?

What does FastMM4 (or other tool) says about the memory leak types, in FULLDEBUGMODE?
It could say something more than "type unknown".

My guess is that your "unmount" implementation (which is not shown here) is leaking memory.

Also note that your code is not thread-safe, so on production, it would fail. But I guess that it won't affect your client code yet (which is blocking).
And that using "fRestTab.Items[ref]" everywhere in your code is slow and error more than a dedicated local variable.

Offline

#3 2016-04-01 10:55:56

StxLog
Member
From: France
Registered: 2015-09-14
Posts: 58

Re: Memory leak when connected with 2 client

Thanks for your reply and sorry for the lack of information.
The memory leak is on server side.

There is my full Unmount procedure

procedure TMainServer.UnmountREST(Ctxt: TSQLRestServerURIContext);
var ref: RawUTF8;
begin
  if UrlDecodeNeedParameters(Ctxt.Parameters, 'ref') then begin
    ref := Ctxt['ref'];
    fLog.Enter();
    fLog.Add.Log(sllInfo, 'Ref: '+ref);


    if fHTTPServer.RemoveServer(fRestTab.Items[ref]) then begin
      with fRestTab.Items[ref] do begin
        DB.DBClose;
        DB.Free;
        Model.Free;
      end;
      fRestTab.Remove(ref);  //autoFree because of [doOwnsValues]
      fLog.Add.Log(sllInfo, ' -- '''+ref+''' unmounted');
    end else begin
      fLog.Add.Log(sllError, ' xx '''+ref+''' couldn''t be unmounted');
    end;

  end else begin
    fLog.Add.Log(sllError, 'Unmout called without parameter');
    Ctxt.Results(['ERREUR: ref missing']);
  end;
end;

I was just using System.ReportMemoryLeaksOnShutdown := true,
but now I have added FastMM4 with FullDebugMode, and the objects who leaks are still marked as "unknow". Should I enable other options from FastMM?

The thing is, I only have memory leaks when i'm connecting client to the mounted REST, but none if I just mount then unmount REST.


Thanks for your advices, this is juste a sample server to show you my problem, I'm going to add some lock to MountREST and UnmountREST. And I will change the access method for my REST instance, thanks!

Offline

#4 2016-04-01 11:18:45

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,706
Website

Re: Memory leak when connected with 2 client

What are the stack trace reported by FastMM4 in EnableMemoryLeakReporting;FullDebugMode ?
Take a look at the .txt file generated in the .exe folder.

Offline

#5 2016-04-01 12:57:46

StxLog
Member
From: France
Registered: 2015-09-14
Posts: 58

Re: Memory leak when connected with 2 client

I don't know if that relevant, but i've noticed something: if I delete the second .s3db file (the one from the mounted REST), and then launch the server app and client and connect to first REST, mount second REST, unmount it (without connecting to it) and disconnect, when closing server app I also have a memory leak, but not if the .s3db is already created.
Here is a link to the report for this: http://pastebin.com/0ntcVirL

And a link to the report when I connect the client to the mounted REST, my initial problem: http://pastebin.com/ZFLcDcZn


Everything point to the Mount and Unmount methods, but i can't see what am I doing wrong for freeing the REST instance?

Or maybe is there a better way to mount database at demand with your framework?
Thanks.

Offline

#6 2016-04-01 13:16:27

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,706
Website

Re: Memory leak when connected with 2 client

You are using a  TSQLRestServerDB.Create constructor which never releases its internal TSQLDataBase instance.

Either
- you add the TSQLDataBase instance to your list,
- or you set the aHandleUserAuthentication parameter to TRUE
- or you use the regular TSQLRestServerDB.Create constructor with direct database aDBFileName.

Offline

#7 2016-04-01 13:38:27

StxLog
Member
From: France
Registered: 2015-09-14
Posts: 58

Re: Memory leak when connected with 2 client

Great, thanks you! I've tried with direct database file name and no leak anymore.

But to remind, I was constructing my TSQLRestServerDB like this

TSQLRestServerDB.Create(
          TSQLModel.Create([], ref),
          TSQLDataBase.Create(ExeVersion.ProgramFilePath+refUniqueServeur+'.s3db', '', SQLITE_OPEN_CREATE or SQLITE_OPEN_READWRITE),
          True)

So aHandleUserAuthentication is already set to true. And I was cleaning it like this

        with fRestTab.Items[ref] do begin
          DB.DBClose;
          DB.Free;
          Model.Free;
        end;
        fRestTab.Remove(ref);

DB.DBClose + DB.Free shouldn't be freeing my Database instance?


Anyway, thanks a lot, problem solved! ; )

Offline

#8 2016-04-01 14:41:03

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,706
Website

Re: Memory leak when connected with 2 client

Oups, I was meaning the aOwnDB parameter, of course, not the aHandleUserAuthentication parameter!

Offline

#9 2016-04-01 14:52:00

StxLog
Member
From: France
Registered: 2015-09-14
Posts: 58

Re: Memory leak when connected with 2 client

Oh okay, this makes more sense, you should REST ; ) thanks again

Also, should I do

MyRestServer.Model.Owner := MyRestServer;

or is this nonsense?

Offline

#10 2016-04-01 16:25:07

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,706
Website

Re: Memory leak when connected with 2 client

Then, you do not need to store and free the TSQLModel instance.
It is in practice a good idea.

Offline

Board footer

Powered by FluxBB