При добавлении или удалении задания в / из планировщика Quartz время от времени выдает исключение JobPersistenceException (следующее за предыдущим SQLiteException).
Вещи, которые кажутся заслуживающими внимания:
- Quartz.NET 2.01 + System.Data.SQLite 1.0.66 (
обе последние версии на момент написаниятолько что заметили, что есть бинарный пакет для SQLite 1.0.82) - исключение также выдается, если в настоящее время не выполняется ни одно задание / триггер (я наблюдаю за прослушивателями Quartz)
- задания добавляются вручную из контекста пользовательского интерфейса (мне нужно около 10-20 повторений, чтобы вызвать ошибку, но это кажется совершенно случайным)
Кажется, все работает нормально (несколько заданий, параллельное выполнение, постоянство после перезапуска приложения), пока я не касаюсь AddJob () / DeleteJob ()После расширенного тестирования я уверен, что это не связано с добавление / удаление вакансий. Проблемы с блокировкой / доступом к базе данных являются общей проблемой.
Есть ли какая-либо рекомендуемая процедура, которую я не знаю, которую необходимо соблюдать при добавлении / удалении заданий?
Что-то не так с моей конфигурацией ISchedulerFactory? (см. ниже)
Дополнительные
- Я пробовал использовать System.Data.SQLite 1.0.82, что только усугубило ситуацию. Я почти постоянно получаю сообщение «Ошибка SQLite (5): база данных заблокирована», как только Quartz выполняет задание.
- Quartz.NET перечисляет System.Data.SQLite 1.0.56 в качестве поддерживаемого поставщика баз данных, поэтому при использовании более новой версии можно ожидать проблем. Однако я не рассматриваю вариант возврата с 1.0.66, поскольку в IIRC было внесено множество улучшений / исправлений.
- Я взглянул на ствол разработки Quartz.NET между версией выпуска 2.0.1 (624) и текущей версией головы (669). Кажется, нет связанных исправлений.
- Я подозреваю, что это проблема System.Data.SQLite. Я наткнулся на несколько сообщений (о разных версиях SQLite), в которых упоминалось, что могут быть некоторые проблемы с внутренним удалением ресурсов, при этом файл БД заблокирован.
Дополнение 2
На данный момент я отказался от этого. Я много чего пробовал, но разработка должна продолжаться. Я переключился на другой тип базы данных (Firebird), которая до сих пор, кажется, отлично работает с Quartz.
Если у кого-то это сработает, я все равно хотел бы об этом услышать.
-
Детали исключения:
Quartz.JobPersistenceException: «Не удалось зафиксировать транзакцию ADO.NET. Файл базы данных заблокирован \ r \ ndatabase заблокирована»
Куча
bei Quartz.Impl.AdoJobStore.JobStoreSupport.CommitConnection(ConnectionAndTransactionHolder cth, Boolean openNewTransaction)
bei Quartz.Impl.AdoJobStore.JobStoreSupport.ExecuteInNonManagedTXLock(String lockName, Func`2 txCallback)
bei Quartz.Impl.AdoJobStore.JobStoreTX.ExecuteInLock(String lockName, Func`2 txCallback)
bei Quartz.Impl.AdoJobStore.JobStoreSupport.RemoveJob(JobKey jobKey)
bei Quartz.Core.QuartzScheduler.DeleteJob(JobKey jobKey)
bei Quartz.Impl.StdScheduler.DeleteJob(JobKey jobKey)
InnerException SQLiteException: «Файл базы данных заблокирован \ r \ ndatabase заблокирована»
Куча
bei System.Data.SQLite.SQLite3.Step(SQLiteStatement stmt)
bei System.Data.SQLite.SQLiteDataReader.NextResult()
bei System.Data.SQLite.SQLiteDataReader..ctor(SQLiteCommand cmd, CommandBehavior behave)
bei System.Data.SQLite.SQLiteCommand.ExecuteReader(CommandBehavior behavior)
bei System.Data.SQLite.SQLiteCommand.ExecuteNonQuery()
bei System.Data.SQLite.SQLiteTransaction.Commit()
bei Quartz.Impl.AdoJobStore.JobStoreSupport.CommitConnection(ConnectionAndTransactionHolder cth, Boolean openNewTransaction)
Источник исключения: cth.Transaction.Commit (); в этом методе Quartz.NET.
/// <summary>
/// Commit the supplied connection.
/// </summary>
/// <param name="cth">The CTH.</param>
/// <param name="openNewTransaction">if set to <c>true</c> opens a new transaction.</param>
/// <throws>JobPersistenceException thrown if a SQLException occurs when the </throws>
protected virtual void CommitConnection(ConnectionAndTransactionHolder cth, bool openNewTransaction)
{
CheckNotZombied(cth);
if (cth.Transaction != null)
{
try
{
IsolationLevel il = cth.Transaction.IsolationLevel;
cth.Transaction.Commit();
if (openNewTransaction)
{
// open new transaction to go with
cth.Transaction = cth.Connection.BeginTransaction(il);
}
}
catch (Exception e)
{
throw new JobPersistenceException("Couldn't commit ADO.NET transaction. " + e.Message, e);
}
}
}
Вот как я создаю ISchedulerFactory:
public static ISchedulerFactory CreateSQLiteSchedFactory(SQLiteConnection sqlConn, string tablePrefix) {
// db provider hinzufügen
var metaData = new DbMetadata();
metaData.AssemblyName = "System.Data.SQLite,Version=1.0.66.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139";
metaData.BindByName = true;
metaData.CommandBuilderType = typeof(SQLiteCommandBuilder);
metaData.CommandType = typeof(SQLiteCommand);
metaData.ConnectionType = typeof(SQLiteConnection);
metaData.ExceptionType = typeof(SQLiteException);
metaData.ParameterDbType = typeof(TypeAffinity);
metaData.ParameterDbTypePropertyName = "DbType";
metaData.ParameterNamePrefix = "@";
metaData.ParameterType = typeof(SQLiteParameter);
metaData.UseParameterNamePrefixInParameterCollection = true;
DbProvider.RegisterDbMetadata("SQLite-1066", metaData);
// konfiguration für factory erstellen
NameValueCollection properties = new NameValueCollection();
properties["quartz.scheduler.instanceName"] = "TestScheduler";
properties["quartz.scheduler.instanceId"] = "instance_one";
properties["quartz.threadPool.type"] = "Quartz.Simpl.SimpleThreadPool, Quartz";
properties["quartz.threadPool.threadCount"] = "5";
properties["quartz.threadPool.threadPriority"] = "Normal";
properties["quartz.jobStore.misfireThreshold"] = "60000";
properties["quartz.jobStore.type"] = "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz";
properties["quartz.jobStore.useProperties"] = "false";
properties["quartz.jobStore.dataSource"] = "default";
properties["quartz.jobStore.tablePrefix"] = tablePrefix;
properties["quartz.jobStore.clustered"] = "true";
properties["quartz.jobStore.lockHandler.type"] = "Quartz.Impl.AdoJobStore.UpdateLockRowSemaphore, Quartz";
properties["quartz.dataSource.default.connectionString"] = sqlConn.ConnectionString;
properties["quartz.dataSource.default.provider"] = "SQLite-1066";
// factory erzeugen
return new StdSchedulerFactory(properties);
}
SQLiteConnection создается со строкой подключения, подобной «Data Source = c: \ mydb.db; Version = 3;» и все кварцевые таблицы инициализируются с помощью поставляемого скрипта SQL