DBTASKID(1) gives ? (NULL)

Posted by SJProgress on 13-Feb-2020 10:42

Hi,

we face a strange problem with DBTASKID function. He have some replication code relying in DBTASKID() function. (OE 11.6 and OE 11.7)
Code looks like:

TRIGGER PROCEDURE FOR REPLICATION-WRITE OF customer OLD BUFFER oldcust.

DO ON ERROR UNDO, LEAVE:
CREATE RepData.
ASSIGN
RepData.RepDataID = NEXT-VALUE(RepdataID)
RepData.DTCreate = NOW
RepData.TransID = DBTASKID(1)
RepData.TabName = "customer"
...
.
END.

Everything works fine.

- Database  is connect (always same one database via TCP; we do no disconnect/connect; only one connect at startup)
- Record Repdata is created
- NEXT-VALUE(RepdataID) from sequence works OK
- all data is written OK

!!  BUT RepData.TransID = DBTASKID(1) = ?

I will open an issue with tech support. But we cannot reproduce this situation in any way. Just see that it happens from time to time.

So question to the community:

Maybe someone alraedy faced this problem?

Any hints where to search or look to be able to make DBTASKID(1) to become NULL ?

Thanks for any feedback
Josef

Posted by SJProgress on 13-Feb-2020 16:11

I will improve existing code to use DBTASKID(LDBNAME(BUFFER RepData)) and see, if problem reoccurs.

(Might take some months. Then I will have to dig again for possible other reasons for this symtom.)

Thanks a lot.

All Replies

Posted by gus bjorklund on 13-Feb-2020 13:42

the 4GL runtime and the database both delay transaction starts as long as they can. this causes the 4GL to sometimes think a transaction is active when one is not. depending on how the 4GL code is written, this may happen in several different ways.

a simple example is when you update a record that is share locked. if no fields involved in indexes are changed, then the database transaction will not start until the updated record is sent to the database. often this happens at END statement of the transaction block.

thus DBTASKID may return ? in cases where the 4GL code appears as if it should have started a transaction.

Posted by SJProgress on 13-Feb-2020 13:51

>>will not start until the updated record is sent to the database. often this happens at END statement of the transaction block.

Thanks for this information.

But can this be possible for code in a  "trigger procedure for REPLICATION-WRITE"??

(Should not have been start the transaction always at this point?)

Posted by George Potemkin on 13-Feb-2020 14:03

DBTASKID() should get value if you just send any request to a database:

/* Touch db, for example through VST: */
DO TRANSACTION:
  FIND FIRST DICTDB._MyConnection NO-LOCK /*WHERE _MyConn-Id LT 0 NO-ERROR*/.
  MESSAGE DBTASKID("DICTDB") VIEW-AS ALERT-BOX.
END.

Posted by frank.meulblok on 13-Feb-2020 14:28

In addition to what Gus said, there's something else to consider if you have multiple databases connected at the same time.

The ABL client will not tell a database that it has an active transaction, until it accesses that database (= performing any CRUD operation) within that transaction.

And with a database remaining unaware, it won't be assigning a number for DBTASKID to return.

Simplified example, code such as this:

FIND FIRST db1.table1 SHARE-LOCK.

DO TRANSACTION:

 FIND FIRST db2.table2 NO-LOCK.

 MESSAGE DBTASKID("db1") DBTASKID("db2").

END.

will show "? <some number>".

That's because the client is not interacting with "db1" after the transaction starts (despite signalling it may be going to modifiy data before the transaction started), but it is interacting with "db2" (even though it doesn't plan to update anything there).

Posted by SJProgress on 13-Feb-2020 14:38

While I am sure we only connect to one database and DBTASKID(1) / DBTASKID("DICTDB") and DBTASKID("myDBName") should give the same result for below code:

CREATE RepData.

ASSIGN

RepData.RepDataID = NEXT-VALUE(RepdataID)

RepData.DTCreate = NOW

RepData.TransID = DBTASKID(1) / DBTASKID("DICTDB") / DBTASKID("myDBName")

.

What do you think is the most appropriate/reliable variant of DBTASKID() to securely hit the database that holds the transaction for table RepData?

(we do not use preceeding database aliases in our code.)

- DBTASKID("myDBName") is no good solution as it will break in cases where I connect to the database with some alias.

- DBTASKID(1) will break, if I connect to two databases (which we currently do not do - but just in case) and databse with table RepData is DB2

- DBTASKID("DICTDB") will break too, if I connect two databases in wrong order (I assume)

Would it be possible to query the database alias or number that table "RepData" from my transation is in?

Posted by SJProgress on 13-Feb-2020 14:52

I did some test with Sports2000 and found, that DBTASKID(1) is bad in situations where I have a pending disconnect.

Connecting to Sports2000 and running the following code:

disconnect sports2000.

do transaction:

 find first customer.

 Message DBTASKID(1) DBTASKID("DICTDB") DBTASKID("sports2000") view-as alert-box.

end.

gives: ? 27954 27954

Bug?? - Feature??

Posted by George Potemkin on 13-Feb-2020 15:05

> Would it be possible to query the database alias or number that table "RepData" from my transation is in?

DBTASKID(LDBNAME(BUFFER RepData))

Posted by gus bjorklund on 13-Feb-2020 15:58

> On Feb 13, 2020, at 9:05 AM, George Potemkin wrote:

>

> DBTASKID() should get value if you just send any request to a database:

>

>

no, it shouldn’t. you can read no-lock and share-lock without a transaction. your example has an explicit transaction block, so maybe dbtaskid() should start one in that case.

it should start one if you are changing the database.

Posted by gus bjorklund on 13-Feb-2020 16:04

> On Feb 13, 2020, at 9:40 AM, SJProgress wrote:

>

> CREATE RepData.

>

> ASSIGN

>

> RepData.RepDataID = NEXT-VALUE(RepdataID)

>

> RepData.DTCreate = NOW

>

> RepData.TransID = DBTASKID(1) / DBTASKID("DICTDB") / DBTASKID("myDBName")

>

>

for this case, dbtaskid (1) should force a transaction start. however, the record has not yet been created (depending on indexing), it must be sent to the database and created. then the transaction id can be retrieved and the TrandID field updated. then the record has to be sent back for the update.

you can force an earlier create by

zzz = rowid(RepData).

at some earlier point, and then

RepData.TransID = dbtaskid(1).

but to further complicate this example, the ASSIGN wants to delay sending the record over until the assign has finished.=

Posted by SJProgress on 13-Feb-2020 16:11

I will improve existing code to use DBTASKID(LDBNAME(BUFFER RepData)) and see, if problem reoccurs.

(Might take some months. Then I will have to dig again for possible other reasons for this symtom.)

Thanks a lot.

Posted by George Potemkin on 13-Feb-2020 16:44

> your example has an explicit transaction block, so maybe dbtaskid() should start one in that case.

Inside the transaction block (explicit or not) DBTASKID() returns a transaction number after any db request (for example, after reading a sequence, VST or application's table). At the same moment the session creates the entry in transaction table in shared memory. Yes, transaction may become ACTIVE some time later when the changes are saved to the database. But the value of DBTASKID() function is not related to the transaction status.

This thread is closed