When will a Preemptive safe trigger run or not run in Preemptive?

Here is a really tecky question:
What are the conditions that would cause a preemptive-safe trigger to run in non preemptive?

If I’ve make all my triggers preemptive-safe: will they always run preemptive? or does it depend on the context of the Process (especially when called from remote)?

One of my guys recently converted all triggers in a database to preemptive: and wrote some logging code: and found that the triggers were running in a non-preemptive process (when saving records from remote).

The thread safety of triggers is dependent on several factors.

https://speakerdeck.com/miyako/purezentesiyon-4d-world-tour-2019-1ri-mu-yori?slide=14

(it’s partially Japanese but you’d get the gist)

The trigger code is analysed for each table that are called via one of the listed database write commands.

If the table name is hard-coded, only the trigger of the specified table is analysed.

If the table is passed via a dereferenced pointer, the trigger of all tables are analysed.

Same for “default table” context.

As always, the entire call chain starting from the trigger is analysed.

If there are no unsafe code (interprocess variables, etc) the trigger is considered thread safe.

Miyako,

I set the “Can be run in preemptive process” attribute to enabled in all of the processes, referenced in all of the triggers, in my application. It compiles without error.

Then I tested my trigger to see if it was running preemptive. Compiled, Client/Server, Process Properties (procMode:bit 1 = preemptive execution), in the trigger method.

The trigger method is a call to a single method, which is set to “Can be run in preemptive process”:

Table_Trigger // This method contains all of the trigger code, and it is preemptive process enabled.

ProcMode bit 1 is off in the trigger method, running compiled, client/server.

Windows 10 Home current
4D v17R4 234574 64bit

I need suggestions on how to make the triggers run preemptive.

Thanks!

Miyako,

I don’t think you understood our question.

We’ve made a database that DOES HAVE ALL OF IT’S TRIGGERS PTS. (preemptive thread safe).

We’ve successfully compiled the database with a method that calls “SAVE RECORD($AnyTable->)” that is marked as PTS: so we know all the triggers are PTS.

However, we think we’ve verified that triggers (at least the ones we’ve tested) are NOT running in a Preemptive process. (we tested the state of the process “mode”)

So: what’s up?

I think it is important to understand the distinction between the thread safety of the process and the thread safety of the method.

if the trigger method is thread safe, you can call it from a preemptive process or worker on the server.

but that has nothing to do with the thread safety of the “execute on server” method property, a.k.a. the so called twin process, or trigger process, which is always cooperative.

Miyako,

I think it is important to understand the distinction between the thread safety of the process and the thread safety of the method.
Yes: That’s what we’re trying to understand: but I don’t think anyone’s answered that yet.

but that has nothing to do with the thread safety of the “execute on server” method property
I’m not asking about anything to do with the “execute on server” method property.

I’ll state my question again:
scenario: we have a database that has all table triggers are thread-safe.
BUT: upon saving a record (initiated from a remote client), we discover that the trigger did not run in thread safe mode.

SO: we’re asking: how can we ensure that the twin-server-side process from a client process will actually run it’s triggers in thread-safe?

  • and can it get switched to non-thread-safe?
  • and if it get’s switched to non thread-safe, can it subsequently switch back to thread-safe?

how can we ensure that the twin-server-side process from a client process will actually run it’s triggers in thread-safe?

that is not possible.
even if the trigger method is thread safe,
the process (twin-server-side process from a client process) is not thread safe to start with, by design.

and can it get switched to non-thread-safe?

no.

and if it get’s switched to non thread-safe, can it subsequently switch back to thread-safe?

triggers don’t start a new process. that is the point.

ok: now that answers my question.

Not an answer that I necessarily like… but as least now we know.

So the twin-processes on the server for each client aren’t preemptive…

You would think this would quickly cause an issue with scaling since in an exceptionally modest case of 100 users running 4 processes each (which also require a twin server process) there would be 400 non-preemptive processes on the server, many of which could be waiting for IO—which is the exact problem preemptive execution was created to alleviate.

I can understand why 4D would do this…
I think the issue is context: A 4D remote can save records with thread-safe triggers, and then save records in another table with non-thread-safe triggers. It can also call methods marked as “execute on server” that are or are not thread-safe. Within all of this: 4D runs them in the same server-side twin process: and maintains the context (process variables, record selections, etc).

I would think that it would be possible to switch the server side process between preemptive and cooperative as needed.
Since the server-side twin process is inactive while the client is not saving a record (or running an Execute on server process), it seems to me that when a remote client saves a record: that the process could decide at the moment: is this a thread-safe trigger? then process on a separate processor.

4D 17: Even if you are running a trigger in a fully preemptive process on the server (or 4D single user), a trigger creates a secondary process when it executes. If I recall, this process is called “REST”. In early 17 releases these extra processes would be created but never cleaned up. I also had strange locking problems with records created in preemptive processes. I disabled the processes from running in preemptive mode and all the record locking issues went away.

I no longer see the extra process created in 17R. I’m hoping all the locking issues are solved in version 18.

What is your use of trigger actually ? Are you using ODBC or external SQL ?
Trigger is a convenience not a fatality.
You can perhaps do the same without using it.
And so no more problem no ?

Manuel,

Triggers are primarily for data integrity across multiple tables, and real-time accounting. Not for ODBC or SQL. Our triggers run on a 4D Remote or 4D Server record save.

Yes, there is always a way to re-code what was a negatively impacting trigger method into thread-safe process.

I have done that, and continue to do it.

The hope is that 4D will in the future make 4D Remote’s triggers run pre-emptive/multi-threaded, so that we don’t have to greatly complicate our code.

Simple, elegant, beautiful is not possible re-writing most trigger code into pre-emptive/multi-threaded.

We love, simple, elegant, beautiful coding.

We wait on 4D for pre-emptive/multi-threaded triggers.

: Dan KELLEY

So the twin-processes on the server for each client aren’t
preemptive…

Actually, it is not exactly true. 4D create 2 twin processes on the server, one cooperative and one preemptive. Depending on the code you execute on the client, one of the 2 process will be used, for every queries, the preemptive process will be used. For CUD operation, the cooperative process will be used, no matter if the trigger are thread safe or not. I have no idea if this behavior may change in the future but my tests show me this behavior.

If you are using the “Execute on server” method property, the code will be fully executed on the cooperative twin process (even the queries). A nice feature could be that a thread-safe method could be run in the preemptive twin process, but I don’t know if that is possible.

Today, the only way to make your trigger to be run preemptively, is to run a server-side process or server-side worker preemptively with some code that will call a thread-safe trigger.

Gabriel,

Over this last weekend I implemented an execute-on-server (attribute) method that does a CALL WORKER which run thread-safe.

So I now have the performance I sought, but the WORKER does not run in the TRANSACTIONS, that trigger code does.

CALL WORKER using Signal(s) seems to be a very elegant way of running multi-threaded code from a trigger.

It would be simpler for us developers if we could designate a TRANSACTION CONTEXT for a WORKER.

Thanks.

Yes using an external preemptive worker force the cycle to be asynchronous between the calling process (client) and the executing process (server). Which make the transaction process to be separated -> You cannot start a transaction in the client process and finalize it in the worker (or vice-versa)

Transaction must be used in a synchronous execution cycle. It work even with a “Execute on server” method with code executed on the cooperative twin process

Miyako,
I’ve been thinking more about this.
I understand that even if all triggers are thread-safe: that a remote-side process could call an “execute on server” in-line method: which would NOT necessarily be a thread-safe method: and this is a valid reason why the server-side twin process cannot be thread-safe.

HOWEVER: Assuming I launch, on 4D Remote, a preemptive process on client: then any an all code that that process can call in line, should be PTS (preemptive thread safe). So therefore, it makes sense to me that a preemptive remote-side method could, and should be able to have preemptive triggers running on the server.