#51 Re: mORMot 1 » How configure the server to finish inactive threads? » 2020-11-16 15:57:42

pvn0 wrote:

From what I understand, your own code blocks the threads from exiting gracefully. You should never do that and you should never kill a thread.

My own code do nothing. Threads are there, running forever and I'm not killing them.
I just would like that mORMot kill them, *if* they are doing nothing for a period of time...

macfly wrote:

Actually, what you want to do is finish the application, right?

Ending threads being executed by the application will not allow the executable to be replaced.

No, I can finish the app anytime, but "brutally"! If I call "shutdown" any moment, some thread may not finished its job and the client will receive an error.

But, if there is no thread running, I can call "shutdown" to kill the HTTP server quietly and replace the binary (with a cup of coffee in my hand...), as the new version of the service is already running in a new URL (instance-B), which means that my service is never down.


Guys,

All services are registered in a database. All clients should first get the URL from there to know where is the active instance.
If I want to replace any binary I will first change that register, pointing to a new instance (B), which means that all new requests will use the new version (B).
Then I just wait "the period of time" knowing that mORMot will kill all threads that are in stand by... only the server will stay active, then I call shutdown.

That is what I do to maintain the service online 24h/d. If you, guys, know another way to do that — I mean, always having an instance available for the clients — please, teach me.

#53 Re: mORMot 1 » How configure the server to finish inactive threads? » 2020-11-16 14:15:51

I wrote with a context in my mind and I might haven't express myself in a good way.
I know that "The threads are part of the thread pool, so they are by definition allocated at startup, until the process exits." and there is nothing wrong with. What I'm asking is if there is a (default) way to a server kill those threads automatically, if they are inactive.

"Finishing" such threads is not the solution to the problem of DB connections, for instance.

It can help. If there are no new requests, threads will die and for the next new request they will be created again, using a new connection.

"allow me to replace binaries without forcing a shutdown" need a better explanation (my mistake, sorry):
I want to implement a "A/B service" which means that, if I need to replace the instance-A, I will first publish an instance-B, change the URI and port (that remains in a database) for all new requests point to the new service instance. Then, when all requests finished in instance-A, I'm able to remove the binary, replacing with the same version in B — for the next version I can do the opposite, starting with instance-B.

If threads are not killed automatically, I need to check the server using another approach only to know if something stay in process.
Just as an example, using IIS + FastCGI + fpWeb, I can setup IIS to kill my threads using the approach that I've explained above. But I'm not using IIS or Apache... just mORMot.

Have you understood now?

#54 mORMot 1 » How configure the server to finish inactive threads? » 2020-11-16 11:58:30

mdbs99
Replies: 22

How can I configure the server to finish inactive threads in a period of time?
Doing that will prevent some errors — see https://synopse.info/forum/viewtopic.ph … 398#p33398 — and allow me to replace binaries without forcing a shutdown, for example.

--
Original post https://synopse.info/forum/viewtopic.ph … 402#p33402

#55 Re: mORMot 1 » Remote JSON REST Service - calling COM in Service fails » 2020-11-13 18:52:59

Just to mention: TADOConnection has a KeepConnection property, which means that. And it works.
But I need my services more stronger than that. For example, if the network has down/broke, KeepConnection is not enough and an exception will occur.

So, when I got this exception was because it was running in a develop database, which is renew every day, dropping all connections of course.

#56 Re: mORMot 1 » Remote JSON REST Service - calling COM in Service fails » 2020-11-13 18:34:09

macfly wrote:

I mentioned above that I save the time that connection was last requested and renew before reaching the timeout.
So this problem does not occur in my case.

I'm not sure if I can use like you did. In my mind, to do that I will always need to check the connection before, for each method on server.
I can save the time that the thread got the connection, then code a function that will check that time and renew the connection before give it to the program... but doing that every time is error prone, IMHO.

Would be nice 1) mORMot kill threads in a period of time or 2) I kill my threads in a period of time.
But I'm waiting @ab say something, as I need that working but I would like to do in a best way considering how the framework works.

The question is whether this would apply to mORMot.
I don't know if in mORMot there is a way to intercept when the connection is requested.

Other problem is that I'm not using mORMot DB (SynOleDB). If I did, I wouldn't have any problem, as Arnaud already check this internally, renewing connections time to time.

#57 Re: mORMot 1 » Remote JSON REST Service - calling COM in Service fails » 2020-11-13 17:46:09

macfly wrote:

...
But is this a problem? What error could occur if a connection that is not being used is idle on a thread that is also not in use?

The DBMS will kill connection in a period of time on idle, but the thread won't know about it; then the client will get an exception.

#58 Re: mORMot 1 » Remote JSON REST Service - calling COM in Service fails » 2020-11-13 17:10:03

macfly wrote:

...When a new request occurs, its expiration will be checked and if it is expired it is renewed.

That it was I meant when said "...if no client sent a request, you can't be able to remove any other inactivity connection", i.e., first you need a request then you could do other things like renew connections.

In the case of mORMot is need to analyze how and when the connection is requested, - but I believe that it will not be possible to do the same.

In this case the timer would be better.

Yes, this is the question.
You will have to dig deep into the code and tests to see how and when threads and connections are created and released by mORMot.

But I'm writing here to get some help from who knows the internal framework sad

@ab?

#59 Re: mORMot 1 » Remote JSON REST Service - calling COM in Service fails » 2020-11-13 15:14:13

@macfly
If I understood correct the concept of your pool, you could still get an exception by inactivity. I mean, if no client sent a request, you can't be able to remove any other inactivity connection... but only by a timer (do you use?).

Anyway, I still thinking that would be nice and easier if the server finished threads by end of a (configurable) period.

At least in my case, I don't care if next request will be the first that will create all threads again, as all were gone eventually. Does mORMot can do that?

#60 Re: mORMot 1 » Remote JSON REST Service - calling COM in Service fails » 2020-11-13 12:53:41

Maybe the easiest way is inform the server to release threads in a period of time, then I can setup this period as being less than the current timeout of DBMS.

I will search about it, but if you @ab or anybody can tell me first, I will appreciate.

#61 Re: mORMot 1 » The Trim in SysUtils conflicts with the Trim defined in SynCommons » 2020-11-13 12:40:16

There is Spring4D but I've never used and I don't think it is relevant, due we already have mORMot.

#62 Re: mORMot 1 » Remote JSON REST Service - calling COM in Service fails » 2020-11-13 11:56:58

ab wrote:

What you created is some kind of a connection pool: one connection per thread.
You don't recreate the connection for each request. smile

As my service was defined as sicSingle, it wouldn't be one connection per request?

EDIT: ah, Ok. I think only the implementation of the interface will be create per request, not threads.

Of course, I understand that the connections are not reused from one thread to another.
But if you use a thread pool, like the mORMot HTTP server, then it is not an issue to not reuse the connections.

Also note that in the long-term, some DB connections tends to break, or may have resource leaks.
A typical problem is when the connection is not used for a while, there may be a timeout on the server side, and the connection is closed. Then you get an exception on the client side, the next time you use it.

That was exactly what I would test today... and got an exception:

{
"errorCode":500,
"error":
{"EOleException":{"EOleException":"Connection failure"}}
}

With a few number of clients, we have seen it often. For instance, during the night there is almost no service usage, so first time in the morning, the DB connections raise errors.
What we do in SynDB is to release and reinitialize a connection after some time or number of requests, even if the thread is still the same.

You know that I can't use SynDB due the legacy code... Do you have any tip so I can implement something similar?
Which method should I override to put a timer? Maybe an external service doing fake pings?

#63 Re: mORMot 1 » Remote JSON REST Service - calling COM in Service fails » 2020-11-13 11:29:45

Actually, I'm not using any connection pool yet. That's why I'm using threadvar.

#64 Re: mORMot 1 » Remote JSON REST Service - calling COM in Service fails » 2020-11-12 21:25:02

macfly wrote:

Good to know about this solution.

I have a legacy application, where I created a connection pool.
Each thread has its own connection.
But this application is not using mORMot.

That's why I posted this today https://synopse.info/forum/viewtopic.ph … 382#p33382 - for help/inspire more people with the same issues.

Out of curiosity, when is the datamodule destroyed?
Is automatically destroyed when the thread ends?

It's created on BeginCurrentThread and destroyed on EndCurrentThread,  overrided methods.

#65 Re: mORMot 1 » Splitting a big ERP written in Delphi 7 to microservices » 2020-11-12 21:12:53

@Javierus
Yes, I create the MainDataModule and open the connection.
No, this DataModule just have one ADOConnection and 2~3 ADOQuery for others thing that I'm not using on this service.
The business rules were coded (mostly) in classes, no DataModules. But all SQL/Scripts that are used by these classes are in ADOQuery among many DataModules, just for not write SQL as strings and use theirs master/detail features for reports, etc.

#66 Re: mORMot 1 » Splitting a big ERP written in Delphi 7 to microservices » 2020-11-12 19:29:58

Guys,
I would like to share with you the solution that I've found, due my restrictions with the legacy code.

As I said before and here > Remote JSON REST Service - calling COM in Service fails, I'm using the MainDataModule as a threadvar plus overriding BeginCurrentThread/EndCurrentThread from TSQLRestServer to start it.

Event though I'm not using a connection pool (yet), the performance is good (~200ms average per request, executing multiple SQL (query and insert) in a MSSQL database and processing data to return for the client).

#67 Re: mORMot 1 » Remote JSON REST Service - calling COM in Service fails » 2020-11-12 18:33:09

But the whole DataModule is a threadvar.
On OnCreate event there is just calls to conn.Close; fill user and passwd; conn.Open.
In my mind it should be thread-safe... Didn't raise exception anymore, it fixed itself smile

#68 Re: mORMot 1 » Remote JSON REST Service - calling COM in Service fails » 2020-11-12 18:05:21

ab wrote:

If it is a threadvar, no need to use a lock to access it.

That was my first thought until receiving a "BORDBK50.DLL error" (I believe was that DLL, but sometime I got error, sometimes not - I was trying to reproduce to post here but it didn't fail). Maybe it was because I was calling ActiveX.CoInitialize() instead SynOleDB version... anyway, it isn't in production yet, I will do mor tests. Thanks.

#69 Re: mORMot 1 » Remote JSON REST Service - calling COM in Service fails » 2020-11-12 14:20:01

Actually, it's working now using BeginCurrentThread/EndCurrentThread.
I've changed the code to call SynOleDb.CoInit/CoUninit instead from ActiveX unit—maybe because it's thread safe.
Also, I've instantiate the ADOConnection, inside a MainDataModule, using a TSynLocker.

I have just one question: the MainDataModule is a threadvar. Is it really needed using a TSynLocker on BeginCurrentThread/EndCurrentThread?

#70 Re: mORMot 1 » Remote JSON REST Service - calling COM in Service fails » 2020-11-12 13:39:04

I have the same problem.

I've tried some:
1. overrided BeginCurrentThread/EndCurrentThread from TSQLRestServer, calling CoInitialize(nil)/CoUnitialize respectively.
2. implemented HttpServer.OnHttpThreadStart/OnHttpThreadTerminate for TSQLHttpServer, calling CoInitialize(nil)/CoUnitialize respectively.

I'm using ADOConnection directly.

  TServRest = class(TSQLRestServer)
  public
    procedure BeginCurrentThread(Sender: TThread); override;
    procedure EndCurrentThread(Sender: TThread); override;
  end;

  TServServer = class(TSQLHttpServer)
  private
    procedure DoThreadStart(Sender: TThread); 
    procedure DoThreadTerminate(Sender: TThread);
  public
    constructor Create(const aPort: AnsiString;
      const aServers: array of TSQLRestServer; const aDomainName: AnsiString='+';
      aHttpServerKind: TSQLHttpServerOptions=HTTP_DEFAULT_MODE; ServerThreadPoolCount: Integer=32;
      aHttpServerSecurity: TSQLHttpServerSecurity=secNone; const aAdditionalURL: AnsiString='';
      const aQueueName: SynUnicode=''; aHeadersUnFiltered: boolean=false); overload;
  end;

constructor TServServer.Create;
begin
  inherited Create(aPort, aServers, aDomainName, aHttpServerKind, ServerThreadPoolCount,
    aHttpServerSecurity, aAdditionalURL, aQueueName, aHeadersUnFiltered);
  HttpServer.OnHttpThreadStart := DoThreadStart;
  HttpServer.OnHttpThreadTerminate := DoThreadTerminate;
end;

#71 Re: mORMot 1 » The Trim in SysUtils conflicts with the Trim defined in SynCommons » 2020-11-12 10:21:52

pvn0 wrote:

Delphi has been obsolete for a while, if anyone can name one thing that gives delphi advantage over FPC I would love to hear it. Delphi is a paid product and they only added linux support a few years ago and even then it's based on LLVM compiler chain so minimum effort there (I guess you have to do that when you fire your entire compiler team). I only use Delphi because of a legacy product I have to support and I don't have time right now to rewrite for FPC. I think this is the same for the very small community still using Delphi.

I totally agree with you about Delphi, but I was talking about Object Pascal in general.
I use Delphi 7 because of a legacy product too. New projects I use FPC/Lazarus.

#72 Re: mORMot 1 » The Trim in SysUtils conflicts with the Trim defined in SynCommons » 2020-11-12 03:10:43

wangming wrote:

...Delphi’s market share in China is almost negligible. Few people use it. They are all using java.net go, etc...

Unfortunately, I believe this occur not only in China, but also around the world.
There are huge companies to promote Java, .NET, and Go. Combine their status with a multimillionaire marketing, you can get most programmers.... Even though such languages, tools, etc could be worse than others considered "dead" and not "fancy" as Object Pascal.

#73 Re: mORMot 1 » Renaming TSQLRecord to TORM » 2020-11-11 16:48:11

@macfly

You're saying that "format settings" should be coded as a constant? Besides, it should be used always when call "TSynStr.Format()"?
Put the format in a constant is not a good idea, if your system should work with other format... but this is another history.
Look, what I'm saying is this:

var
  fs: TFormatSettings;
  du: TSynDateTimeUtils;
  s1, s2, s3: string;
begin
  fs... // initialized with some format
  s1 := du.ToString(Now, fs); // I can do this OR...
  du.FormatSettings := fs; // ...I can set just one time
  s2 := du.ToString(Now); // ...and it will use du.FormatSettings here (overloaded method)...
  s3 := du.ToString(Now); // ...and here
end;

#74 Re: mORMot 1 » Renaming TSQLRecord to TORM » 2020-11-11 13:12:00

Junior/RO wrote:
mdbs99 wrote:

@Junior/RO

There is no `class var` in old Delphi versions to keep some fields that could be shared among functions in an object/record structure.


Right, but this isn't the same use case. When a pure function would use a class var?

I'm not talking about pure function, but a group of functions with the same context, which can use (or not) some local variables (object fields) per instance (like a class, but not).

For example, suppose we have a TSynDateTimeUtils—doesn't matter the name, which is the main subject on this thread—and it has a Format() method for convert to String. See, not a FormatDateTime() but only Format() because we already know the context!
Ok, the original FormatDateTime() has a overload with a TFormatSettings parameter. So, why not a code a field with this type to "setup" all functions of this context (object)?

Nowadays, TFormatSettings has its own functions... same ideia.

PS: I'm just used original naming that we know. But better names would be ToString(), ToRawUTF8(), etc instead Format().
PPS: the TFormatSettings name is wrong. "Format what?". A better name would be TDateTimeFormatSettings or just TDateTimeSettings.

#75 Re: mORMot 1 » Renaming TSQLRecord to TORM » 2020-11-11 01:26:51

@Junior/RO

There is no `class var` in old Delphi versions to keep some fields that could be shared among functions in an object/record structure.

I prefer not using classes for simple routines that a function can do the job. However, I dont think procedures / functions should be in interface section for users of any framework—we have OOP—because they may not have a good semantic; we can get name collisions; functions can be overriden easily just adding an unit after another (see Trim), etc. Pascal has the perfect structure to do it, which is called plain "old" object or, nowadays, "record with methods".

#76 Re: mORMot 1 » Renaming TSQLRecord to TORM » 2020-11-10 02:28:13

What about a "helper" object/record with all string functions? Another one for Date/Time functions, and so on.
Those will have a plain name (which @ab likes) but no collision with RTL; they would keep together all routines that have the same context; we can use code-completion for search any function from those helpers.

IMHO, that would be a "win-win" solution.

#77 Re: mORMot 1 » Splitting a big ERP written in Delphi 7 to microservices » 2020-10-31 22:26:43

I'll have simultaneous users and that is why I need multi thread.
Today we have a monolithic ERP using a single connect, but the main goal is to share the business rules on it on a website (written in Java).

The code has a TMainDataModule, which has a "singleton" MainDataModule variable. The ADOConnection is there. All other Forms and DataModule point to it.
Then, I thought in another possibility: What about using the MainDataModule as a threadvar?
- I don't need to implement my own pool
- the ERP code remains exactly it is (mostly)
- I "just" need to initialize/finalize the instance on each thread

What do you think?

#78 Re: mORMot 1 » Renaming TSQLRecord to TORM » 2020-10-31 17:34:41

macfly wrote:

Shouldn't these methods be static?  (class methods)

You can't use "class function" in object types—it compiles the object (witch is strange), but if you write TSynString.Trim(), you'll get a compile error.

Or your suggestion ist to maintain a global instance of the created objects?

It is up to you.

#79 Re: mORMot 1 » Renaming TSQLRecord to TORM » 2020-10-31 16:29:56

Maybe a framework/lib shouldn't export any procedure or function, only types. These prevent any compilation issues, as you can't use a type—which has the same name for another—in arguments that doen't fit in.

We already have objects and classes. Why should I export for users—in interface section—functions if I can code an plain-object with a group of functions, which has the same context, on it?

TrimU() could have any context... but what about this:

TSynString = object
public
  function Trim(const aValue: RawUTF8): RawUTF8;
  function Copy(...
  // etc...
end;

TSynDateTime = object
public
  function Iso8601ToDateTime(const S: RawByteString): TDateTime; overload;
  function Iso8601ToDateTimePUTF8Char(P: PUTF8Char; L: integer = 0): TDateTime;
  {$ifdef HASINLINE}inline;{$endif}
  procedure Iso8601ToDateTimePUTF8CharVar(P: PUTF8Char; L: integer; var result: TDateTime);
  // etc...
end;

Using "Syn" as a prefix is another discussion... it was just an example.

But if these types have or not a prefix, I can still "rename a lot of functions", if I want to, instead each function:

type
  TMormotStringHelper = mormot.core.base.TSynString;

#80 Re: mORMot 1 » Modularization of application with interfaces and dll (or bpl?) » 2020-10-31 16:18:54

BPL is good only when it was well coded, otherwise the code would be like a monolithic, taking out any benefits for use it.
IMO a good way to have modularization using DLL is using the same approach as dbExpress (since Delphi 7):
- create a entry point Interface
- the library exports a function that creates an instance tha implements the interface

Instead of having a lot of functions inside a DLL, we have just one entry point using object-oriented approach.

#81 Re: mORMot 1 » Splitting a big ERP written in Delphi 7 to microservices » 2020-10-31 14:38:16

So, Arnaud, you asked which DB I'm using and I've answered above...

I can't use OleDB or any other connection type (in this first refactoring) because those services uses a lot of TADOQuery, which some has master/detail, events, and so on. I believe it will be simpler keeping ADOConnection, but I'm sure that I need to implements a connection pool for them, otherwise the new services/daemon won't have a good performance.

Do you have any more tips or advices about my thoughts on that architecture?

#82 Re: mORMot 1 » Renaming TSQLRecord to TORM » 2020-10-29 15:26:32

macfly wrote:

If it were a change in a production framework I would agree that it would be unnecessary.

But it is a new version where other changes are already being made that will force refactoring.

So the time to change is now, after it is released it will no longer make sense...

You can do this:

type
  TSynTextWriter = SynCommons.TTextWriter;
  {...many others...}
  {...even functions...}

If you use a lot, you may think create an include file "syncommons.alias.inc"—I prefer writing in each unit, though.

#83 Re: mORMot 1 » Renaming TSQLRecord to TORM » 2020-10-29 15:21:55

xalo wrote:

I'm fully agree with this pattern @ab. It's very semantic, consistent and furthermore the simpler the better.
IMHO TSyn* for all types is not necessary at all. When we use some type (ie TOrm), you are in a "bounded context" where each type has its meaning in the context of its domain.
If there is a conflict, there are several easy solutions.

Bounded context for new projects from scratch, you're right.
But I'm used to maintain old projects, which mostly never used mORMot, and it could be very hard to do a refactoring to use mORMot when those conflicts pop up. But yes, there are several solutions.

#84 Re: mORMot 1 » Renaming TSQLRecord to TORM » 2020-10-28 15:57:09

ab wrote:

For core types, we use TSyn* because the root class already exist.
For example, we used TSynDictionary or TSynQueue because TDictionary and TQueue already exist in the RTL.

Devil's Advocate: if Delphi introduce a new TOrm class, will you rename the current TOrm class to TSynOrm (breaking the code)?
If not, IMHO this argument hasn't much sense... I mean, we don't control others frameworks/libs naming.
IMO, either we use a (abstract) prefix in all class or just simple names, doesn't matter what others are doing out there.

#85 Re: mORMot 1 » Renaming TSQLRecord to TORM » 2020-10-28 11:55:24

I think Pascal should have a syntax that could end this discussion about using or not prefixes:

uses
  mormot.core.base as mcore;

function Clean(const aValue: RawUTF8): RawUTF8;
begin
  result := mcore.Trim(aValue);
end;

Even better it would be, if we can use the same alias for more than one unit:

uses
  mormot.core.base as mormot;
  mormot.core.datetime as mormot;

function MyTrim(const aValue: RawUTF8): RawUTF8;
begin
  result := mormot.Trim(aValue);
end;

function MyIso8601ToDateTime(const aValue: RawByteString): TDateTime;
begin
  result := mormot.Iso8601ToDateTime(aValue);
end;

So, if you got any conflict, you just need to use an alias (a little one) as prefix!

#86 Re: mORMot 1 » Renaming TSQLRecord to TORM » 2020-10-28 11:44:58

pvn0 wrote:
edwinsn wrote:

You remind me of some of the naming conflicts, such as TTextWritter - sometimes I have to use SynCommons.TTextWritter

It becomes a none issue if you stick to the correct uses clause unit order :

{RTL}
{Libraries}
{Personal}

But then again if you're in a VCL project the IDE will be happy to shit all over your uses clause.

That's what I've been doing for years and works (mostly time).


edwinsn wrote:

[...]
I completely agree with you! `TSyn` for all types.

Comparing mORMot2 vs first one, it seems that Arnaud was replacing all Synopse "Syn" references, using clean prefixes only related with the modules... that's what I thought.

We know that "Syn" stands for Synopse, but is it consistent with the rest of code? I'm asking because the main prefix for all units now is "mormot", no more "Syn".

#87 Re: mORMot 1 » Renaming TSQLRecord to TORM » 2020-10-28 03:59:04

edwinsn,
Some frameworks/libs use the same prefix for all types (e.g. Indy, TMS, JEDI, DevExpress, etc) but, even though it could minimize eventual collision names, it's not perfect as anyone can use the same prefix as other already has used.
It could be very verbose either. Imagine typing TMormotTextWritter or TMormotOrm (!!).
Simple names are better to read, however it could have colision... difficult decison.
However, in my opinion, if the framework/lib uses an abstract prefix (e.g. Id, Jv, dx, etc), it must be in all types. That is consistency.

#88 Re: mORMot 1 » Renaming TSQLRecord to TORM » 2020-10-28 03:06:49

ab wrote:

I have made another pass or types renaming in mORMot 2.
It sounds consistent everywhere now.

You may think renaming all TSyn* prefix too.

#89 Re: mORMot 1 » Splitting a big ERP written in Delphi 7 to microservices » 2020-10-27 20:33:17

I think I can use OleDB after the first refactor, due the deadline...
If I can code a pool for the same connection component, everything on ERP will continue working without MUCH changes, as the ERP will use 1 connection at all as always be.

I never used mORMot OleDB, so I don't know if I gonna have some issues (e.g. all queries will run smoothly?) and other things that I didn't think yet.
I'm using MSSQL 2008.

I will see the slides now...

#90 Re: mORMot 1 » Splitting a big ERP written in Delphi 7 to microservices » 2020-10-27 19:24:37

If I could change the main connection, won't be a problem using just mORMOt. Unfortunately, I can't.
I need to keep things working in ERP code, while the code is migrating for services—one code, multiple apps running.

Most of classes were coded following a good separation between GUI, rules and services. "Services" are classes  that access external resources, like database (but using the global variable connection).

What I'm thinking to do—and I would like to read your opinions—is:
1. Implement a simple connection pool using ADO. Lock/create or get a connection avaible/Unlock.
2. Then, I'm gonna change those classes to use the pool instead the main connection. For the ERP perspective, it will be the same; but for the new mORMot services, it will be thread safe.
3. Repeat 1 and 2 for other classes/services.

Do you think that is a good strategy to rewrite the code?
What could be the better place—according to mORMot framework—to initialize this pool instance?

#91 Re: mORMot 1 » Splitting a big ERP written in Delphi 7 to microservices » 2020-10-27 13:27:46

I've never configureted the IIS + http.sys, so I would like a little more help, if you don't mind.

Well, I wrote the basics... now I have more information:
- First services will be used on local network, which means (for me) that I do not need to use IIS for them, but you see some advantage using it?
- The ERP code has a "classic" datamodule with a ADOConnection component, which is used all over the code. Should I initialize that connection in a specific place that is thread safe?

About automatic updates, I didn't work on it, but you remembered well. I will take a look on the sources.

#92 mORMot 1 » Splitting a big ERP written in Delphi 7 to microservices » 2020-10-26 14:59:57

mdbs99
Replies: 15

Hello everybody,

I'm going to split a big ERP to microservices. Those services will be consumed by ERP and a website written in Java.
My final goal is to write everything in FPC, but now the first services need to be written in Delphi 7, due the code is very "Windows like", using ADO for connection, some 3rd components, etc. The server also will be Windows, running IIS.

I have a good experience using FPC with FastCGI module on Windows+IIS, since its beginning. The newest version also have a lot of improvements, very different comparing early versions based on WebBroker... however, as those services need to be written in Delphi 7, I can't use FPC web framework.

I know that mORMot has everything that I need to write services in Delphi 7 (which will be rewritten in FPC in the future) but I would like to receive some ideas to use the best approach considering the environment and features that must have:

- Windows Server + IIS
- A/B deploy: service shouldn't stay off line, if we update a new version of it

Using FPC + FastCGI it's "easy" and I know the path... what about mORMot?

best regards, thanks.

#93 Re: mORMot 1 » Compile SynPdf sample error on lazarus 2.0.10 + FPC 3.2.0 » 2020-10-21 15:19:41

ab wrote:

Lazarus/LCL is not supported for SynPdf.

What you mean? On Github page says "Targets Delphi 5 up to Delphi 10.3 Rio (and latest version of FPC)..." so, it suppose to work on FPC, but you say LCL... I got confused here.

I've never used SynPdf so, sorry if my question sounds silly.

#94 Re: mORMot 1 » RecordLoadJSON(): how to know what has changed in JSON? » 2020-10-19 22:42:32

macfly,
I'm thinking to do some like that. The issue is how do it in an automatically way. I don't want to test every field manually, using Exists(), at first.

However, if it isn't possible, I will code this in regression tests.

Thanks.

#95 Re: mORMot 1 » RecordLoadJSON(): how to know what has changed in JSON? » 2020-10-16 22:05:14

If your API is not backward compatible, then you need to use a versioned API.

It's not my API, otherwise it wouldn't have this issue smile

I don't think that something magic may happen in RecordLoadJSON

"Roger that."

Perhaps a record is not the best structure for you, something more versatile like a TDocVariant or a class with custom serialization (not published properties, but manual reader/writer methods) may be used.
Then, once it is stabilized, use records.

I've coded the client that consume the API and it's written using records/objects all over the place—I can't change this now.

Thanks for try to help. If the API continues changing, I will continue investigating what has changed.

#96 Re: mORMot 1 » RecordLoadJSON(): how to know what has changed in JSON? » 2020-10-16 17:43:28

I may set this option to make it safer, however I've already got change even in field types on the API.

I think this API is new and still under construction...

IIRC, TTextWriter.RegisterCustomJSONSerializerFromText() shows a nice error, if record/object do not match with its "data string configuration". It would be nice, if RecordLoadJSON() could do the same.

#97 mORMot 1 » RecordLoadJSON(): how to know what has changed in JSON? » 2020-10-16 11:14:22

mdbs99
Replies: 6

I'm consuming a third API using RecordLoadJSON() a lot with proper TTextWriter.RegisterCustomJSONSerializerFromText() to register and fill records/objects "automagically".

My issue is that they, eventually, change the API without notify us before, and it breaks the system. What I do is search API responses—using the log—to find out which has changes, which new fields they introduced, etc.

Is there a way for mORMot tells me which is different between the JSON response and my record(s) structure?

best regards.

#98 Re: mORMot 1 » mORMot2: questions » 2020-10-13 18:04:50

macfly wrote:

An advantage, is that it forces you to specify the object avoiding ambiguities of functions with the same name in different units.

Exactly.

macfly wrote:

In the case of mORMot, I even always identify the unit in the code to avoid any ambiguity.

S := SynCommons.Trim (S);

Which will be much more verbose in mORMot2, due to the new unit names...

#99 Re: mORMot 1 » mORMot2: questions » 2020-10-13 16:28:36

1. I didn't mean that these functions need to work with the same type and/or same private attributes. I meant that it might be good to group similar functions into an "auxiliary" object, which contains everything about a subject. For example, in another topic, you told us about the `TrimControlChars ()` function, which we didn't know about. Whether this function exists within an object, e.g. "TSynStringHelper", it could be easier to find it instead search all functions inside an unit. Besides, the code completion will tell us about all related (string stuff) in this "help object".

2. Understood, thanks.

#100 mORMot 1 » mORMot2: questions » 2020-10-13 13:15:24

mdbs99
Replies: 5

mORMot2 is coming much better organized, congratulations.

I have 2 questions:
1. mORMot keeps using old-object types (which is good, of course) so why not group many functions inside them? For example

mormot.core.rtti.pas

has a lot of functions such

ClassFieldPropWithParents, ClassFieldInt64, ClassFieldInstance, ClassFieldPropWithParentsFromUTF8...

. Why not a TClassField* type to put all these functions there?

2. Because Delphi's bug using old-object, mORMot has code like this:

{$ifdef USERECORDWITHMETHODS}
  TSynMonitorUsageID = record
  {$else}
  TSynMonitorUsageID = object
  {$endif USERECORDWITHMETHODS}
  public

But I've found some "object" without this directive; sometimes using {$A-}, sometimes not. Why so?

Thanks.

Board footer

Powered by FluxBB