You are not logged in.
Log shows you have 6 working threads, not 32! Execution time is >30sec, default timeout is 10sec if I remember.
Declared in SynCommons.pas, do you mix old and new pas files?
Port 465 for SMTP with SSL/TLS encryption? Do you build same app bitness (32/64). Check your dll bitness if you use openssl.
Better to create separate restserver for interface/methods with different root uri ex. api. Then you will have
/orm/Product for orm access and /api/Product for interface and method calls. In production when you put your service behind reverse proxy you will pass only /api uri, /orm access is not recommended for direct client access. You can still access /orm for some housekeeping on localhost.
mormot.db.proxy.pas
thanks. but yo write "TSQL*ConnectionProperties is connection pool, not real db connection! Real db connection is TSQL*ConnectionProperties"
it is same!
TSQL*ConnectionProperties = TSQL*ConnectionProperties
TSQL*ConnectionProperties.ThreadSafeConnection
Yes it is right approach. Just note, TSQL*ConnectionProperties is connection pool, not real db connection! Real db connection is TSQL*ConnectionProperties.ThreadSafeConnection and is created for your running thread. This connection is keep open and reuse for every call in same thread, because db connect is time expensive. I you really need to close thread connection you can call TSQL*ConnectionProperties.EndCurrentThread at the end of your interface/method.
Thanks @ab, problem solved with last changes, no more exception.
Problem is connected to firebird client lib, when aFbProps.Free; in Stop, (aFbProps: TSqlDBIbxConnectionProperties). When I remove aFbProps from app, no exception is raised!
Same code on Windows as console or service, or Linux console don't rise any exception, only as daemon forked/process.
Linux daemon raise exception on stop (systemctl stop ..)
EXCOS EAccessViolation (9df7d740) [Main] at 442bec ../../src/core/mormot.core.os.posix.inc (3183)
./myapp --start # is OK
./myapp --stop # raise same exception
./myapp --c # console is OK
Please note that there is no Name field inside the TJsonRecord:
There is in your first post and also dynamic not static array, TBytes!
You need to change record to packed record, also change Name type from string to RawUtf8 type.
In your first log you have >3sec on connection, not on exec statement. First statement in thread must make connection 3sec,after that execution of statements is fast in same thread. Connection pool normal behavior
All main developers work together including @bellard and @chqrlie and plan to merge all changes in one repo.
Sorry Daniel, not a answer to your question, just use this topic for info of fork of quickjs where continue development of this lib.
https://github.com/quickjs-ng/quickjs
Some projects using quickjs switch to this fork. Any plan for mormot?
Looking at Single query-Data table, async servers have worse result then ORM!
[async,nopin] mormot-postgres-async2 use 1 server, 56*2 threads (56*2-1=111 db connections)
all other tests use (56*(8-1)=392 db connections)
Not fair for [async,nopin]!
Edited:
Looking at Data updates-Data table, for multiple updates 10,15,20, [async,nopin] benefit from lower db connections
56*(8-1)=392 db connections
Comparing M1/M2 context, main deafferents is M2 have arrays, M1 don't have (in FPC).
And nestedRecord/Array is array of fields in M1, in M2 we have "fields" property as array of fields.
context.json M1 and M2 https://mega.nz/file/SbBUGaYQ#fwFb07BBX … qM4J3hyeU0
for sample https://synopse.info/forum/viewtopic.ph … 163#p41163
Hi @itSDS,
0. Can you check/compare M1/M2 context.json, http://localhost:888/root/wrapper/context
1. Yes, depend of mustache template. I'm not sure that is possible correct order of types. I only look at SMS template to convert to pas2js template and in this template first is created all enums, then all record, then all arrays. Manuel modification is needed (reorder types) in created client.
2. Check 0. In M2 context we have all nested records/arrays as temporary type. This is needed for client that don't support nested types. Pas2js support nested records and arrays from some version, but don't support nested array of nested record and we need this temp types
// New Delphi/FPC version support
TPeople = packed record
MyArr: array of record
name: RawUtf8;
end;
end;
// in Pas2JS, must be transformed to
TTmp1 = record
name: RawUtf8;
end;
TMyArr2 = array of TTmp1;
TPeople: TMyArr2;
This temp types also simplify code to all helper variant2* *2variant functions, if you use this types.
Some changes must be done in template if you don't need this temp types. Looking at context.json
"nestedRecord": {
"nestedRecord": null,
"fields": [
{
"typeWrapper": "wRawUtf8",
"typeSource": "RawUtf8",
"typeDelphi": "RawUtf8",
"typePascal": "String",
"typeCS": "string",
"typeJava": "String",
"typeTS": "string",
"typeSwagger": {
"type": "string"
},
"propName": "Name", <---
"fullPropName": "CatNested.Name", <---
"nestedIdentation": " ",
"isSimple": true
},
You can change in template fullPropName to propName, depend what you need to be generated.
I tested only on FPC and Pas2JS template. Delphi testing is needed and all clients templates.
It is good to add field isNested: true to all anonymous temp types to be able to create or not code for this types in template!
Please try/test my fork mormot.soa.codegen.pas. Pull request is 2 days ago, testing is welcome before merge.
https://github.com/TTomas/mORMot2/blob/ … odegen.pas
Discussed in topic https://synopse.info/forum/viewtopic.php?id=6375
@ab, I create pull request
@rdevine, I update pull request
This type of interface is tested
TSex = (cMale, cFemale);
TCat = packed record
Name: RawUtf8;
Sex: TSex;
Birthday: TDateTime;
end;
TCatDynArray = array of TCat;
TCat3Array = array[0..2] of TCat;
TPeople = packed record
FirstName: RawUtf8;
LastName: RawUtf8;
Sex: TSex;
Birthday: TDateTime;
Cat: TCat;
CatNested: packed record
Name: RawUtf8;
Sex: TSex;
Birthday: TDateTime;
end;
Cat3: TCat3Array;
Cats: TCatDynArray;
CatsNested: array of TCat;
end;
ICalculator = interface(IInvokable)
['{9A60C8ED-CEB2-4E09-87D4-4A16F496E5FE}']
function Add(n1, n2: integer): integer;
function ArrayValue(const arrJSON: RawUtf8; ix: integer): variant;
function CountArray(const jsn: RawUtf8): integer;
function SumArray(const jsn: RawUtf8): double;
procedure FullName(const aFirstName, aLastName: RawUtf8;
var aFullName: RawUtf8; var aSize: integer);
function CatIsMale(const aCat: TCat): Boolean;
function GetCat: TCat;
function GetPeople(aId: integer; var aPeople: TPeople): Boolean;
function AddPeople(const aPeople: TPeople): integer;
function AddCat2People(const aCat: TCat; var aPeople: TPeople): boolean;
end;
With this changes created client need only 2 manual changes, move array types before TPeople record and Replace All nested type names with correct type name.
In https://github.com/TTomas/mORMot-Web/tr … Pas2JSTest you can view context.json and exported client mORMotPas2JsClientExport.pas and edited mORMotPas2JsClient.pas.
I try version without helper record/array function, In GetPeople onSuccess to use
onSuccess(TPeople(res[0]),Boolean(res[1]));
For simple types we can use this casting, but Birthday TDateTime field will be corrupt in pas2js. After this type of casting this field is no longer TDateTime (double/Number) but String (mormot serialize as iso8601) and using any datetime function is broken. This is JS world!
Using helper functions everythig is OK. Template is modified SMS and still there is a lot of SMS code in template. Need more testing on different structures.
@ab we need correct type naming for temporary nested fields (CatNested/CatsNested).
Hi @rdevine
I create Pull request
-Some small changes to be Pas2JS compatible without TMS Web Core
-Calculator service add code export wrappers and add procedure with 2 var params for testing
-Pas2JS Template in Template folder. Generated code mORMotPas2JsClient.pas is used in Pas2JSTest without any change
http://localhost:888/root/wrapper
-Pas2JSTest Demo
-Async and Sync calls works
I tested only on FPC and Pas2JS, not in Delphi or TMS Web Core
My fork: https://github.com/TTomas/mORMot-Web
Thanks @vs for report of exception with LEGACYFIREBIRDAPIONLY. I found problem with definition of isc_start_transaction dll function in fbintf.
More info in this topic: https://forum.lazarus.freepascal.org/in … opic=66087
Thanks @ab, new router feature is great and very powerful addon.
Just one note, if I create SOA method <func>, without using any router.rewrite/setup, OnBeforeUri is called with all URI:
http://localhost/root/func/*
http://localhost/root/func/Variable/P/a/t/h/...
Is this as designed or working by chance (by mistake)?
Microservices with several servers usually use reverse proxy server.
-> Myservice1
client N -> proxy(nginx) -> Myservice2
-> Myservice3
-> Myservice1
Do you change line 616 cMaxStm = 50 in mormot.db.sql.ibx. Previous test on 2.5 pass without problem. Fb3 have limit of 255 statements, Fb2.5???
Edited: Or execute statement size exceeded 64K limit on Fb2.5, Fb3 and above have limit of 10MB.
Batch insert create execute block statement with 50 insert statements inside.
Do you try to add define LEGACYFIREBIRDAPIONLY to force using of legacy api with Fb4/5?
@vs, do you success build with delphi, if yes delphi version please. @ab alearedy commit changes in trunk.
@ab, I create push request.
2. Correct, this is the reason I create ibx mormot driver. Main goal is to have 0 active transaction if nobody is connected to app/server.
{$define ZEOSTRANS} is only for testing, not recomended for production.
This results is expected with default config. 3 reasons:
1. Zeos have better parameters and results binding, low level access to Fb Api records, without copy strings/varchars.
2. Zeos only use CommitRetaining after any statement and never close/Commit transaction. Ibx use StartTransaction, execute statement and CommitTransaction. This is by design. You can simulate zeos transaction handling if you enable {$define ZEOSTRANS} at line 48 in mormot.db.sql.ibx.
3. fbintf for Fb3 and above use new Firebird3 interface, by default, which is slower then old legacy ICS/Interbase Api. zeos use only legacy Api. Look in fbintf, IB.pas lines 76-81, you can force to use legacy Api interface only for Fb3/4/5 by defining LEGACYFIREBIRDAPIONLY define to your project. You will see Direct/Trans insertion speed increase for Fb4/5 in range of Fb25 (Fb2.5 use only legacy interface)
I am a bit confused about how you make the routing.
By parsing URI in OnBeforeURI event.
Try to find out what is registered in TRestRouter.Setup for you application, and why your requests are not routed.
If you don't use the REST routing, you could use the HTTP server routing, or HTTP interception.
As I said before this RestServer doesn't have any ORM or SOA interface or method, just OnBeforeURI event and nothing is registered in Router. Adding:
aWebhookRestServer.Router.Setup(mGET, 'test', rnMethod);
aWebhookRestServer.Router.Setup(mPOST, '', rnMethod);
I solve problem for this project and now OnBeforeUri is called. I use MyRestServer just to separate code(baseness logic) and define newroot needed for reverse proxy. I will create two published methods and use router.rewrite instead of OnBeforeURI.
As this topic title said I have 2-3 years old mormot1 project broken to migrate to m2. This project replace some Magento(PHP) eCommerce image processing URI, where I start using OnBeforeURI. RestServer root is 'Assets' and need to process:
http://localhost/Assets/image/Variable/P/a/t/h/image1.jpg
as static server serve original image and
http://localhost/Assets/img/<func>/<x>/<y>/Variable/P/a/t/h/image1.jpg
http://localhost/Assets/img/resize/200/200/Variable/P/a/t/h/image1.jpg
On first call resize original image to 200x200 and keep/cache on disk, next call serve image as static server.
I know this is not standard use of OnBeforeURI in RestServer and don't insist any change. This URI serving can be moved to HTTP server interception.
One possible change will be line 7546 if (node=nil) to try to call OnBeforeURI before ctx.Error('Invalid URI'...
MVCServerFirebirdIbx.dpr(54,17) Error: identifier idents no member "CreateDescendingPK"
property CreateDescendingPK was changed to CreateDescendingOnlyPK, ex\mvc-blog need to change
MVCServerFirebirdIbx.dpr(95,1) Error: Can't open resource file "D:\Projects\Delphi\Components\Synopse\mORMot2-2.0.stable\ex\mvc-blog\mormot.win.default.manifest.res"
I usually copy this file to project folder from mormot2/src. Line 10 can be changed to:
{$R ..\..\src\mormot.win.default.manifest.res}
mormot.db.sql.ibx.pas(119):
property FirebirdLibraryPathName: RawUtf8
but
mormot.db.sql.ibx.pas(87):
fFirebirdLibraryPathName: string;
Yes need to be changed to string. I will try to create push request.
I see dcc32 Error, you use Delphi!
mormot.db.sql.ibx.pas is Fpc/Lazarus only, based on IBX for Lazarus interface: https://www.mwasoftware.co.uk/fb-pascal-api
Never tested with Delphi. Try to install fbintf1.4-0 on Delphi. Any help is welcome.
Sorry for bad line numbers not from trunk. I don't use router. TMyRestServer receive different aRoot on creation 'newroot/webhook', BeforeUri response to POST request on 'newroot/webhook' and GET request to 'newroot/webhook/test' for testing that service is alive. In mormot1 and mormot2 before router, OnBeforeUri is called for all 'newroot/webhook*' requests.
TMyRestServer = class(TRestServerFullMemory)
private
function BeforeUri(Ctxt: TRestServerUriContext): boolean;
public
constructor CreateWithOwnModel(aModelServer: ThkOrmRestServerDB;
aProps: TSqlDBConnectionPropertiesThreadSafe;
aShApiConfig: ThkConfigSh;
const aRoot: RawUtf8;
aKorBr, aMagBr, aDrzBr, aLoyArtPay: integer;
aMapPay, aMapShip: ThkMapDynArray); reintroduce; overload;
end;
implementation
constructor TMyRestServer.CreateWithOwnModel(
aModelServer: ThkOrmRestServerDB;
aProps: TSqlDBConnectionPropertiesThreadSafe;
aShApiConfig: ThkConfigSh; const aRoot: RawUtf8;
aKorBr, aMagBr, aDrzBr, aLoyArtPay: integer;
aMapPay, aMapShip: ThkMapDynArray);
begin
inherited CreateWithOwnModel([], false, aRoot);
...
OnBeforeURI:=BeforeURI;
end;
Correct trunk line numbers in mormot.rest.server :-), line 7544 node := fRouter.Lookup(ctxt);, node is nil and never reach line 7584 OnBeforeUri(ctxt).
Lookup line 5978 result := pointer(TRestTreeNode(fTree[Ctxt.Method].Root).Lookup(p, Ctxt));, result is nil
I workaround comment lines 7543..7548, just to build new version of my service and everithing is OK now. Just workaround.
Migrate mormot2 to trunk and my production service fail.
I have TMyRestServer = class(TRestServerFullMemory) , without interface or published method, only OnBeforeUri.
OnBeforeUri never call!
procedure TRestServer.Uri(var Call: TRestUriParams) return Error in line 7553 // 4. decode..
never reach //6. line 7589 OnBeforeUri
Older version of mormot2 from beginning of 2023 work OK.
Not expected. What about "-s 8 -t 1" vs "-s 8 -t 2", both use same number of dbpull connections 8, -t1 (10K req/sec, 25 threads) , -t2 (7K req/sec, 33 threads). For -t 1 look like R0 make db connections?
@ab, something is wrong with asyncserver or dbpull!
Running /rawdb wrk test (/db,/asyncdb also) ./raw -s 8 -t 8, total threads 64, I expect 64 connection to postgresql, but only 56 found.
DBPull conections are servers*(threads-1), not servers*threads!
Running ./raw -s 8 -t 4, only 24 connections, not 32
You can check active statements/connection with:
SELECT * from pg_stat_activity WHERE query='select id,randomNumber from World where id=$1'
When raw start we have 8 (servers) connections from main thread to load cache. Running wrk create servers*(threads-1) connections.
Main thread connections + threads connection = servers*threads, but worker threads use only s*(t-1).
Source /ex/techempower-bench, pull 2 days ago, 23.08.2024
raw -s 8 -t 8, htop show 81 threads.
> I think so, it has to include offline mode (I'll try that via service workers) and some processing on client side.
In that case you must build JS application using JS,TS,pas2js or DWScript and use mormot as API rest server.
> Thanks, I've heard about htmx before but haven't tried it yet.
> Do you create a method based functions on mORMot side that you call from htmx?
No I don't use method based functions, only mormot MVC app. htmx expect HTML response, not json and replace only part of HTML in your browser. Browser don't need to parse json data and create/modify DOM, just replace/add/delete some part of HTML in DOM. Easy way to transform SSR app to SPA app.
Yes @ab, almost no change in mormot mvc application.
Some hint, look for HX-Request in header, it is needed if user refresh page.
Just add this code in the end of procedure GetViewInfo, in mvc example.
if context.Request.InHeader['HX-Request']<>'' then
begin
_ObjAddProps(['Htmx', _Obj([
'HX-Request', context.Request.InHeader['HX-Request'],
'HX-Trigger', context.Request.InHeader['HX-Trigger'],
'HX-Trigger-Name', context.Request.InHeader['HX-Trigger-Name'],
'HX-Target', context.Request.InHeader['HX-Target'],
'HX-Prompt', context.Request.InHeader['HX-Prompt']
])], info);
end;
Then in template you can use
{{^main.Htmx}} {{>header}} {{>masthead}} {{/main.Htmx}}
<!-- your page -->
{{^main.Htmx}} {{>footer}} {{/main.Htmx}}
Depend of type of your web app. Do you really need JS?
I build some internal projects with mormot mvc + mustache + htmx + Bootstrap5 and I will recommend to try htmx ( https://htmx.org/ ).
Try to remove last #13#10 in headers variable.
@mvp, nice gist. Just some comment about concurrency, maybe add as param to perform, for all db test to use 512 (will have impact on active connections to postgers), for plaintext to use 1k or 4k, the best concurrency for each test.
@ab
Do you mean than 228 threads is slower than less threads?
Yes looking in the pgbench test table for 96Cpu server, best result when connections=cpu, for read only. We are testing postgres server and depend on I/O shared resource speed (hdd), the bigger bottleneck, too many threads, too many connections drop performance. Write/update connections have more stress to IO.
@mvp, different HW, hdd/ssd/raidX, maybe tfb HW. I know we miss async client. On slow hdd threads count is more important as in my old server.
Great results for /json /plaintext.
Looking again on read only connection table https://techcommunity.microsoft.com/t5/ … 06462#fn:1
for 96vCpu compare 100 and 800 connections.
I don't find similar table for write/update connections, but for sure impact will be much more worse for shared I/O (hdd/ssd).
Maybe you can create one more test (docker) with same code but reduced threads/connections (2*CPU) and in one round see differences.
Playing with mormot2 master from today. Best results on 24 vCpu are 2 servers with 12 threads and include flags hsoThreadSmooting and hsoReusePort. Some strange results when run docker -p 8080:8080 vs --net=host, compared with ntex. ntex run faster with -p, mormot with --net=host. Also result running raw native on host without docker. mormot is faster then ntex if run with --net!!!
T1 T2 T3 AVG
Native raw 12 24 2 196.817 196.177 196.558 196.517
docker run -p 8080:8080
raw 12 24 2 132.468 123.207 126.877 127.517
ntex 144.824 148.111 146.022 146.319
docker run --net=host
raw 12 24 2 186.369 188.466 186.378 187.071
ntex 137.510 136.848 136.738 137.032
Just checked /json on Ubuntu 18.04, Kernel 4.15 as host for docker, previous tests are on U22.04 VM, to be sure thаt different kernel, glibc/MM version have any impact.
Small improvements on all tests, also for ntex. The Ubuntu version does not affect the test!
Workers Ser. Thr. T1 T2 T3 AVG
libc MM 48 2 24 121.680 126.686 124.550 124.305
mimalloc 48 2 24 125.354 120.953 127.821 124.709
ntex [tokio,platform] 144.824 148.111 146.022 146.319
Just tried Microsoft mimalloc MM. https://github.com/microsoft/mimalloc
No noticeable improvements, same results as libc MM. ldd /usr/local/bin/raw confirm libmimalloc.so is used.
I used alpine docker https://github.com/emerzon/alpine-mimalloc.
Dockerfile
# ... Same builder
FROM emerzon/alpine-mimalloc
COPY --from=builder /build/bin/fpc-x86_64-linux/raw /usr/local/bin/raw
RUN apk --update add postgresql-client && \
# Workaround musl vs glibc entrypoint
mkdir /lib64 && \
ln -s /lib/ld-musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2
EXPOSE 8080
CMD ["raw"]
If you use SQLite and store as RawJSON you can use JSON functions and operators.
https://www.sqlite.org/json1.html
This json is not array! You need record
TMyData = packed record
currentYear: integer;
ActualWeek: integer;
weeks: TWeekArray;
end;
Then you can use RecordLoadJson/RecordSaveJson from mormot.core.json.pas
If TWeek = class, not record, then name array TWeekObjArray = array of TWeek. Then you can use
Rtti.RegisterObjArray(T,Tweek);
In your case TWeek is packed record, just use:
Rtti.RegisterType(TypeInfo(TWeek));
Rtti.RegisterType(TypeInfo(TWeekArray));
If you need compatibility with older Delphi versions and Fpc then you can use
Rtti.RegisterFromText(TypeInfo(TWeek), __TWeek);
where __TWeek is const string with definition of record, read documentation for details.
Edited :-), Sorry typo TRtti->Rtti