//Codeunit 99998 d.OPEN('##1############'); FOR n := 1 TO 10000 DO BEGIN GJL.INIT; GJL."Line No." := n; GJL.VALIDATE("Journal Template Name", 'GENERAL'); GJL.VALIDATE("Journal Batch Name", 'BATCH1'); GJL.VALIDATE("Posting Date", TODAY); GJL.VALIDATE("Document Type", GJL."Document Type"::Invoice); GJL.VALIDATE("Document No.", 'ABC123'); GJL."Account Type" := GJL."Account Type"::"G/L Account"; GJL.VALIDATE("Account No.", '101010'); GJL.VALIDATE(Amount, n); GJL.VALIDATE("Bal. Account No.", '101010'); GJL.INSERT; d.UPDATE(1, n); END;
//Codeunit 99999 d.OPEN('##1############'); FOR n := 1 TO 10000 DO BEGIN GJL.INIT; GJL."Line No." := n; GJL.VALIDATE("Journal Template Name", 'GENERAL'); GJL.VALIDATE("Journal Batch Name", 'BATCH2'); GJL.VALIDATE("Posting Date", TODAY); GJL.VALIDATE("Document Type", GJL."Document Type"::Invoice); GJL.VALIDATE("Document No.", 'ABC123'); GJL."Account Type" := GJL."Account Type"::"G/L Account"; GJL.VALIDATE("Account No.", '101010'); GJL.VALIDATE(Amount, n); GJL.VALIDATE("Bal. Account No.", '101010'); GJL.INSERT; d.UPDATE(1, n); END; d.CLOSE;
//Codeunit 99998 d.OPEN('##1############'); FOR n := 1 TO 10000 DO BEGIN GJL.INIT; GJL."Line No." := n; GJL.VALIDATE("Journal Template Name", 'GENERAL'); GJL.VALIDATE("Journal Batch Name", 'BATCH1'); GJL.VALIDATE("Posting Date", TODAY); GJL.VALIDATE("Document Type", GJL."Document Type"::Invoice); GJL.VALIDATE("Document No.", 'ABC123'); GJL."Account Type" := GJL."Account Type"::"G/L Account"; GJL.VALIDATE("Account No.", '101010'); GJL.VALIDATE(Amount, n); GJL.VALIDATE("Bal. Account No.", '101010'); GJL.INSERT; JnlLineDim.INIT; JnlLineDim."Table ID" := 81; JnlLineDim."Journal Template Name" := 'GENERAL'; JnlLineDim."Journal Batch Name" := 'BATCH1'; JnlLineDim."Journal Line No." := n; JnlLineDim."Dimension Code" := 'COSTCENTER'; JnlLineDim."Dimension Value Code" := '1003'; JnlLineDim.INSERT; d.UPDATE(1, n); END;
//Codeunit 99999 d.OPEN('##1############'); FOR n := 1 TO 10000 DO BEGIN GJL.INIT; GJL."Line No." := n; GJL.VALIDATE("Journal Template Name", 'GENERAL'); GJL.VALIDATE("Journal Batch Name", 'BATCH2'); GJL.VALIDATE("Posting Date", TODAY); GJL.VALIDATE("Document Type", GJL."Document Type"::Invoice); GJL.VALIDATE("Document No.", 'ABC123'); GJL."Account Type" := GJL."Account Type"::"G/L Account"; GJL.VALIDATE("Account No.", '101010'); GJL.VALIDATE(Amount, n); GJL.VALIDATE("Bal. Account No.", '101010'); GJL.INSERT; JnlLineDim.INIT; JnlLineDim."Table ID" := 81; JnlLineDim."Journal Template Name" := 'GENERAL'; JnlLineDim."Journal Batch Name" := 'BATCH2'; JnlLineDim."Journal Line No." := n; JnlLineDim."Dimension Code" := 'COSTCENTER'; JnlLineDim."Dimension Value Code" := '1003'; JnlLineDim.INSERT; d.UPDATE(1, n); END; d.CLOSE;
Comments
MVP - Dynamics NAV
My BLOG
NAVERTICA a.s.
Keep in mind that SQL Server won't lock only the record you are inserting. It will also lock (I think at the best scenario) the previous and the next one.
So debug it and use also the SQL managment studio to investigate your locks...
You need to take into account different things together 8)
MVP - Dynamics NAV
My BLOG
NAVERTICA a.s.
I can make a simple conclusion here. The reason why TEST2 triggered the locking would be because, there are certain validations (e.g. GenJnlLine.Validate("no.", 'XXXXX') itself that is triggering modifications to JnlLineDim. So when I make explicit changes to JnlLineDim in TEST2, this triggered the table locking error.
This is the only explanation I can make, that would justify TEST3 giving a positive result (no table lock error) and TEST2 giving a negative result. (prompting a table lock error). Apart from that, I cannot provide any more explanations.
You need to use the client monitor to see what statements are being locked, eg to see if there any range locks applied. They are the usual problem as they HAVE TO go from record to record but Navision normally places them on a range of values. The result is that one record gets locked past the range in both the previous & next batches (or lines).
The inserts themselves should normally be fine, though if you don't have the 'row lock only' set on the alter database page in NAV you may be getting page locks.
In this case I would guess you could be getting a page lock on the second index of the j-dim table. The one that's only used if you're deleting a dimension value. Try removing that index.
BTW at this point (debugging) the 'Delayed Inserts' that Kamil mentioned are a pain. If you want to flush them to the SQL server use The advantage of that over other methods is that it won't set any extra locks.
TVision Technology Ltd
The code above is triggered by GenJnlLine.VALIDATE("No.", 'XYZ'); The debugger stopped at line 07.
After I remarked all the setranges coming from validation of GenJnlLine then I was able to run TEST2 with positive results.
Ok, this could be probably caused the upper and lower limit setranges lock described in :
http://blogs.msdn.com/microsoft_dynamics_nav_sustained_engineering/archive/2008/02/06/record-level-locking-in-the-sql-server-option-for-microsoft-dynamics-nav.aspx
But what still puzzles me, is why this error did not happen for TEST1? As TEST1 also had 2 codeunits running validations on GenJnlLine, and this means that there are 2 codeunits modifying the JnlLineDim table, but with no errors. And the error only happens when an "explicit" change to JnlLineDim is done in TEST2.
Because 'UpdateNoLocks' doesn't. Navision made what has turned out to be a silly mistake when they added the SQL interface. Whenever you write to the database the runtime does the equivalent of a LOCKTABLE on the specific table. With the Native DB this is required because it's the only way to lock anything but with SQL the changed records will be locked anyway and consistency will be preserved. All the LOCKTABLE does is cause every subsequent READ from the database to place locks that block sessions that try to modify records that are even BESIDE the ones this session just looks at.
The reason to use read locks in a database is so you can lock things you are going to 'increment' or alter in a way that depends on the current value. The SQL Serializable and REPEATABLE READ locking levels are designed for just this, using them for an entire transaction (as the runtime does) is only a reasonable idea when you absolutely must have only committed data and don't care about multi user performance.
The way to get best performance is to reduce the locking to the minimum that enforces the consistency of the data. UpdateNoLocks promises this, and is almost there, all that would need is to get rid of the implicit LOCKTABLE on any modify and add a method of locking ONLY the next statement on a table (eg: use "LOCKTABLE(FALSE)" as the syntax; it would be safe on earlier clients.) This would allow for the manual locking that UpdateNoLocks promises.
It turns out that Navision would need almost no locking above the basic provided by SQL for this, plus a little more to prevent deadlocks (Mostly, both, because of the Indexes views). If the required changes include a fix for number series (easy) and Entry Numbers (maybe autoincrement numbers, possibly a variation on a Number series) it turns out that Navision could support thousands of users on ordinary hardware. After all the processing power available to the classic client would scale out to every single PC you own.
This gives Microsoft a problem; Axapta. Right now a Navision system can support maybe 20 users actively posting without going to extreme measures like effectively putting the database onto a ramdisk. That leaves a huge gap for Axapta. If Navision could support 300 users on any old POS hardware Axapta would be dead, dead; dead. And nothing of value was lost.
PS: This message seems to have diverted at the end, but, I'll let it stand.
TVision Technology Ltd