Hello world!

Welcome to WordPress.com. This is your first post. Edit or delete it and start blogging!

Advertisements
Posted in Uncategorized | 1 Comment

Performance on server degrades after an AOS crash

 

If the server machine’s response, slows down after an AOS crash, then standard Windows Error Reporting process could be a reason. Whenever there’s a crash, a process called DW20.exe kick in, spike the CPU, and then it dies.

This can be avoided by Disabling Error Reporting Tool.

To disabling error reporting only for AOS,

1. Go to Control Panel.
2. Click System.
3. Go to the Advanced tab.
4. Click Error Reporting.
If Enable Error reporting  is selected, click on choose programs and browse axserv32.exe

 

Alternative: http://support.microsoft.com/kb/841477

Posted in Uncategorized | Leave a comment

Communicator and Ax

A couple of years back I developed a messenger in Ax and a few of my colleagues used it for fun. Now its a reality, you can integrate the communicator into Ax, with a little code.

Follow these Guidelines:

Download and install the communicator API from Communicator 2007 API download

Use the com class wizard in Ax to create Ax classes wrapping API classes of Microsoft Office Communicator APIs.

Use the documentation provided in Communicator API Reference.

 

That is all!!! Have fun 

Posted in Computers and Internet | 1 Comment

Tables Tracer Tool

 

Necessity is the mother of invention. Working in Ax, I often get into a situation, where I need to know the tables/sqls involved in a flow. I know, its not just me. This motivated me to develop a simple tool.

Ax developers (Business & Technical), here is this piece of cake to trace tables in Ax2009.

Once you download & import this into Ax2009, all you need to do is just

  • Click on start
  • Do the functional flow
  • Click on stop.

That’s it, This tool will list you all the tables involved in that flow, along with call stack, source code, actual sql on database.

Along with these, you can also export this trace or import a new trace.

Link : http://axtablestracer.codeplex.com/

Screenshot:

PS: Do remember to enable client tracing on Server Configuration.

Enjoy .

Posted in Computers and Internet | Leave a comment

Caching Mechanism in AX

 

Caching , one of the concepts generally ignored by the developer . This mainly seems to be due to the underlying complexity involved in analyzing it .

 

But in reality, its not that complicated. Lets break it  

 

Caching in simple terms, is a temporary storage area where frequently accessed data can be stored for rapid access. Once the data is stored in the cache, it can be used in the future by accessing the cached copy rather than re-fetching or recomputing the original data. (Definition Source: wikipedia)

 

In Ax, we have 2 caches.

 

  1. Server Cache
  2. Client Cache

 

Client Cache is used only when a select is executed on client-tier. If a record is not in client cache, it looks in server Cache, and if not, it costs a database fetch. Maximum #records stored in client cache are 100 per table, per company, where as, in ServerCache, this limit is 2000.

 

To know whether a record is fetched from client cache, server cache or database, use wasCached() of the TableBuffer used in Select.

 

 
select * from salesTable where salesTable.salesId == 'AR_2009';
info(strfmt("%1",salesTable.wasCached()));

 

 

We have 2 types of caching mechanisms in Ax:

  Set Based Cache – Caches a set of records.
  Single-record Cache – Caches records based on Select Statement used.

 

Set Based Caching:

 

As the name implies, with set based caching, we can cache a set of records.

 

A set of records fetched by a Select Statement can be cached by using an instance of RecordViewCache, provided the Select is with NoFetch option. (Temporary tables & Selects with Joins cannot be cached this way)

 

After instantiating, the RecordViewCache is used by Select that matches at least the where clause that the RecordViewCache was instantiated with.

 

For example:

 

MarkUpTrans       markupTrans;
RecordViewCache cache;
;
 
//Defining records for the record view Cache, this select will not fetch any record.
select NoFetch markupTrans where markupTrans.ModuleType ==  ModuleInventCustVend::Cust;

print markupTrans .RecId; //Nothing is printed as nothing is fetched.

cache = new RecordViewCache(salesTable); //Records are cached.

//From here, any Select of Sales MarkupTrans , will fetch cached records from record view cache.
select markupTrans where markupTrans.ModuleType ==  ModuleInventCustVend::Cust && MarkupTrans.TransRecId > xxxxxxxxx;

info(strfmt("%1",markupTrans.wasCached()));


 

In a TTSBlock, if the select is used with forUpdate, it will lock all the records in RecordViewCache, and  the next select forUpdate statements will use locked records from cache.

(To check these records, use the sql select (with nolock) * from <<TableName>> where ……… )

 

If select is used without any where clause, entire table will be cached. A record view cache will be destroyed, as soon it goes out of scope.

 

But there are better ways to cache an entire Table. Lets see that:

 

Entire Table Cache:

 

Once a record fetched, the entire table will get cached, for a period of AOS lifetime, when the cacheLookup Property of a Table is set to EntireTable.  This cached full set of records , will reside in the Server Cache.  Once fetched, cache will have set of table records, per company.

 

Note: In my experiments I found that, when a record of a table(cached EntireTable) is fetched, remaining records are not fetched…  

 

Single-Record Caching

 

Instead of caching a set of records, we can cache individual records that were fetched succesfully based on a ‘where’ clause of ‘select ‘, using Single record caching. When a table is set to Single Record Caching, all records fetched from database are placed in cache, provided it is they are fetched with unique index key. Subsequent fetches are made from cache.

 

A table is set to Single Record Cache by setting cache-lookup property to one of the following:

  NotInTTS
  Found
  NotFound

However, there is a significant difference among these. Depending on usage, corresponding type must be used.

 

NotInTTS:

 

When a table is set to NotInTTS, a fetch operation inside a transaction will fetch the record from database. Subsequent fetches are made from cache. This is an useful mechanism to avoid reading/updating outdated data inside a transaction.

 

Fetch made outside a transaction:  Client cache -> Server Cache -> Database -> Server Cache -> Client Cache

Fetch made inside a transaction: Client cache -> Server Cache -> Database -> Server Cache -> Client Cache

 

Found:

 

When a table is set to Found, a fetch for update inside a transaction will fetch record from database. Subsequent fetches are made from cache.   This is an useful mechanism to avoid updating outdated data inside a transaction.

 

Fetch made outside a transaction: Client cache -> Server Cache -> Database -> Server Cache -> Client Cache

Fetch made inside a transaction: Client cache -> Server Cache -> Database -> Server Cache -> Client Cache

Fetch made inside a transaction for update : Client cache -> Server Cache -> Database -> Server Cache -> Client Cache

Fetch made inside a transaction that doesn’t exist: Client cache -> Server Cache -> Database -> Server Cache -> Client Cache

 

NotFound:

 

When a table is set to NotFound, it functions like Found, except that it also caches non succesful fetches. Which means, when a record that does not exist is fetched from database, the condition used for fetching is cached. Subsequent non succesful fetches, will not search full database. Instead, just concludes from cache that record does not exist.

 

Fetch made outside a transaction: Client cache -> Server Cache -> Database -> Server Cache -> Client Cache

Fetch made inside a transaction: Client cache -> Server Cache -> Database -> Server Cache -> Client Cache

Fetch made inside a transaction for update : Client cache -> Server Cache -> Database -> Server Cache -> Client Cache

Fetch made inside a transaction that doesn’t exist: Client cache -> Server Cache -> Database -> Server Cache -> Client Cache

 

 

DEMO

 

Theoritically, these concepts are always confusing   . So to get a feel of what is happening, here is small demo walk through .

Create 3 tables CacheNotInTTS, CacheFound, CacheFoundAndEmpty, CacheEntireTable each with different CacheLookup property and with following 2 fields, Id, Name. Create a primary index on field ‘Id’.

 

 

Insert 100 records (more than 2000 records) into these records. Use the following job:

 

    int i;
    CacheNotInTTS       notInTTS;
    CacheFound          found;
    CacheFoundAndEmpty  foundAndEmpty;
    ;

    for (i=0;i < 100; i++)
    {
        notInTTS.Id = i;
        notInTTS.Name = 'Demo' +int2str(i);
        found.Id = i;
        found.Name = 'Demo' +int2str(i);
        foundAndEmpty.Id = i;
        foundAndEmpty.Name = 'Demo' +int2str(i);

        notInTTS.insert();
        found.insert();
        foundAndEmpty.insert(); 
    }

 

Create a class, on these tables

 

class CacheDemo
{
    CacheNotInTTS notInTTS;
    CacheFound found;
    CacheFoundAndEmpty foundAndEmpty;
}

 

Before starting our Demo, we have clear the records these tables in client cache as well as in server cache . So, create 2 static methods, one to run on client and other to run on server.

Note: Tools > Development Tools >  Application Objects  > Refresh Data will only clear client cache.

 

client static void flushClientCache()
{
    ;    

    Dictionary::dataFlush(tablenum(CacheNotInTTS));
    Dictionary::dataFlush(tablenum(CacheFound));
    Dictionary::dataFlush(tablenum(CacheFoundAndEmpty));
}
 

server static void flushClientCache()
{
    ;    

    Dictionary::dataFlush(tablenum(CacheNotInTTS));
    Dictionary::dataFlush(tablenum(CacheFound));
    Dictionary::dataFlush(tablenum(CacheFoundAndEmpty));
}

 

//For simplicity lets have a method calling client and server methods
static void flushCache()
{
    ;    

    CacheDemo::flushServerCache();
    CacheDemo::flushClientCache();
}


 

Then, a method to fetch the records:

 

void fetchRecords(int _key=0, boolean _forUpdate = false)
{
    notInTTS.selectForUpdate(_forUpdate);
    found.selectForUpdate(_forUpdate);
    foundAndEmpty.selectForUpdate(_forUpdate);
    
    select notInTTS where notInTTS.id == _key;
    select found where found.id == _key;
    select foundAndEmpty where foundAndEmpty.id == _key;
}


A method to give information about how a record is fetched:

 

void printCacheStatus(str description)
{
    ;
 
    info("Cache Status \t" + description + "\t NotInTTS       : "+enum2str(notInTTS.wasCached()));
    info("Cache Status \t" + description + "\t Found          : "+enum2str(found.wasCached()));
    info("Cache Status \t" + description + "\t FoundAndEmpty  : "+enum2str(foundAndEmpty.wasCached()));
}


Finally, the main method containing Demo: (You may modify this, to experiement in ur ways  

static void main(Args _args)
{
    CacheDemo demo = new CacheDemo();
    ;

    CacheDemo::flushCache();
    demo.printCacheStatus('Initially after flushing Cache');
    demo.fetchRecords();
    demo.printCacheStatus('Fetch all records in all tables, with out where clause');
    demo.fetchRecords(12);
    demo.printCacheStatus('When record 12 is fetched for the first time');
    demo.fetchRecords(12);
    demo.printCacheStatus('When record 12 is fetched again');
    demo.fetchRecords(13);
    demo.printCacheStatus('When record 13 is fetched');
    demo.fetchRecords(113);
    demo.printCacheStatus('When record 113 is fetched');
    demo.fetchRecords(113);
    demo.printCacheStatus('When record 113 is next fetched');
    ttsbegin;
   
        demo.fetchRecords(12, false);
        demo.printCacheStatus('When record 12 is fetched inside a TTS Block');
        demo.fetchRecords(12, true);
        demo.printCacheStatus('When record 12 is fetched forUpdate inside a TTS Block');
        demo.fetchRecords(12, true);
        demo.printCacheStatus('When record 12 is fetched forUpdate again inside a TTS Block');

    ttsAbort;
}


 

I got the following Output, well theory is working

  NotInTTS Found FoundAndEmpty

Initially after flushing Cache

NotCached NotCached NotCached

Fetch all records in all tables, with out where clause

NotCached NotCached NotCached

When record 12 is fetched for the first time (Some record)

NotCached NotCached NotCached

When record 12 is fetched again

RecordCached RecordCached RecordCached

When record 13 is fetched (Another record)

NotCached NotCached NotCached

When record 113 is fetched (Non existing record)

NotCached NotCached NotCached

When record 113 is next fetched

NotCached NotCached RecordCached

When record 12 is fetched inside a TTS Block

NotCached RecordCached RecordCached

When record 12 is fetched forUpdate inside a TTS Block

NotCached NotCached NotCached

 

 

Conclusion:

No caching technique is better than others, each one has its own significance. Else, microsoft would’ve applied that single best caching mechanism on all tables.

When to use what?

RecordViewCache: Use it, When it is needed to cache a set of records temporarily in a code block.

EntireTableCache: Use it when #records is expected to be very less and it is acceptable to read outdated data.  Tables like Small data tables (like Calendar, Country Region),  Group Tables (like custgroup, vendgroup), parameter tables etc where #records will be less, but are frequently referred should be entire table cached.

NotInTTS: Use it when #records is not less, it is acceptable to read outdated data outside a transaction and it is not acceptable to read/refer outdated data inside a transaction. Header tables(like SalesTable, Projtable, LedgerTable),Lines tables (like SalesLine, PurchLine ), Transactional tables (like VendTrans, MarkupTrans),ParmUpdate tables, etc where it is not acceptable to refer outdated data inside a transaction should be NotInTTS cached.

Found: Use it when #records is not less, it is acceptable to read/refer outdated data inside/outside a transaction and record usually exists. Typically lookup tables (like Unit, CustTable, VendTable) where record generally exists can be Found cached.

NotFound:  Use it when #records is not less, and it is acceptable to read/refer outdated data inside/outside a transaction and especially when by default records do not exist and/or #unsuccesful hits to database is high.
Tables like discount tables, setup tables where by default records do not exist and/or #unsuccesful hits to database is high can be NotFound cached.

None: Tables that are heavily updated or where it’s unacceptable to read outdated data should not be cached.

 

Optimizing the Caching alone will not improve the performance of the system. Its just one of the factors. There are several other factors like optimized code, ideal use of Indexes, #threads used(batch), configuration/hardware etc. All these factors will effectively make a better ERP system. 

Posted in Computers and Internet | 5 Comments

Dirty tricks on Ax License

 

In the
world of Ax, Licenses play a major role. To continue with our day to day work,
at times, we will need to do some dirty work too …

 

This
entry is result of those dirty works . Let
me go through the following topics:

 

  • Copying License from one data
    base to another
  • Removing License of a
    particular module
  • Solving Sync issues after
    removing license
  • Working without licenses!!!

 

 

 

Copying License

 

<<Applicable when you already have this license of a
module in another database and dont have a License file>>

 

In Ax, Licenses are stored in Database. The tables used as data
source in License Information form are:

 

1. SysLicenseCodeSort

2. SysConfig

 

I didn’t do any micro research here, but, quickly jumped into
conclusion that License is stored only these tables as no other tables are
declared or used. 

 

Euphoria.. !! Copying these tables from another database will solve the
purpose 🙂

 

So, everything we need to do now is:

 

  1. Backup and truncate records from
    Destination DB
  2. Copy
    records from Licensed DB. This can be by:

 

  1. Exporting and importing records
    • Export records using export
      tool.
    • Import the exported records.

 

Or

 

  1. Cross database query.
    • Insert into
      <<Destination DB>>.<<Tablename>> (Select * from
      <<LicensedDB>>.<<Tablename>>)

 

PS: But, after doing this, I found that original developer
license is gone. When I imported basic license again, I had all modules

 

 

Removing License of a module:

 

Sometimes, developer environment may have full license and
production environment may not. To simulate environment, we will have to remove
License of few modules.

 

This can be done as follows: (But, before doing this, make sure
that you have a backup of your database.)

 

  1. Click Administration > Setup > System > License information
  2. Goto Modules Tab
  3. Select
    the module and delete the LicenseCode
  4. Save and
    synchronize when prompted.

 

Solving Synchronization issues

 

Sometimes, after removing the license, synchronization will
fail. This is because a field, having configuration key of a removed module, is
part of an unique index.

 

For example, if Warehouse Management License is removed from a
DB having Demo data, we will get synchronization error on InventDim Table.

 

This is because, field WMSPalletId is a part of Warehouse
Management configuration Key. When that license is removed, this field gets
disabled and indexed set of fields will now have duplicate records.

 

There are many ways to solve this, like

 

  1. Make the index as non Unique. or
  2. Delete
    duplicate records from Database using a set of Sql queries. or
  3. In the
    EDT of this field, remove the configuration key. How? See below section.

 

Working without License

 

It is not impossible to

 

  • remove a configuration key
    from an EDTor field
  • Make a configuration
    independent of License
  • Make entire Ax License free!!

 

Follow these steps:

 

  1. Export the EDT or Config key to an
    xpo file
  2. Remove
    the config key or license in the xpo
  3. Import
    the file back.

 

To make Ax License free, export all configuration keys into an
xpo and edit it 

 

If this is first time and you dont have license to open AOT,
then you can import the xpo from command prompt as

 

Ax32.exe -startupcmd=autorun_"<<xml file locating the
xpo>>"

 

 

Well, very dirty ofcourse, but hacks are always dirty … 

 

Posted in Computers and Internet | 6 Comments

Global Cache

 
 
 Many times because of flawed implementation designs, we often need global variables. We may use a table with a key field and a container field for this purpose, but the simplest way of doing this will be using a Global Cache.
 A global cache is an instance of class – SysGlobalCache, which is nothing but a Map containing Maps with Userid as Key. These Maps contain the actual value and a key.
 In Ax, we have three(infact 4) Global Caches – Infolog.globalCache(), Appl.globalCache(), ClassFactory.GlobalCache().
 
How to use:
 To Set a Value:
 static void GlobalCacheSet(Args _args)
 {
     SysGlobalCache globalCache;
     ;
     globalCache = ClassFactory.globalCache();
     globalCache.set(curuserid(), 1, "One");
 } 
 To Get the Value:
 static void GlobalCacheGet(Args _args)
 {
     SysGlobalCache globalCache;
     ;
     globalCache = ClassFactory.globalCache();
     print globalCache.get(curuserid(), 1);
     globalcache.remove(curuserid(), 1);
     pause;
 }
In the above the example, we can also use Infolog.globalCache() or Appl.globalCache().
 
Why do we have three Caches?
 
Its simple, the term "Caching" comes when there is something to do with Performance.
 
Infolog Object resides in Client and Application Object resides in Server.

To share your global variable across the network and accept the performance penalty of a client/server call, use the infolog variable (Info class) or appl variable (Application class) instead of ClassFactory.

ClassFactory is a special class, which has instances residing at both server and client. At run time, two instances of the ClassFactory class exist, and they share name classFactory. However confusing the implementation details might sound, this is a powerful concept. When you call a method on classFactory from code running on the client, you are calling the classFactory object on the client tier; when you call a method on classFactory from code running on the server, you are calling the classFactory object on the server tier. Remember this when debugging the ClassFactory class.
 
These four objects along with an instance of VersionControl will sustain for a duration of Dynamics Ax client session.
Posted in Computers and Internet | Leave a comment