#1 mORMot 1 » Getting 404 errors with higher traffic » 2019-10-10 11:53:26

Daniel
Replies: 2

For months now in live deployment we had reports of 404 errors that we could not reproduce.
I made a small test app, to try reproduce the problem.
It contains a websocket server and client. It has timer with adjustable interval. When timer fires message is sent to server which sends it back to all connected clients.
When client receives the message it adds the latency to memo.
Message contains a timestamp and receiving client calculates difference from timestamp to now, thus latency.

When testing I quickly saw that reducing the interval under 40 ms causes 404 exceptions. This is with single instance - one server and one client in same exe.

Second problem is with several clients, if I run about 4 clients and set interval to 100 ms, after a while again 404 exception happens.

I wonder if you can help me understand what is the problem, as this seems to reproduce the errors we have in live environment.

I include the source for this project:

DFM:

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 305
  ClientWidth = 393
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object bServer: TSpeedButton
    Left = 7
    Top = 8
    Width = 60
    Height = 26
    AllowAllUp = True
    GroupIndex = 1
    Caption = 'Server'
    OnClick = Button1Click
  end
  object bClient: TSpeedButton
    Left = 73
    Top = 8
    Width = 60
    Height = 26
    AllowAllUp = True
    GroupIndex = 2
    Caption = 'Client'
    OnClick = Button2Click
  end
  object Memo1: TMemo
    Left = 8
    Top = 74
    Width = 185
    Height = 215
    ReadOnly = True
    TabOrder = 0
  end
  object eHost: TEdit
    Left = 8
    Top = 47
    Width = 130
    Height = 21
    TabOrder = 1
    Text = 'localhost'
  end
  object Memo2: TMemo
    Left = 199
    Top = 74
    Width = 185
    Height = 215
    ReadOnly = True
    TabOrder = 2
  end
  object ePort: TEdit
    Left = 144
    Top = 47
    Width = 38
    Height = 21
    TabOrder = 3
    Text = '9000'
  end
  object eInterval: TLabeledEdit
    Left = 327
    Top = 47
    Width = 57
    Height = 21
    EditLabel.Width = 38
    EditLabel.Height = 13
    EditLabel.Caption = 'Interval'
    TabOrder = 4
    Text = '100'
  end
  object Timer1: TTimer
    Enabled = False
    Interval = 100
    OnTimer = Timer1Timer
    Left = 264
    Top = 40
  end
end

PAS

unit frmMain;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.Buttons,

  SynCommons,
  mORMot,
  mORMotHttpClient,
  mORMotHttpServer,
  SynBidirSock;

type
  TOSSServer = class;
  TOSSMessage = class;
  TOssCallback = class;

  IOSSMessage = interface(IInvokable)
  ['{4F30D230-AA46-4013-A900-33A7B79DD175}']
    function GetTimeStamp: TDateTimeMS;
    procedure SetTimeStamp(const Value: TDateTimeMS);
    property TimeStamp: TDateTimeMS read GetTimeStamp write SetTimeStamp;
  end;

  TOSSMessage = class(TPersistent)
  private
    fTimeStamp: TDateTimeMS;
    function GetTimeStamp: TDateTimeMS;
    procedure SetTimeStamp(const Value: TDateTimeMS);
  published
    property TimeStamp: TDateTimeMS read GetTimeStamp write SetTimeStamp;
  end;

  IOSSCallback = interface(IInvokable)
    ['{C561AB6F-61E0-4009-A1A0-26630A6A6D1F}']
    procedure ReceiveOssMessage(const AOSSMessage: TOSSMessage);
  end;

  IOSSService = interface(IInvokable)
    ['{DDB2805E-3521-4784-928B-95CE0B9FB4DD}']
    procedure SendOssMessage(const AOSSMessage: TOSSMessage);
    procedure SubscribeToOss(const ACallback: IOSSCallback);
  end;

  TForm1 = class(TForm)
    Memo1: TMemo;
    eHost: TEdit;
    Memo2: TMemo;
    bServer: TSpeedButton;
    bClient: TSpeedButton;
    ePort: TEdit;
    eInterval: TLabeledEdit;
    Timer1: TTimer;
    procedure Button1Click(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    fHttpServer: TSQLHttpServer;
    fWebSocketServerRest: TWebSocketServerRest;
    fServer: TSQLRestServerFullMemory;
    fClient: TSQLHttpClientWebsockets;
    fService: IOSSService;
    fCallback: IOSSCallback;
  public
    procedure BeforeDestruction; override;
    property HttpServer: TSQLHttpServer read fHttpServer;
    property WebSocketServerRest: TWebSocketServerRest read fWebSocketServerRest;
    property Server: TSQLRestServerFullMemory read fServer;
  end;

  TOSSServer = class(TInterfacedObject, IOSSService)
  private
    fClients: TInterfaceList;
  protected
    procedure SendOssMessage(const AOSSMessage: TOSSMessage);
    procedure SubscribeToOss(const ACallback: IOSSCallback);
  public
    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;
    procedure ServiceStart();
  published
  end;

  TOssCallback = class(TInterfacedCallback,IOSSCallback)
  private
  protected
    procedure ReceiveOssMessage(const AOSSMessage: TOSSMessage);
  end;

var
  Form1: TForm1;
  Lock: TRTLCriticalSection;

implementation

{$R *.dfm}

uses
  System.DateUtils;

const
  TRANSMISSION_KEY = 'OSS_WebSockets';

procedure TForm1.BeforeDestruction;
begin
  inherited;
  fService := nil;
  fCallback := nil;
  FreeAndNil(fHttpServer);
  FreeAndNil(fServer);
  FreeAndNil(fClient);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Port: string;
begin
  if not bServer.Down then
  begin
    FreeAndNil(fHttpServer);
    FreeAndNil(fServer);
    exit;
  end;
  fServer := TSQLRestServerFullMemory.CreateWithOwnModel([]);
  Server.ServiceDefine(TOSSServer,[IOSSService],sicShared).ByPassAuthentication := true;
  Server.CreateMissingTables;
  fHttpServer := TSQLHttpServer.Create(ePort.text, [Server], '+', useBidirSocket, 32, secSynShaAes, 'OSS');
  fWebSocketServerRest := HttpServer.WebSocketsEnable(Server, TRANSMISSION_KEY, True);
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  s: string;
begin
  if not bClient.Down then
  begin
    Timer1.Enabled := false;
    fService := nil;
    fCallback := nil;
    FreeAndNil(fClient);
    exit;
  end;
  fClient := TSQLHttpClientWebsockets.Create(eHost.Text, ePort.Text, TSQLModel.Create([]));
  fClient.Model.Owner := fClient;
  s := fClient.WebSocketsUpgrade(TRANSMISSION_KEY);
  if s <> '' then
    raise Exception.Create(s);
  if not fClient.ServerTimeStampSynchronize then
    raise Exception.CreateFmt(fClient.LastErrorMessage+
      '. Error connecting to OSS server at %s:%d', [fClient.HostName, fClient.Port]);
  fClient.ServiceDefine([IOSSService],sicShared);
  if not fClient.Services.Resolve(IOSSService, fService) then
    raise Exception.Create('Service IOSSService unavailable');
  fCallback := TOssCallback.Create(fClient, IOssCallback);
  fService.SubscribeToOss(fCallback);
  Timer1.Enabled := true;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
  vMessage: TOSSMessage;
begin
  vMessage := TOSSMessage.Create;
  try
    vMessage.TimeStamp := now;
    Timer1.Interval := StrToIntDef(eInterval.Text, Timer1.Interval);
    try
      fService.SendOssMessage(vMessage);
    except
      on e: exception do
      begin
        Timer1.Enabled := false;
        Form1.Memo2.Lines.Add(e.Message);
        raise;
      end;
    end;
  finally
    vMessage.Free;
  end;
end;

{ TOSSServer }

procedure TOSSServer.AfterConstruction;
begin
  inherited;
  ServiceStart;
end;

procedure TOSSServer.BeforeDestruction;
begin
  inherited;
  fClients.Free;
end;

procedure TOSSServer.SendOssMessage(const AOSSMessage: TOSSMessage);
var
  i: integer;
begin
  for I := 0 to fClients.Count - 1 do
  try
    (fClients[i] as IOSSCallback).ReceiveOssMessage(AOSSMessage);
  except

  end;
end;

procedure TOSSServer.ServiceStart;
begin
  fClients := TInterfaceList.Create;
end;

procedure TOSSServer.SubscribeToOss(const ACallback: IOSSCallback);
begin
  fClients.Add(ACallBack);
end;

{ TOssCallback }

procedure TOssCallback.ReceiveOssMessage(const AOSSMessage: TOSSMessage);
var
  LatencyMS: integer;
begin
  EnterCriticalSection(Lock);
  try
    LatencyMS := MilliSecondsBetween(now, AOSSMessage.TimeStamp);
    Form1.Memo2.Lines.Add(IntToStr(LatencyMS));
  finally
    LeaveCriticalSection(Lock);
  end;
end;

{ TOSSMessage }

function TOSSMessage.GetTimeStamp: TDateTimeMS;
begin
  result := fTimeStamp;
end;

procedure TOSSMessage.SetTimeStamp(const Value: TDateTimeMS);
begin
  fTimeStamp := Value;
end;

initialization
  TInterfaceFactory.RegisterInterfaces([
    TypeInfo(IOSSMessage),
    TypeInfo(IOSSService),
    TypeInfo(IOSSCallback)]);
  InitializeCriticalSection(Lock);

finalization
  DeleteCriticalSection(Lock);
end.

#2 Re: mORMot 1 » Bug in param matching in remote interface invokation » 2019-09-22 18:26:18

It's not string that is the problem, it's the GUID.

It turns out the last signature I posted doesn't work either, but doesn't raise an exception, instead the CallBack param is always nil.

So it's this const AClientID: TClientID that doesn't work

TClientID is a TGUID which is a record:

  TGUID = record
    D1: Cardinal;
    D2: Word;
    D3: Word;
    D4: array[0..7] of Byte;
  end;

From my limited debugging it seems that it fails to read record correctly but this is not detected correctly so it raise exception on following parameter in case of string, or doesn't raise anything at all in case of interface param.

#3 mORMot 1 » Bug in param matching in remote interface invokation » 2019-09-22 11:25:20

Daniel
Replies: 4

I'm using WebSockets for remote interface calls and think I have found a bug.

So given these types:

TClientID = TGUID;
TSessionID = Int64;
IOSSCallback = invokable interface

I had this method in interface:

function SubscribeToOss(const ACallback: IOSSCallback; const AClientID: TClientID: TSessionId;

Which worked fine, then I extended the params to:

function SubscribeToOss(const ACallback: IOSSCallback; const AClientID: TClientID; const AComputer: string; const AApplicationName: string; const AUserName: string): TSessionId;

And of course recompiled both sides, but Server raises:
IOSSService.SubscribeToOss failed on AComputer:string [missing or invalid value]

Which is not true, the AComputer params is present, but there seems to be a bug in server in param matching.

Then I changed the signature again, just rearranged the order of params into this:

function SubscribeToOss(const AComputer: string; const AApplicationName: string; const AClientID: TClientID; const ACallback: IOSSCallback): TSessionId;

And now it works fine again.

So the problem has something to do with order of params, in particular, having String after Interface or GUID.

I could investigate more, but hope this is enough info for you guys to find the bug.

#4 mORMot 1 » Avoid param object destruction in TServiceMethodExecute.AfterExecute » 2019-09-21 13:30:03

Daniel
Replies: 0

I'm transferring objects and have found they get destroyed in TServiceMethodExecute.AfterExecute;
I want to keep them alive a bit more and destroy them myself.
Is this possible ?

And 2nd unrelated question:
Can I somehow set TDateTime to be sent with millisecond precision without using SynCommons TDateTimeMS type ?

Regards,
Daniel

#5 mORMot 1 » Exception in bswap64 » 2019-07-22 13:15:43

Daniel
Replies: 1

After pulling latest I now get:

First chance exception at $00638D4F. Exception class $C0000005 with message 'access violation at 0x00638d4f: read of address 0x00000000'.

SynCommons.bswap64(0)
SynCrypto.TSHA512.Final((...),True)
SynCrypto.THMAC_SHA512.Done((...),False)
SynCrypto.HMAC_SHA512(???,???,???,13,(...))
SynCrypto.HMAC_SHA512(???,???,???)
SynCrypto.PBKDF2_HMAC_SHA512(',4:Òô'#$15'õÃ'#$19'.þ'#$14#2'±]a;)UQik™À'#9#$10#$1E'ÙzûœµOPbñç@)ÚQ%õ\e'#$1D'W'#$10'?›“Ì"''9Õ¦°'#$F'Ü'#$16''')Û¿ƒ‰'#$F'õ'#$9D'E[ÝA)'#$AD'^*·'#1#8'Ú'#4'݉'#$9D'AŒL„7S9Ÿ.P¸8ŒÏ'#4'¬ª.oѺ™i'#$18'SïŽGÉÑ^à•ÃÝ.–µÌ'#$1F'B'#$15,'Developer',16,(...))
SynCrypto.TAESPRNG.Seed
SynCrypto.TAESPRNG.Create(???,1048576,256)
SynCrypto.SetMainAESPRNG
mORMot.TSQLRestServer.Create($2B840B0,False)
mORMot.TSQLRestServerFullMemory.Create(???,False)
mORMot.TSQLRestServer.CreateWithOwnModel(???,False,'root')

32 bit project in XE Berlin.

#6 Re: mORMot 1 » SetCurrentThreadName causes External Exception when debugging » 2019-06-23 09:02:56

NOSETTHREADNAME conditional is defined by default in Synopse.inc and apparently no one uses it as it doesn't even compile without it any more.

Thread naming is quite handy feature for debugging and should be fixed and turned on by default, IMHO.
Surely the majority of users have no problem with it.

EDIT: In general it is better to have optional additional features as conditional that turns it *on*, rather that conditional that turns something *off*.
In case of NOSETTHREADNAME for example you have to modify Synopse.inc to comment out NOSETTHREADNAME

If it was simply SETTHREADNAME and it was commented out in Synopse.inc, you could simply define it in project options or another .inc file, without the need to modify Synopse.inc.

As a bonus this is more readable:
{$IFDEF SETTHREADNAME}
than this:
{$IFNDEF NOSETTHREADNAME}

#9 mORMot 1 » Exception in WebSocket destruction since update to latest » 2019-06-12 21:48:45

Daniel
Replies: 3

I pulled the latest mormot yesterday, and now I am getting an exception during shutdown.

First chance exception at $77581E0B. Exception class $C0000005 with message 'access violation at 0x77581e0b: write of address 0x00000014'.

SynBidirSock.TWebCrtSocketProcess.SetLastPingTicks(False)
SynBidirSock.TWebCrtSocketProcess.SendFrame((focConnectionClose, [(out of bound) 2,(out of bound) 4,(out of bound) 5,(out of bound) 6], 8235635, ''))
SynBidirSock.TWebSocketProcess.Destroy
SynBidirSock.TWebCrtSocketProcess.Destroy
SynBidirSock.TWebSocketProcessClient.Destroy
System.TObject.Free
SynBidirSock.THttpClientWebSockets.Destroy
System.TObject.Free
mORMotHttpClient.TSQLHttpClientWinSock.InternalClose
mORMot.TSQLRestClientURI.Destroy

Is a bug in mormot, or am I supposed to do something differently ?

#10 Re: mORMot 1 » Websockets client - how to detect the closing of the socket? » 2019-06-10 16:01:35

Frankly, problems with such basic functionality as disconnect are discouraging.
I introduced Mormot into my company for it's WebSocket implementation but my colleagues now doubt it it was a good choice.

#11 Re: mORMot 1 » Websockets client - how to detect the closing of the socket? » 2019-06-10 08:52:35

Has anyone tested and confirmed that this works now ?

#12 mORMot 1 » Using Keepalive ? » 2019-04-12 09:55:35

Daniel
Replies: 1

Both Synopse sockets and websockets seem to implement KeepAlive mechanism.
I'm using WebSockets and I'd like to make use of KeepAlive to detect disconnects.

I tried playing with the available properties but could not get it to do anything.

I assume that once connection is established any subsequent disconnect should be detected after keepAlive period expires.

So, what do I have to do/set for KeepAlive to be in use ?

#14 mORMot 1 » Service startup exception: TSynLog.AutoTable VMT entry already set » 2019-02-13 14:28:57

Daniel
Replies: 2

We have a service application which contains two WebSocket servers.
This morning the service didn't start and the following windows event log was logged:

TSynLog.AutoTable VMT entry already set

So that only gets raised in

class function TSynLog.FamilyCreate: TSynLogFamily;
...
    if PPointer(PVMT)^<>nil then
      raise ESynException.CreateUTF8('%.AutoTable VMT entry already set',[self]);


We can not reproduce the problem locally and from looking at the code I can't figure out how can it possibly happen, except if both services access TSynLog.Family at the same time.

Do you have any pointers/suggestions as to what could be the problem or how to avoid it ?

We have since added the following as first code in DPR file

begin
  TSynLog.Family.Level := [sllException, sllExceptionOS];
  TSynLog.Family.AutoFlushTimeOut := 10;

Will that perhaps indirectly fix the problem by ensuring Family is created as first thing the app does ?

Thanks for your time.

#15 mORMot 1 » WebSocket Server detecting disconnected clients without sessions ? » 2019-02-11 08:38:30

Daniel
Replies: 1

I using Mormot in scenario similar to the chat demo 31. Clients send messages and Server should send it to all other clients.

So when server receives a message it iterates a list of connected clients and sends the message to all except client which sent it.

I don't use authentication, and so SessionClosed is not called, is there another way to detect when a client has disconnected ?

#16 mORMot 1 » Setting WebSockets.Loopdelay to 0 ? » 2018-11-24 14:20:35

Daniel
Replies: 1

Hi,

My use case is chat like - client sending changes to sever which then sends them to all other clients.
I need minimal latency in communication between clients and so I've set LoopDelay of TWebSocketProcessSettings to 0.

Am I correct in assuming LoopDelay = 0 helps ? Is there any downside to doing this ?
Is there anything else that I may do (or not do) to improve latency with WebSockets ?

Thanks

#17 Re: mORMot 1 » Implementing OAuth2 » 2018-11-13 19:47:04

Has anyone done anything more with this ?

#18 mORMot 1 » Minimal SessionID implementation » 2018-11-12 11:48:47

Daniel
Replies: 4

Hello,

I'm planning to use Mormot in scenario similar to the chat demo 31. Clients send messages and Server should send it to all other clients.
So basically the only thing I need to do is make sure I don't send the message back to same client that sent it.

I suppose to do this I have to identify sessions, but I don't want full fledged authentication system, I only want each connection to get unique SessionID.
How would I go about doing this ?

#21 Re: mORMot 1 » Compiler warnings with D2007 » 2016-12-13 12:04:01

Yep, that fixed the SynCommons.pas, thanks !

But not the mORMot.pas:

[DCC Warning] mORMot.pas(26912): W1035 Return value of function 'TJSONObjectDecoder.EncodeAsSQLPrepared' might be undefined

#22 Re: mORMot 1 » Compiler warnings with D2007 » 2016-12-13 11:23:40

Could it be possible to add IFDEF to get rid of the warnings ? Our build system fails on warnings.

#23 mORMot 1 » Compiler warnings with D2007 » 2016-12-13 10:45:17

Daniel
Replies: 6

[DCC Warning] SynCommons.pas(56189): W1035 Return value of function 'TSynTableFieldProperties.GetValue' might be undefined
[DCC Warning] mORMot.pas(26808): W1035 Return value of function 'TJSONObjectDecoder.EncodeAsSQLPrepared' might be undefined

Board footer

Powered by FluxBB