Showing posts with label Siebel Best Practices. Show all posts
Showing posts with label Siebel Best Practices. Show all posts

Tuesday, July 20, 2010

Purging Vs Deleting Workflow Process Instances

This is one of the important information you might be interested in, if you frequently debug a Workflow Process at run-time by increasing the Monitoring Level under "Administration - Business Process -> Workflow Deployment" view.

Whenever you increased the Monitoring Level to "4-Debug", system starts capturing the WF steps details along with the each Process Property at each step, to give a better picture of what has exactly happened at each step and it is very helpful for debug purpose. You can see all Run-time degugging details in "Administration - Business Process -> Workflow Instance Monitor" view.

"Process Instances" gives you all the Workflow Instances run once the Monitoring Level has been increased. It all tells the currently running WF Step.

"Step Instances" captures the details of each step along with its start and end time.

"Process Properties" captures the value of each process property in each WF step.

Though this information is useful but it is a good practice to decreased the log level back to "0-None", once you are done with debugging and also the most important thing you should do is "Purge" all the Workflow Process Instances. Navigate to "Administration Business Process -> Workflow Instance Monitor -> Process Instances", query for the workflow process and click "Purge".

Now, why I am saying this last step of purging these records as "very important"?

Let me tell me what actually happened when I was debugging one of the workflow issue. I increased the monitoring level to 4 for debugging purpose and that workflow was having around 30 workflow steps and around 50 process properties. Additionally, this workflow runs on the child activities (average number around 50) of the Service Request. So you try calculate the number of process property instances record (in S_WFA_STPRP_LOG), system will create in a single workflow run = 30 x 50 x 50 = 75000. Big number isn't it?? So if I debug this workflow few number of times, the number of records will be really huge. But remember, as far as you are purging the records after debugging finishes, then no issues, but if you don't then these records will pile up and some day your database size will run short out of physical space.
So what actually happened was, I made few changes in the workflow and deployed again. Went to "Administration Business Process -> Workflow Deployment" and queried the workflow process and hit "Activate". In the below "child items" applet, system creates a new version of the workflow process with Deployment Status = Active and the older record becomes gets its status = Inactive. So I thought this record is Inactive, let me delete this old instance. I did that few number of times and never realizes that it will just delete this record from "S_WFA_DPLOY_DEF" table and its corresponding child records in "S_WFA_STPRP_LOG" table will keep sitting there. And there is no way you can "Purge" these records from the UI using "Purge" button because the parent record has been deleted earlier.

So it end up having millions of records in this table without parent record and later we realized the database is running short of physical memory and when I checked for the tables in the database which all having huge number of records, we found "S_WFA_STPRP_LOG" table was leading the race. I did the clean-up of the child records via running some SQLs in the database by finding the orphan records in "S_WFA_STPRP_LOG" and learned the lesson using "Purge" button after completing the debugging.

Hope this will also help you in future.

Tuesday, April 28, 2009

FirstRecord() in PreCanInvoke Event : Refresh Issue

Today I am going to tell you about an interesting issue that we faced in our Production Env and really worked hard to figure it out the root cause for it.


The issue was :

On Activity Form applet, we have "Comments" field, which is 2000 characters long and user to write a detail description in this field related to that activity record. Now few of the users faced the issue that "while typing some text in Comments field, suddenly the whole text written get deleted and Comments field become blank". User get frustrated as he need to type in the whole text again and also the next time not sure whether he will able to complete putting text in Comments field or not. For a temporary workaround they used to first type in all the text in a NotePad file and then copy paste the complete text at once in Comments field and save the record.

What we tried :

When we tried replicating this issue in our dedicated env, we were unable to replicate it. After lot much of effort our QA Team was able to replicate it sometimes on our thin client. But the problem was this is not always happening with them as well. On some of the machines we were not able to replicate it at all. Totally a random behaviour.

Now the problem was to find out the steps so that we can replicate it. We have one option to try i.e. by disabling all the Server Script and check if we can replicate the issue in thin client (since in dedicated client everything was working fine).

We changed the "EnableScripting" = False and restart the Siebel server, and found that the issue is not coming anymore. So we narrowed down for the root cause of the problem to Scripting.


And, here is what we finally found :

On PreCanInvoke method of the applet we had a script written, something like :


if (MethodName == "Test")
{
if(this.BusComp().FirstRecord())
CanInvoke = "True";
return (CancelOperation);
}


So, what was happening when user was typing some text in Comments field, it seems like IE web page get refreshed and due to this the code written on "PreCanInvoke" method was getting invoked and due to the "FirstRecord()" method used inside the code, the current record get refreshed and resulting in no text in Comments field.

Finally, we were able to find the root cause of the problem, now its the time to look for some workaround. So we decided not to enable/disable button using PreCavInvoke script, rather put a check message in PreInvoke method when there is no record displayed. So here below is final version of script we used :

PreCanInvoke Method

if (MethodName == "Test")
{

CanInvoke = "True";
return (CancelOperation);

}


PreInvoke Method :

if(MethodName == "Test")
{
if(this.BusComp().FirstRecord())
{
TheApplication().RaiseErrorText("This operation is not valid, when record is not displayed");

}
return (CancelOperation);
}


Recommendation:
Avoid writing scripting on PreCanInvoke Method of Applet, as this is the event which fires on each and every action we do on the applet + at the time of automatic refresh of web page.

Wednesday, January 28, 2009

Automatic Trailing WildCards : A CFG Parameter

If you do a Query in any View of Siebel application and you observe some performance issue in getting the query result set, then "AutomaticTrailingWildCards" parameter is one of the culprit, which is available in Siebel.CFG under [SWE] section.

What it does
Lets say you want to query on "Name" field of Opportunity List Applet with value "siebelmantra", then system will fetch Opportunities with name like siebelmantra, siebelmantra1, siebelmantra Oppty, etc etc. That means it automatically suffix a "*" (asterick wild card character) after the string being queried. To confirm this, press "Alt + r" (Refine Query), you will see "siebelmantra*" was being queried in Name field, though you never mentioned "*" while querying. This is the effect due to the TRUE value of "AutomaticTrailingWildCards" parameter.

Recommendation
Open Siebel.cfg file and search for "AutomaticTrailingWildCards" parameter under [SWE] section and set its value to "FALSE". Restart the Siebel Servers and observe the difference by again querying on the UI.

Thursday, January 22, 2009

Early Instantiation of Business Component

In this article, I want to showcase a nice way that we should use while scripting in Siebel. This also comes under the Siebel scripting best practices.

I need to write a Script for a simple requirement : "I want to get a "Opportunity Name" and "Product Name" from "Opportunity" & "Opportunity Product" business component respectively. Opportunity Product is a child business component of Opportunity. Lets suppose, The Opportunity Id in the requirement is : "1-6JKM".

Here are few ways by which we can code :

First Method
var BO = TheApplication().GetBusObject("Opportunity");
var OBC = BO.GetBusComp("Opportunity");
with (OBC)
{
ClearToQuery();
SetViewMode(3);
SetSearchSpec("Id","1-6JKM");
ExecuteQuery();

if(FirstRecord())
{
var OPBC = BO.GetBusComp("Opportunity Product");
with (OPBC)
{
ClearToQuery();
SetSearchSpec("Oppty Id","1-6JKM");
ExecuteQuery();
if(FirstRecord())
{
TheApplication().RaiseErrorText("Opportunity Name = " + OBC.GetFieldValue("Name") + ", Product = " + OPBC.GetFieldValue("Product"));
}
}
OPBC = null;
}
}
OBC = null;
BO = null;


Second Method
var BO = TheApplication().GetBusObject("Opportunity");
var OBC = BO.GetBusComp("Opportunity");
with (OBC)
{
ClearToQuery();
SetViewMode(3);
SetSearchSpec("Id","1-6JKM");
ExecuteQuery();

if(FirstRecord())
{
var OPBC = BO.GetBusComp("Opportunity Product");
with (OPBC)
{
ExecuteQuery();
if(FirstRecord())
{
TheApplication().RaiseErrorText("Opportunity Name = " + OBC.GetFieldValue("Name") + ", Product = " + OPBC.GetFieldValue("Product"));
}
}
OPBC = null;
}
}
OBC = null;
BO = null;

Third Method
var BO = TheApplication().GetBusObject("Opportunity");
var OBC = BO.GetBusComp("Opportunity");
var OPBC = BO.GetBusComp("Opportunity Product");
with (OBC)
{
ClearToQuery();
SetViewMode(3);
SetSearchSpec("Id","1-6JKM");
ExecuteQuery();

if(FirstRecord())
{

if (OPBC.FirstRecord())
{
TheApplication().RaiseErrorText("Opportunity Name = " + OBC.GetFieldValue("Name") + ", Product = " + OPBC.GetFieldValue("Product"));
}
}
}
OPBC = null;
OBC = null;
BO = null;



If you had a glance on three different ways mentioned above, you will realize the third method is the most efficient method. Let me try to explain what exactly we have tried to achieve here.

First method is the simplest one, where to get any field value we query for Business Component with the row_id and do a GetFieldValue on the resultant record.
Second method is bit tricky, as you can see no need to query on Opportunity Product. "ExecuteQuery" is sufficient to fetch the records. This is the power of Link mentioned for the "Opportunity Product" in "Opportunity" business Object. Siebel is smart enough to initialize the link along with business component, whenever instantiated.
And best one is the third method, as you can see the Opportunity Product is instantiated right in the beginning. So when we query on Opportunity business component, the Siebel will automatically returns the associated Opportunity Product records as well. No need to put any extra "ExecuteQuery". This is what we call "Early Instantiation" of Business Component.