We're writing a routine which either returns an editable object, or a status object that says the underlying record is locked.
We're using C# and .NET Framework 4.8 with the Progress OpenEdge ODBC driver against an OpenEdge database. The record might be locked by legacy ABL code, which is why we want to check with a ReadCommitted transaction to see if it's safe for us to start editing it.
Functionally, the code works fine, doing exactly what we expect it to do. When the underlying record is not locked, it returns the object in a matter of milliseconds; when it's locked, it returns an object that describes the locked status of the record.
But when the underlying record is indeed locked it takes upwards of 15 seconds to return with the expected "ERROR [HY000] [DataDirect][ODBC Progress OpenEdge Wire Protocol driver][OPENEDGE]Failure getting record lock on a record from table PUB.i-mst."
I have tried decreasing the CommandTimeout value, but that only (eventually, as I decrease it incrementally) ultimately changes the failure to a timeout error.
Is there some lower-level setting to control how long either ODBC or OpenEdge takes to wait for a lock to be released before failing?
Here's the code:
public static dynamic ReadOdbcForEdit(OdbcConnection connection, string type, string criteria, string domain,
string parentClass, string application)
{
connection.Open();
var transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted);
Type objectType = Object.GetJohnstonType(parentClass + type, domain, application);
var tempObj = Activator.CreateInstance(objectType);
try
{
var odbcCommand = new OdbcCommand(criteria)
{
Connection = connection,
Transaction = transaction,
CommandTimeout = 30
};
var reader = odbcCommand.ExecuteReader();
while (reader.Read())
{
foreach (var property in tempObj.GetType().GetProperties())
{
var propertyType = property.PropertyType;
var propertyName = property.Name;
if (propertyType.IsArray ||
propertyType.IsGenericType &&
propertyType.GetGenericTypeDefinition() == typeof(List<>))
{
continue;
}
try
{
if (reader[propertyName].GetType() != typeof(DBNull))
{
property.SetValue(tempObj, reader[propertyName]);
}
}
catch (Exception e)
{
Logging.Message($"Could not fill {propertyName} from database column");
Logging.Exception(e);
}
}
}
return tempObj;
}
catch (Exception e)
{
var openRecordStatus = new OpenRecordStatus
{
StatusCode = e.HResult,
StatusMessage = e.Message
};
return openRecordStatus;
}
}
You probably want to adjust -SQLLockWaitTimeout
https://knowledgebase.progress.com/articles/Article/What-is-the-SQLLockWaitTimeout-Parameter
The -SQLLockWaitTimeout parameter is used to identify the number of seconds to wait when a lock conflict occurs. The default is 5 seconds.
This value applies to all lock conflicts experienced by SQL applications. So an installation that gets a lot of lock conflicts (does a lot of updating) would want to consider the impact of changing this parameter.
For older versions of Progress (prior to 11.4): https://knowledgebase.progress.com/articles/Article/P123923
The PROSQL_LOCKWAIT_TIMEOUT environment variable was introduced in 9.1D06 and is used to limit how long a client will wait for a record that has a share or exclusive lock against it. This setting does not effect and is not needed for an SQL Client with an isolation level of READ UNCOMMITTED, because it will read a record that has a share or exclusive lock against it.
The PROSQL_LOCKWAIT_TIMEOUT environment variable enables one to determine how long SQL clients will wait in a lock queue for a particular record. The environment variable must be present before a broker is started and is applied to every SQL connection of the broker.
The minimum time-out value is the default of five seconds (DFLT_LOCKWAIT-TIMEOUT). The maximum time-out value is limited to a 32-bit integer value of 4,294,967,295 seconds or 1,193,046.5 hours.
This environment variable can be set prior to starting the database broker or AdminServer. For example, to set it to 30 seconds:
UNIX: PROSQL_LOCKWAIT_TIMEOUT=30 ; export PROSQL_LOCKWAIT_TIMEOUT
Windows: Control Panel -> System -> Advanced tab -> Environment Variables -> System Variables. Add a new variable.
In OpenEdge 11.4 and later there is a -SQLLockWaitTimeout startup parameter that can be used to accomplish the same goal as the environment variable. See article: 000064602, What is the -SQLLockWaitTimeout Parameter? for additional information.