Friday, 30 March 2012

Load test agent weighting

If you come across MSDN page regarding Load test agent weighting but still not sure about the load distribution when some are set to 100 and some to lower than that, below are the two example that should help explain more.

  • Agent 1: weight 30. Agent 2: weight 70, Total users: 1000
    - Agent 1 will get 300 users
    - Agent 2 will get 700 users
  • Agent 1: weight 50. Agent 2: weight 100, Total users: 1000
    - Agent 1 will get 333 users
    - Agent 2 will get 667 users

    For this example, the total weight is 150 (Agent 1 + Agent 2).
    Then get the percentage of each agent with this total weight, that leads to
    Agent 1 = 50/150 = 33.3333% of the total users = 333 users
    Agent 2 = 100/150 = 66.6667%  of the total users = 667 users

Screenshot of Load test agent properties

Case of bad performance due to EntireTable CacheLookup

Recently I was looking at an issue where changing a quantity in Purchase order line is taking up to a minute long. Tracing it and found the root cause was due to the CacheLookup property is set to EntireTable.
(This happens to intercompany order)

One of the code triggered from the PurchLine.PurchQty change is the code below which do an insert into ‘InterCompanyEndpointActionPolicyTransfer’ table.

This table has a property of CacheLookup = EntireTable, which its default behaviour is cache the entire table into memory if less than 128KB or write them to disk if more than 128KB.

A check into the data size of this table, it is around ~58MB, hence, each insert into this table is flushing the cache (for this table), then rewrite them all into the disc again.

In my case, changing this 'CacheLookup' property to "Found" resolve it.
The .insert() now completed in less than a second.

Thursday, 8 March 2012

Microsoft Dynamics AX version and kernel/build number

Refer to this link for the Dynamics AX build number: 

AX2009 and above

AX4 and below

If you need to check what's the hotfix included in the rollup or build, click on the link on the "Comment" column.

Eg. AX2009 SP1 Roll Up 7:$en-us$2503850

Last updated: 25/05/2012 (with new link to AX2009 and above)

Wednesday, 7 March 2012

Result of referencing field with inactive configuration key

Result of referencing field with inactive configuration key, the quick answer are:
  • When used in query criteria, it ignore the criteria in AX and replace with another one in SQL

    From AX: Field1 == ''

    To SQL: 1==1

    From AX: Field1 == 'anyValue'

    To SQL: 1==0

    Empty string converted to 1 and a non-empty string converted to 0
  • When referencing for its value, it just simply returns nothing/empty string
Below is an example of the test:
  1. A configuration key – 'ADeletedConfig' (a disabled key)
  2. A table with 3 fields created, 'Field1' has a configuration key which has been disabled (ADeletedConfig)
  3. Table browser showing the existing records in the table
  4. A job and its execution result

Table field with disabled configuration key

Showing records in table

Demo code

SQL statement from Trace Parser

Result of demo code execution

Thursday, 1 March 2012

Add sales order confirmation into batch through code

One of the performance work done for our client is to create an additional button for quick order confirmation. They usually use all the default settings and don't make any changes during the prompt and most of the case they just post all the quantity. We retain the existing posting button and add another one for quick post (order confirmation). The main objective is just to let them post it then regain control and move on to the next task rather than spending time waiting for the prompt and click OK, then wait for the process to finish.

Without any code development, it can actually be done through the "Batch" button at the post Confirmation prompt, but the requirement we have is to complete the work in the least time possible, what we've done is just to bring this manual user process into code.

The usual posting can be done by 2 lines:

salesFormLetter = SalesFormLetter::construct(DocumentStatus::PackingSlip); 

But that involve wait time, the user needs to wait till the posting to finish before they regain control and move on to the next task, so we modify it a bit to put the job into batch and return the control to user.

I've extracted the code into a job to show it here.
The "Init values" section is done as a setup in our solution, but I've replaced it with hard coded text here to shows the use of SalesFormLetter and add them into batch through code.

It is separated into 3 sections:

  1. Initialize the values
  2. Construct the SalesFormLetter class and pass in the required parameters & PrintJobSettings
  3. Create the batch job and task

static void Job1(Args _args)
    SalesFormLetter     salesFormLetter;
    SalesId             salesId;
    SalesTable          salesTable;
    BatchInfo           batchInfo;
    BatchHeader         batchHeader;
    BatchGroupId        groupId;
    BatchCaption        batchInfoCaption, batchHeaderCaption;
    PrintJobSettings    printJobSettings;
    boolean             printToFile;
    NoYes               printReport;
    FileName            fileName;

    //Init values ==============================================================
    salesId             = "SO-101263";
    salesTable          = salesTable::find(salesId);
    fileName            = strFmt("\\\\serverName\\SOConfirmation_%1.pdf", salesId);
    groupId             = "TST";
    batchInfoCaption    = "Order confirmation posting: " + salesId;
    batchHeaderCaption  = "Order confirmation: " + salesId;
    printReport         = NoYes::Yes;
    printToFile         = false; //If 'printReport' is true, then this indicate print to file or printer

    //Construct SalesFormLetter and init values ================================
    salesFormLetter = SalesFormLetter::construct(DocumentStatus::Confirmation);
    salesFormLetter.transDate          (systemdateget());
    salesFormLetter.specQty            (SalesUpdate::All);
    salesFormLetter.proforma           (NoYes::No);
    salesFormLetter.printFormLetter    (printReport);
    salesFormLetter.printCODLabel      (NoYes::No);
    salesFormLetter.printFreightSlip   (NoYes::No);
    salesFormLetter.printShippingLabel (NoYes::No);
    salesFormLetter.usePrintManagement (false);
    salesFormLetter.creditRemaining    (salesFormLetter.creditRemaining());
    salesFormLetter.initParameters(salesFormLetter.salesParmUpdate(), Printout::Current);

    printJobSettings = new PrintJobSettings(salesFormLetter.printerSettingsFormletter(PrintSetupOriginalCopy::Original));

    //Init BatchInfo, BatchHeader, and add into batch job ======================
    batchInfo = salesFormLetter.batchInfo();

    batchHeader = batchInfo.parmBatchHeader();

Order confirmation added into batch job

Batch job waiting to be executed

Task within the batch job, waiting to be executed  

Task completed
Note: The order confirmation process added additional task to it when it executes. 

Report printed into the Print Archive