Adds the new timeout method and a new property to store the value:
public DbConnectorCommand Timeout(TimeSpan timeSpan);
public int? TimeoutLength;
Should I save the value in that property as a TimeSpan instead of an int? Testing the size of one TimeSpan instance made it look like they use more memory than an int.
I also added an overload method that takes an int instead of a TimeSpan, since it sounded like it'd be a handy alternative in case someone wants to input the value that the CommandTimeout will take. I can take it back out, though.
public DbConnectorCommand Timeout(int timeout);
Come to think of it, should I call the methods SetTimeout and the property Timeout? So the method name contains a verb?
Test-wise, I added some unit tests for the timeout values, but I could not for the life of me figure out a way to make a SQLite command actually time out. I tried writing a really slow recursive statement, but that didn't work—apparently SQLite only times out when waiting to obtain a lock on the table. Then I tried executing a second command while the slow recursive command is running...
[Test]
public async Task TimeoutAsyncTests()
{
using var connector = CreateConnector();
connector.Command("create table Sequence(Number integer not null);").Execute();
string slowSqlStatement =
@"with recursive count(n) as (select 1 union all select n+1 from count limit 50000000)
insert into Sequence(Number) select n from count;";
var slowStatementRun = Task.Run(() => connector.Command(slowSqlStatement).ExecuteAsync());
Invoking(() => connector.Command("drop table Sequence;").Timeout(TimeSpan.FromSeconds(1)).Execute()).Should().Throw<SQLiteException>();
await slowStatementRun;
}
...but the second command still waited patiently without throwing an exception. Then I tried creating a read-only database file using System.Security.AccessControl...
private static DbConnector CreateReadOnlyConnector()
{
string fileName = Path.GetTempFileName();
var connector = DbConnector.Create(new SQLiteConnection(fileName), new DbConnectorSettings { ProviderMethods = new SqliteProviderMethods(), AutoOpen = true, LazyOpen = true });
var dbFile = new FileInfo(fileName);
FileSecurity dbFileSecurity = FileSystemAclExtensions.GetAccessControl(dbFile);
dbFileSecurity.AddAccessRule(new FileSystemAccessRule(Environment.UserName, FileSystemRights.WriteData, AccessControlType.Deny));
FileSystemAclExtensions.SetAccessControl(dbFile, dbFileSecurity);
return connector;
}
[Test]
public void TimeoutTests()
{
...
var readOnlyConnector = CreateReadOnlyConnector();
Invoking(() => readOnlyConnector.Command("create table Items (ItemId integer primary key, Name text not null)").Timeout(TimeSpan.FromSeconds(1)).Execute()).Should().Throw<SQLiteException>();
}
...but then the test informed me that Access Control List APIs are a Windows-only feature, and I'm on Mac. Then I tried blocking a write command by first opening a read-only FileStream on a database file...
[Test]
public void TimeoutTests()
{
...
string dbFileName = Path.GetTempFileName();
var readOnlyConnector = CreateConnector($"Data Source={dbFileName}");
readOnlyConnector.Command("create table Items (ItemId integer primary key, Name text not null);").Execute();
using (File.OpenRead(dbFileName))
{
Invoking(() => readOnlyConnector.Command("insert into Items (Name) values ('one');").Timeout(TimeSpan.FromSeconds(1)).Execute()).Should().Throw<SQLiteException>();
}
}
...but the command simply waited forever without timing out.
So I'm going to stop banging my head against the wall. Any ideas on how to force a SQLite timeout?
Resolves #6.
Adds the new timeout method and a new property to store the value:
Should I save the value in that property as a
TimeSpan
instead of anint
? Testing the size of oneTimeSpan
instance made it look like they use more memory than anint
.I also added an overload method that takes an
int
instead of aTimeSpan
, since it sounded like it'd be a handy alternative in case someone wants to input the value that theCommandTimeout
will take. I can take it back out, though.Come to think of it, should I call the methods
SetTimeout
and the propertyTimeout
? So the method name contains a verb?Test-wise, I added some unit tests for the timeout values, but I could not for the life of me figure out a way to make a SQLite command actually time out. I tried writing a really slow recursive statement, but that didn't work—apparently SQLite only times out when waiting to obtain a lock on the table. Then I tried executing a second command while the slow recursive command is running...
...but the second command still waited patiently without throwing an exception. Then I tried creating a read-only database file using
System.Security.AccessControl
......but then the test informed me that Access Control List APIs are a Windows-only feature, and I'm on Mac. Then I tried blocking a write command by first opening a read-only
FileStream
on a database file......but the command simply waited forever without timing out.
So I'm going to stop banging my head against the wall. Any ideas on how to force a SQLite timeout?