MSAccess: Err 3146 ODBC Call Fail - Best Strategy for Handling? - ms-access

I have an app that requires local users to Sync back to the SQL server periodically (event based, including upon Close/Exit).
My users have occasional internet/VPN issues that throw the expected "3146" error.
Problem:
When ODBC error is thrown, my app LOSES its mind (global variables are lost, etc.) and the app becomes utterly unusable. There are many subsequent layers of error messages thrown to my users, occasionally requiring a Ctl-Break to interrupt (or task manager).
Question:
I have an err_handler in every module that provides a structured error message. I am able to trap err_number "3146" in the err_handler module, where I attempt an abrupt "Application.Quit" (to avoid the subsequent err messages). I still get a couple subsequent err messages before the application fully terminates.
Is there a better approach to more gracefully handling "3146" errors?
Looking for some good ideas.
Thanks!

If you are handling the error then there should not be a problem. How you should be handling the error is to not do Application.Quit, you should actually do something about the error. A failed connection is not a reason to blow up your app.
Instead, think about caching data locally so that when the connection can be made you can perform your sync again. When you discover your connection failed, stop trying to connect, abort the syncing process, and tell your users "Hey, we couldn't sync now. You might be having VPN issues. Fix those and try to sync again." And all the while your data are still stored in your accdb so that if they go into work the next day and are hardwired into the network they can then sync successfully.

Related

Err Handling - Server Connection Lost

I'm looking for a little guidance. I have an Access App that connects to a server to run SQL queries. I can test the connection (ping) prior to running any queries, but I am not able to gracefully handle when connection is lost mid-stream (which seems to happen too frequently).
I have err_Handing in-place, but I seem to get a nested collection of error messages, including:
3146 - ODBC Error
3151 - ODBC Connection Error
3704 - Object is Closed
2046 - Quit not available
>> Requires Ctl-Break, or Task Mgr to break loose...
I do account for long-duration queries with: db.QueryTimeout = 0; I don't think these are my my issue at this point.
To begin to address my issues, I recently converted over from Global Variables to TempVars, so my app no longer loses its mind with these handled and un-handled errors. Seems now I have marginally more control, but still my Access App gets hung in Err Msg hell.
My desired response to a LOST connection:
Trap the error condition
Message to the user announcing the situation
Write to log fie to capture current state
Graceful exit from Access
Any suggestions or pointers to begin to address this need?
Thank you!
You can freely use global variables (VBA) and they NEVER lose their values if you use a compiled accDE. So the conversion of such variables to tempVars really is not required. Since an accDE (compiled application) never loses VBA values (even without error handling), then the result is a far more robust and reliable application. And a compiled accDE also prevents the VBA code from becoming un-compiled, and also locks down the design of the application from being tampered with.
As for losing a connection, there is really not a viable solution at this point in time. At application start up you can check for a connection and gracefully exit by using this approach:
ACC2000: How to Trap ODBC Logon Error Messages
http://support.microsoft.com/kb/210319
However, during a session with bound forms and a loss of the network connection cannot really be trapped or dealt with in a graceful manor – the only real solution is to address the loss of connection and prevent it in the first place.

MS Access cancel execution of pass-thru query keyboard shortcut

When using SQL pass-thru queries in MS Access, there is a default time-out of 60 seconds, at which point an instruction is sent to the remote server to cancel the request. Is there anyway to send this command from the keyboard similar to Access' own "Ctrl + Break" operation?
Firstly, understanding how Control-C cancels execution. They probably trap that key sequence, and do something special. I strongly suspect that oracle's client apps (SQL*Plus et al) are calling OCIBreak() behind the scenes, and passing in the handle to the server that they obtained when they executed the query with a previous OCI call.
I also suspect that Access isn't doing anything actively after 60 seconds; that's just the timeout it requests at time of execution query. Even more so, I'm beginning to wonder if Access is even requesting that timeout; everything I've read says that the ODBC driver does not support a query timeout, which makes me think it's just a client-side timeout, but I digress...
So - back to this OCIBreak() call. Here's the bad news: I don't think ODBC implements these calls. To be 100% sure, you'd have to take a look at the ODBC driver for oracle sources, but everything I've read indicates that the API call is not exposed.
For reference, I've been googling with these search terms in combination with "OBDC":
ORA-01013 (error when a user cancelled an operation, or when an operation times out)
OCIBreak (OCI function which cancels a pending operation)
--- EDIT #1 ---
As a side note, I really believe that Access is just giving up, and not sending any type of cancel command when the Pass-Through timeout is exceeded. If you take a look at this kb article, the ODBC Driver doesn't even support a query timeout:
PRB: Connection Timeout and Query Timeout Not Supported with Microsoft Oracle ODBC Driver and OLE DB Provider
After the elapsed time, Access probably just stops listening for results. If you were to ask oracle for a list of queries that are still executing, I strongly suspect you'd still see yours listed.
--- EDIT #2 ---
As far as implementing your own "cancel" -- which isn't really a cancel, more of a "keep the UI responsive regardless of the state of a query" -- the keyword here is going be asynchronous. You're going to want to rewrite your code to execute asynchronously so that it isn't blocking the message pump for your UI. I'd start googling for "async query access" and see what pops up. One SO result came up:
Running asynchronous query in MS Access
as well as a decent starting point at xtremevbtalk.com:
http://www.xtremevbtalk.com/showthread.php?t=82631
In effect, instead of firing off code that blocks execution until either a timeout occurs or a result set is returned, you'll be asking access to kick off the code behind the scenes. You'll then set up an event that fires when something further happens, such as letting the user know that the timeout occurred (timeout failure), populating a grid with results (success), etc...)

MySQL TAdoConnnection connection "connected" property incorrectly set True

I have an application which connects to a MySql database using Delphi's TAdoConnection object. This is a very query intensive application. So I create the connection and keep it open to avoid the high resource expense of open/closing database connections. But obviously problems can arise (database restart, network connection failure, etc). So I have built in code to free my database object, recreate it, and reconnect when queries fail.
I have a common function to connect to the database. The relevant code is this:
try
AdoConnection.open;
result := Adoconnection.Connected
except
result := False;
......
end;
I ran some test by turning on and off the MySql database. Everything works fine if the database is off on application startup (i.e. it properly throws an exception). However, if I turn off the database after the application has already successfully connected, subsequent re-connections do not throw exceptions, and additionally falsley report true for AdoConnection.Connected. I am sure the connection object had been freed/recreated first.
It seems there is some sort of caching mechanism going on here (most likely at the hardware/driver level - not application level). Anyone have any ideas?
I observed this also.
Ideas for dealing with it:
If you get an error on running a query then check if it's a connection issue and if so try to reconnect and then run the query again.
If your program uses a timer and waits a while between running batches of queries then perform a simple query (maybe "select now()") before each batch and if this fails then try to reconnect.

In a client-server relationship, should the server always rethrow the exception to the client?

I have a set of web services (the server), and an app which consumes this (client). In this sort of relationship, should the server always throw exceptions (ie in the throw block, rethrow the caught exception), and the client catch this. Exceptions which the server can handle, it will deal with and not rethrow, but everything else will be thrown to the calling layer for further action (the consuming app can raise a msg box or whatever).
Is this a good example of an exception that can be dealt with: A file cannot be written because the directory requires special privileges, so if this raises an exception, the file is written somewhere which does not require admin rights.
Thanks
There are more than one type of error.
For errors that the client can correct and retry, give them instructions on what to correct.
For other errors, where retrying will make no difference, such as an unauthorized action, let the user know why they can't perform the action, and if there's anything they can do to change the matter.
As you suggested, if the client issues a request to update the record, and something on the server, out of the control of the client, occurs, but the server can recover, then don't notify the client. If you need to know, then have the server notify you.
If the error occurs on the server, but the server isn't able to recover, you definitely need to notify the client of the failure and to either notify you, or try again later. Again, the system should notify you.
That particular error sounds like a configuration problem on the server, so the client doesn't have any means of action and shouldn't be given that information.
I generally cover that kind of error under a generic error message ("System error, please contact your system administrator") and log the error on the server (for later inspection).

Fatal errors in live servers

I'm writing some client/server software and I'm facing the following design issue. Normally, I use a VERIFY macro very liberally - if something is wrong in an user's machine, I want the software to fail and log the error so it can be fixed. I was never a fan of ignoring any kind of errors.
However, I'm now writing a server. If the server dies, many clients go down, so the server should die as little as possible. Therefore, I don't know how to treat some conditions that I'd treat as fatal exceptions otherwise.
For example, I get a network packet from an user who isn't logged in. Even though it shouldn't happen, I have enough experience to know "impossible" errors do happen from time to time. So I'm pretty sure if I do a fatal error on these cases, the server WILL crash eventually. On the other hand, I could log and ignore the error and continue, but I'm afraid some bugs may go undetected this way.
What would you do in a situation like this one?
If you can recover from the error, than obviously it wasn't fatal. I can't see the benefit of failing if you can log the error and continue execution - the most important thing is that you've captured the error on log. If you can recover and continue to operate as normal, than that is the best course.
You should implement in addition a notification system (server monitoring) that depending on the error level would notify you in varying degrees of urgency so you'd pick up as soon as possible on something time critical. There are generic system like that for servers, such as Nagios and Munin. You should have look at what they do and see if you can take something from them and implement / integrate it into your system.
Regardless, you should try to make sure client instances are as sandboxed as possible. A client thread going down shouldn't take down the entire server - ever (at least in theory).