ZConnection - force a complete reload of database libraries
ZConnection - force a complete reload of database libraries
Continuing the slightly offtopic http://zeoslib.sourceforge.net/viewtopi ... 40&t=97224 I managed to force longer SQL operations in a worker thread and added a cancel option to call TerminateThread on it. Seems to be working so far.
My only concern is that - as expected - this leaves lots of objects unfreed and leaks memory.
Can I somehow ask TZConnection to unload all libraries and load them again? I'm wondering if this would yield some positive results.
My only concern is that - as expected - this leaves lots of objects unfreed and leaks memory.
Can I somehow ask TZConnection to unload all libraries and load them again? I'm wondering if this would yield some positive results.
Delphi 12.2, Zeos 8 from latest GIT snapshot
Using:
- MySQL server 8.0.18; libmysql.dll 8.0.40 x64 5.7.19 x68, libmariadb.dll 3.3.11
- Oracle server 11.2.0, 12.1.0, 19.0.0; oci.dll 21.15
- MSSQL 2012, 2019; sybdb.dll FreeTDS_3102
- SQLite 3.47
Using:
- MySQL server 8.0.18; libmysql.dll 8.0.40 x64 5.7.19 x68, libmariadb.dll 3.3.11
- Oracle server 11.2.0, 12.1.0, 19.0.0; oci.dll 21.15
- MSSQL 2012, 2019; sybdb.dll FreeTDS_3102
- SQLite 3.47
Re: ZConnection - force a complete reload of database libraries
When you call WinAPI TerminateThread, you just halt the Execute method but nothing prevents thread destructor or any other method from running. So just dispose all objects you created there.
Re: ZConnection - force a complete reload of database libraries
Unfortunately it's not that easy The worker thread allocates nothing, it just receives a method pointer to execute. After aborting I'm immediately calling ZConnection.Disconnect to force the connection to close, cleaning up any half-states.Fr0sT wrote:When you call WinAPI TerminateThread, you just halt the Execute method but nothing prevents thread destructor or any other method from running. So just dispose all objects you created there.
For example, if I call TerminateThread during a '.Connect' operation, these are leaked:
An unexpected memory leak has occurred. The unexpected small block leaks are:
9 - 24 bytes: Unknown x 6
25 - 40 bytes: TList x 1, Unknown x 91
41 - 56 bytes: TZOracleDriver x 1, TZNativeLibraryLoader x 2, TCriticalSection x 2, TZDefaultIdentifierConvertor x 1, TZUnmodifiableCollection x 4, Unknown x 60
57 - 72 bytes: TZOracleDatabaseInfo x 1, TZCollection x 7, Unknown x 2
73 - 88 bytes: TZHashMap x 2, TZDriverManager x 1, Unknown x 1
89 - 104 bytes: TZClientVariantManager x 1, TZURL x 1
105 - 120 bytes: Unknown x 1
121 - 136 bytes: TZURLStringList x 1
137 - 152 bytes: TZAnyValue x 2
201 - 216 bytes: Unknown x 1
233 - 248 bytes: TZOracleConnection x 1
313 - 344 bytes: Unknown x 1
345 - 376 bytes: Unknown x 1
473 - 520 bytes: TZOracle9iPlainDriver x 2
617 - 664 bytes: TZOracleDatabaseMetadata x 1
The sizes of unexpected leaked medium and large blocks are: 3112, 3112
Terminating .Open sometimes seems not to leak anything.
Delphi 12.2, Zeos 8 from latest GIT snapshot
Using:
- MySQL server 8.0.18; libmysql.dll 8.0.40 x64 5.7.19 x68, libmariadb.dll 3.3.11
- Oracle server 11.2.0, 12.1.0, 19.0.0; oci.dll 21.15
- MSSQL 2012, 2019; sybdb.dll FreeTDS_3102
- SQLite 3.47
Using:
- MySQL server 8.0.18; libmysql.dll 8.0.40 x64 5.7.19 x68, libmariadb.dll 3.3.11
- Oracle server 11.2.0, 12.1.0, 19.0.0; oci.dll 21.15
- MSSQL 2012, 2019; sybdb.dll FreeTDS_3102
- SQLite 3.47
Re: ZConnection - force a complete reload of database libraries
Ah, I thought you're talking about threaded BLOB loading only
Well, terminating a thread is always a very bad practice and should be used only as extreme measure. I'd recommend trying API functions for that, f.ex. for MySQL functions like mysql_kill() / mysql_query() with KILL stmt; connect-stage could be controlled with timeouts or even mysql_real_connect_nonblocking() but the latter must be first supported by Zeos.
Well, terminating a thread is always a very bad practice and should be used only as extreme measure. I'd recommend trying API functions for that, f.ex. for MySQL functions like mysql_kill() / mysql_query() with KILL stmt; connect-stage could be controlled with timeouts or even mysql_real_connect_nonblocking() but the latter must be first supported by Zeos.
Re: ZConnection - force a complete reload of database libraries
The trick here is that I can pass any method pointer, let it be ZConnection.Connect, TZMetaData.Open or TZQuery.ExecSQL. The solution must include but not exclusive to .Connect and should work on all protocols. As according to my knowledge there is no such thing, especially since Zeos's design is asynchronous.Fr0sT wrote:connect-stage could be controlled with timeouts or even mysql_real_connect_nonblocking()
I am aware that TerminateThread is kind of a last-resort, this is what I could come up with which fulfills all the above requirements (I'm open to suggestions, though). Plus it seems to be working nicely. Apart from the leak
So, is there a command which forces a ZConnection to unload all libraries for me?
Delphi 12.2, Zeos 8 from latest GIT snapshot
Using:
- MySQL server 8.0.18; libmysql.dll 8.0.40 x64 5.7.19 x68, libmariadb.dll 3.3.11
- Oracle server 11.2.0, 12.1.0, 19.0.0; oci.dll 21.15
- MSSQL 2012, 2019; sybdb.dll FreeTDS_3102
- SQLite 3.47
Using:
- MySQL server 8.0.18; libmysql.dll 8.0.40 x64 5.7.19 x68, libmariadb.dll 3.3.11
- Oracle server 11.2.0, 12.1.0, 19.0.0; oci.dll 21.15
- MSSQL 2012, 2019; sybdb.dll FreeTDS_3102
- SQLite 3.47
Re: ZConnection - force a complete reload of database libraries
Well, cancelling a current operation should exist in some form for all drivers. Cancelling a connect is more complicated though... Anyway I don't think you'll achieve what you want by unloading libs. There could exist some internally allocated variables which won't be disposed on thread termination so they will leak.
Anyway it's easier for you to examine sources in src\plain\ZPlainLoader.pas and src\plain\ZPlainDriver.pas and probably do what you want yourself.
Anyway it's easier for you to examine sources in src\plain\ZPlainLoader.pas and src\plain\ZPlainDriver.pas and probably do what you want yourself.
-
- Platinum Boarder
- Posts: 1999
- Joined: 17.01.2011, 14:17
Re: ZConnection - force a complete reload of database libraries
Hello aehimself,
Zeos simply isn't suited for that kind of use (yet). Asume an operation trhat is programmed like this:
If you simply terminate the thread, while it is in the try .. finally block and doing something lengthy, you have no chance of freeing MyStringList. And while we usually don't use TStringList a lot as a local object, other kinds of memory do get allocated like this. So in that case you are liable to get memory leaks in any case - simply because there is no chance of freeing MyStringList because there is no reference to it anymore.
If you want to be able to abort operations you need to extend the driver to be able to do that. And you need to do the thread synchronization for that kind of thing. Something like this could work:
Best regards,
Jan
Zeos simply isn't suited for that kind of use (yet). Asume an operation trhat is programmed like this:
Code: Select all
MyStringList := TStringList.Create;
try
// do something lengthy on MyStringList
finally
FreeAndNil(MyStringList);
end;
If you want to be able to abort operations you need to extend the driver to be able to do that. And you need to do the thread synchronization for that kind of thing. Something like this could work:
- stop execution of the currentlx working thread - set it to get not executed anymore
- call the database operation that is necessary for cancelling the currently running operation.
- make sure, the stopped thread can get the information that the operation was cancelled intentionally
- let the thread continue its operation, so it can free any memory that needs to be freed.
- make sure, Zeos behaves in a decent way when it expects a result set but doesn't get one.
Best regards,
Jan
Re: ZConnection - force a complete reload of database libraries
I had some thought about calling TerminateThread in a middle of a Try ... Finally ... End block a couple of day ago and it did start to bother me. I'll start to look into the sources @Fr0st pointed me to and will try to somehow achieve what I'd like to do.
@Jan,
Your approach is very interesting, I'll definitely play around with it! Just one concern: I have a thread which simply calls a method, like:
If I pause this with threadobj.Suspended := True; and then resume - will this actually suspend the connection / opening the dataset; basically stop the dataflow so I can somehow "inject" my cancel request? As we are working synchronously I suspect yes, but I personally never tried this kind of intervention before.
@Jan,
Your approach is very interesting, I'll definitely play around with it! Just one concern: I have a thread which simply calls a method, like:
Code: Select all
Procedure Execute;
Begin
If Assigned(_proc) Then _proc;
End;
Delphi 12.2, Zeos 8 from latest GIT snapshot
Using:
- MySQL server 8.0.18; libmysql.dll 8.0.40 x64 5.7.19 x68, libmariadb.dll 3.3.11
- Oracle server 11.2.0, 12.1.0, 19.0.0; oci.dll 21.15
- MSSQL 2012, 2019; sybdb.dll FreeTDS_3102
- SQLite 3.47
Using:
- MySQL server 8.0.18; libmysql.dll 8.0.40 x64 5.7.19 x68, libmariadb.dll 3.3.11
- Oracle server 11.2.0, 12.1.0, 19.0.0; oci.dll 21.15
- MSSQL 2012, 2019; sybdb.dll FreeTDS_3102
- SQLite 3.47
Re: ZConnection - force a complete reload of database libraries
Suspending a thread from outside is deprecated and non-reliable. I'm sure this kind of stuff should not go to prod/release versions.
Re: ZConnection - force a complete reload of database libraries
So the good thing is that database systems already seem to support this: Oracle has OCIBreak(), MySQL has mysql_kill and DBLib (FreeTDS?) has dbcancel.
As https://vrogier.github.io/ocilib/doc/ht ... abort.html mentions:
Any call to OCI_Break() has to be done from a separate thread because the thread that has executed a long OCI call is waiting for its OCI call to complete.
Unfortunately though, it is marked as "unused API" in the ZPlainOracleDriver.pas
As the code is there I simply can try to uncomment it. My only question is - how can I call these methods?
I'm thinking on something like this:
...but I'm on the wrong track here, as it does not seem to compile
As https://vrogier.github.io/ocilib/doc/ht ... abort.html mentions:
Any call to OCI_Break() has to be done from a separate thread because the thread that has executed a long OCI call is waiting for its OCI call to complete.
Unfortunately though, it is marked as "unused API" in the ZPlainOracleDriver.pas
Code: Select all
@OracleAPI.OCIBreak := GetAddress('OCIBreak');
Code: Select all
function TZMySQLPlainDriver.Kill(mysql: PMYSQL; Pid: LongInt): Integer;
begin
Result := mysql_kill(mysql, Pid);
end;
Code: Select all
If ZConnection1.DbcDriver = IZMySQLPlainDriver Then (ZConnection1.DbcDriver As IZMySQLPlainDriver).Kill
Delphi 12.2, Zeos 8 from latest GIT snapshot
Using:
- MySQL server 8.0.18; libmysql.dll 8.0.40 x64 5.7.19 x68, libmariadb.dll 3.3.11
- Oracle server 11.2.0, 12.1.0, 19.0.0; oci.dll 21.15
- MSSQL 2012, 2019; sybdb.dll FreeTDS_3102
- SQLite 3.47
Using:
- MySQL server 8.0.18; libmysql.dll 8.0.40 x64 5.7.19 x68, libmariadb.dll 3.3.11
- Oracle server 11.2.0, 12.1.0, 19.0.0; oci.dll 21.15
- MSSQL 2012, 2019; sybdb.dll FreeTDS_3102
- SQLite 3.47
Re: ZConnection - force a complete reload of database libraries
Yep. FB has cancelling as well though I've never used it. As for the fact the functions were commented out, I guess nobody have touched them until this time . I think this should be a general method implemented for all drivers just like PingServer. But for now you can implement the method as you like for testing purposes.
There's GetInstance method to retrieve an object from an interface. You can check its usage in sources.aehimself wrote:My only question is - how can I call these methods?
Re: ZConnection - force a complete reload of database libraries
This was exactly my thought. I started to check the implementation of PingServer to get an idea on how to proceedFr0sT wrote:I think this should be a general method implemented for all drivers just like PingServer.
Guess I need 7.3 for that...? I can not see any references of .GetInstance in 7.2.6 only in 7.3.Fr0sT wrote:There's GetInstance method to retrieve an object from an interface. You can check its usage in sources.
*sigh* time to upgrade again ^^
Delphi 12.2, Zeos 8 from latest GIT snapshot
Using:
- MySQL server 8.0.18; libmysql.dll 8.0.40 x64 5.7.19 x68, libmariadb.dll 3.3.11
- Oracle server 11.2.0, 12.1.0, 19.0.0; oci.dll 21.15
- MSSQL 2012, 2019; sybdb.dll FreeTDS_3102
- SQLite 3.47
Using:
- MySQL server 8.0.18; libmysql.dll 8.0.40 x64 5.7.19 x68, libmariadb.dll 3.3.11
- Oracle server 11.2.0, 12.1.0, 19.0.0; oci.dll 21.15
- MSSQL 2012, 2019; sybdb.dll FreeTDS_3102
- SQLite 3.47
Re: ZConnection - force a complete reload of database libraries
No luck until now. After upgrading to 7.3, I exported the property of TZMySQLConnection in ZDbcMySql.pas:
Then, I uncommented the mysql_kill functions in ZPlainMySqlDriver.pas. Test code looks like this:
The connection is lost with the MySQL server aborting everything, but I guess it's because of the access violations in libmysql.dll...
Code: Select all
Property Handle: PMYSQL Read FHandle;
Code: Select all
Var
a: TZMySQLPlainDriver;
begin
a := ZConnection1.DbcConnection.GetIZPlainDriver.GetInstance As TZMySQLPlainDriver;
a.mysql_kill((ZConnection1.DbcConnection As TZMySQLConnection).Handle, 0);
// a.mysql_query((ZConnection1.DbcConnection As TZMySQLConnection).Handle, 'KILL QUERY');
end;
Delphi 12.2, Zeos 8 from latest GIT snapshot
Using:
- MySQL server 8.0.18; libmysql.dll 8.0.40 x64 5.7.19 x68, libmariadb.dll 3.3.11
- Oracle server 11.2.0, 12.1.0, 19.0.0; oci.dll 21.15
- MSSQL 2012, 2019; sybdb.dll FreeTDS_3102
- SQLite 3.47
Using:
- MySQL server 8.0.18; libmysql.dll 8.0.40 x64 5.7.19 x68, libmariadb.dll 3.3.11
- Oracle server 11.2.0, 12.1.0, 19.0.0; oci.dll 21.15
- MSSQL 2012, 2019; sybdb.dll FreeTDS_3102
- SQLite 3.47
Re: ZConnection - force a complete reload of database libraries
I've little knowledge on MySQL but pid hardly could be 0, I guess you should first get the right value with mysql_thread_id() (according to docs). Moreover the function returns a result, maybe it could shed some light.
Re: ZConnection - force a complete reload of database libraries
I know, it was only a test And - surprise, surprise - changing using libmariadb.dll instead of libmysql.dll does the job nicely (God, I can not express how much I started to dislike Oracle products!). No more access violations, and returns an error "Thead with PID 0 not found" or something similar.
This is how far I got yesterday, plus I saw the warning that mysql_kill is obsolate and will be removed. I still need to test the new (commented) version, but I don't really know how it will work as I must run it from a completely separate context (a new connection, effectively).
Not there yet, but getting close. Once I have a working idea on MySQL I'll start to convert it to a method of ZConnection and add the Oracle version. First of all, I'll need some proper version control set up...
This is how far I got yesterday, plus I saw the warning that mysql_kill is obsolate and will be removed. I still need to test the new (commented) version, but I don't really know how it will work as I must run it from a completely separate context (a new connection, effectively).
Not there yet, but getting close. Once I have a working idea on MySQL I'll start to convert it to a method of ZConnection and add the Oracle version. First of all, I'll need some proper version control set up...
Delphi 12.2, Zeos 8 from latest GIT snapshot
Using:
- MySQL server 8.0.18; libmysql.dll 8.0.40 x64 5.7.19 x68, libmariadb.dll 3.3.11
- Oracle server 11.2.0, 12.1.0, 19.0.0; oci.dll 21.15
- MSSQL 2012, 2019; sybdb.dll FreeTDS_3102
- SQLite 3.47
Using:
- MySQL server 8.0.18; libmysql.dll 8.0.40 x64 5.7.19 x68, libmariadb.dll 3.3.11
- Oracle server 11.2.0, 12.1.0, 19.0.0; oci.dll 21.15
- MSSQL 2012, 2019; sybdb.dll FreeTDS_3102
- SQLite 3.47