This application starting crashing a few months ago, any idea after five years of flawless running this is now occuring or where to look for the problem?
The error reads:
24/04/19 11:36:58.914 - 76_fc2b6e4d - Com Server request timeout. Forcing server to reload - /callback88.cfmx? 24/04/19 11:36:58.914 - 76_fc2b6e4d - Com Server Execution Error: The remote procedure call failed. (Exception from HRESULT: 0x800706BE) - /callback88.cfmx?
Your app is hanging.
Make sure you have UnattendedComMode=On in your .ini file. Otherwise it can be other things that are hanging up the application - a SQL connection, a remote service call, any IO call (like sending emails) etc.
+++ Rick ---
Interesting... UnattendedComMode was equal to Off. I have now changed it to On.
But I was also reading about Error Handling, it has been many years since I last looked at this part of hte documentation. In looking at my app.ini file, I see that DebugMode=On and in my WConnect_Override.h file #UNDEFINE and #DEFINE DEBUGMODE .t. are both commented. And also not set in the WConnect.h file which describes setting this value in the Override file.
What about these other settings, would you now recomend changing the DebugMode in the ini file and/or changing the DebugMode in the WConnect_Override.h file ?
Another thought, maybe run the application in FILE mode and let the user who is generating the error discover the error message. Maybe then I could track it down in my code. In the callback routine that is crashing, I have several comments about possible causes of errors but they are also all trapped. The hard part about discovering the current error, I can't seem to reproduce this in a testing enviroment since this is one of the last steps in a long process of several callbacks that buildup a state in the client browser.
Thanks for your help.
If you're running your app basically in development mode (as you did) there are bound to be failures like that.
Running in file mode is a last ditch scenario. If you got everything set up properly you should get errors in the log that report the failures. If you were hanging due to UI operations, those will now trigger trappable errors so they'll show up in your log.
If this was occasional and rare the issue most likely is a file locking error which prevents a DB write operation. You'll then end up with a File Open dialog or in the case of long long a dialog that says the file is locked and to wait. Again with unattended mode on, these will just trigger errors that are logged.
+++ Rick ---
what I have realized over the past couple of days...
The app gets used once a week for about one hour. Durring this period the server gets hit a several hundred times while about 30 users fill out forms. At the end of their data entry process they request a receipt of their entries which is generated in PDF from XFRX. The callback that is hanging starts off by getting a unique ID number of the receipt process in progress nad hten returns the PDF.
I think the problem I'm seeing is due to some of the users having last weeks (or prior) PDF open in their browser at the time they start the process of data entry nad rather than closing it, they start a new browser tab and login again. In the meantime the older PDF is trying to reload but without the ID number and/or login permissions.
I am going to change the Debug mode and add a line of code to return an error message if an invalid ID number is presented.
Thanks for yoru help!
Should you be 'keeping anything open' as part of your HTTP requests? That seems... wrong. Whatever they are doing on the client shouldn't affect your code on the server.
+++ Rick ---
It was so long ago, I really don't know if anything is open. I wrote most of the application in 2015-2016 and have not needed to touch it in all these years. I did find a note in the code that makes me suspect of a null is arriving when it should be a number ID. The comment in the code said this should never occur but other than a comment, I did not trap the a non-number arriving.
On a worse note, I am scared to try and recompile it since it has been so long since I worked on this code. But I switched the one setting in the ini file and will wait to see what happens tomorrow. Maybe it will bring the problem to light and if not I plan to dive in deep over the weekend.
Best...
Hi Rick,
What can I say, you were correct about setting the UnattendedComMode=On in the .ini file. The errors in the wcError.txt file now include a lot more information about the actual problem. So big thank you!
Now onto the problem at hand, I too was correct back in 2015 when I added a trap for an invalid loJSON object being passed back to the server. The error message reads that is is not an object. My trap that crashed the server was as follows:
IF NOT VARTYPE(loJson.pk) = 'N'
* problem
ENDIF
But I now realize, if the error reads that loJSON is not an object, then I need to first test to validate that it is indeed an object and then test to confirm the PK property is indeed a number. So I am assuming the fix will be as follows:
IF NOT VARTYPE(loJson) = 'O'
ERROR 'loJSON not an object, CB88'
RETURN .F.
ELSE
IF NOT VARTYPE(loJson.pk) = 'N'
ERROR 'loJSON.PK not a Number, CB88'
RETURN .F.
ENDIF
ENDIF
Now my only question before I install these modifications, will error handling in WC respond to the client with the error message in HTML or do I need to handle the error in my JavaScript on the client?
BTW - the app ran today without crashing and only one entry in the wcError log of the user attempting to do whatever is causing the problem to occur. So as far as I am concerned at this point, problem solved and fixing the code will only improve removing the problem.
Again thank you for your time.
If you don't know if the entire type chain exists use TYPE() instead of VARTYPE(). VARTYPE() requires that the entire instance exists and if it doesn't it errors. TYPE() basically evals the string you provide and if it fails returns 'U' for undefined.
Use VARTYPE() to check for known to exist types. Use TYPE() if you also need to check for existence of any part of an expression. VARTYPE() is faster since it doesn't have to eval, but is much more prone to failure.
+++ Rick ---
It has been two weeks since I switched the value in the INI file and during the two cycles of the application it has preformed perfectly both times. I have not yet had the time to recompile the source with the bug traps (TYPE instead of VARTYPE and aborting the request with an error message) but happy at this point that I'm not having problems. The error log shows that an error each week occurred but since it is not locking the application and forcing a restart, I have time before I can make the modifications.
Thank you for pointing me in this direction, I have no idea why I never set this option in the past.
Best,...
Not at all clear why I occasionally have problems with the server crashing while under normal load. But looking back at this conversation, I confirmed the .ini settings and looked at the VFP code and it all looks ok to me. But I really did not understand at the time what you were saying about the difference between VARTYPE() and TYPE(). So my code reads as follows:
IF VARTYPE(loJson) # 'O' OR VARTYPE(loJson.pk) # 'N'
ERROR('my message to user')
RETURN .F.
ENDIF
Does this seem correct to you or is there a better way to rewrite the same basic function with TYPE() ?
IF TYPE(loJson) # 'O' OR TYPE(loJson.pk) # 'N'
ERROR('my message to user')
RETURN .F.
ENDIF
One new consideration that I just realized, the VARTYPE(loJson) # 'N' actually exists in several other places in my wwProcess class. So I want to fix all of the others and hopefully I have stumbled onto the issue.
First off make sure your server is not crashing on errors like this - it should report an error but not be crashing.
Make sure you:
- Don't run in Debug Mode
- In COM make sure the
UnattendedComMode=Onis set inyourApp.ini
The latter won't stop on errors - this should pretty much always be set to On except in rare debugging scenarios where you're running interactively and you might want to see the crash.
Re:
TYPE()vsVARTYPE()
These two commands do different things:
- Both check for a type existance and return the type
TYPE()evaluates a string to see if a type exists in the current execution scopeVARTYPE()checks if a value exists and then returns the type for it
Bottom line is that you can use TYPE() safely and it will not blow up - it'll return U for undefined if
? TYPE("BOGUS.x") && U when none of this exists
loObj = CREATEOBJECT("EMPTY")
? TYPE("loObj") && O
? TYPE("loObj.Bogus") && U
Note it doesn't blow up if a value or value in the object chain doesn't exist - it just returns U. This is because the value is evaluated for a result. If the result doesn't Eval because of an error - it returns U, otherwise the type is returned. In short - TYPE() never throws an exception.
VARTYPE() doesn't work on strings but values, so the values passed into it have to exist. The rules are a bit loose with this function because first level values (like an object or a single variable) is allowed to not exist and returns U but hierarchical object tree where the base is missing blows up.
*** Bogus doesn't exist
? VARTYPE(Bogus) && U works!
? VARTYPE(BOGUS.x) && blows up
loObj = CREATEOBJECT("EMPTY")
? VARTYPE(loObj) && O
? VARTYPE(loObj.Bogus) && U
? VARTYPE(loObj.Bogus.cName) && blows up!
ADDPROPERTY(loObj,"firstname","Rick")
? VARTYPE(loObj.FirstName) && "C"
VARTYPE() basically uses the value passed to it based on the root object or top level variable that's passed to it. As long as that object is valid, then the value's type or U is returned. If part of an object chain can't evaluate however, then VARTYPE() throws an exception.
Because VARTYPE technically doesn't evaluate the value but directly tries to access it, it's a much faster than TYPE() by comparison. That said while the difference maybe 10x or so, even TYPE() is so fast that it doesn't really matter especially with modern CPUs.
I tend to always use VARTYPE() unless I know I can't rely on the object structure being intact. I use TYPE() in dynamic scenarios (which Web Connection uses a lot) where I might not know the structure of objects or where different kinds of objects might be expected.
An easy mistake to make with VARTYPE() is to by accident pass a string type name which yields an incorrect value of C:
*** This is never true!
if VARTYPE("loCustomer.oAddress") == "O"
+++ Rick ---